kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 7cdfa2286fcf7310e20656c9873ad62f59661971
parent 6ff61d3c14d3eacb93b08ccc06f0dc8261fd892f
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed,  3 Jun 2026 11:11:26 -0700

mk: move test mk files to mk/

Diffstat:
MMakefile | 2+-
Mdoc/BUILD.md | 2+-
Mdoc/TESTING.md | 10+++++-----
Mdoc/plan/JIT.md | 2+-
Amk/test.mk | 956+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/test_unit.mk | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dtest/lib/unit.mk | 80-------------------------------------------------------------------------------
Dtest/test.mk | 954-------------------------------------------------------------------------------
8 files changed, 1046 insertions(+), 1042 deletions(-)

diff --git a/Makefile b/Makefile @@ -196,4 +196,4 @@ include mk/maint.mk -include $(LIB_DEPS) -include $(DRIVER_DEPS) -include test/test.mk +include mk/test.mk diff --git a/doc/BUILD.md b/doc/BUILD.md @@ -211,7 +211,7 @@ honest. The check is part of the default `make test` set. ## Tests -Tests are a large family of `make test-*` targets defined in `test/test.mk`, +Tests are a large family of `make test-*` targets defined in `mk/test.mk`, grouped roughly as: ``` diff --git a/doc/TESTING.md b/doc/TESTING.md @@ -14,7 +14,7 @@ exercise see [ASM.md](ASM.md) (assembler/disassembler/`cc -S`), The test tree lives under `test/`, with per-area subdirectories (`test/asm/`, `test/toy/`, `test/smoke/`, `test/libc/`, plus unit-test areas like `test/arch/`, `test/elf/`, `test/opt/`). Build/run wiring lives in -`test/test.mk`. Every harness conforms to **one of four canonical test types**, +`mk/test.mk`. Every harness conforms to **one of four canonical test types**, each backed by a shared library under `test/lib/`, so test infrastructure is written once and reused rather than re-invented per area. @@ -22,7 +22,7 @@ written once and reused rather than re-invented per area. | Type | Library | What it is | |------|---------|------------| -| **U** unit | `kit_unit.h` (+ `unit.mk` build manifest) | a C translation unit linked against libkit, self-checking in-process | +| **U** unit | `kit_unit.h` (+ `test_unit.mk` build manifest) | a C translation unit linked against libkit, self-checking in-process | | **C** corpus | `cf_corpus.sh` | a directory of case files run through one or more *lanes*, each with its own oracle | | **K** scripted | `kit_sh_kit.sh` | a hand-written shell test driving the `kit` binary, judged by golden transcript (mode G) or procedural asserts (mode P) | | **D** differential | `cf_differential.sh` | correctness defined as *agreement* — vs a checked-in baseline, or vs a reference tool | @@ -327,7 +327,7 @@ harnesses on `kit_sh_kit.sh`, in two oracle modes: ## Unit tests (Type U) Lower-level invariants are covered by C unit-test binaries built from -`test/lib/unit.mk` and linking `test/lib/kit_unit.h`. There are two link +`mk/test_unit.mk` and linking `test/lib/kit_unit.h`. There are two link flavors that differ only in what they can reach: `UNIT_TESTS_AR` link the public archive (internal symbols hidden — exercises the public surface), `UNIT_TESTS_OBJS` link the raw objects (internal hidden symbols reachable). These cover ISA @@ -398,14 +398,14 @@ and to triage O1 codegen without `-g`, which perturbs object layout. ## Aggregation and conventions -`test/test.mk` defines the targets. A default `test` aggregate runs the +`mk/test.mk` defines the targets. A default `test` aggregate runs the host-independent lanes (frontend corpora, unit tests, L0/L1 round-trip, the `cf_corpus` engine selftest, the libc-dep guard); the exec-dependent and second-oracle lanes (L2 exec, symmetry, diff-llvm, hostas-toy/cross, smoke, libc conformance) are opt-in so the default run stays host-independent and fast. Bootstrap is *not* part of this test system: it is a separate top-level target (`make bootstrap`, `make bootstrap-debug/release`) driven from the top-level -`Makefile`, not from `test/test.mk` — see the bootstrap section above. +`Makefile`, not from `mk/test.mk` — see the bootstrap section above. Conventions shared across all four test types: - **One report layer.** Every shell harness records through `cf_pass`/`cf_fail`/ diff --git a/doc/plan/JIT.md b/doc/plan/JIT.md @@ -233,7 +233,7 @@ trampoline. - [ ] Regression harness: a scripted `test/run/` suite diffing exit codes and stdout across `.c`, stdin, `.o`, `.a`, multi-file, and `-e` entry cases, plus a `--no-jit` interpreter-vs-JIT cross-check. No - coverage today; wire a `test-run` target into `test/test.mk`. + coverage today; wire a `test-run` target into `mk/test.mk`. ### 3.2 Inspector / debugger surface diff --git a/mk/test.mk b/mk/test.mk @@ -0,0 +1,956 @@ +# Data-driven tests. Included from the top-level Makefile. +# +# - test-driver: aggregate alias that runs all test-driver-* targets. +# - test-driver-cc: narrow CLI behavior checks that do not belong to a specific +# frontend/linker corpus. Depends on the kit driver binary. +# - test-pp: aggregate alias that runs test-pp-ok and test-pp-err. +# - test-pp-ok: C preprocessor success cases; depends on the kit driver binary. +# - test-elf: ELF roundtrip harness in test/elf/; depends only on +# libkit.a and compiles its own test binaries against it. Skipped +# layers are reported (set KIT_TEST_ALLOW_SKIP=1 to allow skips). +# - test-ar: in-process ar reader/writer tests; depends only on +# libkit.a. Set KIT_AR_TEST_HOST=1 to also dump produced bytes +# to /tmp and run the host's `ar t` / `nm --print-armap` as a +# cross-check. +# - test-driver-ar: scenario-driven CLI harness for `kit ar`. Each +# case under test/ar/cases/ runs a small script and diffs stdout. +# Depends on the kit driver binary. +# - test-link: linker + JIT behavioral harness in test/link/; three paths +# per case (roundtrip R, ELF exec E, JIT J). Depends only on libkit.a. +# Set KIT_TEST_ALLOW_SKIP=1 to allow skipped layers. +# - test-macho: Mach-O variant of test-link; defaults to roundtrip+JIT +# paths because hosted Mach-O executable execution is target/SDK-specific. +# - test-parse: aggregate alias that runs test-parse-ok and test-parse-err. +# - test-parse-ok: file-driven C parser success harness in test/parse/; each +# case is a .c source file. Built against the public kit.h surface; +# reuses kit-roundtrip, link-exe-runner, and jit-runner. +# - test-asm: aggregate alias over the per-arch asm lanes test-asm-aa64, +# test-asm-x64 and test-asm-rv64. The file-driven assembler/disassembler +# harness in test/asm/ runs one arch per invocation (KIT_TEST_ARCH) over +# three sub-corpora (encode/, decode/, listing/), one mode per sub-dir. +# aa64 runs the full path set (incl. native exec on aa64 hosts); x64/rv64 +# run the host-independent encode/decode/listing lanes only. See doc/ASM.md. + +TEST_TARGETS = \ + test-cf-corpus-selftest \ + test-aa64-inline \ + test-abi-classify \ + test-ar \ + test-asm \ + test-asm-aa64 \ + test-asm-x64 \ + test-asm-rv64 \ + test-disasm-complete \ + test-asm-roundtrip \ + test-asm-roundtrip-exec \ + test-asm-symmetry \ + test-asm-roundtrip-toy \ + test-hostas-toy \ + test-hostas-cross \ + test-diff-llvm \ + test-bootstrap-toy \ + test-bootstrap-toy-debug \ + test-bootstrap-toy-release \ + test-bounce \ + test-cbackend \ + test-cg-api \ + test-coff \ + test-coff-mingw-import \ + test-coff-windows-ucrt \ + test-debug \ + test-dbg \ + test-driver \ + test-driver-ar \ + test-driver-cas \ + test-driver-cc \ + test-driver-compile \ + test-driver-objcopy \ + test-driver-objdump \ + test-driver-pkg \ + test-driver-strings \ + test-driver-tools \ + test-hash \ + test-driver-strip \ + test-dwarf \ + test-elf \ + test-emu \ + test-emu-unit \ + test-interp \ + test-interp-emu \ + test-interp-toy \ + test-ir-recorder \ + test-isa \ + test-lib-deps \ + test-libc \ + test-libc-glibc \ + test-libc-glibc-rv64 \ + test-libc-musl \ + test-libc-musl-rv64 \ + test-link \ + test-link-reloc-uleb128 \ + test-macho \ + test-native-direct-target \ + test-opt \ + test-parse \ + test-parse-err \ + test-parse-ok \ + test-parse-rv64-wide \ + test-pp \ + test-pp-err \ + test-pp-ok \ + test-rt-headers \ + test-rt-runtime \ + test-link-x64 \ + test-rv64-inline \ + test-rv64-jit \ + test-rv64-tls-link \ + test-smoke-rv64 \ + test-smoke-x64 \ + test-toy \ + test-wasm \ + test-wasm-c \ + test-wasm-front \ + test-wasm-target \ + test-wasm-toy \ + test-x64-dbg \ + test-x64-inline + +DEFAULT_TEST_TARGETS = \ + test-cf-corpus-selftest \ + test-driver \ + test-pp \ + test-elf \ + test-coff \ + test-ar \ + test-link \ + test-toy \ + test-dwarf \ + test-debug \ + test-parse \ + test-asm \ + test-asm-roundtrip \ + test-isa \ + test-aa64-inline \ + test-rv64-inline \ + test-rv64-jit \ + test-rv64-tls-link \ + test-emu \ + test-emu-unit \ + test-interp \ + test-interp-emu \ + test-x64-inline \ + test-x64-dbg \ + test-rt-headers \ + test-lib-deps \ + test-cg-api \ + test-abi-classify \ + test-ir-recorder \ + test-native-direct-target \ + test-opt \ + test-asm-symmetry \ + test-link-reloc-uleb128 \ + test-dbg \ + test-disasm-complete \ + test-macho \ + test-interp-toy \ + test-wasm \ + test-libc \ + test-link-x64 \ + test-rt-runtime \ + test-bounce \ + bootstrap \ + test-bootstrap-toy + +.PHONY: test $(TEST_TARGETS) + +test: $(DEFAULT_TEST_TARGETS) + +# Unit-test binary build rules: two regimes (public vs internal interface). +include mk/test_unit.mk + +# Provision the pinned per-arch container rootfs images that exec_target.sh runs +# kit-emitted binaries inside. This is the ONLY test step that touches the +# network: the cross-arch exec harnesses (toy/parse/link ... path X/E/L) run with +# `podman run --pull=never`, so without these images those paths SKIP. Run once, +# and again only after the pin in test/lib/test_images.sh changes. FORCE=1 +# re-pulls. The images are pinned per-arch by content digest, so they coexist in +# local storage and can never clobber one another. +.PHONY: test-images +test-images: + @bash test/lib/pull_test_images.sh + +# Hermetic self-test of the shared corpus harness engine (test/lib/kit_corpus.sh): +# asserts serial==parallel determinism, SKIP-NA, and the parallel-safety +# invariant (exec_target queued only on the parent, never in a worker). No +# kit binary / podman / qemu needed. +test-cf-corpus-selftest: + @bash test/lib/kit_corpus_selftest.sh + +test-driver: test-driver-cc test-driver-compile test-driver-ar test-driver-cas test-driver-strip test-driver-objcopy test-driver-objdump test-driver-pkg test-driver-strings test-driver-tools + +test-driver-cc: bin + @KIT=$(abspath $(BIN)) sh test/driver/run.sh + +test-driver-compile: bin + @KIT=$(abspath $(BIN)) sh test/compile/run.sh + +# test-cbackend: --emit=c C-source backend, driven through three +# frontends — parse-runner (C), toy-runner (toy), wasm-runner (wat/wasm). +# Each invokes its existing runner with paths=C so a single corpus per +# frontend exercises both the existing backends and the C backend. +# Together they prove the CGTarget seam is frontend-agnostic. +# Unimplemented CGTarget methods report as SKIP; see doc/CBACKEND.md. +KIT_CBACKEND_TEST_JOBS ?= $(if $(KIT_TEST_JOBS),$(KIT_TEST_JOBS),1) +test-cbackend: bin + @KIT_TEST_JOBS=$(KIT_CBACKEND_TEST_JOBS) KIT_TEST_PATHS=C KIT_TEST_ALLOW_SKIP=1 sh test/parse/run.sh + @KIT_TEST_JOBS=$(KIT_CBACKEND_TEST_JOBS) KIT_TEST_PATHS=C KIT=$(abspath $(BIN)) sh test/toy/run.sh + @KIT_TEST_JOBS=$(KIT_CBACKEND_TEST_JOBS) KIT_TEST_PATHS=C KIT=$(abspath $(BIN)) bash test/wasm/run.sh + +# test-wasm-toy: opt-in Toy -> Wasm -> JIT roundtrip. Runs the toy corpus +# under the W path (compile -target wasm32-none, then `kit run` the .wasm, +# which routes back through the lang/wasm frontend to native CG). Most cases +# will fail or skip today; the target exists so progress on the Wasm CGTarget +# is visible without putting noise into the default `test` summary. +# Drop `KIT_TEST_ALLOW_SKIP=1` once the corpus is mostly green. +test-wasm-toy: bin + @KIT_TEST_PATHS=W KIT_TEST_ALLOW_SKIP=1 KIT=$(abspath $(BIN)) sh test/toy/run.sh + +# test-wasm-c: opt-in C -> Wasm -> JIT roundtrip, the C-frontend analogue of +# test-wasm-toy. Runs the test/parse corpus under the W path (compile +# -target wasm32-none, then `kit run -e test_main` the .wasm, which routes +# back through the lang/wasm frontend to native CG). The C corpus exercises +# far more of the language than the toy corpus, so expect many SKIPs for +# not-yet-implemented Wasm lowerings; the target makes that progress visible +# without adding noise to the default `test` summary. Drop +# `KIT_TEST_ALLOW_SKIP=1` once the corpus is mostly green. +test-wasm-c: bin $(PARSE_RUNNER) + @KIT_TEST_PATHS=W KIT_TEST_ALLOW_SKIP=1 KIT=$(abspath $(BIN)) bash test/parse/run.sh + +test-pp: test-pp-ok test-pp-err + +test-pp-ok: bin + @KIT=$(abspath $(BIN)) test/pp/run.sh + +test-pp-err: bin + @KIT=$(abspath $(BIN)) test/pp/run_errors.sh + +# Best-effort kit binary build: Layer D needs build/kit, but the +# binary may not link until enough libkit symbols exist. The harness +# detects a missing binary and skips that layer; don't break test-elf +# when bin fails. +.PHONY: bin-soft +bin-soft: + -@$(MAKE) bin 2>/dev/null || true + +AR_TEST_BIN = build/test/ar_test + +test-ar: $(AR_TEST_BIN) + $(AR_TEST_BIN) + + +test-driver-ar: bin + @KIT=$(abspath $(BIN)) test/ar/run.sh + +test-driver-cas: bin + @KIT=$(abspath $(BIN)) sh test/cas/run.sh + +test-driver-strip: bin + @KIT=$(abspath $(BIN)) test/strip/run.sh + +test-driver-objcopy: bin + @KIT=$(abspath $(BIN)) test/objcopy/run.sh + +test-driver-objdump: bin + @KIT=$(abspath $(BIN)) sh test/objdump/run.sh + +test-driver-pkg: bin + @KIT=$(abspath $(BIN)) sh test/pkg/run.sh + +test-driver-strings: bin + @KIT=$(abspath $(BIN)) sh test/strings/run.sh + +test-driver-tools: bin + @KIT=$(abspath $(BIN)) sh test/tools/run.sh + +# DWARF consumer unit test: builds a hand-crafted DWARF-bearing ELF in +# memory and exercises every kit_dwarf_* entry. It reaches into the +# internal object builder to synthesize the fixture, so link individual +# lib objects rather than libkit.a (which hides internal symbols). +DWARF_TEST_BIN = build/test/dwarf_test + +test-dwarf: $(DWARF_TEST_BIN) + $(DWARF_TEST_BIN) + + +# DWARF producer self-roundtrip unit test. Drives Debug directly, calls +# debug_emit, asserts the produced sections have valid DWARF 5 structure +# (length fields, version, address sizes, expected relocations against +# function symbol). Deliberately bypasses the consumer (kit_dwarf_open) +# so encoder bugs aren't masked by matching decoder bugs. +DEBUG_TEST_BIN = build/test/debug_roundtrip_unit +CFI_TEST_BIN = build/test/debug_cfi_unit + +test-debug: $(DEBUG_TEST_BIN) $(CFI_TEST_BIN) + $(DEBUG_TEST_BIN) + $(CFI_TEST_BIN) + + +# CFI/.eh_frame producer roundtrip for aa64/rv64/x64 (validates the per-arch +# CIE template: code/data-align, return-address reg, CFA-init reg). + +test-dbg: bin + @KIT=$(abspath $(BIN)) sh test/dbg/run.sh + +.PHONY: test-dbg-red +test-dbg-red: bin + @KIT=$(abspath $(BIN)) DBG_STRICT_XFAIL=1 sh test/dbg/run.sh + +# aa64 ISA descriptor-table unit test (doc/ASM.md phase 2). Covers +# every AA64Format the table maps and the alias-precedence invariant +# (first-match disasm picks the alias spelling over the canonical +# form). Internal arch/ surface — needs -Isrc. +AA64_ISA_TEST_BIN = build/test/aa64_isa_test +RV64_DECODE_TEST_BIN = build/test/rv64_decode_test + +test-isa: $(AA64_ISA_TEST_BIN) $(RV64_DECODE_TEST_BIN) + $(AA64_ISA_TEST_BIN) + $(RV64_DECODE_TEST_BIN) + + + +# aa64_sweep_gen: emits one representative encoding per disasm-table row for the +# asm<->disasm self-symmetry sweep (test/asm/symmetry.sh). Needs the internal +# arch/aa64/isa.h surface, so -Isrc + LIB_OBJS like the ISA unit test. +AA64_SWEEP_GEN = build/test/aa64_sweep_gen + +# test-emu: emulator end-to-end integration test. Builds tiny in-memory rv64 +# ELFs and runs them to their exit syscall, asserting the exit code — entirely +# through the PUBLIC kit_emu_* API, so it links the public archive ($(LIB_AR), +# whose `ld -r` step localizes internal symbols). -Isrc is only for the +# header-only rv64 encoders / ELF constants the in-memory ELF builders use. +EMU_RV64_TEST_BIN = build/test/emu_rv64_test + +test-emu: $(EMU_RV64_TEST_BIN) + $(EMU_RV64_TEST_BIN) + +$(EMU_RV64_TEST_BIN): test/emu/rv64_smoke_test.c $(UNIT_HDR_DEPS) $(LIB_AR) + @mkdir -p $(dir $@) + $(CC) $(HOST_CFLAGS) -Iinclude -Isrc -Itest test/emu/rv64_smoke_test.c $(LIB_AR) -o $@ + +# RISC-V ULEB128 diff-reloc application unit test. link_reloc_apply is an +# internal (hidden) symbol, so link the raw lib objects like the other +# internal-surface unit tests rather than libkit.a. +RELOC_ULEB128_TEST_BIN = build/test/reloc_uleb128_unit + +test-link-reloc-uleb128: $(RELOC_ULEB128_TEST_BIN) + $(RELOC_ULEB128_TEST_BIN) + +# test-emu-unit: white-box unit tests for the emulator's INTERNAL units (rv64 +# decoder, EmuAddrSpace, Linux syscall handler) that have no public API. Reaches +# internal symbols -> links $(LIB_OBJS) (mirrors test-interp), not the archive. +EMU_RV64_UNIT_TEST_BIN = build/test/emu_rv64_unit_test + +test-emu-unit: $(EMU_RV64_UNIT_TEST_BIN) + $(EMU_RV64_UNIT_TEST_BIN) + + +# test-interp: threaded-bytecode interpreter unit smoke test. Builds tiny CG IR +# by hand, runs opt_run_o1_interp + interp_lower + the engine, asserts the +# returned value. Reaches internal opt/interp symbols -> links $(LIB_OBJS) and +# needs -Isrc (mirrors test-opt). +INTERP_SMOKE_TEST_BIN = build/test/interp_smoke_test + +test-interp: $(INTERP_SMOKE_TEST_BIN) + $(INTERP_SMOKE_TEST_BIN) + + +# test-interp-emu: differential test of the emulator's INTERP execution mode +# (doc/INTERPRETER.md Phase 4). Builds a tiny rv64 ELF with SD/LD/ecall and runs +# it through kit_emu_run in JIT and INTERP modes (both -O1), asserting the exit +# codes match. Public-API only, but links $(LIB_OBJS) like test-interp to dodge +# the visibility-hidden archive's localized internals. +INTERP_EMU_TEST_BIN = build/test/rv64_interp_smoke_test + +test-interp-emu: $(INTERP_EMU_TEST_BIN) + $(INTERP_EMU_TEST_BIN) + + +# test-interp-toy: run the toy suite's interpreter (--no-jit) path only, +# asserting it matches the golden exit codes (and SKIPping unimplemented ops). +test-interp-toy: bin + @KIT=$(abspath $(BIN)) KIT_TEST_PATHS=I bash test/toy/run.sh + +CG_API_TEST_BIN = build/test/cg_api_test +CG_SWITCH_TEST_BIN = build/test/cg_switch_test +CG_FP_CMP_TEST_BIN = build/test/cg_fp_cmp_test +STRENGTH_REDUCE_TEST_BIN = build/test/strength_reduce_test +TARGET_TEST_BIN = build/test/target_test +HASH_TEST_BIN = build/test/hash_test +ABI_CLASSIFY_TEST_BIN = build/test/abi_classify_test +IR_RECORDER_TEST_BIN = build/test/ir_recorder_test +NATIVE_DIRECT_TARGET_TEST_BIN = build/test/native_direct_target_test + +test-cg-api: $(TARGET_TEST_BIN) $(CG_API_TEST_BIN) $(CG_SWITCH_TEST_BIN) \ + $(CG_FP_CMP_TEST_BIN) $(STRENGTH_REDUCE_TEST_BIN) + $(TARGET_TEST_BIN) + $(CG_API_TEST_BIN) + $(CG_SWITCH_TEST_BIN) + $(CG_FP_CMP_TEST_BIN) + $(STRENGTH_REDUCE_TEST_BIN) + +test-hash: $(HASH_TEST_BIN) + $(HASH_TEST_BIN) + +test-abi-classify: $(ABI_CLASSIFY_TEST_BIN) + $(ABI_CLASSIFY_TEST_BIN) + + + + +test-ir-recorder: $(IR_RECORDER_TEST_BIN) + $(IR_RECORDER_TEST_BIN) + + +test-native-direct-target: $(NATIVE_DIRECT_TARGET_TEST_BIN) + $(NATIVE_DIRECT_TARGET_TEST_BIN) + + +test-toy: bin + @KIT=$(abspath $(BIN)) test/toy/run.sh + +# test-bootstrap-toy: run the Toy corpus through the bootstrapped (self-built) +# stage3 kit instead of the host-built binary, so the self-hosted compiler is +# exercised on real codegen (not just self-reproduction like `make bootstrap`). +# Split per build mode: -debug runs the debug stage3, -release the release +# (-O1) stage3; the aggregate target runs both. +test-bootstrap-toy: test-bootstrap-toy-debug test-bootstrap-toy-release + +test-bootstrap-toy-debug: bootstrap-debug + @KIT='$(abspath $(BUILD_DIR)/debug/bootstrap/stage3/kit)' test/toy/run.sh + +test-bootstrap-toy-release: bootstrap-release + @KIT='$(abspath $(BUILD_DIR)/release/bootstrap/stage3/kit)' test/toy/run.sh + + +# Public-API inline-asm backend tests. These emit a tiny function through CG, +# reopen the object through the public object reader, and assert the expected +# instruction bytes are present in .text. +AA64_INLINE_TEST_BIN = build/test/aa64_inline_test + +test-aa64-inline: $(AA64_INLINE_TEST_BIN) + $(AA64_INLINE_TEST_BIN) + + +RV64_INLINE_TEST_BIN = build/test/rv64_inline_test + +test-rv64-inline: $(RV64_INLINE_TEST_BIN) + $(RV64_INLINE_TEST_BIN) + + +# rv64 JIT smoke test. Builds a tiny rv64 ELF .o in memory, runs it +# through kit_link_session in JIT-output mode, and skips native execution +# on non-riscv64 hosts after exercising the JIT mapping/reloc path. +RV64_JIT_TEST_BIN = build/test/rv64_jit_test + +test-rv64-jit: $(RV64_JIT_TEST_BIN) + @$(RV64_JIT_TEST_BIN); rc=$$?; \ + if [ $$rc -eq 77 ]; then \ + echo " (rv64_jit_test SKIPPED on non-rv64 host)"; \ + exit 0; \ + else \ + exit $$rc; \ + fi + + +# Link-only regression for rv64 TLS Local-Exec lowering (runs on any host). +test-rv64-tls-link: bin + @KIT='$(abspath $(BIN))' bash test/smoke/rv64_tls_link.sh + +X64_INLINE_TEST_BIN = build/test/x64_inline_test + +test-x64-inline: $(X64_INLINE_TEST_BIN) + $(X64_INLINE_TEST_BIN) + + +X64_DBG_TEST_BIN = build/test/x64_dbg_test + +test-x64-dbg: $(X64_DBG_TEST_BIN) + $(X64_DBG_TEST_BIN) + +# Reaches the internal arch/arch.h surface (arch_lookup, ArchDbgOps) -> links +# $(LIB_OBJS), not the archive, whose relocatable merge localizes non-public +# symbols (mirrors the aa64/rv64 arch unit tests above). + +RT_HEADER_TEST_TARGETS = \ + aarch64-linux-gnu \ + x86_64-linux-gnu \ + riscv64-linux-gnu \ + aarch64-apple-darwin \ + x86_64-apple-darwin + +test-rt-headers: bin + @set -e; \ + for target in $(RT_HEADER_TEST_TARGETS); do \ + out="build/test/rt-headers/$$target/smoke.o"; \ + mkdir -p "$$(dirname "$$out")"; \ + $(BIN) cc -target "$$target" -Werror -c test/rt/smoke.c -o "$$out"; \ + done + +# Arch token -> linux rt variant target. The cross-target test harnesses link +# the matching LINUX runtime archive (compiler-rt-style helpers like +# __kit_sext64ti, __kit_assert_fail) rather than the native `rt` target, +# which builds only the host's own variant. Shared by the single-arch +# parse/asm/macho/link tests (via KIT_TEST_ARCH/TEST_RT_DEP) and by +# test-rt-runtime, which sweeps several arches at once. +KIT_TEST_ARCH ?= aa64 +_TEST_RT_aa64 = rt-aarch64-linux +_TEST_RT_aarch64 = rt-aarch64-linux +_TEST_RT_arm64 = rt-aarch64-linux +_TEST_RT_x64 = rt-x86_64-linux +_TEST_RT_x86_64 = rt-x86_64-linux +_TEST_RT_amd64 = rt-x86_64-linux +_TEST_RT_rv64 = rt-riscv64-linux +_TEST_RT_riscv64 = rt-riscv64-linux +TEST_RT_DEP = $(_TEST_RT_$(KIT_TEST_ARCH)) + +# test-rt-runtime compiles each case for the configured arches and links it +# against that arch's LINUX runtime archive, so it must depend on each variant +# (not the host `rt`). A stale per-arch archive otherwise silently breaks the +# link — e.g. a dropped __kit_assert_fail weak def surfaces as an undefined +# reference. KIT_RT_RUNTIME_ARCHES mirrors the default in test/rt/run.sh. +KIT_RT_RUNTIME_ARCHES ?= aa64 x64 rv64 +RT_RUNTIME_DEPS := $(foreach a,$(KIT_RT_RUNTIME_ARCHES),$(_TEST_RT_$(a))) + +test-rt-runtime: bin $(RT_RUNTIME_DEPS) $(LINK_EXE_RUNNER) + @bash test/rt/run.sh + +# Test harness binaries shared by test-elf and test-link. +# Declared as Make targets (not built by the run.sh scripts) so they pick +# up libkit.a changes deterministically. +# +# HARNESS_CFLAGS drops -Wpedantic from the normal host flags; the runners cast +# kit_jit_lookup's void* to a function pointer, which pedantic rejects under +# C11. +HARNESS_CFLAGS = $(filter-out -Wpedantic,$(HOST_CFLAGS)) -Iinclude -Itest + +ROUNDTRIP_BIN = build/test/kit-roundtrip +ROUNDTRIP_BIN_MACHO = build/test/kit-roundtrip-macho +ROUNDTRIP_BIN_COFF = build/test/kit-roundtrip-coff +COFF_IMPORT_SMOKE_BIN = build/test/pe-import-smoke +COFF_IMPORT_MINGW_BIN = build/test/pe-import-mingw +COFF_DSO_FORWARDER_BIN = build/test/pe-dso-forwarder +COFF_MIXED_ARCHIVE_BIN = build/test/pe-mixed-archive +LINK_EXE_RUNNER = build/test/link-exe-runner +JIT_RUNNER = build/test/jit-runner +PARSE_RUNNER = build/test/parse-runner +ASM_RUNNER = build/test/asm-runner +WASM_TOOL = build/test/wasm-tool + +$(ROUNDTRIP_BIN): test/elf/kit-roundtrip.c $(LIB_AR) + @mkdir -p $(dir $@) + $(CC) $(HARNESS_CFLAGS) test/elf/kit-roundtrip.c $(LIB_AR) -o $@ + +# Mach-O peer of kit-roundtrip — read_macho + emit_macho. Used by +# test-link's path R when KIT_TEST_OBJ=macho. +$(ROUNDTRIP_BIN_MACHO): test/macho/kit-roundtrip-macho.c $(LIB_AR) + @mkdir -p $(dir $@) + $(CC) $(HARNESS_CFLAGS) -Isrc test/macho/kit-roundtrip-macho.c $(LIB_AR) -o $@ + +# PE/COFF round-trip harness (test/coff/). All-in-one binary: builds +# hand-crafted ObjBuilders and asserts emit_coff/read_coff round-trip +# stability for both x86_64-windows and aarch64-windows. +$(ROUNDTRIP_BIN_COFF): test/coff/kit-roundtrip-coff.c $(LIB_OBJS) + @mkdir -p $(dir $@) + $(CC) $(HARNESS_CFLAGS) -Isrc test/coff/kit-roundtrip-coff.c $(LIB_OBJS) -o $@ + +# PE import-directory smoke test (test/coff/pe-import-smoke.c). +# Exercises the full chain: short-import shim bytes -> link_add_obj_bytes +# (reclassified as DSO) -> link_resolve -> link_emit_coff. Verifies the +# produced PE32+ via x86_64-w64-mingw32-objdump; skips cleanly if absent. +$(COFF_IMPORT_SMOKE_BIN): test/coff/pe-import-smoke.c $(LIB_OBJS) + @mkdir -p $(dir $@) + $(CC) $(HARNESS_CFLAGS) -Isrc test/coff/pe-import-smoke.c $(LIB_OBJS) -o $@ + +# PE import test against a real mingw archive (test/coff/pe-import-mingw.c). +# Exercises the long-form import-archive absorption path +# (link_add_archive_bytes -> classify_coff_archive_member). Skips cleanly +# when the mingw toolchain isn't installed. +$(COFF_IMPORT_MINGW_BIN): test/coff/pe-import-mingw.c $(LIB_OBJS) + @mkdir -p $(dir $@) + $(CC) $(HARNESS_CFLAGS) -Isrc test/coff/pe-import-mingw.c $(LIB_OBJS) -o $@ + +# read_coff_dso forwarder-export contract (test/coff/pe-dso-forwarder.c). +# Synthesizes a tiny PE32+ DLL with one direct and one forwarder export +# and asserts both surface as OBJ_SEC_NONE globals on the ObjBuilder. +$(COFF_DSO_FORWARDER_BIN): test/coff/pe-dso-forwarder.c $(LIB_OBJS) + @mkdir -p $(dir $@) + $(CC) $(HARNESS_CFLAGS) -Isrc test/coff/pe-dso-forwarder.c $(LIB_OBJS) -o $@ + +# Mixed-member archive (test/coff/pe-mixed-archive.c). Verifies that +# one archive containing both a short-import member and a long-form +# COFF object with a defined data symbol satisfies references through +# both shapes — the same composition libucrt.a uses (API-set imports +# alongside lib64_libucrt_extra_a-*.o helpers). +$(COFF_MIXED_ARCHIVE_BIN): test/coff/pe-mixed-archive.c $(LIB_OBJS) + @mkdir -p $(dir $@) + $(CC) $(HARNESS_CFLAGS) -Isrc test/coff/pe-mixed-archive.c $(LIB_OBJS) -o $@ + +$(LINK_EXE_RUNNER): test/link/harness/link_exe_runner.c $(LIB_AR) + @mkdir -p $(dir $@) + $(CC) $(HARNESS_CFLAGS) test/link/harness/link_exe_runner.c $(LIB_AR) -o $@ + +$(JIT_RUNNER): test/link/harness/jit_runner.c $(LIB_AR) + @mkdir -p $(dir $@) + $(CC) $(HARNESS_CFLAGS) test/link/harness/jit_runner.c $(LIB_AR) -o $@ + +$(PARSE_RUNNER): test/parse/harness/parse_runner.c $(LIB_AR) + @mkdir -p $(dir $@) + $(CC) $(HARNESS_CFLAGS) test/parse/harness/parse_runner.c $(LIB_AR) -o $@ + +$(ASM_RUNNER): test/asm/harness/asm_runner.c $(LIB_AR) + @mkdir -p $(dir $@) + $(CC) $(HARNESS_CFLAGS) test/asm/harness/asm_runner.c $(LIB_AR) -o $@ + +$(WASM_TOOL): test/wasm/harness/wasm_tool.c $(LIB_AR) + @mkdir -p $(dir $@) + $(CC) $(HARNESS_CFLAGS) -I. test/wasm/harness/wasm_tool.c $(LIB_AR) -o $@ + +test-elf: lib bin-soft $(ROUNDTRIP_BIN) + KIT_ELF_UNIT_CFLAGS='$(HOST_MODE_CFLAGS)' \ + KIT_ELF_UNIT_LDFLAGS='$(HOST_MODE_LDFLAGS)' \ + bash test/elf/run.sh + +# PE/COFF round-trip harness plus optional hosted Windows smoke. The +# UCRT smoke self-skips when llvm-mingw is not installed. +test-coff: lib bin rt-aarch64-windows $(ROUNDTRIP_BIN_COFF) $(COFF_IMPORT_SMOKE_BIN) $(COFF_DSO_FORWARDER_BIN) $(COFF_MIXED_ARCHIVE_BIN) + $(ROUNDTRIP_BIN_COFF) + $(COFF_IMPORT_SMOKE_BIN) + $(COFF_DSO_FORWARDER_BIN) + $(COFF_MIXED_ARCHIVE_BIN) + bash test/coff/windows-ucrt-hosted-smoke.sh + bash test/coff/windows-system-dlls-smoke.sh + +# Separate target so it can be skipped gracefully if mingw isn't +# installed. The test itself self-skips on missing tooling, but the +# build target only fires when explicitly requested. +test-coff-mingw-import: lib $(COFF_IMPORT_MINGW_BIN) + $(COFF_IMPORT_MINGW_BIN) + +test-coff-windows-ucrt: bin rt-aarch64-windows + bash test/coff/windows-ucrt-hosted-smoke.sh + +# The parse/asm/macho harnesses select a cross-target via KIT_TEST_ARCH +# (default aa64); the link rt dependency is resolved through the shared +# _TEST_RT_<arch> map defined above (near test-rt-runtime). +test-link: lib $(ROUNDTRIP_BIN) $(ROUNDTRIP_BIN_MACHO) $(LINK_EXE_RUNNER) $(JIT_RUNNER) + bash test/link/run.sh + +# x64 ELF link/reloc-application coverage. test-link defaults to aa64, so +# kit's x64 static-link reloc fixups (R_X64_PLT32/GOTPCREL/TPOFF/...) were +# only ever run via a manual KIT_TEST_ARCH=x64 override. Opt-in (not in the +# default set) because the E path links + runs under podman/qemu-x86_64; the R +# path (roundtrip + reloc layout) runs on any host. +test-link-x64: lib rt-x86_64-linux $(ROUNDTRIP_BIN) $(LINK_EXE_RUNNER) $(JIT_RUNNER) + @KIT_TEST_ARCH=x64 KIT_TEST_PATHS=RE bash test/link/run.sh + +test-macho: lib $(TEST_RT_DEP) $(ROUNDTRIP_BIN_MACHO) $(LINK_EXE_RUNNER) $(JIT_RUNNER) + KIT_TEST_OBJ=macho \ + KIT_TEST_ARCH=$${KIT_TEST_ARCH:-aa64} \ + KIT_TEST_PATHS=$${KIT_TEST_PATHS:-RJ} \ + KIT_TEST_ALLOW_SKIP=$${KIT_TEST_ALLOW_SKIP:-1} \ + bash test/link/run.sh + +OPT_TEST_BIN = build/test/cg_ir_lower_test +TINY_INLINE_TEST_BIN = build/test/tiny_inline_test + +test-opt: bin $(OPT_TEST_BIN) test-opt-tiny-inline test-opt-inline test-opt-zero-arg test-opt-static-prune-aa64 test-opt-aa64-tail test-opt-prologue-tier + $(OPT_TEST_BIN) + + +test-opt-tiny-inline: bin $(TINY_INLINE_TEST_BIN) + $(TINY_INLINE_TEST_BIN) + + +# Behavioral disasm check: tiny callee `bl` disappears from its caller at -O1. +test-opt-inline: bin + @KIT=$(abspath $(BIN)) bash test/opt/run.sh + +# Behavioral disasm check: a pointer-typed null call arg is not routed through +# a scratch temp at -O1 (PERCALL.md item 3, "zero through a temp"). +test-opt-zero-arg: bin + @KIT=$(abspath $(BIN)) bash test/opt/zero_arg.sh + +.PHONY: test-opt-static-prune-aa64 +test-opt-static-prune-aa64: bin + @KIT=$(abspath $(BIN)) bash test/opt/static_prune_aa64.sh + +.PHONY: test-opt-aa64-tail +test-opt-aa64-tail: bin + @KIT=$(abspath $(BIN)) bash test/opt/aa64_tail_call.sh + +# Structural disasm check: the -O1 known-frame prologue cost-model tiers +# (aa64 reference + ported x64 slim/red-zone and rv64 leaf shapes). +.PHONY: test-opt-prologue-tier +test-opt-prologue-tier: bin + @KIT=$(abspath $(BIN)) bash test/opt/prologue_tier.sh + +test-parse: test-parse-ok test-parse-err + +test-parse-ok: lib $(TEST_RT_DEP) $(PARSE_RUNNER) $(ROUNDTRIP_BIN) $(LINK_EXE_RUNNER) $(JIT_RUNNER) + bash test/parse/run.sh + +test-parse-err: lib $(PARSE_RUNNER) + sh test/parse/run_errors.sh + +# test-asm: aggregate alias running every per-arch asm lane (aa64 + x64 + rv64) +# so `make test` covers all three through one target. The harness runs one arch +# per invocation (KIT_TEST_ARCH); each lane scopes its scratch per arch, so +# the prerequisites are safe to run in parallel under `make -j`. +test-asm: test-asm-aa64 test-asm-x64 test-asm-rv64 + +# test-asm-aa64: the reference lane. aa64 is the default cross-target, and on +# aa64 hosts the exec paths (D/E/J) run natively, so it uses the full default +# path set (HTLDJE). The Makefile owns the harness binaries so they inherit host +# flags consistently with the rest of the test suite. +test-asm-aa64: lib $(TEST_RT_DEP) $(ASM_RUNNER) $(LINK_EXE_RUNNER) $(JIT_RUNNER) + @KIT_TEST_ARCH=aa64 bash test/asm/run.sh + +# x64/rv64 exercise the encode (H), decode (T) and listing (L) corpora on any +# host: H/T/L need no native execution (they only produce/compare bytes), so the +# exec paths (D/E/J) are deliberately excluded and left to the smoke/qemu +# targets. +test-asm-x64: lib $(ASM_RUNNER) + @KIT_TEST_ARCH=x64 KIT_TEST_PATHS=HTL bash test/asm/run.sh +test-asm-rv64: lib $(ASM_RUNNER) + @KIT_TEST_ARCH=rv64 KIT_TEST_PATHS=HT bash test/asm/run.sh + +# Codegen round-trip completeness (doc/ASM_ROUNDTRIP_TESTING.md). These drive +# the `kit` binary itself (cc -S / as / objdump) over a C corpus rather than +# a hand-written asm corpus, so coverage tracks codegen automatically. +# +# test-disasm-complete L0: cc -S must decode every in-function word +# (no `.inst` markers). Host-independent, no exec. +# test-asm-roundtrip L0+L1: also assert cc -c bytes/relocs == cc -S | as. +# test-asm-roundtrip-exec L0+L1+L2: also run direct vs round-tripped object +# and compare exit codes (native arch; opt-in). +# +# Vertical slice: aa64 only for now; L1/L2 run at -O1 (branch-free), L0 at both +# opt levels. Broadening to -O0, other arches, and the default suite is tracked +# in doc/ASM_ROUNDTRIP_TESTING.md once -S symbolization (Phase 2) lands. +test-disasm-complete: bin + @KIT_TEST_ARCH=aa64 KIT_TEST_OPTS="O0 O1" KIT_TEST_PATHS=0 \ + bash test/asm/roundtrip.sh +test-asm-roundtrip: bin + @KIT_TEST_ARCH=aa64 KIT_TEST_OPTS="O0 O1" KIT_TEST_PATHS=01 \ + bash test/asm/roundtrip.sh +test-asm-roundtrip-exec: bin $(JIT_RUNNER) + @KIT_TEST_ARCH=aa64 KIT_TEST_OPTS="O0 O1" KIT_TEST_PATHS=012 \ + bash test/asm/roundtrip.sh + +# test-asm-symmetry: asm<->disasm self-symmetry sweep (aa64). Decode-side sweeps +# every disasm-table form (decode->encode->decode fixed point); encode-side +# asserts every byte the assembler emits over the encode corpus is decodable. +# Catches encode/decode asymmetries the codegen round-trip can't reach (e.g. a +# form one tool handles and the other doesn't). Host-independent, no exec. +test-asm-symmetry: $(ASM_RUNNER) $(AA64_SWEEP_GEN) + @bash test/asm/symmetry.sh + +# test-diff-llvm: differential cross-check of kit against llvm (aa64), as a +# second oracle. Encode lane: kit as vs llvm-mc bytes over the encode corpus. +# Disasm lane: cc -c bytes vs llvm-mc of cc -S (validates kit's disassembler; +# the benign same-section-call reloc-vs-resolve difference is recognized). +# Opt-in; skips cleanly when llvm-mc is absent. +test-diff-llvm: bin + @KIT_TEST_OPTS="O0 O1" bash test/asm/diff_llvm.sh + +# test-asm-roundtrip-toy: L2 exec round-trip over the Toy corpus (native arch). +# Reuses the ~150 toy cases (full CG op set, exit-code oracle) for free +# round-trip coverage: kit cc -S | kit as | kit run, exit must match. +# Opt-in; native target. Found a real miscompile (dropped .inst) the hand +# corpus never reached. +test-asm-roundtrip-toy: bin + @bash test/asm/roundtrip_toy.sh + +# test-hostas-toy: feed one native `cc -S` to BOTH kit's own `as` and clang (a +# third-party host assembler), link + run each, and assert the toy exit-code +# oracle. Only the assembler differs between the two lanes, so the clang lane is +# the real test: a standard assembler can't paper over a private-dialect quirk +# the way kit's own `as` can (cf. test/asm/diff_llvm.sh, but by execution). +# cc -S is now object-format-aware, so the native Mach-O clang lane GATES by +# default (both lanes 312/0); KIT_HOSTAS_ENFORCE_CLANG=0 demotes it to XFAIL. +# Opt-in; skips cleanly if clang is absent. +test-hostas-toy: bin + @bash test/asm/hostas_toy.sh + +# test-hostas-cross: the same two-assembler-by-execution idea as test-hostas-toy, +# but CROSS — `cc -S -target <triple>` for ELF Linux arches (aarch64/x86_64/ +# riscv64), assembled by kit-as AND clang, linked static (+ the start.c crt) +# with kit ld, and run under podman/qemu via test/lib/exec_target.sh. Each +# target self-skips unless the host has a clang cross target, a runner, a +# working `cc -S | kit as` for that arch, and a passing bounded exec smoke — +# so it runs green on whatever the host supports (aarch64-linux today; x86_64 +# pends the x64 cc -S symbolizer, riscv64 pends a working rv64 user-mode +# emulator). Opt-in; skips cleanly if clang/podman are absent. +test-hostas-cross: bin + @bash test/asm/hostas_cross.sh + +test-wasm: test-wasm-front test-wasm-target test-wasm-toy + +test-wasm-front: bin $(WASM_TOOL) $(LINK_EXE_RUNNER) $(JIT_RUNNER) + bash test/wasm/run.sh + +# test-wasm-target: structural checks on `kit cc -target wasm32-none` +# output. test/wasm-target/run.sh is a Type C corpus harness whose lanes each +# compile a tiny C or toy fixture and assert a property of the produced module +# bytes (inline-asm opcodes, memory.copy/fill opcodes, exported "memory", +# (import ...) decls). Opt-in: not in the default `test` target because the +# checks depend on the bulk-memory + (import ...) backend work landing first. +test-wasm-target: bin + @KIT=$(abspath $(BIN)) bash test/wasm-target/run.sh + +# test-smoke-x64: phase-1 sanity check for the multi-arch bring-up. Builds a +# tiny freestanding x86_64 ELF with clang --target=x86_64-linux-gnu and +# runs it through test/lib/exec_target.sh's podman/qemu pipeline, +# proving the harness end-to-end before any kit-emitted x64 bytes +# exist. Excluded from the default `test` target because it needs +# podman + lld; opt-in via `make test-smoke-x64`. +test-smoke-x64: + bash test/smoke/x64.sh + +# test-smoke-rv64: phase-2 counterpart of test-smoke-x64. Builds a +# tiny freestanding riscv64 ELF with clang --target=riscv64-linux-gnu +# and runs it through test/lib/exec_target.sh, proving the rv64 lane +# of the harness end-to-end before any kit-emitted rv64 bytes +# exist. Excluded from the default `test` target because it needs +# qemu-riscv64 (or podman with riscv64 emulation) + lld; opt-in via +# `make test-smoke-rv64`. +test-smoke-rv64: + bash test/smoke/rv64.sh + +# test-parse-rv64-wide: end-to-end coverage of the rv64 128-bit scalar types +# — __int128 (i128_*) and IEEE-754 binary128 long double (ldbl128_*) — built +# with kit and run on riscv64. Exercises the soft-float / i128 lowering to +# the compiler-rt-style runtime (fp_tf, fp_ti, int64), the LP64D register-pair +# ABI for 16-byte scalars, and the conditional-branch range fix that large +# soft-float helpers depend on. Opt-in (needs qemu-riscv64 or podman with +# riscv64 emulation), so excluded from the default `test` target; mirrors +# test-smoke-rv64. Run a single case with +# KIT_TEST_ARCH=rv64 bash test/parse/run.sh ldbl128_03_arith +test-parse-rv64-wide: lib rt-riscv64-linux $(PARSE_RUNNER) $(ROUNDTRIP_BIN) \ + $(LINK_EXE_RUNNER) + @KIT_TEST_ARCH=rv64 KIT_TEST_PATHS=RE bash test/parse/run.sh 128 + +# test-bounce: format-bounce stress test. Compiles small programs with +# kit, then bounces each object through chains of format conversions +# (ELF<->Mach-O<->COFF), partial links (ld -r), strip, and archive +# round-trips, relinks, and runs the result, asserting the exit code +# matches a host-cc reference. Stresses obj read/write/reloc, ar, and +# partial-link paths rather than the arches. Defaults to the host-native +# Linux arch (no emulation); sweep others with +# KIT_BOUNCE_ARCHES="aarch64 x64 rv64". Excluded from the default `test` +# target because it needs podman/qemu + a host cc; opt-in via +# `make test-bounce`. +test-bounce: $(BIN) + bash test/bounce/bounce.sh + +# test-libc: aggregate alias that runs test-libc-musl and test-libc-glibc. +# test-libc-musl / test-libc-glibc: end-to-end static + dynamic libc link/run +# on aarch64. Each variant pulls its own pinned sysroot (podman, ~30s on +# first run) and shares the same case files under test/libc/cases/: +# +# test-libc-musl — Alpine 3.20 + musl 1.2.5 (test/libc/musl/) +# test-libc-glibc — Debian bookworm + glibc 2.36 (test/libc/glibc/) +# +# Both build build/rt/aarch64-linux/libkit_rt.a for soft-float / TF +# builtins, and run `kit ld` against the real libc.a (static) and +# libc.so / libc.so.6 (dynamic). Excluded from the +# default `test` target because they need podman; opt-in via +# `make test-libc-musl` / `make test-libc-glibc` (or `make test-libc` for both). +# +# Each sysroot is treated as a real prerequisite via its PROVENANCE +# marker so subsequent runs skip extraction and re-extract only when +# the file is removed (or extract.sh -f forces a rebuild). +MUSL_SYSROOT_MARKER = build/musl-sysroot/PROVENANCE +MUSL_SYSROOT_X64_MARKER = build/musl-sysroot-x64/PROVENANCE +MUSL_SYSROOT_RV64_MARKER = build/musl-sysroot-rv64/PROVENANCE +GLIBC_SYSROOT_MARKER = build/glibc-sysroot/PROVENANCE +GLIBC_SYSROOT_X64_MARKER = build/glibc-sysroot-x64/PROVENANCE +GLIBC_SYSROOT_RV64_MARKER = build/glibc-sysroot-rv64/PROVENANCE + +$(MUSL_SYSROOT_MARKER): test/libc/musl/extract.sh test/libc/musl/Containerfile + @bash test/libc/musl/extract.sh + +$(MUSL_SYSROOT_X64_MARKER): test/libc/musl/extract.sh test/libc/musl/Containerfile.x64 + @bash test/libc/musl/extract.sh -a x64 + +$(MUSL_SYSROOT_RV64_MARKER): test/libc/musl/extract.sh test/libc/musl/Containerfile.rv64 + @bash test/libc/musl/extract.sh -a rv64 + +$(GLIBC_SYSROOT_MARKER): test/libc/glibc/extract.sh test/libc/glibc/Containerfile + @bash test/libc/glibc/extract.sh + +$(GLIBC_SYSROOT_X64_MARKER): test/libc/glibc/extract.sh test/libc/glibc/Containerfile.x64 + @bash test/libc/glibc/extract.sh -a x64 + +$(GLIBC_SYSROOT_RV64_MARKER): test/libc/glibc/extract.sh test/libc/glibc/Containerfile.rv64 + @bash test/libc/glibc/extract.sh -a rv64 + +# test-libc-musl / test-libc-glibc honor KIT_LIBC_ARCHES (default "aa64"; +# values: aa64, x64, rv64). Each enabled arch contributes its sysroot +# PROVENANCE marker and its rt archive to the prerequisite list, so +# `KIT_LIBC_ARCHES="aa64 x64" make test-libc-musl` builds both sysroots +# + both rt archives before the runner script picks them up. +KIT_LIBC_ARCHES ?= aa64 + +# Map an arch token to its musl/glibc sysroot marker and rt target. +_LIBC_MUSL_SYSROOT_aa64 = $(MUSL_SYSROOT_MARKER) +_LIBC_MUSL_SYSROOT_x64 = $(MUSL_SYSROOT_X64_MARKER) +_LIBC_MUSL_SYSROOT_rv64 = $(MUSL_SYSROOT_RV64_MARKER) +_LIBC_GLIBC_SYSROOT_aa64 = $(GLIBC_SYSROOT_MARKER) +_LIBC_GLIBC_SYSROOT_x64 = $(GLIBC_SYSROOT_X64_MARKER) +_LIBC_GLIBC_SYSROOT_rv64 = $(GLIBC_SYSROOT_RV64_MARKER) +_LIBC_RT_aa64 = rt-aarch64-linux +_LIBC_RT_x64 = rt-x86_64-linux +_LIBC_RT_rv64 = rt-riscv64-linux + +LIBC_MUSL_DEPS = $(foreach a,$(KIT_LIBC_ARCHES),$(_LIBC_MUSL_SYSROOT_$(a)) $(_LIBC_RT_$(a))) +LIBC_GLIBC_DEPS = $(foreach a,$(KIT_LIBC_ARCHES),$(_LIBC_GLIBC_SYSROOT_$(a)) $(_LIBC_RT_$(a))) + +test-libc: test-libc-musl test-libc-glibc + +test-libc-musl: bin $(LIBC_MUSL_DEPS) + @KIT_LIBC_ARCHES="$(KIT_LIBC_ARCHES)" bash test/libc/musl/run.sh + +test-libc-glibc: bin $(LIBC_GLIBC_DEPS) + @KIT_LIBC_ARCHES="$(KIT_LIBC_ARCHES)" bash test/libc/glibc/run.sh + +test-libc-musl-rv64: + @$(MAKE) test-libc-musl KIT_LIBC_ARCHES=rv64 + +test-libc-glibc-rv64: + @$(MAKE) test-libc-glibc KIT_LIBC_ARCHES=rv64 + +# Fail if libkit.a depends on any external symbol not in the allowlist, or +# if a relocatable link exposes non-public global definitions. +# External dependency drift in either direction (new dep, or stale entry) is a +# failure. +# +# Inspect the RELEASE artifacts only: the debug build is ASan/UBSan-instrumented +# and pulls in __asan_*/__ubsan_* externals that are not part of the shipped +# library's dependency surface. +LIB_DEPS_RELEASE_DIR = build/release +LIB_DEPS_AR = $(LIB_DEPS_RELEASE_DIR)/libkit.a +LIB_DEPS_ACTUAL = $(LIB_DEPS_RELEASE_DIR)/libkit.deps.txt +LIB_RELOC = $(LIB_DEPS_RELEASE_DIR)/libkit.reloc.o +LIB_RELOC_BAD = $(LIB_DEPS_RELEASE_DIR)/libkit.reloc.bad-symbols.txt + +test-lib-deps: + @$(MAKE) lib RELEASE=1 + @mkdir -p $(dir $(LIB_DEPS_ACTUAL)) + @python3 scripts/lib_external_deps.py $(LIB_DEPS_AR) > $(LIB_DEPS_ACTUAL) + @diff -u scripts/lib_deps.allowlist $(LIB_DEPS_ACTUAL) \ + || { echo "libkit.a external symbol set drifted from scripts/lib_deps.allowlist"; exit 1; } + @python3 scripts/lib_reloc_defined_prefixes.py $(LIB_DEPS_AR) \ + --output $(LIB_RELOC) --ar $(AR) --cc $(CC) > $(LIB_RELOC_BAD) + @test ! -s $(LIB_RELOC_BAD) \ + || { echo "libkit relocatable link exposes non-public symbols"; cat $(LIB_RELOC_BAD); exit 1; } diff --git a/mk/test_unit.mk b/mk/test_unit.mk @@ -0,0 +1,82 @@ +# mk/test_unit.mk — build rules for the C unit-test binaries. +# +# Included by mk/test.mk. Replaces the hand-written per-binary rules (each a +# $(CC) ... SRC $(LIB_AR|LIB_OBJS) -o $@ triple) with two static-pattern rules, +# one per regime. Registering a unit test is two lines: add its stem to a list +# and set <stem>_SRC. Run-targets (test-isa, test-cg-api, ...) stay in mk/test.mk +# and invoke build/test/<stem> directly, so per-binary sequencing and skip +# semantics (e.g. rv64-jit's exit-77 wrapper) are unchanged. +# +# Two regimes, by the interface under test. We prefer tests that exercise the +# PUBLIC interface — keep a test public unless it genuinely needs internals. +# +# PUBLIC public headers only (-Iinclude), linked against the public +# archive libkit.a. Internal (visibility-hidden) symbols are +# unreachable, so these prove the public surface is self-sufficient. +# INTERNAL additionally sees src/ internal headers (-Isrc) and links the raw +# objects (LIB_OBJS), exposing internal symbols. For units that have +# no public API (ISA tables, ABI classifier, IR/opt internals, ...). +# +# Both add -Itest so `#include "lib/kit_unit.h"` resolves; neither adds -Ilang +# (no unit test needs the frontend-private headers). emu_rv64_test is a +# deliberate exception kept in mk/test.mk — see the note there. + +UNIT_HDR_DEPS := test/lib/kit_unit.h test/arch/inline_public_test.h + +# Deferred (=) so they pick up HOST_CFLAGS regardless of include order. +UNIT_CFLAGS_PUBLIC = $(HOST_CFLAGS) -Iinclude -Itest +UNIT_CFLAGS_INTERNAL = $(HOST_CFLAGS) -Iinclude -Isrc -Itest + +# ---- registrations: stem lists + per-stem source --------------------------- + +UNIT_TESTS_PUBLIC := \ + ar_test target_test cg_api_test cg_switch_test cg_fp_cmp_test hash_test \ + rv64_jit_test aa64_inline_test rv64_inline_test x64_inline_test \ + strength_reduce_test +ar_test_SRC := test/ar/ar_test.c +target_test_SRC := test/api/target_test.c +hash_test_SRC := test/api/hash_test.c +cg_api_test_SRC := test/api/cg_type_test.c +cg_switch_test_SRC := test/api/cg_switch_test.c +cg_fp_cmp_test_SRC := test/api/cg_fp_cmp_test.c +strength_reduce_test_SRC := test/cg/strength_reduce_test.c +rv64_jit_test_SRC := test/link/rv64_jit_test.c +aa64_inline_test_SRC := test/arch/aa64_inline_test.c +rv64_inline_test_SRC := test/arch/rv64_inline_test.c +x64_inline_test_SRC := test/arch/x64_inline_test.c + +UNIT_TESTS_INTERNAL := \ + dwarf_test debug_roundtrip_unit debug_cfi_unit \ + aa64_isa_test rv64_decode_test aa64_sweep_gen \ + reloc_uleb128_unit emu_rv64_unit_test interp_smoke_test \ + rv64_interp_smoke_test abi_classify_test ir_recorder_test \ + native_direct_target_test x64_dbg_test cg_ir_lower_test tiny_inline_test +dwarf_test_SRC := test/dwarf/dwarf_test.c +debug_roundtrip_unit_SRC := test/debug/roundtrip_unit.c +debug_cfi_unit_SRC := test/debug/cfi_unit.c +aa64_isa_test_SRC := test/arch/aa64_isa_test.c +rv64_decode_test_SRC := test/arch/rv64_decode_test.c +aa64_sweep_gen_SRC := test/arch/aa64_sweep_gen.c +reloc_uleb128_unit_SRC := test/link/reloc_uleb128_unit.c +emu_rv64_unit_test_SRC := test/emu/rv64_vm_unit_test.c +interp_smoke_test_SRC := test/interp/interp_smoke_test.c +rv64_interp_smoke_test_SRC := test/emu/rv64_interp_smoke_test.c +abi_classify_test_SRC := test/api/abi_classify_test.c +ir_recorder_test_SRC := test/cg/ir_recorder_test.c +native_direct_target_test_SRC := test/cg/native_direct_target_test.c +x64_dbg_test_SRC := test/arch/x64_dbg_test.c +cg_ir_lower_test_SRC := test/opt/cg_ir_lower_test.c +tiny_inline_test_SRC := test/opt/tiny_inline_test.c + +# ---- build rules ------------------------------------------------------------ +# Secondary expansion lets a static-pattern prerequisite reference the +# per-stem source via $$($$*_SRC). +.SECONDEXPANSION: + +$(UNIT_TESTS_PUBLIC:%=build/test/%): build/test/%: $$($$*_SRC) $(UNIT_HDR_DEPS) $(LIB_AR) + @mkdir -p $(dir $@) + $(CC) $(UNIT_CFLAGS_PUBLIC) $($*_SRC) $(LIB_AR) -o $@ + +$(UNIT_TESTS_INTERNAL:%=build/test/%): build/test/%: $$($$*_SRC) $(UNIT_HDR_DEPS) $(LIB_OBJS) + @mkdir -p $(dir $@) + $(CC) $(UNIT_CFLAGS_INTERNAL) $($*_SRC) $(LIB_OBJS) -o $@ diff --git a/test/lib/unit.mk b/test/lib/unit.mk @@ -1,80 +0,0 @@ -# test/lib/unit.mk — build rules for the C unit-test binaries. -# -# Included by test/test.mk. Replaces the hand-written per-binary rules (each a -# $(CC) ... SRC $(LIB_AR|LIB_OBJS) -o $@ triple) with two static-pattern rules, -# one per regime. Registering a unit test is two lines: add its stem to a list -# and set <stem>_SRC. Run-targets (test-isa, test-cg-api, ...) stay in test.mk -# and invoke build/test/<stem> directly, so per-binary sequencing and skip -# semantics (e.g. rv64-jit's exit-77 wrapper) are unchanged. -# -# Two regimes, by the interface under test. We prefer tests that exercise the -# PUBLIC interface — keep a test public unless it genuinely needs internals. -# -# PUBLIC public headers only (-Iinclude), linked against the public -# archive libkit.a. Internal (visibility-hidden) symbols are -# unreachable, so these prove the public surface is self-sufficient. -# INTERNAL additionally sees src/ internal headers (-Isrc) and links the raw -# objects (LIB_OBJS), exposing internal symbols. For units that have -# no public API (ISA tables, ABI classifier, IR/opt internals, ...). -# -# Both add -Itest so `#include "lib/kit_unit.h"` resolves; neither adds -Ilang -# (no unit test needs the frontend-private headers). emu_rv64_test is a -# deliberate exception kept in test.mk — see the note there. - -UNIT_HDR_DEPS := test/lib/kit_unit.h test/arch/inline_public_test.h - -# Deferred (=) so they pick up HOST_CFLAGS regardless of include order. -UNIT_CFLAGS_PUBLIC = $(HOST_CFLAGS) -Iinclude -Itest -UNIT_CFLAGS_INTERNAL = $(HOST_CFLAGS) -Iinclude -Isrc -Itest - -# ---- registrations: stem lists + per-stem source --------------------------- - -UNIT_TESTS_PUBLIC := \ - ar_test cg_api_test cg_switch_test cg_fp_cmp_test hash_test rv64_jit_test \ - aa64_inline_test rv64_inline_test x64_inline_test strength_reduce_test -ar_test_SRC := test/ar/ar_test.c -hash_test_SRC := test/api/hash_test.c -cg_api_test_SRC := test/api/cg_type_test.c -cg_switch_test_SRC := test/api/cg_switch_test.c -cg_fp_cmp_test_SRC := test/api/cg_fp_cmp_test.c -strength_reduce_test_SRC := test/cg/strength_reduce_test.c -rv64_jit_test_SRC := test/link/rv64_jit_test.c -aa64_inline_test_SRC := test/arch/aa64_inline_test.c -rv64_inline_test_SRC := test/arch/rv64_inline_test.c -x64_inline_test_SRC := test/arch/x64_inline_test.c - -UNIT_TESTS_INTERNAL := \ - dwarf_test debug_roundtrip_unit debug_cfi_unit \ - aa64_isa_test rv64_decode_test aa64_sweep_gen \ - reloc_uleb128_unit emu_rv64_unit_test interp_smoke_test \ - rv64_interp_smoke_test abi_classify_test ir_recorder_test \ - native_direct_target_test x64_dbg_test cg_ir_lower_test tiny_inline_test -dwarf_test_SRC := test/dwarf/dwarf_test.c -debug_roundtrip_unit_SRC := test/debug/roundtrip_unit.c -debug_cfi_unit_SRC := test/debug/cfi_unit.c -aa64_isa_test_SRC := test/arch/aa64_isa_test.c -rv64_decode_test_SRC := test/arch/rv64_decode_test.c -aa64_sweep_gen_SRC := test/arch/aa64_sweep_gen.c -reloc_uleb128_unit_SRC := test/link/reloc_uleb128_unit.c -emu_rv64_unit_test_SRC := test/emu/rv64_vm_unit_test.c -interp_smoke_test_SRC := test/interp/interp_smoke_test.c -rv64_interp_smoke_test_SRC := test/emu/rv64_interp_smoke_test.c -abi_classify_test_SRC := test/api/abi_classify_test.c -ir_recorder_test_SRC := test/cg/ir_recorder_test.c -native_direct_target_test_SRC := test/cg/native_direct_target_test.c -x64_dbg_test_SRC := test/arch/x64_dbg_test.c -cg_ir_lower_test_SRC := test/opt/cg_ir_lower_test.c -tiny_inline_test_SRC := test/opt/tiny_inline_test.c - -# ---- build rules ------------------------------------------------------------ -# Secondary expansion lets a static-pattern prerequisite reference the -# per-stem source via $$($$*_SRC). -.SECONDEXPANSION: - -$(UNIT_TESTS_PUBLIC:%=build/test/%): build/test/%: $$($$*_SRC) $(UNIT_HDR_DEPS) $(LIB_AR) - @mkdir -p $(dir $@) - $(CC) $(UNIT_CFLAGS_PUBLIC) $($*_SRC) $(LIB_AR) -o $@ - -$(UNIT_TESTS_INTERNAL:%=build/test/%): build/test/%: $$($$*_SRC) $(UNIT_HDR_DEPS) $(LIB_OBJS) - @mkdir -p $(dir $@) - $(CC) $(UNIT_CFLAGS_INTERNAL) $($*_SRC) $(LIB_OBJS) -o $@ diff --git a/test/test.mk b/test/test.mk @@ -1,954 +0,0 @@ -# Data-driven tests. Included from the top-level Makefile. -# -# - test-driver: aggregate alias that runs all test-driver-* targets. -# - test-driver-cc: narrow CLI behavior checks that do not belong to a specific -# frontend/linker corpus. Depends on the kit driver binary. -# - test-pp: aggregate alias that runs test-pp-ok and test-pp-err. -# - test-pp-ok: C preprocessor success cases; depends on the kit driver binary. -# - test-elf: ELF roundtrip harness in test/elf/; depends only on -# libkit.a and compiles its own test binaries against it. Skipped -# layers are reported (set KIT_TEST_ALLOW_SKIP=1 to allow skips). -# - test-ar: in-process ar reader/writer tests; depends only on -# libkit.a. Set KIT_AR_TEST_HOST=1 to also dump produced bytes -# to /tmp and run the host's `ar t` / `nm --print-armap` as a -# cross-check. -# - test-driver-ar: scenario-driven CLI harness for `kit ar`. Each -# case under test/ar/cases/ runs a small script and diffs stdout. -# Depends on the kit driver binary. -# - test-link: linker + JIT behavioral harness in test/link/; three paths -# per case (roundtrip R, ELF exec E, JIT J). Depends only on libkit.a. -# Set KIT_TEST_ALLOW_SKIP=1 to allow skipped layers. -# - test-macho: Mach-O variant of test-link; defaults to roundtrip+JIT -# paths because hosted Mach-O executable execution is target/SDK-specific. -# - test-parse: aggregate alias that runs test-parse-ok and test-parse-err. -# - test-parse-ok: file-driven C parser success harness in test/parse/; each -# case is a .c source file. Built against the public kit.h surface; -# reuses kit-roundtrip, link-exe-runner, and jit-runner. -# - test-asm: aggregate alias over the per-arch asm lanes test-asm-aa64, -# test-asm-x64 and test-asm-rv64. The file-driven assembler/disassembler -# harness in test/asm/ runs one arch per invocation (KIT_TEST_ARCH) over -# three sub-corpora (encode/, decode/, listing/), one mode per sub-dir. -# aa64 runs the full path set (incl. native exec on aa64 hosts); x64/rv64 -# run the host-independent encode/decode/listing lanes only. See doc/ASM.md. - -TEST_TARGETS = \ - test-cf-corpus-selftest \ - test-aa64-inline \ - test-abi-classify \ - test-ar \ - test-asm \ - test-asm-aa64 \ - test-asm-x64 \ - test-asm-rv64 \ - test-disasm-complete \ - test-asm-roundtrip \ - test-asm-roundtrip-exec \ - test-asm-symmetry \ - test-asm-roundtrip-toy \ - test-hostas-toy \ - test-hostas-cross \ - test-diff-llvm \ - test-bootstrap-toy \ - test-bootstrap-toy-debug \ - test-bootstrap-toy-release \ - test-bounce \ - test-cbackend \ - test-cg-api \ - test-coff \ - test-coff-mingw-import \ - test-coff-windows-ucrt \ - test-debug \ - test-dbg \ - test-driver \ - test-driver-ar \ - test-driver-cas \ - test-driver-cc \ - test-driver-compile \ - test-driver-objcopy \ - test-driver-objdump \ - test-driver-pkg \ - test-driver-strings \ - test-driver-tools \ - test-hash \ - test-driver-strip \ - test-dwarf \ - test-elf \ - test-emu \ - test-emu-unit \ - test-interp \ - test-interp-emu \ - test-interp-toy \ - test-ir-recorder \ - test-isa \ - test-lib-deps \ - test-libc \ - test-libc-glibc \ - test-libc-glibc-rv64 \ - test-libc-musl \ - test-libc-musl-rv64 \ - test-link \ - test-link-reloc-uleb128 \ - test-macho \ - test-native-direct-target \ - test-opt \ - test-parse \ - test-parse-err \ - test-parse-ok \ - test-parse-rv64-wide \ - test-pp \ - test-pp-err \ - test-pp-ok \ - test-rt-headers \ - test-rt-runtime \ - test-link-x64 \ - test-rv64-inline \ - test-rv64-jit \ - test-rv64-tls-link \ - test-smoke-rv64 \ - test-smoke-x64 \ - test-toy \ - test-wasm \ - test-wasm-c \ - test-wasm-front \ - test-wasm-target \ - test-wasm-toy \ - test-x64-dbg \ - test-x64-inline - -DEFAULT_TEST_TARGETS = \ - test-cf-corpus-selftest \ - test-driver \ - test-pp \ - test-elf \ - test-coff \ - test-ar \ - test-link \ - test-toy \ - test-dwarf \ - test-debug \ - test-parse \ - test-asm \ - test-asm-roundtrip \ - test-isa \ - test-aa64-inline \ - test-rv64-inline \ - test-rv64-jit \ - test-rv64-tls-link \ - test-emu \ - test-emu-unit \ - test-interp \ - test-interp-emu \ - test-x64-inline \ - test-x64-dbg \ - test-rt-headers \ - test-lib-deps \ - test-cg-api \ - test-abi-classify \ - test-ir-recorder \ - test-native-direct-target \ - test-opt \ - test-asm-symmetry \ - test-link-reloc-uleb128 \ - test-dbg \ - test-disasm-complete \ - test-macho \ - test-interp-toy \ - test-wasm \ - test-libc \ - test-link-x64 \ - test-rt-runtime \ - test-bounce \ - bootstrap \ - test-bootstrap-toy - -.PHONY: test $(TEST_TARGETS) - -test: $(DEFAULT_TEST_TARGETS) - -# Unit-test binary build rules: two regimes (public vs internal interface). -include test/lib/unit.mk - -# Provision the pinned per-arch container rootfs images that exec_target.sh runs -# kit-emitted binaries inside. This is the ONLY test step that touches the -# network: the cross-arch exec harnesses (toy/parse/link ... path X/E/L) run with -# `podman run --pull=never`, so without these images those paths SKIP. Run once, -# and again only after the pin in test/lib/test_images.sh changes. FORCE=1 -# re-pulls. The images are pinned per-arch by content digest, so they coexist in -# local storage and can never clobber one another. -.PHONY: test-images -test-images: - @bash test/lib/pull_test_images.sh - -# Hermetic self-test of the shared corpus harness engine (test/lib/kit_corpus.sh): -# asserts serial==parallel determinism, SKIP-NA, and the parallel-safety -# invariant (exec_target queued only on the parent, never in a worker). No -# kit binary / podman / qemu needed. -test-cf-corpus-selftest: - @bash test/lib/kit_corpus_selftest.sh - -test-driver: test-driver-cc test-driver-compile test-driver-ar test-driver-cas test-driver-strip test-driver-objcopy test-driver-objdump test-driver-pkg test-driver-strings test-driver-tools - -test-driver-cc: bin - @KIT=$(abspath $(BIN)) sh test/driver/run.sh - -test-driver-compile: bin - @KIT=$(abspath $(BIN)) sh test/compile/run.sh - -# test-cbackend: --emit=c C-source backend, driven through three -# frontends — parse-runner (C), toy-runner (toy), wasm-runner (wat/wasm). -# Each invokes its existing runner with paths=C so a single corpus per -# frontend exercises both the existing backends and the C backend. -# Together they prove the CGTarget seam is frontend-agnostic. -# Unimplemented CGTarget methods report as SKIP; see doc/CBACKEND.md. -KIT_CBACKEND_TEST_JOBS ?= $(if $(KIT_TEST_JOBS),$(KIT_TEST_JOBS),1) -test-cbackend: bin - @KIT_TEST_JOBS=$(KIT_CBACKEND_TEST_JOBS) KIT_TEST_PATHS=C KIT_TEST_ALLOW_SKIP=1 sh test/parse/run.sh - @KIT_TEST_JOBS=$(KIT_CBACKEND_TEST_JOBS) KIT_TEST_PATHS=C KIT=$(abspath $(BIN)) sh test/toy/run.sh - @KIT_TEST_JOBS=$(KIT_CBACKEND_TEST_JOBS) KIT_TEST_PATHS=C KIT=$(abspath $(BIN)) bash test/wasm/run.sh - -# test-wasm-toy: opt-in Toy -> Wasm -> JIT roundtrip. Runs the toy corpus -# under the W path (compile -target wasm32-none, then `kit run` the .wasm, -# which routes back through the lang/wasm frontend to native CG). Most cases -# will fail or skip today; the target exists so progress on the Wasm CGTarget -# is visible without putting noise into the default `test` summary. -# Drop `KIT_TEST_ALLOW_SKIP=1` once the corpus is mostly green. -test-wasm-toy: bin - @KIT_TEST_PATHS=W KIT_TEST_ALLOW_SKIP=1 KIT=$(abspath $(BIN)) sh test/toy/run.sh - -# test-wasm-c: opt-in C -> Wasm -> JIT roundtrip, the C-frontend analogue of -# test-wasm-toy. Runs the test/parse corpus under the W path (compile -# -target wasm32-none, then `kit run -e test_main` the .wasm, which routes -# back through the lang/wasm frontend to native CG). The C corpus exercises -# far more of the language than the toy corpus, so expect many SKIPs for -# not-yet-implemented Wasm lowerings; the target makes that progress visible -# without adding noise to the default `test` summary. Drop -# `KIT_TEST_ALLOW_SKIP=1` once the corpus is mostly green. -test-wasm-c: bin $(PARSE_RUNNER) - @KIT_TEST_PATHS=W KIT_TEST_ALLOW_SKIP=1 KIT=$(abspath $(BIN)) bash test/parse/run.sh - -test-pp: test-pp-ok test-pp-err - -test-pp-ok: bin - @KIT=$(abspath $(BIN)) test/pp/run.sh - -test-pp-err: bin - @KIT=$(abspath $(BIN)) test/pp/run_errors.sh - -# Best-effort kit binary build: Layer D needs build/kit, but the -# binary may not link until enough libkit symbols exist. The harness -# detects a missing binary and skips that layer; don't break test-elf -# when bin fails. -.PHONY: bin-soft -bin-soft: - -@$(MAKE) bin 2>/dev/null || true - -AR_TEST_BIN = build/test/ar_test - -test-ar: $(AR_TEST_BIN) - $(AR_TEST_BIN) - - -test-driver-ar: bin - @KIT=$(abspath $(BIN)) test/ar/run.sh - -test-driver-cas: bin - @KIT=$(abspath $(BIN)) sh test/cas/run.sh - -test-driver-strip: bin - @KIT=$(abspath $(BIN)) test/strip/run.sh - -test-driver-objcopy: bin - @KIT=$(abspath $(BIN)) test/objcopy/run.sh - -test-driver-objdump: bin - @KIT=$(abspath $(BIN)) sh test/objdump/run.sh - -test-driver-pkg: bin - @KIT=$(abspath $(BIN)) sh test/pkg/run.sh - -test-driver-strings: bin - @KIT=$(abspath $(BIN)) sh test/strings/run.sh - -test-driver-tools: bin - @KIT=$(abspath $(BIN)) sh test/tools/run.sh - -# DWARF consumer unit test: builds a hand-crafted DWARF-bearing ELF in -# memory and exercises every kit_dwarf_* entry. It reaches into the -# internal object builder to synthesize the fixture, so link individual -# lib objects rather than libkit.a (which hides internal symbols). -DWARF_TEST_BIN = build/test/dwarf_test - -test-dwarf: $(DWARF_TEST_BIN) - $(DWARF_TEST_BIN) - - -# DWARF producer self-roundtrip unit test. Drives Debug directly, calls -# debug_emit, asserts the produced sections have valid DWARF 5 structure -# (length fields, version, address sizes, expected relocations against -# function symbol). Deliberately bypasses the consumer (kit_dwarf_open) -# so encoder bugs aren't masked by matching decoder bugs. -DEBUG_TEST_BIN = build/test/debug_roundtrip_unit -CFI_TEST_BIN = build/test/debug_cfi_unit - -test-debug: $(DEBUG_TEST_BIN) $(CFI_TEST_BIN) - $(DEBUG_TEST_BIN) - $(CFI_TEST_BIN) - - -# CFI/.eh_frame producer roundtrip for aa64/rv64/x64 (validates the per-arch -# CIE template: code/data-align, return-address reg, CFA-init reg). - -test-dbg: bin - @KIT=$(abspath $(BIN)) sh test/dbg/run.sh - -.PHONY: test-dbg-red -test-dbg-red: bin - @KIT=$(abspath $(BIN)) DBG_STRICT_XFAIL=1 sh test/dbg/run.sh - -# aa64 ISA descriptor-table unit test (doc/ASM.md phase 2). Covers -# every AA64Format the table maps and the alias-precedence invariant -# (first-match disasm picks the alias spelling over the canonical -# form). Internal arch/ surface — needs -Isrc. -AA64_ISA_TEST_BIN = build/test/aa64_isa_test -RV64_DECODE_TEST_BIN = build/test/rv64_decode_test - -test-isa: $(AA64_ISA_TEST_BIN) $(RV64_DECODE_TEST_BIN) - $(AA64_ISA_TEST_BIN) - $(RV64_DECODE_TEST_BIN) - - - -# aa64_sweep_gen: emits one representative encoding per disasm-table row for the -# asm<->disasm self-symmetry sweep (test/asm/symmetry.sh). Needs the internal -# arch/aa64/isa.h surface, so -Isrc + LIB_OBJS like the ISA unit test. -AA64_SWEEP_GEN = build/test/aa64_sweep_gen - -# test-emu: emulator end-to-end integration test. Builds tiny in-memory rv64 -# ELFs and runs them to their exit syscall, asserting the exit code — entirely -# through the PUBLIC kit_emu_* API, so it links the public archive ($(LIB_AR), -# whose `ld -r` step localizes internal symbols). -Isrc is only for the -# header-only rv64 encoders / ELF constants the in-memory ELF builders use. -EMU_RV64_TEST_BIN = build/test/emu_rv64_test - -test-emu: $(EMU_RV64_TEST_BIN) - $(EMU_RV64_TEST_BIN) - -$(EMU_RV64_TEST_BIN): test/emu/rv64_smoke_test.c $(UNIT_HDR_DEPS) $(LIB_AR) - @mkdir -p $(dir $@) - $(CC) $(HOST_CFLAGS) -Iinclude -Isrc -Itest test/emu/rv64_smoke_test.c $(LIB_AR) -o $@ - -# RISC-V ULEB128 diff-reloc application unit test. link_reloc_apply is an -# internal (hidden) symbol, so link the raw lib objects like the other -# internal-surface unit tests rather than libkit.a. -RELOC_ULEB128_TEST_BIN = build/test/reloc_uleb128_unit - -test-link-reloc-uleb128: $(RELOC_ULEB128_TEST_BIN) - $(RELOC_ULEB128_TEST_BIN) - -# test-emu-unit: white-box unit tests for the emulator's INTERNAL units (rv64 -# decoder, EmuAddrSpace, Linux syscall handler) that have no public API. Reaches -# internal symbols -> links $(LIB_OBJS) (mirrors test-interp), not the archive. -EMU_RV64_UNIT_TEST_BIN = build/test/emu_rv64_unit_test - -test-emu-unit: $(EMU_RV64_UNIT_TEST_BIN) - $(EMU_RV64_UNIT_TEST_BIN) - - -# test-interp: threaded-bytecode interpreter unit smoke test. Builds tiny CG IR -# by hand, runs opt_run_o1_interp + interp_lower + the engine, asserts the -# returned value. Reaches internal opt/interp symbols -> links $(LIB_OBJS) and -# needs -Isrc (mirrors test-opt). -INTERP_SMOKE_TEST_BIN = build/test/interp_smoke_test - -test-interp: $(INTERP_SMOKE_TEST_BIN) - $(INTERP_SMOKE_TEST_BIN) - - -# test-interp-emu: differential test of the emulator's INTERP execution mode -# (doc/INTERPRETER.md Phase 4). Builds a tiny rv64 ELF with SD/LD/ecall and runs -# it through kit_emu_run in JIT and INTERP modes (both -O1), asserting the exit -# codes match. Public-API only, but links $(LIB_OBJS) like test-interp to dodge -# the visibility-hidden archive's localized internals. -INTERP_EMU_TEST_BIN = build/test/rv64_interp_smoke_test - -test-interp-emu: $(INTERP_EMU_TEST_BIN) - $(INTERP_EMU_TEST_BIN) - - -# test-interp-toy: run the toy suite's interpreter (--no-jit) path only, -# asserting it matches the golden exit codes (and SKIPping unimplemented ops). -test-interp-toy: bin - @KIT=$(abspath $(BIN)) KIT_TEST_PATHS=I bash test/toy/run.sh - -CG_API_TEST_BIN = build/test/cg_api_test -CG_SWITCH_TEST_BIN = build/test/cg_switch_test -CG_FP_CMP_TEST_BIN = build/test/cg_fp_cmp_test -STRENGTH_REDUCE_TEST_BIN = build/test/strength_reduce_test -HASH_TEST_BIN = build/test/hash_test -ABI_CLASSIFY_TEST_BIN = build/test/abi_classify_test -IR_RECORDER_TEST_BIN = build/test/ir_recorder_test -NATIVE_DIRECT_TARGET_TEST_BIN = build/test/native_direct_target_test - -test-cg-api: $(CG_API_TEST_BIN) $(CG_SWITCH_TEST_BIN) $(CG_FP_CMP_TEST_BIN) \ - $(STRENGTH_REDUCE_TEST_BIN) - $(CG_API_TEST_BIN) - $(CG_SWITCH_TEST_BIN) - $(CG_FP_CMP_TEST_BIN) - $(STRENGTH_REDUCE_TEST_BIN) - -test-hash: $(HASH_TEST_BIN) - $(HASH_TEST_BIN) - -test-abi-classify: $(ABI_CLASSIFY_TEST_BIN) - $(ABI_CLASSIFY_TEST_BIN) - - - - -test-ir-recorder: $(IR_RECORDER_TEST_BIN) - $(IR_RECORDER_TEST_BIN) - - -test-native-direct-target: $(NATIVE_DIRECT_TARGET_TEST_BIN) - $(NATIVE_DIRECT_TARGET_TEST_BIN) - - -test-toy: bin - @KIT=$(abspath $(BIN)) test/toy/run.sh - -# test-bootstrap-toy: run the Toy corpus through the bootstrapped (self-built) -# stage3 kit instead of the host-built binary, so the self-hosted compiler is -# exercised on real codegen (not just self-reproduction like `make bootstrap`). -# Split per build mode: -debug runs the debug stage3, -release the release -# (-O1) stage3; the aggregate target runs both. -test-bootstrap-toy: test-bootstrap-toy-debug test-bootstrap-toy-release - -test-bootstrap-toy-debug: bootstrap-debug - @KIT='$(abspath $(BUILD_DIR)/debug/bootstrap/stage3/kit)' test/toy/run.sh - -test-bootstrap-toy-release: bootstrap-release - @KIT='$(abspath $(BUILD_DIR)/release/bootstrap/stage3/kit)' test/toy/run.sh - - -# Public-API inline-asm backend tests. These emit a tiny function through CG, -# reopen the object through the public object reader, and assert the expected -# instruction bytes are present in .text. -AA64_INLINE_TEST_BIN = build/test/aa64_inline_test - -test-aa64-inline: $(AA64_INLINE_TEST_BIN) - $(AA64_INLINE_TEST_BIN) - - -RV64_INLINE_TEST_BIN = build/test/rv64_inline_test - -test-rv64-inline: $(RV64_INLINE_TEST_BIN) - $(RV64_INLINE_TEST_BIN) - - -# rv64 JIT smoke test. Builds a tiny rv64 ELF .o in memory, runs it -# through kit_link_session in JIT-output mode, and skips native execution -# on non-riscv64 hosts after exercising the JIT mapping/reloc path. -RV64_JIT_TEST_BIN = build/test/rv64_jit_test - -test-rv64-jit: $(RV64_JIT_TEST_BIN) - @$(RV64_JIT_TEST_BIN); rc=$$?; \ - if [ $$rc -eq 77 ]; then \ - echo " (rv64_jit_test SKIPPED on non-rv64 host)"; \ - exit 0; \ - else \ - exit $$rc; \ - fi - - -# Link-only regression for rv64 TLS Local-Exec lowering (runs on any host). -test-rv64-tls-link: bin - @KIT='$(abspath $(BIN))' bash test/smoke/rv64_tls_link.sh - -X64_INLINE_TEST_BIN = build/test/x64_inline_test - -test-x64-inline: $(X64_INLINE_TEST_BIN) - $(X64_INLINE_TEST_BIN) - - -X64_DBG_TEST_BIN = build/test/x64_dbg_test - -test-x64-dbg: $(X64_DBG_TEST_BIN) - $(X64_DBG_TEST_BIN) - -# Reaches the internal arch/arch.h surface (arch_lookup, ArchDbgOps) -> links -# $(LIB_OBJS), not the archive, whose relocatable merge localizes non-public -# symbols (mirrors the aa64/rv64 arch unit tests above). - -RT_HEADER_TEST_TARGETS = \ - aarch64-linux-gnu \ - x86_64-linux-gnu \ - riscv64-linux-gnu \ - aarch64-apple-darwin \ - x86_64-apple-darwin - -test-rt-headers: bin - @set -e; \ - for target in $(RT_HEADER_TEST_TARGETS); do \ - out="build/test/rt-headers/$$target/smoke.o"; \ - mkdir -p "$$(dirname "$$out")"; \ - $(BIN) cc -target "$$target" -Werror -c test/rt/smoke.c -o "$$out"; \ - done - -# Arch token -> linux rt variant target. The cross-target test harnesses link -# the matching LINUX runtime archive (compiler-rt-style helpers like -# __kit_sext64ti, __kit_assert_fail) rather than the native `rt` target, -# which builds only the host's own variant. Shared by the single-arch -# parse/asm/macho/link tests (via KIT_TEST_ARCH/TEST_RT_DEP) and by -# test-rt-runtime, which sweeps several arches at once. -KIT_TEST_ARCH ?= aa64 -_TEST_RT_aa64 = rt-aarch64-linux -_TEST_RT_aarch64 = rt-aarch64-linux -_TEST_RT_arm64 = rt-aarch64-linux -_TEST_RT_x64 = rt-x86_64-linux -_TEST_RT_x86_64 = rt-x86_64-linux -_TEST_RT_amd64 = rt-x86_64-linux -_TEST_RT_rv64 = rt-riscv64-linux -_TEST_RT_riscv64 = rt-riscv64-linux -TEST_RT_DEP = $(_TEST_RT_$(KIT_TEST_ARCH)) - -# test-rt-runtime compiles each case for the configured arches and links it -# against that arch's LINUX runtime archive, so it must depend on each variant -# (not the host `rt`). A stale per-arch archive otherwise silently breaks the -# link — e.g. a dropped __kit_assert_fail weak def surfaces as an undefined -# reference. KIT_RT_RUNTIME_ARCHES mirrors the default in test/rt/run.sh. -KIT_RT_RUNTIME_ARCHES ?= aa64 x64 rv64 -RT_RUNTIME_DEPS := $(foreach a,$(KIT_RT_RUNTIME_ARCHES),$(_TEST_RT_$(a))) - -test-rt-runtime: bin $(RT_RUNTIME_DEPS) $(LINK_EXE_RUNNER) - @bash test/rt/run.sh - -# Test harness binaries shared by test-elf and test-link. -# Declared as Make targets (not built by the run.sh scripts) so they pick -# up libkit.a changes deterministically. -# -# HARNESS_CFLAGS drops -Wpedantic from the normal host flags; the runners cast -# kit_jit_lookup's void* to a function pointer, which pedantic rejects under -# C11. -HARNESS_CFLAGS = $(filter-out -Wpedantic,$(HOST_CFLAGS)) -Iinclude -Itest - -ROUNDTRIP_BIN = build/test/kit-roundtrip -ROUNDTRIP_BIN_MACHO = build/test/kit-roundtrip-macho -ROUNDTRIP_BIN_COFF = build/test/kit-roundtrip-coff -COFF_IMPORT_SMOKE_BIN = build/test/pe-import-smoke -COFF_IMPORT_MINGW_BIN = build/test/pe-import-mingw -COFF_DSO_FORWARDER_BIN = build/test/pe-dso-forwarder -COFF_MIXED_ARCHIVE_BIN = build/test/pe-mixed-archive -LINK_EXE_RUNNER = build/test/link-exe-runner -JIT_RUNNER = build/test/jit-runner -PARSE_RUNNER = build/test/parse-runner -ASM_RUNNER = build/test/asm-runner -WASM_TOOL = build/test/wasm-tool - -$(ROUNDTRIP_BIN): test/elf/kit-roundtrip.c $(LIB_AR) - @mkdir -p $(dir $@) - $(CC) $(HARNESS_CFLAGS) test/elf/kit-roundtrip.c $(LIB_AR) -o $@ - -# Mach-O peer of kit-roundtrip — read_macho + emit_macho. Used by -# test-link's path R when KIT_TEST_OBJ=macho. -$(ROUNDTRIP_BIN_MACHO): test/macho/kit-roundtrip-macho.c $(LIB_AR) - @mkdir -p $(dir $@) - $(CC) $(HARNESS_CFLAGS) -Isrc test/macho/kit-roundtrip-macho.c $(LIB_AR) -o $@ - -# PE/COFF round-trip harness (test/coff/). All-in-one binary: builds -# hand-crafted ObjBuilders and asserts emit_coff/read_coff round-trip -# stability for both x86_64-windows and aarch64-windows. -$(ROUNDTRIP_BIN_COFF): test/coff/kit-roundtrip-coff.c $(LIB_OBJS) - @mkdir -p $(dir $@) - $(CC) $(HARNESS_CFLAGS) -Isrc test/coff/kit-roundtrip-coff.c $(LIB_OBJS) -o $@ - -# PE import-directory smoke test (test/coff/pe-import-smoke.c). -# Exercises the full chain: short-import shim bytes -> link_add_obj_bytes -# (reclassified as DSO) -> link_resolve -> link_emit_coff. Verifies the -# produced PE32+ via x86_64-w64-mingw32-objdump; skips cleanly if absent. -$(COFF_IMPORT_SMOKE_BIN): test/coff/pe-import-smoke.c $(LIB_OBJS) - @mkdir -p $(dir $@) - $(CC) $(HARNESS_CFLAGS) -Isrc test/coff/pe-import-smoke.c $(LIB_OBJS) -o $@ - -# PE import test against a real mingw archive (test/coff/pe-import-mingw.c). -# Exercises the long-form import-archive absorption path -# (link_add_archive_bytes -> classify_coff_archive_member). Skips cleanly -# when the mingw toolchain isn't installed. -$(COFF_IMPORT_MINGW_BIN): test/coff/pe-import-mingw.c $(LIB_OBJS) - @mkdir -p $(dir $@) - $(CC) $(HARNESS_CFLAGS) -Isrc test/coff/pe-import-mingw.c $(LIB_OBJS) -o $@ - -# read_coff_dso forwarder-export contract (test/coff/pe-dso-forwarder.c). -# Synthesizes a tiny PE32+ DLL with one direct and one forwarder export -# and asserts both surface as OBJ_SEC_NONE globals on the ObjBuilder. -$(COFF_DSO_FORWARDER_BIN): test/coff/pe-dso-forwarder.c $(LIB_OBJS) - @mkdir -p $(dir $@) - $(CC) $(HARNESS_CFLAGS) -Isrc test/coff/pe-dso-forwarder.c $(LIB_OBJS) -o $@ - -# Mixed-member archive (test/coff/pe-mixed-archive.c). Verifies that -# one archive containing both a short-import member and a long-form -# COFF object with a defined data symbol satisfies references through -# both shapes — the same composition libucrt.a uses (API-set imports -# alongside lib64_libucrt_extra_a-*.o helpers). -$(COFF_MIXED_ARCHIVE_BIN): test/coff/pe-mixed-archive.c $(LIB_OBJS) - @mkdir -p $(dir $@) - $(CC) $(HARNESS_CFLAGS) -Isrc test/coff/pe-mixed-archive.c $(LIB_OBJS) -o $@ - -$(LINK_EXE_RUNNER): test/link/harness/link_exe_runner.c $(LIB_AR) - @mkdir -p $(dir $@) - $(CC) $(HARNESS_CFLAGS) test/link/harness/link_exe_runner.c $(LIB_AR) -o $@ - -$(JIT_RUNNER): test/link/harness/jit_runner.c $(LIB_AR) - @mkdir -p $(dir $@) - $(CC) $(HARNESS_CFLAGS) test/link/harness/jit_runner.c $(LIB_AR) -o $@ - -$(PARSE_RUNNER): test/parse/harness/parse_runner.c $(LIB_AR) - @mkdir -p $(dir $@) - $(CC) $(HARNESS_CFLAGS) test/parse/harness/parse_runner.c $(LIB_AR) -o $@ - -$(ASM_RUNNER): test/asm/harness/asm_runner.c $(LIB_AR) - @mkdir -p $(dir $@) - $(CC) $(HARNESS_CFLAGS) test/asm/harness/asm_runner.c $(LIB_AR) -o $@ - -$(WASM_TOOL): test/wasm/harness/wasm_tool.c $(LIB_AR) - @mkdir -p $(dir $@) - $(CC) $(HARNESS_CFLAGS) -I. test/wasm/harness/wasm_tool.c $(LIB_AR) -o $@ - -test-elf: lib bin-soft $(ROUNDTRIP_BIN) - KIT_ELF_UNIT_CFLAGS='$(HOST_MODE_CFLAGS)' \ - KIT_ELF_UNIT_LDFLAGS='$(HOST_MODE_LDFLAGS)' \ - bash test/elf/run.sh - -# PE/COFF round-trip harness plus optional hosted Windows smoke. The -# UCRT smoke self-skips when llvm-mingw is not installed. -test-coff: lib bin rt-aarch64-windows $(ROUNDTRIP_BIN_COFF) $(COFF_IMPORT_SMOKE_BIN) $(COFF_DSO_FORWARDER_BIN) $(COFF_MIXED_ARCHIVE_BIN) - $(ROUNDTRIP_BIN_COFF) - $(COFF_IMPORT_SMOKE_BIN) - $(COFF_DSO_FORWARDER_BIN) - $(COFF_MIXED_ARCHIVE_BIN) - bash test/coff/windows-ucrt-hosted-smoke.sh - bash test/coff/windows-system-dlls-smoke.sh - -# Separate target so it can be skipped gracefully if mingw isn't -# installed. The test itself self-skips on missing tooling, but the -# build target only fires when explicitly requested. -test-coff-mingw-import: lib $(COFF_IMPORT_MINGW_BIN) - $(COFF_IMPORT_MINGW_BIN) - -test-coff-windows-ucrt: bin rt-aarch64-windows - bash test/coff/windows-ucrt-hosted-smoke.sh - -# The parse/asm/macho harnesses select a cross-target via KIT_TEST_ARCH -# (default aa64); the link rt dependency is resolved through the shared -# _TEST_RT_<arch> map defined above (near test-rt-runtime). -test-link: lib $(ROUNDTRIP_BIN) $(ROUNDTRIP_BIN_MACHO) $(LINK_EXE_RUNNER) $(JIT_RUNNER) - bash test/link/run.sh - -# x64 ELF link/reloc-application coverage. test-link defaults to aa64, so -# kit's x64 static-link reloc fixups (R_X64_PLT32/GOTPCREL/TPOFF/...) were -# only ever run via a manual KIT_TEST_ARCH=x64 override. Opt-in (not in the -# default set) because the E path links + runs under podman/qemu-x86_64; the R -# path (roundtrip + reloc layout) runs on any host. -test-link-x64: lib rt-x86_64-linux $(ROUNDTRIP_BIN) $(LINK_EXE_RUNNER) $(JIT_RUNNER) - @KIT_TEST_ARCH=x64 KIT_TEST_PATHS=RE bash test/link/run.sh - -test-macho: lib $(TEST_RT_DEP) $(ROUNDTRIP_BIN_MACHO) $(LINK_EXE_RUNNER) $(JIT_RUNNER) - KIT_TEST_OBJ=macho \ - KIT_TEST_ARCH=$${KIT_TEST_ARCH:-aa64} \ - KIT_TEST_PATHS=$${KIT_TEST_PATHS:-RJ} \ - KIT_TEST_ALLOW_SKIP=$${KIT_TEST_ALLOW_SKIP:-1} \ - bash test/link/run.sh - -OPT_TEST_BIN = build/test/cg_ir_lower_test -TINY_INLINE_TEST_BIN = build/test/tiny_inline_test - -test-opt: bin $(OPT_TEST_BIN) test-opt-tiny-inline test-opt-inline test-opt-zero-arg test-opt-static-prune-aa64 test-opt-aa64-tail test-opt-prologue-tier - $(OPT_TEST_BIN) - - -test-opt-tiny-inline: bin $(TINY_INLINE_TEST_BIN) - $(TINY_INLINE_TEST_BIN) - - -# Behavioral disasm check: tiny callee `bl` disappears from its caller at -O1. -test-opt-inline: bin - @KIT=$(abspath $(BIN)) bash test/opt/run.sh - -# Behavioral disasm check: a pointer-typed null call arg is not routed through -# a scratch temp at -O1 (PERCALL.md item 3, "zero through a temp"). -test-opt-zero-arg: bin - @KIT=$(abspath $(BIN)) bash test/opt/zero_arg.sh - -.PHONY: test-opt-static-prune-aa64 -test-opt-static-prune-aa64: bin - @KIT=$(abspath $(BIN)) bash test/opt/static_prune_aa64.sh - -.PHONY: test-opt-aa64-tail -test-opt-aa64-tail: bin - @KIT=$(abspath $(BIN)) bash test/opt/aa64_tail_call.sh - -# Structural disasm check: the -O1 known-frame prologue cost-model tiers -# (aa64 reference + ported x64 slim/red-zone and rv64 leaf shapes). -.PHONY: test-opt-prologue-tier -test-opt-prologue-tier: bin - @KIT=$(abspath $(BIN)) bash test/opt/prologue_tier.sh - -test-parse: test-parse-ok test-parse-err - -test-parse-ok: lib $(TEST_RT_DEP) $(PARSE_RUNNER) $(ROUNDTRIP_BIN) $(LINK_EXE_RUNNER) $(JIT_RUNNER) - bash test/parse/run.sh - -test-parse-err: lib $(PARSE_RUNNER) - sh test/parse/run_errors.sh - -# test-asm: aggregate alias running every per-arch asm lane (aa64 + x64 + rv64) -# so `make test` covers all three through one target. The harness runs one arch -# per invocation (KIT_TEST_ARCH); each lane scopes its scratch per arch, so -# the prerequisites are safe to run in parallel under `make -j`. -test-asm: test-asm-aa64 test-asm-x64 test-asm-rv64 - -# test-asm-aa64: the reference lane. aa64 is the default cross-target, and on -# aa64 hosts the exec paths (D/E/J) run natively, so it uses the full default -# path set (HTLDJE). The Makefile owns the harness binaries so they inherit host -# flags consistently with the rest of the test suite. -test-asm-aa64: lib $(TEST_RT_DEP) $(ASM_RUNNER) $(LINK_EXE_RUNNER) $(JIT_RUNNER) - @KIT_TEST_ARCH=aa64 bash test/asm/run.sh - -# x64/rv64 exercise the encode (H), decode (T) and listing (L) corpora on any -# host: H/T/L need no native execution (they only produce/compare bytes), so the -# exec paths (D/E/J) are deliberately excluded and left to the smoke/qemu -# targets. -test-asm-x64: lib $(ASM_RUNNER) - @KIT_TEST_ARCH=x64 KIT_TEST_PATHS=HTL bash test/asm/run.sh -test-asm-rv64: lib $(ASM_RUNNER) - @KIT_TEST_ARCH=rv64 KIT_TEST_PATHS=HT bash test/asm/run.sh - -# Codegen round-trip completeness (doc/ASM_ROUNDTRIP_TESTING.md). These drive -# the `kit` binary itself (cc -S / as / objdump) over a C corpus rather than -# a hand-written asm corpus, so coverage tracks codegen automatically. -# -# test-disasm-complete L0: cc -S must decode every in-function word -# (no `.inst` markers). Host-independent, no exec. -# test-asm-roundtrip L0+L1: also assert cc -c bytes/relocs == cc -S | as. -# test-asm-roundtrip-exec L0+L1+L2: also run direct vs round-tripped object -# and compare exit codes (native arch; opt-in). -# -# Vertical slice: aa64 only for now; L1/L2 run at -O1 (branch-free), L0 at both -# opt levels. Broadening to -O0, other arches, and the default suite is tracked -# in doc/ASM_ROUNDTRIP_TESTING.md once -S symbolization (Phase 2) lands. -test-disasm-complete: bin - @KIT_TEST_ARCH=aa64 KIT_TEST_OPTS="O0 O1" KIT_TEST_PATHS=0 \ - bash test/asm/roundtrip.sh -test-asm-roundtrip: bin - @KIT_TEST_ARCH=aa64 KIT_TEST_OPTS="O0 O1" KIT_TEST_PATHS=01 \ - bash test/asm/roundtrip.sh -test-asm-roundtrip-exec: bin $(JIT_RUNNER) - @KIT_TEST_ARCH=aa64 KIT_TEST_OPTS="O0 O1" KIT_TEST_PATHS=012 \ - bash test/asm/roundtrip.sh - -# test-asm-symmetry: asm<->disasm self-symmetry sweep (aa64). Decode-side sweeps -# every disasm-table form (decode->encode->decode fixed point); encode-side -# asserts every byte the assembler emits over the encode corpus is decodable. -# Catches encode/decode asymmetries the codegen round-trip can't reach (e.g. a -# form one tool handles and the other doesn't). Host-independent, no exec. -test-asm-symmetry: $(ASM_RUNNER) $(AA64_SWEEP_GEN) - @bash test/asm/symmetry.sh - -# test-diff-llvm: differential cross-check of kit against llvm (aa64), as a -# second oracle. Encode lane: kit as vs llvm-mc bytes over the encode corpus. -# Disasm lane: cc -c bytes vs llvm-mc of cc -S (validates kit's disassembler; -# the benign same-section-call reloc-vs-resolve difference is recognized). -# Opt-in; skips cleanly when llvm-mc is absent. -test-diff-llvm: bin - @KIT_TEST_OPTS="O0 O1" bash test/asm/diff_llvm.sh - -# test-asm-roundtrip-toy: L2 exec round-trip over the Toy corpus (native arch). -# Reuses the ~150 toy cases (full CG op set, exit-code oracle) for free -# round-trip coverage: kit cc -S | kit as | kit run, exit must match. -# Opt-in; native target. Found a real miscompile (dropped .inst) the hand -# corpus never reached. -test-asm-roundtrip-toy: bin - @bash test/asm/roundtrip_toy.sh - -# test-hostas-toy: feed one native `cc -S` to BOTH kit's own `as` and clang (a -# third-party host assembler), link + run each, and assert the toy exit-code -# oracle. Only the assembler differs between the two lanes, so the clang lane is -# the real test: a standard assembler can't paper over a private-dialect quirk -# the way kit's own `as` can (cf. test/asm/diff_llvm.sh, but by execution). -# cc -S is now object-format-aware, so the native Mach-O clang lane GATES by -# default (both lanes 312/0); KIT_HOSTAS_ENFORCE_CLANG=0 demotes it to XFAIL. -# Opt-in; skips cleanly if clang is absent. -test-hostas-toy: bin - @bash test/asm/hostas_toy.sh - -# test-hostas-cross: the same two-assembler-by-execution idea as test-hostas-toy, -# but CROSS — `cc -S -target <triple>` for ELF Linux arches (aarch64/x86_64/ -# riscv64), assembled by kit-as AND clang, linked static (+ the start.c crt) -# with kit ld, and run under podman/qemu via test/lib/exec_target.sh. Each -# target self-skips unless the host has a clang cross target, a runner, a -# working `cc -S | kit as` for that arch, and a passing bounded exec smoke — -# so it runs green on whatever the host supports (aarch64-linux today; x86_64 -# pends the x64 cc -S symbolizer, riscv64 pends a working rv64 user-mode -# emulator). Opt-in; skips cleanly if clang/podman are absent. -test-hostas-cross: bin - @bash test/asm/hostas_cross.sh - -test-wasm: test-wasm-front test-wasm-target test-wasm-toy - -test-wasm-front: bin $(WASM_TOOL) $(LINK_EXE_RUNNER) $(JIT_RUNNER) - bash test/wasm/run.sh - -# test-wasm-target: structural checks on `kit cc -target wasm32-none` -# output. test/wasm-target/run.sh is a Type C corpus harness whose lanes each -# compile a tiny C or toy fixture and assert a property of the produced module -# bytes (inline-asm opcodes, memory.copy/fill opcodes, exported "memory", -# (import ...) decls). Opt-in: not in the default `test` target because the -# checks depend on the bulk-memory + (import ...) backend work landing first. -test-wasm-target: bin - @KIT=$(abspath $(BIN)) bash test/wasm-target/run.sh - -# test-smoke-x64: phase-1 sanity check for the multi-arch bring-up. Builds a -# tiny freestanding x86_64 ELF with clang --target=x86_64-linux-gnu and -# runs it through test/lib/exec_target.sh's podman/qemu pipeline, -# proving the harness end-to-end before any kit-emitted x64 bytes -# exist. Excluded from the default `test` target because it needs -# podman + lld; opt-in via `make test-smoke-x64`. -test-smoke-x64: - bash test/smoke/x64.sh - -# test-smoke-rv64: phase-2 counterpart of test-smoke-x64. Builds a -# tiny freestanding riscv64 ELF with clang --target=riscv64-linux-gnu -# and runs it through test/lib/exec_target.sh, proving the rv64 lane -# of the harness end-to-end before any kit-emitted rv64 bytes -# exist. Excluded from the default `test` target because it needs -# qemu-riscv64 (or podman with riscv64 emulation) + lld; opt-in via -# `make test-smoke-rv64`. -test-smoke-rv64: - bash test/smoke/rv64.sh - -# test-parse-rv64-wide: end-to-end coverage of the rv64 128-bit scalar types -# — __int128 (i128_*) and IEEE-754 binary128 long double (ldbl128_*) — built -# with kit and run on riscv64. Exercises the soft-float / i128 lowering to -# the compiler-rt-style runtime (fp_tf, fp_ti, int64), the LP64D register-pair -# ABI for 16-byte scalars, and the conditional-branch range fix that large -# soft-float helpers depend on. Opt-in (needs qemu-riscv64 or podman with -# riscv64 emulation), so excluded from the default `test` target; mirrors -# test-smoke-rv64. Run a single case with -# KIT_TEST_ARCH=rv64 bash test/parse/run.sh ldbl128_03_arith -test-parse-rv64-wide: lib rt-riscv64-linux $(PARSE_RUNNER) $(ROUNDTRIP_BIN) \ - $(LINK_EXE_RUNNER) - @KIT_TEST_ARCH=rv64 KIT_TEST_PATHS=RE bash test/parse/run.sh 128 - -# test-bounce: format-bounce stress test. Compiles small programs with -# kit, then bounces each object through chains of format conversions -# (ELF<->Mach-O<->COFF), partial links (ld -r), strip, and archive -# round-trips, relinks, and runs the result, asserting the exit code -# matches a host-cc reference. Stresses obj read/write/reloc, ar, and -# partial-link paths rather than the arches. Defaults to the host-native -# Linux arch (no emulation); sweep others with -# KIT_BOUNCE_ARCHES="aarch64 x64 rv64". Excluded from the default `test` -# target because it needs podman/qemu + a host cc; opt-in via -# `make test-bounce`. -test-bounce: $(BIN) - bash test/bounce/bounce.sh - -# test-libc: aggregate alias that runs test-libc-musl and test-libc-glibc. -# test-libc-musl / test-libc-glibc: end-to-end static + dynamic libc link/run -# on aarch64. Each variant pulls its own pinned sysroot (podman, ~30s on -# first run) and shares the same case files under test/libc/cases/: -# -# test-libc-musl — Alpine 3.20 + musl 1.2.5 (test/libc/musl/) -# test-libc-glibc — Debian bookworm + glibc 2.36 (test/libc/glibc/) -# -# Both build build/rt/aarch64-linux/libkit_rt.a for soft-float / TF -# builtins, and run `kit ld` against the real libc.a (static) and -# libc.so / libc.so.6 (dynamic). Excluded from the -# default `test` target because they need podman; opt-in via -# `make test-libc-musl` / `make test-libc-glibc` (or `make test-libc` for both). -# -# Each sysroot is treated as a real prerequisite via its PROVENANCE -# marker so subsequent runs skip extraction and re-extract only when -# the file is removed (or extract.sh -f forces a rebuild). -MUSL_SYSROOT_MARKER = build/musl-sysroot/PROVENANCE -MUSL_SYSROOT_X64_MARKER = build/musl-sysroot-x64/PROVENANCE -MUSL_SYSROOT_RV64_MARKER = build/musl-sysroot-rv64/PROVENANCE -GLIBC_SYSROOT_MARKER = build/glibc-sysroot/PROVENANCE -GLIBC_SYSROOT_X64_MARKER = build/glibc-sysroot-x64/PROVENANCE -GLIBC_SYSROOT_RV64_MARKER = build/glibc-sysroot-rv64/PROVENANCE - -$(MUSL_SYSROOT_MARKER): test/libc/musl/extract.sh test/libc/musl/Containerfile - @bash test/libc/musl/extract.sh - -$(MUSL_SYSROOT_X64_MARKER): test/libc/musl/extract.sh test/libc/musl/Containerfile.x64 - @bash test/libc/musl/extract.sh -a x64 - -$(MUSL_SYSROOT_RV64_MARKER): test/libc/musl/extract.sh test/libc/musl/Containerfile.rv64 - @bash test/libc/musl/extract.sh -a rv64 - -$(GLIBC_SYSROOT_MARKER): test/libc/glibc/extract.sh test/libc/glibc/Containerfile - @bash test/libc/glibc/extract.sh - -$(GLIBC_SYSROOT_X64_MARKER): test/libc/glibc/extract.sh test/libc/glibc/Containerfile.x64 - @bash test/libc/glibc/extract.sh -a x64 - -$(GLIBC_SYSROOT_RV64_MARKER): test/libc/glibc/extract.sh test/libc/glibc/Containerfile.rv64 - @bash test/libc/glibc/extract.sh -a rv64 - -# test-libc-musl / test-libc-glibc honor KIT_LIBC_ARCHES (default "aa64"; -# values: aa64, x64, rv64). Each enabled arch contributes its sysroot -# PROVENANCE marker and its rt archive to the prerequisite list, so -# `KIT_LIBC_ARCHES="aa64 x64" make test-libc-musl` builds both sysroots -# + both rt archives before the runner script picks them up. -KIT_LIBC_ARCHES ?= aa64 - -# Map an arch token to its musl/glibc sysroot marker and rt target. -_LIBC_MUSL_SYSROOT_aa64 = $(MUSL_SYSROOT_MARKER) -_LIBC_MUSL_SYSROOT_x64 = $(MUSL_SYSROOT_X64_MARKER) -_LIBC_MUSL_SYSROOT_rv64 = $(MUSL_SYSROOT_RV64_MARKER) -_LIBC_GLIBC_SYSROOT_aa64 = $(GLIBC_SYSROOT_MARKER) -_LIBC_GLIBC_SYSROOT_x64 = $(GLIBC_SYSROOT_X64_MARKER) -_LIBC_GLIBC_SYSROOT_rv64 = $(GLIBC_SYSROOT_RV64_MARKER) -_LIBC_RT_aa64 = rt-aarch64-linux -_LIBC_RT_x64 = rt-x86_64-linux -_LIBC_RT_rv64 = rt-riscv64-linux - -LIBC_MUSL_DEPS = $(foreach a,$(KIT_LIBC_ARCHES),$(_LIBC_MUSL_SYSROOT_$(a)) $(_LIBC_RT_$(a))) -LIBC_GLIBC_DEPS = $(foreach a,$(KIT_LIBC_ARCHES),$(_LIBC_GLIBC_SYSROOT_$(a)) $(_LIBC_RT_$(a))) - -test-libc: test-libc-musl test-libc-glibc - -test-libc-musl: bin $(LIBC_MUSL_DEPS) - @KIT_LIBC_ARCHES="$(KIT_LIBC_ARCHES)" bash test/libc/musl/run.sh - -test-libc-glibc: bin $(LIBC_GLIBC_DEPS) - @KIT_LIBC_ARCHES="$(KIT_LIBC_ARCHES)" bash test/libc/glibc/run.sh - -test-libc-musl-rv64: - @$(MAKE) test-libc-musl KIT_LIBC_ARCHES=rv64 - -test-libc-glibc-rv64: - @$(MAKE) test-libc-glibc KIT_LIBC_ARCHES=rv64 - -# Fail if libkit.a depends on any external symbol not in the allowlist, or -# if a relocatable link exposes non-public global definitions. -# External dependency drift in either direction (new dep, or stale entry) is a -# failure. -# -# Inspect the RELEASE artifacts only: the debug build is ASan/UBSan-instrumented -# and pulls in __asan_*/__ubsan_* externals that are not part of the shipped -# library's dependency surface. -LIB_DEPS_RELEASE_DIR = build/release -LIB_DEPS_AR = $(LIB_DEPS_RELEASE_DIR)/libkit.a -LIB_DEPS_ACTUAL = $(LIB_DEPS_RELEASE_DIR)/libkit.deps.txt -LIB_RELOC = $(LIB_DEPS_RELEASE_DIR)/libkit.reloc.o -LIB_RELOC_BAD = $(LIB_DEPS_RELEASE_DIR)/libkit.reloc.bad-symbols.txt - -test-lib-deps: - @$(MAKE) lib RELEASE=1 - @mkdir -p $(dir $(LIB_DEPS_ACTUAL)) - @python3 scripts/lib_external_deps.py $(LIB_DEPS_AR) > $(LIB_DEPS_ACTUAL) - @diff -u scripts/lib_deps.allowlist $(LIB_DEPS_ACTUAL) \ - || { echo "libkit.a external symbol set drifted from scripts/lib_deps.allowlist"; exit 1; } - @python3 scripts/lib_reloc_defined_prefixes.py $(LIB_DEPS_AR) \ - --output $(LIB_RELOC) --ar $(AR) --cc $(CC) > $(LIB_RELOC_BAD) - @test ! -s $(LIB_RELOC_BAD) \ - || { echo "libkit relocatable link exposes non-public symbols"; cat $(LIB_RELOC_BAD); exit 1; }