commit 5a7810a26c913cc58cc897e93e2de2f64b8d6a01
parent f86b3b6be24d5cfedceb4fb18bf379235a95ee73
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 30 Apr 2026 06:29:38 -0700
Add tcc-cc test suite
Diffstat:
6 files changed, 123 insertions(+), 17 deletions(-)
diff --git a/Makefile b/Makefile
@@ -24,6 +24,7 @@
# make test SUITE=m1pp m1pp suite, every arch
# make test SUITE=p1 ARCH=amd64 p1 suite, one arch
# make test SUITE=scheme1 scheme1 .scm fixtures, every arch
+# make test SUITE=tcc-cc tcc-boot2 compiles tests/cc, aarch64
# make image build the per-arch container image
# make tools bootstrap M0/hex2-0/catm for ARCH
# make tables regen pre-pruned P1/P1-<arch>.M1 tables
@@ -212,9 +213,10 @@ $(CC_BINS): build/%/cc/cc.scm: $(CC_SRCS) build/%/.image build/%/tools/M0
# the per-arch container against the flattened TU, then assembles the
# resulting P1pp into a runnable ELF using the standard P1pp pipeline.
# The resulting binary embeds tcc's $(TCC_TARGET) codegen, so match
-# $(ARCH) to it (amd64↔X86_64, riscv64↔RISCV64) if you want to run the
-# binary natively in the container. tcc.flat.c lives outside the per-
-# arch tree because it depends on TCC_TARGET, not the build arch.
+# $(ARCH) to it (aarch64↔ARM64, amd64↔X86_64, riscv64↔RISCV64) if you
+# want generated programs to run natively in the container. tcc.flat.c
+# lives outside the per-arch tree because it depends on TCC_TARGET, not
+# the build arch.
TCC_TARGET ?= X86_64
TCC_FLAT := build/tcc/$(TCC_TARGET)/tcc.flat.c
@@ -318,6 +320,21 @@ $(TCC_GCC_BIN): $(TCC_FLAT) build/$(TCC_GCC_ARCH)/vendor/mes-libc/libc.flat.c \
sh scripts/build-tcc-gcc.sh $@ $(TCC_FLAT) \
build/$(TCC_GCC_ARCH)/vendor/mes-libc/libc.flat.c
+# --- tcc-cc test harness support -----------------------------------------
+#
+# The ARM64-targeted tcc-boot2 can compile/link tests/cc directly, but
+# its ARM64 build does not accept .S inputs yet. Build the one tiny
+# _start object with the host cross assembler, then let tcc link it
+# with each test fixture under the tcc-cc suite.
+
+HOST_CC ?= cc
+TCC_CC_ARCH := aarch64
+TCC_CC_START := build/$(TCC_CC_ARCH)/tcc-cc/start.o
+
+$(TCC_CC_START): tcc-cc/$(TCC_CC_ARCH)/start.S
+ mkdir -p $(@D)
+ $(HOST_CC) -target aarch64-linux-gnu -c -o $@ -x assembler $<
+
# --- Native tools (opt-in dev-loop helpers) -------------------------------
NATIVE_TOOLS := build/native-tools/M1 build/native-tools/hex2 build/native-tools/m1pp
@@ -387,6 +404,10 @@ TEST_CC_LIBC_DEPS := $(TEST_CC_DEPS) \
$(foreach a,$(TEST_ARCHES),build/$(a)/vendor/mes-libc/libc.P1pp) \
P1/entry-libc.P1pp P1/elf-end.P1pp
+TEST_TCC_CC_DEPS := build/$(TCC_CC_ARCH)/.image \
+ build/$(TCC_CC_ARCH)/tcc-boot2/tcc-boot2 \
+ $(TCC_CC_START)
+
test:
ifeq ($(SUITE),)
@$(MAKE) --no-print-directory test SUITE=m1pp
@@ -416,6 +437,12 @@ else ifeq ($(SUITE),cc)
else ifeq ($(SUITE),cc-libc)
@$(MAKE) --no-print-directory $(TEST_CC_LIBC_DEPS)
sh scripts/run-tests.sh --suite=cc-libc $(if $(ARCH_FILTER),--arch=$(ARCH_FILTER)) $(NAMES)
+else ifeq ($(SUITE),tcc-cc)
+ @if [ -n "$(ARCH_FILTER)" ] && [ "$(ARCH_FILTER)" != "$(TCC_CC_ARCH)" ]; then \
+ echo "tcc-cc currently supports ARCH=$(TCC_CC_ARCH) only" >&2; exit 2; \
+ fi
+ @$(MAKE) --no-print-directory TCC_TARGET=ARM64 $(TEST_TCC_CC_DEPS)
+ sh scripts/run-tests.sh --suite=tcc-cc --arch=$(TCC_CC_ARCH) $(NAMES)
else
- @echo "unknown SUITE='$(SUITE)' (m1pp | p1 | scheme1 | cc-util | cc-lex | cc-pp | cc-cg | cc | cc-libc)" >&2; exit 2
+ @echo "unknown SUITE='$(SUITE)' (m1pp | p1 | scheme1 | cc-util | cc-lex | cc-pp | cc-cg | cc | cc-libc | tcc-cc)" >&2; exit 2
endif
diff --git a/docs/TCC-TODO.md b/docs/TCC-TODO.md
@@ -120,8 +120,8 @@ Harness target: `make tcc-boot2 ARCH=aarch64` (see Makefile +
cc.scm on the flattened TU inside the container, and feeds the P1pp
into the standard `boot-build-p1pp.sh` pipeline. `TCC_TARGET` selects
which tcc codegen target gets baked into the binary
-(default `X86_64`); pick `ARCH` to match if you want the result to
-run natively in the per-arch container.
+(default `X86_64`; use `ARM64` for aarch64); pick `ARCH` to match if
+you want generated programs to run natively in the per-arch container.
## Resolved — offsetof-style const expr in `options_W[]` (line 18026)
diff --git a/scripts/boot-run-tests.sh b/scripts/boot-run-tests.sh
@@ -14,7 +14,7 @@
## host preflights lint and passes the explicit kept list down.
##
## Env: ARCH=aarch64|amd64|riscv64
-## Usage: boot-run-tests.sh --suite=<m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc|cc-libc> [name ...]
+## Usage: boot-run-tests.sh --suite=<m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc|cc-libc|tcc-cc> [name ...]
set -eu
@@ -35,7 +35,7 @@ while [ "$#" -gt 0 ]; do
done
case "$SUITE" in
- m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc|cc-libc) ;;
+ m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc|cc-libc|tcc-cc) ;;
"") echo "$0: --suite required" >&2; exit 2 ;;
*) echo "$0: unknown suite '$SUITE'" >&2; exit 2 ;;
esac
@@ -501,6 +501,75 @@ run_cc_libc_suite() {
done
}
+## --- tcc-cc suite -------------------------------------------------------
+##
+## Runs the plain tests/cc fixtures through the tcc-boot2 binary. The
+## Makefile builds tcc-boot2 with TCC_TARGET=ARM64 and supplies a tiny
+## aarch64 _start object at build/aarch64/tcc-cc/start.o, so this suite
+## can link and execute non-libc C tests directly on the target arch.
+run_tcc_cc_suite() {
+ if [ "$ARCH" != "aarch64" ]; then
+ echo " FAIL [$ARCH] tcc-cc"
+ echo " tcc-cc currently supports ARCH=aarch64 only" >&2
+ return
+ fi
+
+ tcc=build/$ARCH/tcc-boot2/tcc-boot2
+ start=build/$ARCH/tcc-cc/start.o
+ if [ ! -x "$tcc" ]; then
+ echo " FAIL [$ARCH] tcc-cc"
+ echo " missing $tcc -- run 'make test SUITE=tcc-cc ARCH=$ARCH'" >&2
+ return
+ fi
+ if [ ! -e "$start" ]; then
+ echo " FAIL [$ARCH] tcc-cc"
+ echo " missing $start -- run 'make test SUITE=tcc-cc ARCH=$ARCH'" >&2
+ return
+ fi
+ if ! "$tcc" -version 2>/dev/null | grep 'AArch64' >/dev/null; then
+ echo " FAIL [$ARCH] tcc-cc"
+ echo " $tcc is not an AArch64-targeted tcc; rebuild with TCC_TARGET=ARM64" >&2
+ return
+ fi
+
+ [ -n "$NAMES" ] || NAMES=$(discover tests/cc c)
+ for name in $NAMES; do
+ src=tests/cc/$name.c
+ [ -e "$src" ] || { echo " SKIP $name (no .c)"; continue; }
+ if [ -e tests/cc/$name.expected ]; then
+ expout=$(cat tests/cc/$name.expected)
+ else
+ expout=
+ fi
+ if [ -e tests/cc/$name.expected-exit ]; then
+ expexit=$(cat tests/cc/$name.expected-exit)
+ else
+ expexit=0
+ fi
+
+ elf=build/$ARCH/tests/tcc-cc/$name
+ workdir=build/$ARCH/.work/tests/tcc-cc/$name
+ label="[$ARCH] tcc-cc/$name"
+ mkdir -p "$(dirname "$elf")" "$workdir"
+
+ tcc_log=$workdir/tcc.log
+ if ! "$tcc" -nostdlib "$start" "$src" -o "$elf" \
+ >"$tcc_log" 2>&1; then
+ fail "$label" "tcc compile/link failed:" "$tcc_log"
+ continue
+ fi
+
+ tmp=$(mktemp)
+ if "./$elf" >"$tmp" 2>&1; then
+ act_exit=0
+ else
+ act_exit=$?
+ fi
+ act_out=$(cat "$tmp"); rm -f "$tmp"
+ _cc_check "$label" "$expout" "$expexit" "$act_out" "$act_exit"
+ done
+}
+
case "$SUITE" in
m1pp) run_m1pp_suite ;;
p1) run_p1_suite ;;
@@ -511,4 +580,5 @@ case "$SUITE" in
cc-cg) run_cc_cg_suite ;;
cc) run_cc_suite ;;
cc-libc) run_cc_libc_suite ;;
+ tcc-cc) run_tcc_cc_suite ;;
esac
diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh
@@ -25,6 +25,7 @@
## cc-pp tests/cc-pp/<name>.c — pp pipeline byte-diff (+ .scm).
## cc-cg tests/cc-cg/<name>.scm — cg emit -> P1pp -> ELF -> run.
## cc tests/cc/<name>.c — cc -> P1pp -> ELF -> run.
+## tcc-cc tests/cc/<name>.c — tcc-boot2 -> ELF -> run.
##
## All three arches by default; --arch restricts to one.
##
@@ -50,8 +51,8 @@ while [ "$#" -gt 0 ]; do
done
case "$SUITE" in
- m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc|cc-libc) ;;
- "") echo "$0: --suite required (m1pp | p1 | scheme1 | cc-util | cc-lex | cc-pp | cc-cg | cc | cc-libc)" >&2; exit 2 ;;
+ m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc|cc-libc|tcc-cc) ;;
+ "") echo "$0: --suite required (m1pp | p1 | scheme1 | cc-util | cc-lex | cc-pp | cc-cg | cc | cc-libc | tcc-cc)" >&2; exit 2 ;;
*) echo "$0: unknown suite '$SUITE'" >&2; exit 2 ;;
esac
diff --git a/scripts/stage1-flatten.sh b/scripts/stage1-flatten.sh
@@ -17,7 +17,7 @@
## artifact downstream stages consume. No container needed.
##
## Usage:
-## scripts/stage1-flatten.sh [--arch <X86_64|I386|RISCV64>] [--verify]
+## scripts/stage1-flatten.sh [--arch <X86_64|I386|RISCV64|ARM64|AARCH64>] [--verify]
set -eu
@@ -34,10 +34,11 @@ while [ $# -gt 0 ]; do
done
case "$ARCH" in
- X86_64) MES_ARCH=x86_64; HAVE_LL=1 ;;
- I386) MES_ARCH=x86; HAVE_LL=0 ;;
- RISCV64) MES_ARCH=riscv64; HAVE_LL=1 ;;
- AARCH64) echo "AARCH64 not in live-bootstrap; tcc.c lacks an arm64-gen.c" >&2; exit 2 ;;
+ X86_64|x86_64) MES_ARCH=x86_64; HAVE_LL=1; TCC_TARGET_DEFINE=X86_64; CPP_ARCH=x86_64 ;;
+ I386|i386) MES_ARCH=x86; HAVE_LL=0; TCC_TARGET_DEFINE=I386; CPP_ARCH=x86 ;;
+ RISCV64|riscv64) MES_ARCH=riscv64; HAVE_LL=1; TCC_TARGET_DEFINE=RISCV64; CPP_ARCH=riscv64 ;;
+ ARM64|arm64|AARCH64|aarch64)
+ MES_ARCH=riscv64; HAVE_LL=1; TCC_TARGET_DEFINE=ARM64; CPP_ARCH=aarch64 ;;
*) echo "unknown ARCH: $ARCH" >&2; exit 2 ;;
esac
@@ -157,7 +158,7 @@ FLAT=$WORK/tcc.flat.c
-I "$MES_INCLUDE_LINUX" \
-I "$MES_INCLUDE" \
-D __linux__=1 \
- -D __${MES_ARCH}__=1 \
+ -D __${CPP_ARCH}__=1 \
-D BOOTSTRAP=1 \
-D HAVE_LONG_LONG=$HAVE_LL \
-D inline= \
@@ -173,7 +174,7 @@ FLAT=$WORK/tcc.flat.c
-D CONFIG_USE_LIBGCC=1 \
-D "TCC_VERSION=\"0.9.26\"" \
-D ONE_SOURCE=1 \
- -D TCC_TARGET_${ARCH}=1 \
+ -D TCC_TARGET_${TCC_TARGET_DEFINE}=1 \
"$SRC/tcc.c" > "$FLAT"
LINES=$(wc -l < "$FLAT")
diff --git a/tcc-cc/aarch64/start.S b/tcc-cc/aarch64/start.S
@@ -0,0 +1,7 @@
+.globl _start
+_start:
+ ldr x0, [sp]
+ add x1, sp, #8
+ bl main
+ mov x8, #93
+ svc #0