kit

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

test.mk (70342B)


      1 # Data-driven tests. Included from the top-level Makefile.
      2 #
      3 # - test-driver: aggregate alias that runs all test-driver-* targets.
      4 # - test-driver-cc: narrow CLI behavior checks that do not belong to a specific
      5 #   frontend/linker corpus. Depends on the kit driver binary.
      6 # - test-pp: aggregate alias that runs test-pp-ok and test-pp-err.
      7 # - test-pp-ok: C preprocessor success cases; depends on the kit driver binary.
      8 # - test-elf: ELF roundtrip harness in test/elf/; depends only on
      9 #   libkit.a and compiles its own test binaries against it. Skipped
     10 #   layers are reported (set KIT_TEST_ALLOW_SKIP=1 to allow skips).
     11 # - test-ar:  in-process ar reader/writer tests; depends only on
     12 #   libkit.a. Set KIT_AR_TEST_HOST=1 to also dump produced bytes
     13 #   to /tmp and run the host's `ar t` / `nm --print-armap` as a
     14 #   cross-check.
     15 # - test-driver-ar: scenario-driven CLI harness for `kit ar`. Each
     16 #   case under test/ar/cases/ runs a small script and diffs stdout.
     17 #   Depends on the kit driver binary.
     18 # - test-link: linker + JIT behavioral harness in test/link/; three paths
     19 #   per case (roundtrip R, ELF exec E, JIT J). Depends only on libkit.a.
     20 #   Set KIT_TEST_ALLOW_SKIP=1 to allow skipped layers.
     21 # - test-macho: Mach-O variant of test-link; defaults to roundtrip+JIT
     22 #   paths because hosted Mach-O executable execution is target/SDK-specific.
     23 # - test-parse: aggregate alias that runs test-parse-ok and test-parse-err.
     24 # - test-parse-ok: file-driven C parser success harness in test/parse/; each
     25 #   case is a .c source file. Built against the public kit.h surface;
     26 #   reuses kit-roundtrip, link-exe-runner, and jit-runner.
     27 # - test-asm: aggregate alias over the per-arch asm lanes test-asm-aa64,
     28 #   test-asm-x64 and test-asm-rv64. The file-driven assembler/disassembler
     29 #   harness in test/asm/ runs one arch per invocation (KIT_TEST_ARCH) over
     30 #   three sub-corpora (encode/, decode/, listing/), one mode per sub-dir.
     31 #   aa64 runs the full path set (incl. native exec on aa64 hosts); x64/rv64
     32 #   run the host-independent encode/decode/listing lanes only. See doc/ASM.md.
     33 
     34 TEST_TARGETS = \
     35     test-cf-corpus-selftest \
     36     test-aa64-inline \
     37     test-arm32-inline \
     38     test-abi-classify \
     39     test-ar \
     40     test-asm \
     41     test-asm-aa64 \
     42     test-asm-x64 \
     43     test-asm-rv64 \
     44     test-asm-rv32 \
     45     test-toy-rv32 \
     46     test-parse-rv32 \
     47     test-disasm-complete \
     48     test-asm-roundtrip \
     49     test-asm-roundtrip-exec \
     50     test-asm-symmetry \
     51     test-asm-roundtrip-toy \
     52     test-hostas-toy \
     53     test-hostas-cross \
     54     test-diff-llvm \
     55     test-bootstrap-toy \
     56     test-bootstrap-toy-debug \
     57     test-bootstrap-toy-release \
     58     test-build \
     59     test-build-pure \
     60     test-bounce \
     61     test-cbackend \
     62     test-cg-api \
     63     test-coff \
     64     test-coff-mingw-import \
     65     test-coff-windows-o1-abi \
     66     test-coff-windows-ucrt \
     67     test-debug \
     68     test-dbg \
     69     test-driver \
     70     test-driver-ar \
     71     test-driver-cpio \
     72     test-driver-cas \
     73     test-driver-make \
     74     test-driver-cc \
     75     test-driver-targets \
     76     test-driver-native-macos-sdk \
     77     test-driver-stack-protector \
     78     test-driver-help \
     79     test-driver-build \
     80     test-driver-build-coord \
     81     test-driver-lsan \
     82     test-driver-objcopy \
     83     test-driver-objdump \
     84     test-driver-pkg \
     85     test-dist \
     86     test-selfdist \
     87     test-release-macos-aa64 \
     88     test-driver-strings \
     89     test-driver-tools \
     90     test-driver-wasm \
     91     test-gram \
     92     test-driver-gram \
     93     test-hash \
     94     test-cas-api \
     95     test-release-index \
     96     test-driver-strip \
     97     test-dwarf \
     98     test-ecosystem \
     99     test-elf \
    100     test-extlink \
    101     test-emu \
    102     test-emu-unit \
    103     test-interp \
    104     test-interp-emu \
    105     test-interp-toy \
    106     test-ir-recorder \
    107     test-isa \
    108     test-lib-deps \
    109     test-libc \
    110     test-libc-glibc \
    111     test-libc-glibc-rv64 \
    112     test-libc-musl \
    113     test-libc-musl-rv64 \
    114     test-link \
    115     test-link-compat \
    116     test-link-riscv-flags \
    117     test-link-reloc-uleb128 \
    118     test-link-reloc-desc \
    119     test-link-reloc-apply \
    120     test-link-jit-tls-relax \
    121     test-link-coff-weak-alias \
    122     test-link-dso-aa64 \
    123     test-macho \
    124     test-native-direct-target \
    125     test-opt \
    126     test-parse \
    127     test-parse-err \
    128     test-parse-ok \
    129     test-parse-rv64-wide \
    130     test-pp \
    131     test-pp-err \
    132     test-pp-ok \
    133     test-pp-file-escape \
    134     test-pp-include-next \
    135     test-rpn-lang \
    136     test-rt-headers \
    137     test-rt-runtime \
    138     test-rt-backtrace \
    139     test-link-x64 \
    140     test-rv64-inline \
    141     test-rv64-jit \
    142     test-rv32-jit \
    143     test-rv64-tls-link \
    144     test-smoke-rv64 \
    145     test-smoke-rv32 \
    146     test-smoke-arm32 \
    147     test-smoke-x64 \
    148     test-smoke-x64-macos \
    149     test-smoke-ios-app \
    150     test-toy \
    151     test-wasm \
    152     test-wasm-c \
    153     test-wasm-front \
    154     test-wasm-target \
    155     test-wasm-toy \
    156     test-x64-dbg \
    157     test-x64-inline
    158 
    159 DEFAULT_TEST_TARGETS = \
    160     test-cf-corpus-selftest \
    161     test-driver \
    162     test-pp \
    163     test-elf \
    164     test-coff \
    165     test-ar \
    166     test-link \
    167     test-toy \
    168     test-dwarf \
    169     test-debug \
    170     test-parse \
    171     test-asm \
    172     test-asm-roundtrip \
    173     test-isa \
    174     test-aa64-inline \
    175     test-arm32-inline \
    176     test-rv64-inline \
    177     test-rv64-jit \
    178     test-rv32-jit \
    179     test-rv64-tls-link \
    180     test-emu \
    181     test-emu-unit \
    182     test-interp \
    183     test-interp-emu \
    184     test-x64-inline \
    185     test-x64-dbg \
    186     test-rt-headers \
    187     test-lib-deps \
    188     test-cg-api \
    189     test-abi-classify \
    190     test-ir-recorder \
    191     test-native-direct-target \
    192     test-opt \
    193     test-asm-symmetry \
    194     test-link-reloc-uleb128 \
    195     test-link-reloc-desc \
    196     test-link-reloc-apply \
    197     test-link-jit-tls-relax \
    198     test-link-coff-weak-alias \
    199     test-dbg \
    200     test-disasm-complete \
    201     test-macho \
    202     test-extlink \
    203     test-interp-toy \
    204     test-wasm \
    205 		test-libc \
    206 		test-link-x64 \
    207 		test-rt-runtime \
    208 		test-rt-backtrace \
    209 		test-gram \
    210 		test-driver-gram \
    211 		test-bounce \
    212 		bootstrap \
    213 		test-bootstrap-toy
    214 
    215 TIER1_TEST_TARGETS = \
    216     test-t1-harness \
    217     test-t1-build \
    218     test-t1-driver \
    219     test-t1-build-coord \
    220     test-t1-core-api \
    221     test-t1-pp \
    222     test-t1-c-front \
    223     test-t1-cg \
    224     test-t1-opt \
    225     test-t1-arch \
    226     test-t1-asm-disas \
    227     test-t1-obj \
    228     test-t1-link-jit \
    229     test-t1-cross \
    230     test-t1-rt \
    231     test-t1-debug \
    232     test-t1-emu-interp \
    233     test-t1-wasm \
    234     test-t1-tools \
    235     test-t1-dist \
    236     test-t1-gram
    237 
    238 TIER1_PARALLEL_TARGETS = $(filter-out test-t1-build,$(TIER1_TEST_TARGETS))
    239 
    240 T1_LOG = test/tier1/run_logged.sh
    241 
    242 .PHONY: test test-full test-tier1 $(TIER1_TEST_TARGETS) \
    243         $(TEST_TARGETS) windows-ucrt-sysroots hosted-glibc-images
    244 
    245 test: test-tier1
    246 
    247 test-tier1:
    248 	@$(MAKE) --no-print-directory test-t1-build
    249 	@$(MAKE) --no-print-directory -j$(NCPU) $(TIER1_PARALLEL_TARGETS)
    250 
    251 test-full: $(DEFAULT_TEST_TARGETS)
    252 
    253 test-t1-harness:
    254 	@$(T1_LOG) harness bash test/lib/kit_corpus_selftest.sh
    255 
    256 test-t1-build:
    257 	@$(T1_LOG) build $(MAKE) --no-print-directory lib bin rt
    258 
    259 test-t1-driver:
    260 	@$(T1_LOG) driver env KIT=$(abspath $(BIN)) sh test/tier1/system/driver-kitchen.sh
    261 
    262 test-t1-build-coord: bin
    263 	@$(T1_LOG) build-coord env KIT=$(abspath $(BIN)) sh test/tier1/system/buildcoord-kitchen.sh
    264 
    265 test-t1-core-api:
    266 	@$(T1_LOG) core-api $(MAKE) --no-print-directory test-hash test-cas-api test-release-index test-abi-classify
    267 
    268 test-t1-pp: bin
    269 	@$(T1_LOG) pp env KIT=$(abspath $(BIN)) test/tier1/frontend/run-kitchen.sh pp
    270 
    271 test-t1-c-front: bin
    272 	@$(T1_LOG) c-front env KIT=$(abspath $(BIN)) test/tier1/frontend/run-kitchen.sh c
    273 
    274 test-t1-cg: bin
    275 	@$(T1_LOG) cg $(MAKE) --no-print-directory test-cg-api test-ir-recorder
    276 	@$(T1_LOG) toy env KIT=$(abspath $(BIN)) test/tier1/frontend/run-kitchen.sh toy
    277 
    278 test-t1-opt:
    279 	@$(T1_LOG) opt $(MAKE) --no-print-directory test-opt-tiny-inline test-opt-inline test-opt-zero-arg
    280 
    281 test-t1-arch:
    282 	@$(T1_LOG) arch $(MAKE) --no-print-directory test-isa test-aa64-inline test-x64-inline test-rv64-inline test-arm32-inline
    283 
    284 test-t1-asm-disas: bin
    285 	@$(T1_LOG) asm-disas env KIT=$(abspath $(BIN)) test/tier1/objlink/asm.sh
    286 
    287 test-t1-obj: bin
    288 	@$(T1_LOG) obj env KIT=$(abspath $(BIN)) test/tier1/objlink/object.sh
    289 	@$(T1_LOG) ar $(MAKE) --no-print-directory test-ar
    290 
    291 test-t1-link-jit: bin
    292 	@$(T1_LOG) link env KIT=$(abspath $(BIN)) test/tier1/objlink/link.sh
    293 	@$(T1_LOG) jit-relax $(MAKE) --no-print-directory test-link-jit-tls-relax
    294 	@$(T1_LOG) interop env KIT=$(abspath $(BIN)) test/tier1/objlink/interop.sh
    295 
    296 test-t1-cross: bin
    297 	@$(T1_LOG) cross env KIT=$(abspath $(BIN)) test/tier1/cross/run.sh
    298 
    299 test-t1-rt: bin
    300 	@$(T1_LOG) rt test/tier1/system/runtime-headers.sh
    301 
    302 test-t1-debug:
    303 	@$(T1_LOG) debug test/tier1/system/debug-dwarf.sh
    304 
    305 test-t1-emu-interp:
    306 	@$(T1_LOG) emu-interp $(MAKE) --no-print-directory test-emu test-interp test-interp-emu
    307 
    308 test-t1-wasm: bin
    309 	@$(T1_LOG) wasm env KIT=$(abspath $(BIN)) test/tier1/frontend/run-kitchen.sh wasm
    310 	@$(T1_LOG) wasm-driver $(MAKE) --no-print-directory test-driver-wasm
    311 
    312 test-t1-tools:
    313 	@$(T1_LOG) tools test/tier1/system/tools.sh
    314 
    315 test-t1-dist:
    316 	@$(T1_LOG) dist test/tier1/system/dist.sh
    317 
    318 test-t1-gram:
    319 	@$(T1_LOG) gram $(MAKE) --no-print-directory test-driver-gram
    320 
    321 # Unit-test binary build rules: two regimes (public vs internal interface).
    322 include mk/test_unit.mk
    323 
    324 # Provision the pinned per-arch container rootfs images that exec_target.sh runs
    325 # kit-emitted binaries inside. This is the ONLY test step that touches the
    326 # network: the cross-arch exec harnesses (toy/parse/link ... path X/E/L) run with
    327 # `podman run --pull=never`, so without these images those paths SKIP. Run once,
    328 # and again only after the pin in test/lib/test_images.sh changes. FORCE=1
    329 # re-pulls. The images are pinned per-arch by content digest, so they coexist in
    330 # local storage and can never clobber one another.
    331 .PHONY: test-images
    332 test-images:
    333 	@bash test/lib/pull_test_images.sh
    334 
    335 # Hermetic self-test of the shared corpus harness engine (test/lib/kit_corpus.sh):
    336 # asserts serial==parallel determinism, SKIP-NA, and the parallel-safety
    337 # invariant (exec_target queued only on the parent, never in a worker). No
    338 # kit binary / podman / qemu needed.
    339 test-cf-corpus-selftest:
    340 	@bash test/lib/kit_corpus_selftest.sh
    341 
    342 test-driver: test-driver-help test-driver-cc test-driver-targets test-driver-native-macos-sdk test-driver-stack-protector test-driver-build test-driver-build-coord test-driver-ar test-driver-cpio test-driver-cas test-driver-make test-driver-strip test-driver-objcopy test-driver-objdump test-driver-pkg test-driver-strings test-driver-tools test-driver-diagnostics test-driver-wasm
    343 
    344 test-driver-help: bin
    345 	@KIT=$(abspath $(BIN)) sh test/driver/help_uniform.sh
    346 
    347 test-driver-cc: bin
    348 	@KIT=$(abspath $(BIN)) sh test/driver/run.sh
    349 
    350 test-driver-targets: bin
    351 	@KIT=$(abspath $(BIN)) sh test/driver/targets.sh
    352 
    353 test-driver-native-macos-sdk: bin
    354 	@KIT=$(abspath $(BIN)) sh test/driver/native_macos_sdk.sh
    355 
    356 test-driver-stack-protector: bin
    357 	@KIT=$(abspath $(BIN)) sh test/driver/stack_protector.sh
    358 
    359 test-driver-build: bin
    360 	@KIT=$(abspath $(BIN)) sh test/buildcmds/run.sh
    361 
    362 test-driver-build-coord: bin
    363 	@KIT=$(abspath $(BIN)) sh test/buildcoord/run.sh
    364 
    365 BUILD_PURE_TEST_BIN = build/test/build_pure_test
    366 BUILD_PUBLIC_LINK_TEST_BIN = build/test/build_public_link_test
    367 
    368 test-build-pure: $(BUILD_PURE_TEST_BIN)
    369 	$(BUILD_PURE_TEST_BIN)
    370 
    371 test-build-public-link: $(BUILD_PUBLIC_LINK_TEST_BIN)
    372 	$(BUILD_PUBLIC_LINK_TEST_BIN)
    373 
    374 test-build: test-build-pure test-build-public-link test-driver-build-coord
    375 
    376 test-rpn-lang: lib
    377 	@KIT_BUILD_DIR=$(abspath $(BUILD_DIR)/test/rpn-lang) sh test/rpn-lang/run.sh
    378 
    379 ifeq ($(HOST_OS),linux)
    380 test-driver-lsan:
    381 	@$(MAKE) test-driver RELEASE=0 BUILD_DIR=build/lsan \
    382 	    ASAN_OPTIONS=detect_leaks=1:halt_on_error=1:abort_on_error=1
    383 else
    384 test-driver-lsan:
    385 	@printf '%s\n' 'SKIP test-driver-lsan: LeakSanitizer requires HOST_OS=linux'
    386 endif
    387 
    388 # test-cbackend: --emit=c C-source backend, driven through three
    389 # frontends — parse-runner (C), toy-runner (toy), wasm-runner (wat/wasm).
    390 # Each invokes its existing runner with paths=C so a single corpus per
    391 # frontend exercises both the existing backends and the C backend.
    392 # Together they prove the CGTarget seam is frontend-agnostic.
    393 # Unimplemented CGTarget methods report as SKIP; see doc/CBACKEND.md.
    394 KIT_CBACKEND_TEST_JOBS ?= $(if $(KIT_TEST_JOBS),$(KIT_TEST_JOBS),1)
    395 test-cbackend: bin
    396 	@KIT_TEST_JOBS=$(KIT_CBACKEND_TEST_JOBS) KIT_TEST_PATHS=C KIT_TEST_ALLOW_SKIP=1 sh test/parse/run.sh
    397 	@KIT_TEST_JOBS=$(KIT_CBACKEND_TEST_JOBS) KIT_TEST_PATHS=C KIT=$(abspath $(BIN)) sh test/toy/run.sh
    398 	@KIT_TEST_JOBS=$(KIT_CBACKEND_TEST_JOBS) KIT_TEST_PATHS=C KIT=$(abspath $(BIN)) bash test/wasm/run.sh
    399 
    400 # test-wasm-toy: opt-in Toy -> Wasm -> JIT roundtrip. Runs the toy corpus
    401 # under the W path (compile -target wasm32-none, then `kit run` the .wasm,
    402 # which routes back through the lang/wasm frontend to native CG). Most cases
    403 # will fail or skip today; the target exists so progress on the Wasm CGTarget
    404 # is visible without putting noise into the default `test` summary.
    405 # Drop `KIT_TEST_ALLOW_SKIP=1` once the corpus is mostly green.
    406 test-wasm-toy: bin
    407 	@KIT_TEST_PATHS=W KIT_TEST_ALLOW_SKIP=1 KIT=$(abspath $(BIN)) sh test/toy/run.sh
    408 
    409 # test-wasm-c: opt-in C -> Wasm -> JIT roundtrip, the C-frontend analogue of
    410 # test-wasm-toy. Runs the test/parse corpus under the W path (compile
    411 # -target wasm32-none, then `kit run -e test_main` the .wasm, which routes
    412 # back through the lang/wasm frontend to native CG). The C corpus exercises
    413 # far more of the language than the toy corpus, so expect many SKIPs for
    414 # not-yet-implemented Wasm lowerings; the target makes that progress visible
    415 # without adding noise to the default `test` summary. Drop
    416 # `KIT_TEST_ALLOW_SKIP=1` once the corpus is mostly green.
    417 test-wasm-c: bin $(PARSE_RUNNER)
    418 	@KIT_TEST_PATHS=W KIT_TEST_ALLOW_SKIP=1 KIT=$(abspath $(BIN)) bash test/parse/run.sh
    419 
    420 test-pp: test-pp-ok test-pp-err test-pp-file-escape test-pp-include-next
    421 
    422 test-pp-ok: bin
    423 	@KIT=$(abspath $(BIN)) test/pp/run.sh
    424 
    425 test-pp-err: bin
    426 	@KIT=$(abspath $(BIN)) test/pp/run_errors.sh
    427 
    428 test-pp-file-escape: bin
    429 	@KIT=$(abspath $(BIN)) bash test/pp/run_file_escape.sh
    430 
    431 test-pp-include-next: bin
    432 	@KIT=$(abspath $(BIN)) bash test/pp/run_include_next.sh
    433 
    434 # Best-effort kit binary build: Layer D needs build/kit, but the
    435 # binary may not link until enough libkit symbols exist. The harness
    436 # detects a missing binary and skips that layer; don't break test-elf
    437 # when bin fails.
    438 .PHONY: bin-soft
    439 bin-soft:
    440 	-@$(MAKE) bin 2>/dev/null || true
    441 
    442 AR_TEST_BIN = build/test/ar_test
    443 
    444 test-ar: $(AR_TEST_BIN)
    445 	$(AR_TEST_BIN)
    446 
    447 
    448 test-driver-ar: bin
    449 	@KIT=$(abspath $(BIN)) test/ar/run.sh
    450 
    451 test-driver-cpio: bin
    452 	@KIT=$(abspath $(BIN)) sh test/cpio/run.sh
    453 
    454 test-driver-cas: bin
    455 	@KIT=$(abspath $(BIN)) sh test/cas/run.sh
    456 
    457 test-driver-make: bin
    458 	@KIT=$(abspath $(BIN)) sh test/make/run.sh
    459 
    460 
    461 test-driver-strip: bin build/test/object_rewrite_test
    462 	@build/test/object_rewrite_test
    463 	@KIT=$(abspath $(BIN)) test/strip/run.sh
    464 	@KIT=$(abspath $(BIN)) sh test/strip/linked.sh
    465 
    466 test-driver-objcopy: bin
    467 	@KIT=$(abspath $(BIN)) test/objcopy/run.sh
    468 
    469 test-driver-objdump: bin
    470 	@KIT=$(abspath $(BIN)) sh test/objdump/run.sh
    471 
    472 test-driver-pkg: bin
    473 	@KIT=$(abspath $(BIN)) sh test/pkg/run.sh
    474 
    475 # test-selfdist: hermetic self-distribution suite — drives `kit update` over
    476 # packages built with `kit pkg create` (the same operation `make dist` runs),
    477 # entirely under a sandbox $KIT_HOME so it touches no real install. Covers
    478 # offline install/flip/rollback/prune, explicit-key + wrong-key + tamper
    479 # rejection, stock-minisign bootstrap verification, and the networked +
    480 # monotonic channel-index path over file:// (self-skips if curl is absent).
    481 test-selfdist: bin
    482 	@KIT=$(abspath $(BIN)) sh test/dist/run.sh
    483 
    484 # test-release-macos-aa64: full release-artifact smoke for the runnable arm64
    485 # macOS target. Drives scripts/release.sh with the non-release test key into an
    486 # isolated output dir, then proves both the .kpkg update path and the .tar.gz
    487 # bootstrap-unpack path produce runnable kit binaries under separate KIT_HOME
    488 # roots. Opt-in because it performs a release cross-build.
    489 test-release-macos-aa64: bin
    490 	@KIT=$(abspath $(BIN)) sh test/release/run.sh
    491 
    492 # test-extlink: prove kit-emitted objects link with the *system* linkers
    493 # (LLVM lld ELF/COFF/Mach-O personalities + Apple ld), not just kit's own
    494 # `ld`. Lanes self-skip when a linker / SDK is absent, so it is safe in the
    495 # default set on hosts without lld installed.
    496 test-extlink: bin
    497 	@KIT=$(abspath $(BIN)) sh test/extlink/run.sh
    498 
    499 # test-ecosystem: real-world C build gate. Builds pinned upstream projects
    500 # (SQLite, Lua, cJSON, LZ4, miniz, tinyexpr, yyjson) from the kit cache dir with
    501 # kit, links a driver that exercises real functionality, and asserts the output
    502 # against a checked-in golden AND against the same program built with clang.
    503 # OPT-IN (not in DEFAULT_TEST_TARGETS): it needs sources provisioned first and a
    504 # host clang for the differential. Provision once with `make provision-ecosystem`
    505 # (network); the gate then runs fully offline. Known kit bugs are kept RED on
    506 # purpose (see test/ecosystem/known_bugs/); failing artifacts are preserved
    507 # under build/ecosystem-fail/.
    508 .PHONY: test-ecosystem provision-ecosystem ecosystem-speed
    509 provision-ecosystem:
    510 	@sh scripts/ecosystem.sh fetch all
    511 
    512 test-ecosystem: bin
    513 	@KIT=$(abspath $(BIN)) sh test/ecosystem/run.sh
    514 
    515 ECO_SPEED_AR ?= $(if $(filter command line,$(origin AR)),$(AR),llvm-ar)
    516 
    517 ecosystem-speed:
    518 	@if [ ! -x build/release/kit ]; then \
    519 	  $(MAKE) --no-print-directory RELEASE=1 bin; \
    520 	fi
    521 	@KIT=$(abspath build/release/kit) AR=$(ECO_SPEED_AR) bash scripts/ecosystem_speed.sh
    522 
    523 # test-dist: one-command verification of the dist subsystem (content-addressed
    524 # store + signed packaging). Runs the CAS and pkg driver corpora, which cover
    525 # round-trip, integrity/tamper detection, trust store, and the read-side
    526 # adversarial cases (untrusted-tree path-traversal and NUL-byte rejection) in
    527 # test/cas/run.sh.
    528 test-dist: test-driver-cas test-driver-pkg
    529 
    530 test-driver-strings: bin
    531 	@KIT=$(abspath $(BIN)) sh test/strings/run.sh
    532 
    533 test-driver-tools: bin
    534 	@KIT=$(abspath $(BIN)) sh test/tools/run.sh
    535 
    536 test-driver-diagnostics: bin
    537 	@KIT=$(abspath $(BIN)) sh test/diagnostics/run.sh
    538 
    539 test-driver-wasm: bin
    540 	@KIT=$(abspath $(BIN)) sh test/driver-wasm/run.sh
    541 
    542 # test-gram: gram subsystem functional + diagnostic suite. Drives `kit gram` to
    543 # generate parsers/lexers/machines, compiles the generated C + the test drivers
    544 # against libkit.a, runs them, and checks the error fixtures. Drops -Wpedantic
    545 # (the generated C targets portable C, like the harness binaries).
    546 test-gram: bin
    547 	@KIT=$(abspath $(BIN)) \
    548 	  KIT_GRAM_CC='$(CC)' \
    549 	  KIT_GRAM_CFLAGS='$(filter-out -Wpedantic,$(HOST_CFLAGS)) -Iinclude' \
    550 	  KIT_GRAM_SYSROOT='$(HOST_SYSROOT_CFLAGS)' \
    551 	  KIT_GRAM_LIB='$(abspath $(LIB_AR))' \
    552 	  KIT_GRAM_BUILD='$(abspath $(BUILD_DIR)/test/gram)' \
    553 	  sh test/gram/run.sh
    554 
    555 # test-driver-gram: `kit gram` CLI-behavior smoke (stdin, --dump-sexpr,
    556 # --sample-tokens, default output naming, exit codes).
    557 test-driver-gram: bin
    558 	@KIT=$(abspath $(BIN)) \
    559 	  KIT_GRAM_BUILD='$(abspath $(BUILD_DIR)/test/gram-driver)' \
    560 	  sh test/gram/run_driver.sh
    561 
    562 # DWARF consumer unit test: builds a hand-crafted DWARF-bearing ELF in
    563 # memory and exercises every kit_dwarf_* entry. It reaches into the
    564 # internal object builder to synthesize the fixture, so link individual
    565 # lib objects rather than libkit.a (which hides internal symbols).
    566 DWARF_TEST_BIN = build/test/dwarf_test
    567 
    568 test-dwarf: $(DWARF_TEST_BIN)
    569 	$(DWARF_TEST_BIN)
    570 
    571 
    572 # DWARF producer self-roundtrip unit test. Drives Debug directly, calls
    573 # debug_emit, asserts the produced sections have valid DWARF 5 structure
    574 # (length fields, version, address sizes, expected relocations against
    575 # function symbol). Deliberately bypasses the consumer (kit_dwarf_open)
    576 # so encoder bugs aren't masked by matching decoder bugs.
    577 DEBUG_TEST_BIN = build/test/debug_roundtrip_unit
    578 CFI_TEST_BIN = build/test/debug_cfi_unit
    579 
    580 test-debug: $(DEBUG_TEST_BIN) $(CFI_TEST_BIN)
    581 	$(DEBUG_TEST_BIN)
    582 	$(CFI_TEST_BIN)
    583 
    584 
    585 # CFI/.eh_frame producer roundtrip for aa64/rv64/x64 (validates the per-arch
    586 # CIE template: code/data-align, return-address reg, CFA-init reg).
    587 
    588 test-dbg: bin
    589 	@KIT=$(abspath $(BIN)) sh test/dbg/run.sh
    590 
    591 .PHONY: test-dbg-red
    592 test-dbg-red: bin
    593 	@KIT=$(abspath $(BIN)) DBG_STRICT_XFAIL=1 sh test/dbg/run.sh
    594 
    595 # aa64 ISA descriptor-table unit test (doc/ASM.md phase 2). Covers
    596 # every AA64Format the table maps and the alias-precedence invariant
    597 # (first-match disasm picks the alias spelling over the canonical
    598 # form). Internal arch/ surface — needs -Isrc.
    599 AA64_ISA_TEST_BIN = build/test/aa64_isa_test
    600 RV64_DECODE_TEST_BIN = build/test/rv64_decode_test
    601 RV32_DECODE_TEST_BIN = build/test/rv32_decode_test
    602 ARM32_DECODE_TEST_BIN = build/test/arm32_decode_test
    603 
    604 test-isa: $(AA64_ISA_TEST_BIN) $(RV64_DECODE_TEST_BIN) $(RV32_DECODE_TEST_BIN) \
    605           $(ARM32_DECODE_TEST_BIN)
    606 	$(AA64_ISA_TEST_BIN)
    607 	$(RV64_DECODE_TEST_BIN)
    608 	$(RV32_DECODE_TEST_BIN)
    609 	$(ARM32_DECODE_TEST_BIN)
    610 
    611 
    612 
    613 # aa64_sweep_gen: emits one representative encoding per disasm-table row for the
    614 # asm<->disasm self-symmetry sweep (test/asm/symmetry.sh). Needs the internal
    615 # arch/aa64/isa.h surface, so -Isrc + LIB_OBJS like the ISA unit test.
    616 AA64_SWEEP_GEN = build/test/aa64_sweep_gen
    617 
    618 # test-emu: emulator end-to-end integration test. Builds tiny in-memory rv64
    619 # ELFs and runs them to their exit syscall, asserting the exit code — entirely
    620 # through the PUBLIC kit_emu_* API, so it links the public archive ($(LIB_AR),
    621 # whose `ld -r` step localizes internal symbols). -Isrc is only for the
    622 # header-only rv64 encoders / ELF constants the in-memory ELF builders use.
    623 EMU_RV64_TEST_BIN = build/test/emu_rv64_test
    624 
    625 test-emu: $(EMU_RV64_TEST_BIN)
    626 	$(EMU_RV64_TEST_BIN)
    627 
    628 $(EMU_RV64_TEST_BIN): test/emu/rv64_smoke_test.c $(UNIT_HDR_DEPS) $(LIB_AR)
    629 	@mkdir -p $(dir $@)
    630 	$(CC) $(HOST_CFLAGS) -Iinclude -Isrc -Itest test/emu/rv64_smoke_test.c $(LIB_AR) -o $@
    631 
    632 # RISC-V ULEB128 diff-reloc application unit test. link_reloc_apply is an
    633 # internal (hidden) symbol, so link the raw lib objects like the other
    634 # internal-surface unit tests rather than libkit.a.
    635 RELOC_ULEB128_TEST_BIN = build/test/reloc_uleb128_unit
    636 
    637 test-link-reloc-uleb128: $(RELOC_ULEB128_TEST_BIN)
    638 	$(RELOC_ULEB128_TEST_BIN)
    639 
    640 # Relocation-descriptor migration guard (doc/plan/RELOC.md, WS-B): pins
    641 # reloc_desc()/reloc_kind_* to the frozen pre-refactor width + classification
    642 # behaviour across every backend arch. Internal-surface, so links the raw lib
    643 # objects like the other internal unit tests.
    644 RELOC_DESC_TEST_BIN = build/test/reloc_desc_test
    645 
    646 test-link-reloc-desc: $(RELOC_DESC_TEST_BIN)
    647 	$(RELOC_DESC_TEST_BIN)
    648 
    649 # Relocation byte-encoder migration guard (doc/plan/RELOC.md, WS-C): pins
    650 # link_reloc_apply to the frozen pre-refactor patched bytes for every
    651 # instruction-immediate kind, so partitioning the encoders per-arch stays
    652 # byte-identical. Internal-surface, so links the raw lib objects.
    653 RELOC_APPLY_TEST_BIN = build/test/reloc_apply_test
    654 
    655 test-link-reloc-apply: $(RELOC_APPLY_TEST_BIN)
    656 	$(RELOC_APPLY_TEST_BIN)
    657 
    658 # In-process JIT TLS Local-Exec relaxation for the Windows (COFF) TEB idiom:
    659 # pins the per-arch byte rewrite (jit_tls_le_relax) for x86-64 and aarch64. The
    660 # aarch64 path is also covered end-to-end on the Windows VM; this is the only
    661 # automated guard for the x86-64 idiom. Internal-surface (raw lib objects).
    662 JIT_TLS_RELAX_TEST_BIN = build/test/jit_tls_relax_test
    663 
    664 test-link-jit-tls-relax: $(JIT_TLS_RELAX_TEST_BIN)
    665 	$(JIT_TLS_RELAX_TEST_BIN)
    666 
    667 # COFF WEAK_EXTERNAL alias capture in read_coff: pins the aux-TagIndex decode
    668 # that lets the linker resolve mingw x86_64's `_setjmp` -> `__intrinsic_setjmp`
    669 # alias (the x64-windows cross-link blocker). Internal-surface (raw lib objects).
    670 COFF_WEAK_ALIAS_TEST_BIN = build/test/coff_weak_alias_test
    671 
    672 test-link-coff-weak-alias: $(COFF_WEAK_ALIAS_TEST_BIN)
    673 	$(COFF_WEAK_ALIAS_TEST_BIN)
    674 
    675 # test-emu-unit: white-box unit tests for the emulator's INTERNAL units (rv64
    676 # decoder, EmuAddrSpace, Linux syscall handler) that have no public API. Reaches
    677 # internal symbols -> links $(LIB_OBJS) (mirrors test-interp), not the archive.
    678 EMU_RV64_UNIT_TEST_BIN = build/test/emu_rv64_unit_test
    679 
    680 test-emu-unit: $(EMU_RV64_UNIT_TEST_BIN)
    681 	$(EMU_RV64_UNIT_TEST_BIN)
    682 
    683 
    684 # test-interp: threaded-bytecode interpreter unit smoke test. Builds tiny CG IR
    685 # by hand, runs opt_run_o1_interp + interp_lower + the engine, asserts the
    686 # returned value. Reaches internal opt/interp symbols -> links $(LIB_OBJS) and
    687 # needs -Isrc (mirrors test-opt).
    688 INTERP_SMOKE_TEST_BIN = build/test/interp_smoke_test
    689 
    690 test-interp: $(INTERP_SMOKE_TEST_BIN)
    691 	$(INTERP_SMOKE_TEST_BIN)
    692 
    693 
    694 # test-interp-emu: differential test of the emulator's INTERP execution mode
    695 # (doc/INTERPRETER.md Phase 4). Builds a tiny rv64 ELF with SD/LD/ecall and runs
    696 # it through kit_emu_run in JIT and INTERP modes (both -O1), asserting the exit
    697 # codes match. Public-API only, but links $(LIB_OBJS) like test-interp to dodge
    698 # the visibility-hidden archive's localized internals.
    699 INTERP_EMU_TEST_BIN = build/test/rv64_interp_smoke_test
    700 
    701 test-interp-emu: $(INTERP_EMU_TEST_BIN)
    702 	$(INTERP_EMU_TEST_BIN)
    703 
    704 
    705 # test-interp-toy: run the toy suite's interpreter (--no-jit) path only,
    706 # asserting it matches the golden exit codes (and SKIPping unimplemented ops).
    707 test-interp-toy: bin
    708 	@KIT=$(abspath $(BIN)) KIT_TEST_PATHS=I bash test/toy/run.sh
    709 
    710 CG_API_TEST_BIN = build/test/cg_api_test
    711 CG_SWITCH_TEST_BIN = build/test/cg_switch_test
    712 CG_FP_CMP_TEST_BIN = build/test/cg_fp_cmp_test
    713 CG_CONTROL_TEST_BIN = build/test/cg_control_test
    714 CG_CONST_TEST_BIN = build/test/cg_const_test
    715 STRENGTH_REDUCE_TEST_BIN = build/test/strength_reduce_test
    716 TARGET_TEST_BIN = build/test/target_test
    717 HASH_TEST_BIN = build/test/hash_test
    718 CAS_TEST_BIN = build/test/cas_test
    719 RELEASE_INDEX_TEST_BIN = build/test/release_index_test
    720 PANIC_RECOVERY_TEST_BIN = build/test/panic_recovery_test
    721 ABI_CLASSIFY_TEST_BIN = build/test/abi_classify_test
    722 IR_RECORDER_TEST_BIN = build/test/ir_recorder_test
    723 NATIVE_DIRECT_TARGET_TEST_BIN = build/test/native_direct_target_test
    724 
    725 test-cg-api: $(TARGET_TEST_BIN) $(CG_API_TEST_BIN) $(CG_SWITCH_TEST_BIN) \
    726              $(CG_FP_CMP_TEST_BIN) $(CG_CONTROL_TEST_BIN) \
    727              $(CG_CONST_TEST_BIN) \
    728              $(STRENGTH_REDUCE_TEST_BIN) \
    729              $(PANIC_RECOVERY_TEST_BIN)
    730 	$(TARGET_TEST_BIN)
    731 	$(CG_API_TEST_BIN)
    732 	$(CG_SWITCH_TEST_BIN)
    733 	$(CG_FP_CMP_TEST_BIN)
    734 	$(CG_CONTROL_TEST_BIN)
    735 	$(CG_CONST_TEST_BIN)
    736 	$(STRENGTH_REDUCE_TEST_BIN)
    737 	$(PANIC_RECOVERY_TEST_BIN)
    738 
    739 test-hash: $(HASH_TEST_BIN)
    740 	$(HASH_TEST_BIN)
    741 
    742 test-cas-api: $(CAS_TEST_BIN)
    743 	$(CAS_TEST_BIN)
    744 
    745 test-release-index: $(RELEASE_INDEX_TEST_BIN)
    746 	$(RELEASE_INDEX_TEST_BIN)
    747 
    748 test-abi-classify: $(ABI_CLASSIFY_TEST_BIN)
    749 	$(ABI_CLASSIFY_TEST_BIN)
    750 
    751 
    752 
    753 
    754 test-ir-recorder: $(IR_RECORDER_TEST_BIN)
    755 	$(IR_RECORDER_TEST_BIN)
    756 
    757 
    758 test-native-direct-target: $(NATIVE_DIRECT_TARGET_TEST_BIN)
    759 	$(NATIVE_DIRECT_TARGET_TEST_BIN)
    760 
    761 
    762 test-toy: bin
    763 	@KIT=$(abspath $(BIN)) test/toy/run.sh
    764 
    765 # The Toy corpus on the FreeBSD/Windows VMs (test/toy/vm.sh) is now driven by
    766 #   make test-cross TARGET=freebsd|windows DEPTH=full
    767 # which calls test/toy/vm.sh through the harmonized portability surface. See
    768 # doc/plan/PORT.md.
    769 
    770 # test-bootstrap-toy: run the Toy corpus through the bootstrapped (self-built)
    771 # stage3 kit instead of the host-built binary, so the self-hosted compiler is
    772 # exercised on real codegen (not just self-reproduction like `make bootstrap`).
    773 # Split per build mode: -debug runs the debug stage3, -release the release
    774 # (-O1) stage3; the aggregate target runs both.
    775 test-bootstrap-toy: test-bootstrap-toy-debug test-bootstrap-toy-release
    776 
    777 test-bootstrap-toy-debug: bootstrap-debug
    778 	@KIT='$(abspath $(BUILD_DIR)/debug/bootstrap/stage3/kit)' test/toy/run.sh
    779 
    780 test-bootstrap-toy-release: bootstrap-release
    781 	@KIT='$(abspath $(BUILD_DIR)/release/bootstrap/stage3/kit)' test/toy/run.sh
    782 
    783 
    784 # Public-API inline-asm backend tests. These emit a tiny function through CG,
    785 # reopen the object through the public object reader, and assert the expected
    786 # instruction bytes are present in .text.
    787 AA64_INLINE_TEST_BIN = build/test/aa64_inline_test
    788 
    789 test-aa64-inline: $(AA64_INLINE_TEST_BIN)
    790 	$(AA64_INLINE_TEST_BIN)
    791 
    792 
    793 RV64_INLINE_TEST_BIN = build/test/rv64_inline_test
    794 
    795 test-rv64-inline: $(RV64_INLINE_TEST_BIN)
    796 	$(RV64_INLINE_TEST_BIN)
    797 
    798 ARM32_INLINE_TEST_BIN = build/test/arm32_inline_test
    799 
    800 test-arm32-inline: $(ARM32_INLINE_TEST_BIN)
    801 	$(ARM32_INLINE_TEST_BIN)
    802 
    803 
    804 # rv64 JIT smoke test. Builds a tiny rv64 ELF .o in memory, runs it
    805 # through kit_link_session in JIT-output mode, and skips native execution
    806 # on non-riscv64 hosts after exercising the JIT mapping/reloc path.
    807 RV64_JIT_TEST_BIN = build/test/rv64_jit_test
    808 
    809 test-rv64-jit: $(RV64_JIT_TEST_BIN)
    810 	@$(RV64_JIT_TEST_BIN); rc=$$?; \
    811 	  if [ $$rc -eq 77 ]; then \
    812 	    echo "  (rv64_jit_test SKIPPED on non-rv64 host)"; \
    813 	    exit 0; \
    814 	  else \
    815 	    exit $$rc; \
    816 	  fi
    817 
    818 # rv32 JIT smoke test. Mirror of test-rv64-jit: builds a tiny rv32 ELF .o in
    819 # memory, runs it through kit_link_session in JIT-output mode, and skips native
    820 # execution on non-riscv32 hosts after exercising the JIT mapping/reloc path.
    821 RV32_JIT_TEST_BIN = build/test/rv32_jit_test
    822 
    823 test-rv32-jit: $(RV32_JIT_TEST_BIN)
    824 	@$(RV32_JIT_TEST_BIN); rc=$$?; \
    825 	  if [ $$rc -eq 77 ]; then \
    826 	    echo "  (rv32_jit_test SKIPPED on non-rv32 host)"; \
    827 	    exit 0; \
    828 	  else \
    829 	    exit $$rc; \
    830 	  fi
    831 
    832 
    833 # Link-only regression for rv64 TLS Local-Exec lowering (runs on any host).
    834 test-rv64-tls-link: bin
    835 	@KIT='$(abspath $(BIN))' bash test/smoke/rv64_tls_link.sh
    836 
    837 X64_INLINE_TEST_BIN = build/test/x64_inline_test
    838 
    839 test-x64-inline: $(X64_INLINE_TEST_BIN)
    840 	$(X64_INLINE_TEST_BIN)
    841 
    842 
    843 X64_DBG_TEST_BIN = build/test/x64_dbg_test
    844 
    845 test-x64-dbg: $(X64_DBG_TEST_BIN)
    846 	$(X64_DBG_TEST_BIN)
    847 
    848 # Reaches the internal arch/arch.h surface (arch_lookup, ArchDbgOps) -> links
    849 # $(LIB_OBJS), not the archive, whose relocatable merge localizes non-public
    850 # symbols (mirrors the aa64/rv64 arch unit tests above).
    851 
    852 RT_HEADER_TEST_TARGETS = \
    853     aarch64-linux-gnu        \
    854     x86_64-linux-gnu         \
    855     riscv64-linux-gnu        \
    856     aarch64-apple-darwin     \
    857     x86_64-apple-darwin
    858 
    859 test-rt-headers: bin
    860 	@set -e; \
    861 	for target in $(RT_HEADER_TEST_TARGETS); do \
    862 	  out="build/test/rt-headers/$$target/smoke.o"; \
    863 	  mkdir -p "$$(dirname "$$out")"; \
    864 	  $(BIN) cc -target "$$target" -Werror -c test/rt/smoke.c -o "$$out"; \
    865 	done
    866 
    867 # Arch token -> linux rt variant target. The cross-target test harnesses link
    868 # the matching LINUX runtime archive (compiler-rt-style helpers like
    869 # __kit_sext64ti, __kit_assert_fail) rather than the native `rt` target,
    870 # which builds only the host's own variant. Shared by the single-arch
    871 # parse/asm/macho/link tests (via KIT_TEST_ARCH/TEST_RT_DEP) and by
    872 # test-rt-runtime, which sweeps several arches at once.
    873 KIT_TEST_ARCH ?= aa64
    874 _TEST_RT_aa64    = rt-aarch64-linux
    875 _TEST_RT_aarch64 = rt-aarch64-linux
    876 _TEST_RT_arm64   = rt-aarch64-linux
    877 _TEST_RT_x64     = rt-x86_64-linux
    878 _TEST_RT_x86_64  = rt-x86_64-linux
    879 _TEST_RT_amd64   = rt-x86_64-linux
    880 _TEST_RT_rv64    = rt-riscv64-linux
    881 _TEST_RT_riscv64 = rt-riscv64-linux
    882 TEST_RT_DEP      = $(_TEST_RT_$(KIT_TEST_ARCH))
    883 
    884 # test-rt-runtime compiles each case for the configured arches and links it
    885 # against that arch's LINUX runtime archive, so it must depend on each variant
    886 # (not the host `rt`). A stale per-arch archive otherwise silently breaks the
    887 # link — e.g. a dropped __kit_assert_fail weak def surfaces as an undefined
    888 # reference. KIT_RT_RUNTIME_ARCHES mirrors the default in test/rt/run.sh.
    889 KIT_RT_RUNTIME_ARCHES ?= aa64 x64 rv64
    890 RT_RUNTIME_DEPS := $(foreach a,$(KIT_RT_RUNTIME_ARCHES),$(_TEST_RT_$(a)))
    891 LINK_EXE_RUNNER = build/test/link-exe-runner
    892 LINK_SCRIPT_TEST_BIN = build/test/link_script_test
    893 LINK_COMPAT_TEST_BIN = build/test/link_compat_test
    894 
    895 test-rt-runtime: bin $(RT_RUNTIME_DEPS) $(LINK_EXE_RUNNER)
    896 	@bash test/rt/run.sh
    897 
    898 # L3a backtrace round-trip: run a kit-compiled program that prints its own
    899 # backtrace, then symbolize the captured addresses two ways — `kit addr2line`
    900 # (bare addresses) and `kit symbolize` (the raw "#N 0xADDR" stream, annotated
    901 # in place). Same per-arch deps as test-rt-runtime (each arch's linux rt
    902 # archive + the Path-E link runner). See test/rt/addr2line.sh and
    903 # doc/plan/BACKTRACE.md (L3a / WS5).
    904 test-rt-backtrace: bin $(RT_RUNTIME_DEPS) $(LINK_EXE_RUNNER)
    905 	@bash test/rt/addr2line.sh
    906 
    907 # Test harness binaries shared by test-elf and test-link.
    908 # Declared as Make targets (not built by the run.sh scripts) so they pick
    909 # up libkit.a changes deterministically.
    910 #
    911 # HARNESS_CFLAGS drops -Wpedantic from the normal host flags; the runners cast
    912 # kit_jit_lookup's void* to a function pointer, which pedantic rejects under
    913 # C11.
    914 HARNESS_CFLAGS = $(filter-out -Wpedantic,$(HOST_CFLAGS)) -Iinclude -Itest
    915 
    916 ROUNDTRIP_BIN         = build/test/kit-roundtrip
    917 ROUNDTRIP_BIN_MACHO   = build/test/kit-roundtrip-macho
    918 ROUNDTRIP_BIN_COFF    = build/test/kit-roundtrip-coff
    919 COFF_IMPORT_SMOKE_BIN = build/test/pe-import-smoke
    920 COFF_IMPORT_MINGW_BIN = build/test/pe-import-mingw
    921 COFF_DSO_FORWARDER_BIN = build/test/pe-dso-forwarder
    922 COFF_MIXED_ARCHIVE_BIN = build/test/pe-mixed-archive
    923 COFF_IMAGE_READ_BIN    = build/test/pe-image-read
    924 LLVM_MINGW_RELEASE := 20260602
    925 _LLVM_MINGW_UCRT_ROOT = $(or $(KIT_LLVM_MINGW_ROOT),$(KIT_SYSROOTS_DIR)/windows/$(LLVM_MINGW_RELEASE)/ucrt)
    926 LLVM_MINGW_SYSROOT_X64_MARKER      = $(_LLVM_MINGW_UCRT_ROOT)/x86_64-w64-mingw32/PROVENANCE
    927 LLVM_MINGW_SYSROOT_AARCH64_MARKER  = $(_LLVM_MINGW_UCRT_ROOT)/aarch64-w64-mingw32/PROVENANCE
    928 JIT_RUNNER          = build/test/jit-runner
    929 PARSE_RUNNER        = build/test/parse-runner
    930 ASM_RUNNER          = build/test/asm-runner
    931 WASM_TOOL           = build/test/wasm-tool
    932 
    933 $(ROUNDTRIP_BIN): test/elf/kit-roundtrip.c $(LIB_AR)
    934 	@mkdir -p $(dir $@)
    935 	$(CC) $(HARNESS_CFLAGS) test/elf/kit-roundtrip.c $(LIB_AR) -o $@
    936 
    937 # Mach-O peer of kit-roundtrip — read_macho + emit_macho. Used by
    938 # test-link's path R when KIT_TEST_OBJ=macho.
    939 $(ROUNDTRIP_BIN_MACHO): test/macho/kit-roundtrip-macho.c $(LIB_AR)
    940 	@mkdir -p $(dir $@)
    941 	$(CC) $(HARNESS_CFLAGS) -Isrc test/macho/kit-roundtrip-macho.c $(LIB_AR) -o $@
    942 
    943 # PE/COFF round-trip harness (test/coff/). All-in-one binary: builds
    944 # hand-crafted ObjBuilders and asserts emit_coff/read_coff round-trip
    945 # stability for both x86_64-windows and aarch64-windows.
    946 $(ROUNDTRIP_BIN_COFF): test/coff/kit-roundtrip-coff.c $(LIB_OBJS)
    947 	@mkdir -p $(dir $@)
    948 	$(CC) $(HARNESS_CFLAGS) -Isrc test/coff/kit-roundtrip-coff.c $(LIB_OBJS) -o $@
    949 
    950 # PE import-directory smoke test (test/coff/pe-import-smoke.c).
    951 # Exercises the full chain: short-import shim bytes -> link_add_obj_bytes
    952 # (reclassified as DSO) -> link_resolve -> link_emit_coff. Verifies the
    953 # produced PE32+ via x86_64-w64-mingw32-objdump; skips cleanly if absent.
    954 $(COFF_IMPORT_SMOKE_BIN): test/coff/pe-import-smoke.c $(LIB_OBJS)
    955 	@mkdir -p $(dir $@)
    956 	$(CC) $(HARNESS_CFLAGS) -Isrc test/coff/pe-import-smoke.c $(LIB_OBJS) -o $@
    957 
    958 # PE import test against a real mingw archive (test/coff/pe-import-mingw.c).
    959 # Exercises the long-form import-archive absorption path
    960 # (link_add_archive_bytes -> classify_coff_archive_member). Skips cleanly
    961 # when the mingw toolchain isn't installed.
    962 $(COFF_IMPORT_MINGW_BIN): test/coff/pe-import-mingw.c $(LIB_OBJS)
    963 	@mkdir -p $(dir $@)
    964 	$(CC) $(HARNESS_CFLAGS) -Isrc test/coff/pe-import-mingw.c $(LIB_OBJS) -o $@
    965 
    966 # read_coff_dso forwarder-export contract (test/coff/pe-dso-forwarder.c).
    967 # Synthesizes a tiny PE32+ DLL with one direct and one forwarder export
    968 # and asserts both surface as OBJ_SEC_NONE globals on the ObjBuilder.
    969 $(COFF_DSO_FORWARDER_BIN): test/coff/pe-dso-forwarder.c $(LIB_OBJS)
    970 	@mkdir -p $(dir $@)
    971 	$(CC) $(HARNESS_CFLAGS) -Isrc test/coff/pe-dso-forwarder.c $(LIB_OBJS) -o $@
    972 
    973 # Mixed-member archive (test/coff/pe-mixed-archive.c).  Verifies that
    974 # one archive containing both a short-import member and a long-form
    975 # COFF object with a defined data symbol satisfies references through
    976 # both shapes — the same composition libucrt.a uses (API-set imports
    977 # alongside lib64_libucrt_extra_a-*.o helpers).
    978 $(COFF_MIXED_ARCHIVE_BIN): test/coff/pe-mixed-archive.c $(LIB_OBJS)
    979 	@mkdir -p $(dir $@)
    980 	$(CC) $(HARNESS_CFLAGS) -Isrc test/coff/pe-mixed-archive.c $(LIB_OBJS) -o $@
    981 
    982 # PE32+ linked-image reader round-trip (test/coff/pe-image-read.c). Links a
    983 # tiny PIE .exe in memory (import + base reloc), re-opens it via kit_obj_open,
    984 # and asserts the neutral image view + raw escape hatch. Needs no external
    985 # toolchain — runs on every host.
    986 $(COFF_IMAGE_READ_BIN): test/coff/pe-image-read.c $(LIB_OBJS)
    987 	@mkdir -p $(dir $@)
    988 	$(CC) $(HARNESS_CFLAGS) -Isrc test/coff/pe-image-read.c $(LIB_OBJS) -o $@
    989 
    990 $(LLVM_MINGW_SYSROOT_X64_MARKER): scripts/llvm_mingw_sysroot.sh
    991 	@bash scripts/llvm_mingw_sysroot.sh prepare x64
    992 
    993 $(LLVM_MINGW_SYSROOT_AARCH64_MARKER): scripts/llvm_mingw_sysroot.sh
    994 	@bash scripts/llvm_mingw_sysroot.sh prepare aarch64
    995 
    996 windows-ucrt-sysroots: $(LLVM_MINGW_SYSROOT_X64_MARKER) $(LLVM_MINGW_SYSROOT_AARCH64_MARKER)
    997 
    998 $(LINK_EXE_RUNNER): test/link/harness/link_exe_runner.c $(LIB_AR)
    999 	@mkdir -p $(dir $@)
   1000 	$(CC) $(HARNESS_CFLAGS) test/link/harness/link_exe_runner.c $(LIB_AR) -o $@
   1001 
   1002 $(JIT_RUNNER): test/link/harness/jit_runner.c $(LIB_AR)
   1003 	@mkdir -p $(dir $@)
   1004 	$(CC) $(HARNESS_CFLAGS) test/link/harness/jit_runner.c $(LIB_AR) -o $@
   1005 
   1006 $(PARSE_RUNNER): test/parse/harness/parse_runner.c $(LIB_AR)
   1007 	@mkdir -p $(dir $@)
   1008 	$(CC) $(HARNESS_CFLAGS) test/parse/harness/parse_runner.c $(LIB_AR) -o $@
   1009 
   1010 $(ASM_RUNNER): test/asm/harness/asm_runner.c $(LIB_AR)
   1011 	@mkdir -p $(dir $@)
   1012 	$(CC) $(HARNESS_CFLAGS) test/asm/harness/asm_runner.c $(LIB_AR) -o $@
   1013 
   1014 $(WASM_TOOL): test/wasm/harness/wasm_tool.c $(LIB_AR)
   1015 	@mkdir -p $(dir $@)
   1016 	$(CC) $(HARNESS_CFLAGS) -I. test/wasm/harness/wasm_tool.c $(LIB_AR) -o $@
   1017 
   1018 test-elf: lib bin-soft $(ROUNDTRIP_BIN)
   1019 	KIT_ELF_UNIT_CFLAGS='$(HOST_MODE_CFLAGS)' \
   1020 	KIT_ELF_UNIT_LDFLAGS='$(HOST_MODE_LDFLAGS)' \
   1021 	bash test/elf/run.sh
   1022 
   1023 # PE/COFF round-trip harness plus optional hosted Windows smoke. The
   1024 # UCRT smoke self-skips when llvm-mingw is not installed.
   1025 test-coff: lib bin rt-aarch64-windows $(ROUNDTRIP_BIN_COFF) $(COFF_IMPORT_SMOKE_BIN) $(COFF_DSO_FORWARDER_BIN) $(COFF_MIXED_ARCHIVE_BIN) $(COFF_IMAGE_READ_BIN)
   1026 	$(ROUNDTRIP_BIN_COFF)
   1027 	$(COFF_IMPORT_SMOKE_BIN)
   1028 	$(COFF_DSO_FORWARDER_BIN)
   1029 	$(COFF_MIXED_ARCHIVE_BIN)
   1030 	$(COFF_IMAGE_READ_BIN)
   1031 	bash test/coff/windows-ucrt-hosted-smoke.sh
   1032 	bash test/coff/windows-system-dlls-smoke.sh
   1033 
   1034 # Separate target so it can be skipped gracefully if mingw isn't
   1035 # installed. The test itself self-skips on missing tooling, but the
   1036 # build target only fires when explicitly requested.
   1037 test-coff-mingw-import: lib $(COFF_IMPORT_MINGW_BIN)
   1038 	$(COFF_IMPORT_MINGW_BIN)
   1039 
   1040 test-coff-windows-ucrt: bin rt-x86_64-pc-windows rt-aarch64-windows windows-ucrt-sysroots
   1041 	KIT_SYSROOT=$(abspath $(_LLVM_MINGW_UCRT_ROOT)) bash test/coff/windows-ucrt-hosted-smoke.sh
   1042 	KIT_SYSROOT=$(abspath $(_LLVM_MINGW_UCRT_ROOT)) bash test/coff/windows-system-dlls-smoke.sh
   1043 	KIT_SYSROOT=$(abspath $(_LLVM_MINGW_UCRT_ROOT)) bash test/coff/windows-o1-abi-smoke.sh
   1044 
   1045 test-coff-windows-o1-abi: bin rt-x86_64-pc-windows rt-aarch64-windows windows-ucrt-sysroots
   1046 	KIT_SYSROOT=$(abspath $(_LLVM_MINGW_UCRT_ROOT)) bash test/coff/windows-o1-abi-smoke.sh
   1047 
   1048 # Opt-in: run the COFF/PE hosted smokes against a real Windows 11 ARM64 VM, so
   1049 # their per-program run lanes execute for real instead of self-skipping. On
   1050 # Apple Silicon a single hvf-accelerated VM serves both targets: aarch64-windows
   1051 # runs natively and x86_64-windows runs via the in-box x64 emulator, so both run
   1052 # lanes point at the same endpoint. Requires a provisioned VM and a user-supplied
   1053 # Windows ISO (see doc/WINDOWS.md), then boots it, waits for SSH, and runs the
   1054 # smokes against it.
   1055 KIT_WINDOWS_VM_USER ?= kit
   1056 KIT_WINDOWS_VM_PORT ?= 2227
   1057 # SSH key is left unset so windows_vm.sh uses its default in the durable cache
   1058 # (~/.cache/kit/windows-vm/ssh/id_ed25519); the working disk auto-restores from
   1059 # the cached golden, so a clean build/ still finds a ready VM.
   1060 _WIN_VM_ENV = KIT_SYSROOT=$(abspath $(_LLVM_MINGW_UCRT_ROOT)) \
   1061 	KIT_WINDOWS_VM_AARCH64=$(KIT_WINDOWS_VM_USER)@127.0.0.1 KIT_WINDOWS_VM_AARCH64_PORT=$(KIT_WINDOWS_VM_PORT) \
   1062 	KIT_WINDOWS_VM_X64=$(KIT_WINDOWS_VM_USER)@127.0.0.1 KIT_WINDOWS_VM_X64_PORT=$(KIT_WINDOWS_VM_PORT)
   1063 
   1064 test-coff-windows-vm: bin rt-x86_64-pc-windows rt-aarch64-windows windows-ucrt-sysroots
   1065 	bash scripts/windows_vm.sh boot
   1066 	bash scripts/windows_vm.sh wait-ssh 600
   1067 	$(_WIN_VM_ENV) bash test/coff/windows-ucrt-hosted-smoke.sh
   1068 	$(_WIN_VM_ENV) bash test/coff/windows-system-dlls-smoke.sh
   1069 	$(_WIN_VM_ENV) bash test/coff/windows-o1-abi-smoke.sh
   1070 
   1071 # Opt-in linked-PE runtime acceptance. Both architectures execute in the one
   1072 # provisioned Windows 11 ARM64 VM (x64 through the in-box compatibility layer).
   1073 .PHONY: test-driver-strip-windows-vm
   1074 test-driver-strip-windows-vm: bin windows-ucrt-sysroots
   1075 	bash scripts/windows_vm.sh boot
   1076 	bash scripts/windows_vm.sh wait-ssh 600
   1077 	$(_WIN_VM_ENV) KIT=$(abspath $(BIN)) bash test/strip/windows-vm.sh
   1078 
   1079 # The parse/asm/macho harnesses select a cross-target via KIT_TEST_ARCH
   1080 # (default aa64); the link rt dependency is resolved through the shared
   1081 # _TEST_RT_<arch> map defined above (near test-rt-runtime).
   1082 test-link-compat: bin $(LINK_COMPAT_TEST_BIN)
   1083 	$(LINK_COMPAT_TEST_BIN)
   1084 	KIT=$(abspath $(BIN)) sh test/link/compat-driver.sh
   1085 
   1086 test-link-riscv-flags: bin
   1087 	KIT=$(abspath $(BIN)) sh test/link/riscv-flags.sh
   1088 
   1089 test-link: bin $(ROUNDTRIP_BIN) $(ROUNDTRIP_BIN_MACHO) $(LINK_EXE_RUNNER) \
   1090            $(JIT_RUNNER) $(LINK_SCRIPT_TEST_BIN) $(LINK_COMPAT_TEST_BIN)
   1091 	$(LINK_SCRIPT_TEST_BIN)
   1092 	$(LINK_COMPAT_TEST_BIN)
   1093 	KIT=$(abspath $(BIN)) sh test/link/compat-driver.sh
   1094 	bash test/link/run.sh
   1095 
   1096 # x64 ELF link/reloc-application coverage. test-link defaults to aa64, so
   1097 # kit's x64 static-link reloc fixups (R_X64_PLT32/GOTPCREL/TPOFF/...) were
   1098 # only ever run via a manual KIT_TEST_ARCH=x64 override. Opt-in (not in the
   1099 # default set) because the E path links + runs under podman/qemu-x86_64; the R
   1100 # path (roundtrip + reloc layout) runs on any host.
   1101 test-link-x64: lib rt-x86_64-linux $(ROUNDTRIP_BIN) $(LINK_EXE_RUNNER) $(JIT_RUNNER)
   1102 	@KIT_TEST_ARCH=x64 KIT_TEST_PATHS=RE bash test/link/run.sh
   1103 
   1104 # test-link-dso-aa64: ELF shared-library (DSO) harness targeting aarch64-linux.
   1105 # Uses kit cc/ld to produce DSOs and freestanding consumers, runs readelf
   1106 # structural checks, and executes consumers inside podman containers when
   1107 # provisioned. The make target provisions the musl sysroot so the libc
   1108 # dependency lane is active; direct script runs skip that lane if it is absent.
   1109 # Opt-in: requires readelf; runtime checks need provisioned images.
   1110 test-link-dso-aa64: bin $(MUSL_SYSROOT_MARKER)
   1111 	bash test/link/elf-dso-aa64/run.sh
   1112 
   1113 test-macho: lib $(TEST_RT_DEP) $(ROUNDTRIP_BIN_MACHO) $(LINK_EXE_RUNNER) $(JIT_RUNNER)
   1114 	KIT_TEST_OBJ=macho \
   1115 	KIT_TEST_ARCH=$${KIT_TEST_ARCH:-aa64} \
   1116 	KIT_TEST_PATHS=$${KIT_TEST_PATHS:-RJ} \
   1117 	KIT_TEST_ALLOW_SKIP=$${KIT_TEST_ALLOW_SKIP:-1} \
   1118 	bash test/link/run.sh
   1119 
   1120 .PHONY: test-link-macho-symbols
   1121 test-link-macho-symbols: bin $(LINK_EXE_RUNNER)
   1122 	bash test/link/macho-symbols.sh
   1123 
   1124 OPT_TEST_BIN = build/test/cg_ir_lower_test
   1125 TINY_INLINE_TEST_BIN = build/test/tiny_inline_test
   1126 REG_EFFECTS_TEST_BIN = build/test/reg_effects_test
   1127 LOCATION_MIR_TEST_BIN = build/test/location_mir_test
   1128 COMBINE_CSE_TEST_BIN = build/test/combine_cse_test
   1129 NATIVE_EMIT_FRAME_DST_TEST_BIN = build/test/native_emit_frame_dst_test
   1130 FRAME_VALUE_BACKEND_TEST_BIN = build/test/frame_value_backend_test
   1131 NATIVE_PART_CHUNK_TEST_BIN = build/test/native_part_chunk_test
   1132 
   1133 test-opt: bin $(OPT_TEST_BIN) $(REG_EFFECTS_TEST_BIN) $(LOCATION_MIR_TEST_BIN) $(COMBINE_CSE_TEST_BIN) $(NATIVE_EMIT_FRAME_DST_TEST_BIN) $(FRAME_VALUE_BACKEND_TEST_BIN) $(NATIVE_PART_CHUNK_TEST_BIN) test-opt-tiny-inline test-opt-inline test-opt-zero-arg test-opt-static-prune-aa64 test-opt-aa64-tail test-opt-aa64-apple-varargs test-opt-x64-win-tail-sret test-opt-prologue-tier test-opt-whole-program-inline test-opt-lto-phase1 test-opt-redundant-copy-ext test-opt-redundant-frame-sub test-opt-o1-branch-cleanup test-opt-macho-block-alt-entry test-opt-hot-slot-order test-opt-o1-remat test-opt-aa64-x29-bottom test-opt-o1-coalesce test-opt-o1-switch-imm test-opt-o1-inline-cap test-opt-rv64-far-slot test-opt-o1-cmp-imm test-opt-o1-stack-dse test-opt-arm32-emit-temp-pressure test-opt-o1-local-cse test-opt-o1p-aa64 test-opt-o1p-const-divmul test-opt-o1p-combine test-opt-o1p-rider
   1134 	$(OPT_TEST_BIN)
   1135 	$(REG_EFFECTS_TEST_BIN)
   1136 	$(LOCATION_MIR_TEST_BIN)
   1137 	$(COMBINE_CSE_TEST_BIN)
   1138 	$(NATIVE_EMIT_FRAME_DST_TEST_BIN)
   1139 	$(FRAME_VALUE_BACKEND_TEST_BIN)
   1140 	$(NATIVE_PART_CHUNK_TEST_BIN)
   1141 
   1142 
   1143 # Structural disasm check: the -O1 redundant copy / extension folds
   1144 # (addr_of[base+0] -> copy, same-width convert -> copy, zext-of-load -> copy).
   1145 .PHONY: test-opt-redundant-copy-ext
   1146 test-opt-redundant-copy-ext: bin
   1147 	@KIT=$(abspath $(BIN)) bash test/opt/redundant_copy_ext.sh
   1148 
   1149 # Structural disasm check: the -O1 local frame-address sub-CSE (a back-to-back
   1150 # rebuild of the same frame/GOT address is dropped while the holder is live).
   1151 .PHONY: test-opt-redundant-frame-sub
   1152 test-opt-redundant-frame-sub: bin
   1153 	@KIT=$(abspath $(BIN)) bash test/opt/redundant_frame_sub.sh
   1154 
   1155 # Structural+behavioral check: the one-pass O1 branch cleanup + constant
   1156 # cmp_branch folding (same-target collapse, x==x fold, computed-goto preserved).
   1157 .PHONY: test-opt-o1-branch-cleanup
   1158 test-opt-o1-branch-cleanup: bin
   1159 	@KIT=$(abspath $(BIN)) bash test/opt/o1_branch_cleanup.sh
   1160 
   1161 .PHONY: test-opt-macho-block-alt-entry
   1162 test-opt-macho-block-alt-entry: bin $(ROUNDTRIP_BIN_MACHO)
   1163 	@KIT=$(abspath $(BIN)) bash test/opt/macho_block_alt_entry.sh
   1164 
   1165 # Structural disasm check: hot-slot-low frame ordering keeps the hottest spill
   1166 # slots in cheap displacement range (x64 disp8, rv64 imm12 single-instruction).
   1167 .PHONY: test-opt-hot-slot-order
   1168 test-opt-hot-slot-order: bin
   1169 	@KIT=$(abspath $(BIN)) bash test/opt/hot_slot_order.sh
   1170 
   1171 # Structural disasm check: O1 rematerialization recomputes cheap spilled values
   1172 # (small load_imm, addr_of[local]) at the use instead of reloading from a slot.
   1173 .PHONY: test-opt-o1-remat
   1174 test-opt-o1-remat: bin
   1175 	@KIT=$(abspath $(BIN)) bash test/opt/o1_remat.sh
   1176 
   1177 # Structural disasm check: aa64 x29-at-bottom known-frame layout addresses every
   1178 # spill slot as positive ldr/str [x29,#k] (or add-build) -- no sub xN,x29,# at O1.
   1179 .PHONY: test-opt-aa64-x29-bottom
   1180 test-opt-aa64-x29-bottom: bin
   1181 	@KIT=$(abspath $(BIN)) bash test/opt/aa64_x29_bottom.sh
   1182 
   1183 # Behavioral disasm check: linear O1 move coalescing removes copy-chain movs and
   1184 # preserves the imm-through-copy fold (the explicit opt_o1_coalescing flag).
   1185 .PHONY: test-opt-o1-coalesce
   1186 test-opt-o1-coalesce: bin
   1187 	@KIT=$(abspath $(BIN)) bash test/opt/o1_coalesce.sh
   1188 
   1189 # Structural disasm check: O1 switch chains compare case values as immediates
   1190 # (cmp wN,#k) instead of materializing each into a scratch; selector stays pinned.
   1191 .PHONY: test-opt-o1-switch-imm
   1192 test-opt-o1-switch-imm: bin
   1193 	@KIT=$(abspath $(BIN)) bash test/opt/o1_switch_imm.sh
   1194 
   1195 # Behavioral check: the O1 whole-program inliner backs off inlining into an
   1196 # already-huge / high-pressure caller, while small + always_inline still fuse.
   1197 .PHONY: test-opt-o1-inline-cap
   1198 test-opt-o1-inline-cap: bin
   1199 	@KIT=$(abspath $(BIN)) bash test/opt/o1_inline_cap.sh
   1200 
   1201 # Structural disasm check (rv64): after W1.0 hot-slot ordering, hot frame slots
   1202 # are single-instruction ld/sd off(s0) within the imm12 window -- no far-slot
   1203 # lui;addiw;add rebuilds in hot code (O1.md W1.2 -- assessment, no codegen change).
   1204 .PHONY: test-opt-rv64-far-slot
   1205 test-opt-rv64-far-slot: bin
   1206 	@KIT=$(abspath $(BIN)) bash test/opt/rv64_far_slot.sh
   1207 
   1208 # Structural disasm check: O1 folds a load_imm feeding a cmp into cmp #imm, and
   1209 # merges add #k; ldr [#j] into ldr [#k+j] when the offset is legal (O1.md W6).
   1210 .PHONY: test-opt-o1-cmp-imm
   1211 test-opt-o1-cmp-imm: bin
   1212 	@KIT=$(abspath $(BIN)) bash test/opt/o1_cmp_imm.sh
   1213 
   1214 # Behavioral check: same-block stack dead-store elimination is correct (deletes
   1215 # a fully-overwritten spill store, preserves stores across reads/barriers,
   1216 # never touches volatile/atomic) (O1.md W8).
   1217 .PHONY: test-opt-o1-stack-dse
   1218 test-opt-o1-stack-dse: bin
   1219 	@KIT=$(abspath $(BIN)) bash test/opt/o1_stack_dse.sh
   1220 
   1221 # Compile-only ARMv7-M pressure guard: location MIR leaves spill values in
   1222 # frame homes, and the native emitter must stage operand-facing values from its
   1223 # explicit r0-r3 bank without borrowing backend-private IP/LR.
   1224 .PHONY: test-opt-arm32-emit-temp-pressure
   1225 test-opt-arm32-emit-temp-pressure: bin
   1226 	@KIT=$(abspath $(BIN)) bash test/opt/arm32_emit_temp_pressure.sh
   1227 
   1228 # Behavioral+structural check: same-block redundant-load/expression CSE reuses
   1229 # the last load/compute, but never across a may-alias store, call, or volatile.
   1230 .PHONY: test-opt-o1-local-cse
   1231 test-opt-o1-local-cse: bin
   1232 	@KIT=$(abspath $(BIN)) bash test/opt/o1_local_cse.sh
   1233 
   1234 # Structural check (aa64): L3 widens AGG_SET zero-init to wide stores (no long
   1235 # strb run); L10 elides the frame on no-spill leaf functions (O1-PATTERNS L3/L10).
   1236 .PHONY: test-opt-o1p-aa64
   1237 test-opt-o1p-aa64: bin
   1238 	@KIT=$(abspath $(BIN)) bash test/opt/o1p_aa64.sh
   1239 
   1240 # Structural check: constant-operand div/mul folds (sizeof/sizeof, SIZE_MAX
   1241 # guard, /1) while divide-by-zero / runtime divisors keep the op (O1-PATTERNS L9).
   1242 .PHONY: test-opt-o1p-const-divmul
   1243 test-opt-o1p-const-divmul: bin
   1244 	@KIT=$(abspath $(BIN)) bash test/opt/o1p_const_divmul.sh
   1245 
   1246 # Structural+behavioral check: L1 store->load forwarding, L6 sxt-after-load drop,
   1247 # L2 cmp;cset;cbnz->cmp_branch fusion, L4 double-cset drop, L5 copy coalescing
   1248 # (O1-PATTERNS L1/L2/L4/L5/L6). Red-green degrades to green-only without KIT_BASE.
   1249 .PHONY: test-opt-o1p-combine
   1250 test-opt-o1p-combine: bin
   1251 	@KIT=$(abspath $(BIN)) bash test/opt/o1p_combine.sh
   1252 
   1253 # Structural+correctness check: L7 folds a single-use shift into the ALU op
   1254 # (add xD,xB,xS,lsl#k); L8 folds a sxtw/uxtw index into the addressing mode
   1255 # ([Xb,Wm,sxtw#s]); aa64 only, multiply-used not folded (O1-PATTERNS L7/L8).
   1256 .PHONY: test-opt-o1p-rider
   1257 test-opt-o1p-rider: bin
   1258 	@KIT=$(abspath $(BIN)) bash test/opt/o1p_rider.sh
   1259 
   1260 
   1261 test-opt-tiny-inline: bin $(TINY_INLINE_TEST_BIN)
   1262 	$(TINY_INLINE_TEST_BIN)
   1263 
   1264 
   1265 # Behavioral disasm check: tiny callee `bl` disappears from its caller at -O1.
   1266 test-opt-inline: bin
   1267 	@KIT=$(abspath $(BIN)) bash test/opt/run.sh
   1268 
   1269 # Behavioral disasm check: a pointer-typed null call arg is not routed through
   1270 # a scratch temp at -O1 (PERCALL.md item 3, "zero through a temp").
   1271 test-opt-zero-arg: bin
   1272 	@KIT=$(abspath $(BIN)) bash test/opt/zero_arg.sh
   1273 
   1274 .PHONY: test-opt-static-prune-aa64
   1275 test-opt-static-prune-aa64: bin
   1276 	@KIT=$(abspath $(BIN)) bash test/opt/static_prune_aa64.sh
   1277 
   1278 .PHONY: test-opt-aa64-tail
   1279 test-opt-aa64-tail: bin
   1280 	@KIT=$(abspath $(BIN)) bash test/opt/aa64_tail_call.sh
   1281 
   1282 .PHONY: test-opt-aa64-apple-varargs
   1283 test-opt-aa64-apple-varargs: bin
   1284 	@KIT=$(abspath $(BIN)) bash test/opt/aa64_apple_varargs.sh
   1285 
   1286 .PHONY: test-opt-x64-win-tail-sret
   1287 test-opt-x64-win-tail-sret: bin
   1288 	@KIT=$(abspath $(BIN)) bash test/opt/x64_win_tail_sret.sh
   1289 
   1290 # Structural disasm check: the -O1 known-frame prologue cost-model tiers
   1291 # (aa64 reference + ported x64 slim/red-zone and rv64 leaf shapes).
   1292 .PHONY: test-opt-prologue-tier
   1293 test-opt-prologue-tier: bin
   1294 	@KIT=$(abspath $(BIN)) bash test/opt/prologue_tier.sh
   1295 
   1296 # Whole-program (LTO Phase 0) cross-function inlining: a small static callee
   1297 # fuses into its caller at -O1 on every arch, and opt_inline actually fires.
   1298 .PHONY: test-opt-whole-program-inline
   1299 test-opt-whole-program-inline: bin
   1300 	@KIT=$(abspath $(BIN)) bash test/opt/whole_program_inline.sh
   1301 
   1302 .PHONY: test-opt-lto-phase1
   1303 test-opt-lto-phase1: bin
   1304 	@KIT=$(abspath $(BIN)) bash test/opt/lto_phase1.sh
   1305 
   1306 test-parse: test-parse-ok test-parse-err
   1307 
   1308 test-parse-ok: lib $(TEST_RT_DEP) $(PARSE_RUNNER) $(ROUNDTRIP_BIN) $(LINK_EXE_RUNNER) $(JIT_RUNNER)
   1309 	bash test/parse/run.sh
   1310 
   1311 test-parse-err: lib $(PARSE_RUNNER)
   1312 	sh test/parse/run_errors.sh
   1313 
   1314 # test-asm: aggregate alias running every per-arch asm lane (aa64 + x64 + rv64)
   1315 # so `make test` covers all three through one target. The harness runs one arch
   1316 # per invocation (KIT_TEST_ARCH); each lane scopes its scratch per arch, so
   1317 # the prerequisites are safe to run in parallel under `make -j`.
   1318 test-asm: test-asm-aa64 test-asm-x64 test-asm-rv64 test-asm-rv32
   1319 
   1320 # test-asm-aa64: the reference lane. aa64 is the default cross-target, and on
   1321 # aa64 hosts the exec paths (D/E/J) run natively, so it uses the full default
   1322 # path set (HTLDJE). The Makefile owns the harness binaries so they inherit host
   1323 # flags consistently with the rest of the test suite.
   1324 test-asm-aa64: lib $(TEST_RT_DEP) $(ASM_RUNNER) $(LINK_EXE_RUNNER) $(JIT_RUNNER)
   1325 	@KIT_TEST_ARCH=aa64 bash test/asm/run.sh
   1326 
   1327 # x64/rv64 exercise the encode (H), decode (T) and listing (L) corpora on any
   1328 # host: H/T/L need no native execution (they only produce/compare bytes), so the
   1329 # exec paths (D/E/J) are deliberately excluded and left to the smoke/qemu
   1330 # targets.
   1331 test-asm-x64: lib $(ASM_RUNNER)
   1332 	@KIT_TEST_ARCH=x64 KIT_TEST_PATHS=HTL bash test/asm/run.sh
   1333 test-asm-rv64: lib $(ASM_RUNNER)
   1334 	@KIT_TEST_ARCH=rv64 KIT_TEST_PATHS=HT bash test/asm/run.sh
   1335 # riscv32-none-elf is freestanding (no qemu-user exec lane), so like rv64 it
   1336 # runs the host-independent encode (H) + decode (T) lanes only. The rv32_*
   1337 # goldens use a no-compressed ISA so encodings stay a stable 4 bytes.
   1338 test-asm-rv32: lib $(ASM_RUNNER)
   1339 	@KIT_TEST_ARCH=rv32 KIT_TEST_PATHS=HT bash test/asm/run.sh
   1340 
   1341 # test-toy-rv32: the rv32 cross lane (path X) of the Toy corpus, scoped to the
   1342 # rv32 arch only. test/toy/run.sh's cross_one_rv32 compiles each case with
   1343 # `kit cc -target riscv32-none-elf` and runs the freestanding ELF bare-metal
   1344 # under qemu-system-riscv32 via test/lib/exec_bare.sh (the qemu exit code
   1345 # is the exit-code oracle). Self-skips per case when the rv32 toolchain
   1346 # (clang riscv32 + qemu-system-riscv32) is absent. The corpus is green; this
   1347 # lane is opt-in (not in DEFAULT_TEST_TARGETS) because it needs that toolchain,
   1348 # matching the rv64 cross lanes. The only non-passing cases are intentionally
   1349 # unsupported on rv32 (__int128, binary128 long double, LP64-data-model
   1350 # assumptions, aa64-only intrinsics) and carry committed .rv32.skip sidecars.
   1351 test-toy-rv32: bin rt-riscv32-elf-hardfloat
   1352 	@KIT=$(abspath $(BIN)) KIT_TOY_CROSS_ARCHS=rv32 KIT_TEST_PATHS=X \
   1353 		test/toy/run.sh
   1354 
   1355 # test-parse-rv32: the C-parser corpus run for riscv32-none-elf, exec lane (E)
   1356 # only. parse-runner --emit -> kit ld + start crt -> qemu-system-riscv32
   1357 # (test/parse/run.sh's rv32 freestanding E path via exec_bare.sh). Models
   1358 # test-parse-rv64-wide; opt-in (needs the rv32 toolchain/qemu), so excluded
   1359 # from DEFAULT_TEST_TARGETS, matching test-parse-rv64-wide. The corpus is green;
   1360 # the only skips are intentionally-unsupported cases (__int128, binary128 long
   1361 # double, LP64-data-model assumptions) with committed .rv32.skip sidecars.
   1362 test-parse-rv32: lib rt-riscv32-elf-hardfloat $(PARSE_RUNNER) $(ROUNDTRIP_BIN) \
   1363                  $(LINK_EXE_RUNNER)
   1364 	@KIT_TEST_ARCH=rv32 KIT_TEST_PATHS=E bash test/parse/run.sh
   1365 
   1366 # Codegen round-trip completeness (doc/ASM_ROUNDTRIP_TESTING.md). These drive
   1367 # the `kit` binary itself (cc -S / as / objdump) over a C corpus rather than
   1368 # a hand-written asm corpus, so coverage tracks codegen automatically.
   1369 #
   1370 #   test-disasm-complete    L0: cc -S must decode every in-function word
   1371 #                           (no `.inst` markers). Host-independent, no exec.
   1372 #   test-asm-roundtrip      L0+L1: also assert cc -c bytes/relocs == cc -S | as.
   1373 #   test-asm-roundtrip-exec L0+L1+L2: also run direct vs round-tripped object
   1374 #                           and compare exit codes (native arch; opt-in).
   1375 #
   1376 # Vertical slice: aa64 only for now; L1/L2 run at -O1 (branch-free), L0 at both
   1377 # opt levels. Broadening to -O0, other arches, and the default suite is tracked
   1378 # in doc/ASM_ROUNDTRIP_TESTING.md once -S symbolization (Phase 2) lands.
   1379 test-disasm-complete: bin
   1380 	@KIT_TEST_ARCH=aa64 KIT_TEST_OPTS="O0 O1" KIT_TEST_PATHS=0 \
   1381 		bash test/asm/roundtrip.sh
   1382 test-asm-roundtrip: bin
   1383 	@KIT_TEST_ARCH=aa64 KIT_TEST_OPTS="O0 O1" KIT_TEST_PATHS=01 \
   1384 		bash test/asm/roundtrip.sh
   1385 test-asm-roundtrip-exec: bin $(JIT_RUNNER)
   1386 	@KIT_TEST_ARCH=aa64 KIT_TEST_OPTS="O0 O1" KIT_TEST_PATHS=012 \
   1387 		bash test/asm/roundtrip.sh
   1388 
   1389 # test-asm-symmetry: asm<->disasm self-symmetry sweep (aa64). Decode-side sweeps
   1390 # every disasm-table form (decode->encode->decode fixed point); encode-side
   1391 # asserts every byte the assembler emits over the encode corpus is decodable.
   1392 # Catches encode/decode asymmetries the codegen round-trip can't reach (e.g. a
   1393 # form one tool handles and the other doesn't). Host-independent, no exec.
   1394 test-asm-symmetry: $(ASM_RUNNER) $(AA64_SWEEP_GEN)
   1395 	@bash test/asm/symmetry.sh
   1396 
   1397 # test-diff-llvm: differential cross-check of kit against llvm (aa64), as a
   1398 # second oracle. Encode lane: kit as vs llvm-mc bytes over the encode corpus.
   1399 # Disasm lane: cc -c bytes vs llvm-mc of cc -S (validates kit's disassembler;
   1400 # the benign same-section-call reloc-vs-resolve difference is recognized).
   1401 # Opt-in; skips cleanly when llvm-mc is absent.
   1402 test-diff-llvm: bin
   1403 	@KIT_TEST_OPTS="O0 O1" bash test/asm/diff_llvm.sh
   1404 
   1405 # test-asm-roundtrip-toy: L2 exec round-trip over the Toy corpus (native arch).
   1406 # Reuses the ~150 toy cases (full CG op set, exit-code oracle) for free
   1407 # round-trip coverage: kit cc -S | kit as | kit run, exit must match.
   1408 # Opt-in; native target. Found a real miscompile (dropped .inst) the hand
   1409 # corpus never reached.
   1410 test-asm-roundtrip-toy: bin
   1411 	@bash test/asm/roundtrip_toy.sh
   1412 
   1413 # test-hostas-toy: feed one native `cc -S` to BOTH kit's own `as` and clang (a
   1414 # third-party host assembler), link + run each, and assert the toy exit-code
   1415 # oracle. Only the assembler differs between the two lanes, so the clang lane is
   1416 # the real test: a standard assembler can't paper over a private-dialect quirk
   1417 # the way kit's own `as` can (cf. test/asm/diff_llvm.sh, but by execution).
   1418 # cc -S is now object-format-aware, so the native Mach-O clang lane GATES by
   1419 # default (both lanes 312/0); KIT_HOSTAS_ENFORCE_CLANG=0 demotes it to XFAIL.
   1420 # Opt-in; skips cleanly if clang is absent.
   1421 test-hostas-toy: bin
   1422 	@bash test/asm/hostas_toy.sh
   1423 
   1424 # test-hostas-cross: the same two-assembler-by-execution idea as test-hostas-toy,
   1425 # but CROSS — `cc -S -target <triple>` for ELF Linux arches (aarch64/x86_64/
   1426 # riscv64), assembled by kit-as AND clang, linked static (+ the start.c crt)
   1427 # with kit ld, and run under podman/qemu via test/lib/exec_target.sh. Each
   1428 # target self-skips unless the host has a clang cross target, a runner, a
   1429 # working `cc -S | kit as` for that arch, and a passing bounded exec smoke —
   1430 # so it runs green on whatever the host supports (aarch64-linux today; x86_64
   1431 # pends the x64 cc -S symbolizer, riscv64 pends a working rv64 user-mode
   1432 # emulator). Opt-in; skips cleanly if clang/podman are absent.
   1433 test-hostas-cross: bin
   1434 	@bash test/asm/hostas_cross.sh
   1435 
   1436 test-wasm: test-wasm-front test-wasm-target test-wasm-toy
   1437 
   1438 test-wasm-front: bin $(WASM_TOOL) $(LINK_EXE_RUNNER) $(JIT_RUNNER)
   1439 	bash test/wasm/run.sh
   1440 
   1441 # test-wasm-target: structural checks on `kit cc -target wasm32-none`
   1442 # output. test/wasm-target/run.sh is a Type C corpus harness whose lanes each
   1443 # compile a tiny C or toy fixture and assert a property of the produced module
   1444 # bytes (inline-asm opcodes, memory.copy/fill opcodes, exported "memory",
   1445 # (import ...) decls). Opt-in: not in the default `test` target because the
   1446 # checks depend on the bulk-memory + (import ...) backend work landing first.
   1447 test-wasm-target: bin
   1448 	@KIT=$(abspath $(BIN)) bash test/wasm-target/run.sh
   1449 
   1450 # test-smoke-x64: phase-1 sanity check for the multi-arch bring-up. Builds a
   1451 # tiny freestanding x86_64 ELF with clang --target=x86_64-linux-gnu and
   1452 # runs it through test/lib/exec_target.sh's podman/qemu pipeline,
   1453 # proving the harness end-to-end before any kit-emitted x64 bytes
   1454 # exist. Excluded from the default `test` target because it needs
   1455 # podman + lld; opt-in via `make test-smoke-x64`.
   1456 test-smoke-x64:
   1457 	bash test/smoke/x64.sh
   1458 
   1459 # test-smoke-x64-macos: end-to-end check of kit's OWN x86_64 Mach-O
   1460 # compile→link→execute path. kit compiles + links a hello-world to an
   1461 # x86_64-macos Mach-O executable (driving the __stubs/__got import path) and
   1462 # runs it via Rosetta 2 on an Apple-silicon host, asserting stdout + exit
   1463 # code. Excluded from the default `test` target because it needs a Darwin
   1464 # host with Rosetta + a macOS SDK; opt-in via `make test-smoke-x64-macos`.
   1465 test-smoke-x64-macos:
   1466 	bash test/smoke/x64_macos.sh
   1467 
   1468 # test-smoke-ios-app: C-only UIKit smoke for the iOS simulator. It compiles
   1469 # test/smoke/ios_app.c with kit, links UIKit/Foundation/CoreGraphics SDK stubs
   1470 # into an arm64 iOS simulator Mach-O, bundles and ad-hoc signs a minimal .app.
   1471 # Set KIT_IOS_SIM_LAUNCH=1 with a booted simulator to install and launch it.
   1472 test-smoke-ios-app:
   1473 	bash test/smoke/ios_app.sh
   1474 
   1475 # test-smoke-rv64: phase-2 counterpart of test-smoke-x64. Builds a
   1476 # tiny freestanding riscv64 ELF with clang --target=riscv64-linux-gnu
   1477 # and runs it through test/lib/exec_target.sh, proving the rv64 lane
   1478 # of the harness end-to-end before any kit-emitted rv64 bytes
   1479 # exist. Excluded from the default `test` target because it needs
   1480 # qemu-riscv64 (or podman with riscv64 emulation) + lld; opt-in via
   1481 # `make test-smoke-rv64`.
   1482 test-smoke-rv64:
   1483 	bash test/smoke/rv64.sh
   1484 
   1485 # test-smoke-rv32: behavioral oracle for riscv32-none-elf codegen under
   1486 # qemu-system-riscv32 (ilp32f + ilp32 lanes, i64 + soft-float). Skips if the
   1487 # rv32 toolchain/qemu prerequisites are absent (see test/lib/check_rv32_env.sh).
   1488 test-smoke-rv32:
   1489 	bash test/smoke/rv32.sh
   1490 
   1491 # test-smoke-arm32: behavioral oracle for arm-none-eabi (Cortex-M3, Thumb-2)
   1492 # codegen under qemu-system-arm -machine mps2-an385 (single soft-float lane,
   1493 # i64 + soft-double + a <=4-int-arg call + a switch). Skips if the arm32
   1494 # toolchain/qemu prerequisites are absent (see test/lib/check_arm32_env.sh).
   1495 # The macro lane (arm32_macros.sh) verifies the -march/-mcpu/-mfpu/-mfloat-abi
   1496 # __ARM_* predefines via a #error-guarded compile; it needs only `kit` (and
   1497 # host clang, when present, for the cross-check) — no qemu/lld.
   1498 test-smoke-arm32:
   1499 	bash test/smoke/arm32_macros.sh
   1500 	bash test/smoke/arm32.sh
   1501 
   1502 # The whole-toolchain bare-metal smoke (compile + assemble reset stub + link +
   1503 # boot under qemu-system, for aarch64/x86_64/riscv64/riscv32) is now driven by
   1504 #   make test-cross TARGET=freestanding
   1505 # whose exec_bare backend (test/lib/exec_bare.sh) owns the per-arch reset stubs,
   1506 # linker scripts, and exit oracles. See doc/plan/PORT.md.
   1507 
   1508 # test-parse-rv64-wide: end-to-end coverage of the rv64 128-bit scalar types
   1509 # — __int128 (i128_*) and IEEE-754 binary128 long double (ldbl128_*) — built
   1510 # with kit and run on riscv64. Exercises the soft-float / i128 lowering to
   1511 # the compiler-rt-style runtime (fp_tf, fp_ti, int64), the LP64D register-pair
   1512 # ABI for 16-byte scalars, and the conditional-branch range fix that large
   1513 # soft-float helpers depend on. Opt-in (needs qemu-riscv64 or podman with
   1514 # riscv64 emulation), so excluded from the default `test` target; mirrors
   1515 # test-smoke-rv64. Run a single case with
   1516 #   KIT_TEST_ARCH=rv64 bash test/parse/run.sh ldbl128_03_arith
   1517 test-parse-rv64-wide: lib rt-riscv64-linux $(PARSE_RUNNER) $(ROUNDTRIP_BIN) \
   1518                       $(LINK_EXE_RUNNER)
   1519 	@KIT_TEST_ARCH=rv64 KIT_TEST_PATHS=RE bash test/parse/run.sh 128
   1520 
   1521 # test-bounce: format-bounce stress test. Compiles small programs with
   1522 # kit, then bounces each object through chains of format conversions
   1523 # (ELF<->Mach-O<->COFF), partial links (ld -r), strip, and archive
   1524 # round-trips, relinks, and runs the result, asserting the exit code
   1525 # matches a host-cc reference. Stresses obj read/write/reloc, ar, and
   1526 # partial-link paths rather than the arches. Defaults to the host-native
   1527 # Linux arch (no emulation); sweep others with
   1528 # KIT_BOUNCE_ARCHES="aarch64 x64 rv64". Excluded from the default `test`
   1529 # target because it needs podman/qemu + a host cc; opt-in via
   1530 # `make test-bounce`.
   1531 test-bounce: $(BIN)
   1532 	bash test/bounce/bounce.sh
   1533 
   1534 # test-libc: aggregate alias that runs test-libc-musl and test-libc-glibc.
   1535 # test-libc-musl / test-libc-glibc: end-to-end static + dynamic libc link/run
   1536 # on aarch64. Each variant pulls its own pinned sysroot (podman, ~30s on
   1537 # first run) and shares the same case files under test/libc/cases/:
   1538 #
   1539 #   test-libc-musl   — Alpine 3.20 + musl 1.2.5    (test/libc/musl/)
   1540 #   test-libc-glibc  — Debian bookworm + glibc 2.36 (test/libc/glibc/)
   1541 #
   1542 # Both build build/rt/aarch64-linux/libkit_rt.a for soft-float / TF
   1543 # builtins, and run `kit ld` against the real libc.a (static) and
   1544 # libc.so / libc.so.6 (dynamic). Excluded from the
   1545 # default `test` target because they need podman; opt-in via
   1546 # `make test-libc-musl` / `make test-libc-glibc` (or `make test-libc` for both).
   1547 #
   1548 # Each sysroot is treated as a real prerequisite via its PROVENANCE
   1549 # marker so subsequent runs skip extraction and re-extract only when
   1550 # the file is removed (or extract.sh -f forces a rebuild).
   1551 MUSL_SYSROOT_MARKER       = $(KIT_SYSROOTS_DIR)/linux-musl/PROVENANCE
   1552 MUSL_SYSROOT_X64_MARKER   = $(KIT_SYSROOTS_DIR)/linux-musl-x64/PROVENANCE
   1553 MUSL_SYSROOT_RV64_MARKER  = $(KIT_SYSROOTS_DIR)/linux-musl-rv64/PROVENANCE
   1554 GLIBC_SYSROOT_MARKER      = $(KIT_SYSROOTS_DIR)/linux-glibc/PROVENANCE
   1555 GLIBC_SYSROOT_X64_MARKER  = $(KIT_SYSROOTS_DIR)/linux-glibc-x64/PROVENANCE
   1556 GLIBC_SYSROOT_RV64_MARKER = $(KIT_SYSROOTS_DIR)/linux-glibc-rv64/PROVENANCE
   1557 
   1558 $(MUSL_SYSROOT_MARKER): test/libc/musl/extract.sh test/libc/musl/Containerfile
   1559 	@bash test/libc/musl/extract.sh
   1560 
   1561 $(MUSL_SYSROOT_X64_MARKER): test/libc/musl/extract.sh test/libc/musl/Containerfile.x64
   1562 	@bash test/libc/musl/extract.sh -a x64
   1563 
   1564 $(MUSL_SYSROOT_RV64_MARKER): test/libc/musl/extract.sh test/libc/musl/Containerfile.rv64
   1565 	@bash test/libc/musl/extract.sh -a rv64
   1566 
   1567 $(GLIBC_SYSROOT_MARKER): test/libc/glibc/extract.sh test/libc/glibc/Containerfile
   1568 	@bash test/libc/glibc/extract.sh
   1569 
   1570 $(GLIBC_SYSROOT_X64_MARKER): test/libc/glibc/extract.sh test/libc/glibc/Containerfile.x64
   1571 	@bash test/libc/glibc/extract.sh -a x64
   1572 
   1573 $(GLIBC_SYSROOT_RV64_MARKER): test/libc/glibc/extract.sh test/libc/glibc/Containerfile.rv64
   1574 	@bash test/libc/glibc/extract.sh -a rv64
   1575 
   1576 # test-libc-musl / test-libc-glibc honor KIT_LIBC_ARCHES (default "aa64";
   1577 # values: aa64, x64, rv64). Each enabled arch contributes its sysroot
   1578 # PROVENANCE marker and its rt archive to the prerequisite list, so
   1579 # `KIT_LIBC_ARCHES="aa64 x64" make test-libc-musl` builds both sysroots
   1580 # + both rt archives before the runner script picks them up.
   1581 KIT_LIBC_ARCHES ?= aa64
   1582 
   1583 # Map an arch token to its musl/glibc sysroot marker and rt target.
   1584 _LIBC_MUSL_SYSROOT_aa64  = $(MUSL_SYSROOT_MARKER)
   1585 _LIBC_MUSL_SYSROOT_x64   = $(MUSL_SYSROOT_X64_MARKER)
   1586 _LIBC_MUSL_SYSROOT_rv64  = $(MUSL_SYSROOT_RV64_MARKER)
   1587 _LIBC_GLIBC_SYSROOT_aa64 = $(GLIBC_SYSROOT_MARKER)
   1588 _LIBC_GLIBC_SYSROOT_x64  = $(GLIBC_SYSROOT_X64_MARKER)
   1589 _LIBC_GLIBC_SYSROOT_rv64 = $(GLIBC_SYSROOT_RV64_MARKER)
   1590 _LIBC_RT_aa64            = rt-aarch64-linux
   1591 _LIBC_RT_x64             = rt-x86_64-linux
   1592 _LIBC_RT_rv64            = rt-riscv64-linux
   1593 
   1594 LIBC_MUSL_DEPS  = $(foreach a,$(KIT_LIBC_ARCHES),$(_LIBC_MUSL_SYSROOT_$(a)) $(_LIBC_RT_$(a)))
   1595 LIBC_GLIBC_DEPS = $(foreach a,$(KIT_LIBC_ARCHES),$(_LIBC_GLIBC_SYSROOT_$(a)) $(_LIBC_RT_$(a)))
   1596 
   1597 test-libc: test-libc-musl test-libc-glibc
   1598 
   1599 test-libc-musl: bin $(LIBC_MUSL_DEPS)
   1600 	@KIT_LIBC_ARCHES="$(KIT_LIBC_ARCHES)" bash test/libc/musl/run.sh
   1601 
   1602 test-libc-glibc: bin $(LIBC_GLIBC_DEPS)
   1603 	@KIT_LIBC_ARCHES="$(KIT_LIBC_ARCHES)" bash test/libc/glibc/run.sh
   1604 
   1605 # FreeBSD hosted executable smoke (static + dynamic). Sysroots come from
   1606 # scripts/freebsd_sysroot.sh (cached base.txz extracts under ~/.cache/kit). By
   1607 # default these validate compile/link + ELF metadata; set KIT_FREEBSD_RUN_VM=1
   1608 # to boot the cached FreeBSD VMs and execute each binary under real FreeBSD.
   1609 test-freebsd: bin
   1610 	@KIT_FREEBSD_LINK=both bash test/libc/freebsd/run.sh
   1611 
   1612 test-freebsd-static: bin
   1613 	@KIT_FREEBSD_LINK=static bash test/libc/freebsd/run.sh
   1614 
   1615 test-freebsd-dynamic: bin
   1616 	@KIT_FREEBSD_LINK=dynamic bash test/libc/freebsd/run.sh
   1617 
   1618 test-libc-musl-rv64:
   1619 	@$(MAKE) test-libc-musl KIT_LIBC_ARCHES=rv64
   1620 
   1621 test-libc-glibc-rv64:
   1622 	@$(MAKE) test-libc-glibc KIT_LIBC_ARCHES=rv64
   1623 
   1624 # The hosted cross-compile-and-run suite is now `make test-cross` (engine:
   1625 # scripts/cross_test.sh, source of truth: scripts/hosted.sh). See doc/plan/PORT.md.
   1626 # hosted-glibc-images is kept: scripts/provision.sh pulls the per-arch Debian run
   1627 # images for the linux-glibc run lanes through it (alpine/musl uses test-images).
   1628 HOSTED_GLIBC_IMAGES = \
   1629     docker.io/arm64v8/debian:bookworm-slim \
   1630     docker.io/amd64/debian:bookworm-slim \
   1631     docker.io/riscv64/debian:trixie-slim
   1632 
   1633 hosted-glibc-images:
   1634 	@for img in $(HOSTED_GLIBC_IMAGES); do \
   1635 	  podman image exists "$$img" 2>/dev/null || podman pull "$$img" || \
   1636 	    echo "warn: could not pull $$img (linux-glibc run lanes will SKIP)"; \
   1637 	done
   1638 
   1639 # Fail if libkit.a depends on any external symbol not in the allowlist, or
   1640 # if a relocatable link exposes non-public global definitions.
   1641 # External dependency drift in either direction (new dep, or stale entry) is a
   1642 # failure.
   1643 #
   1644 # Inspect the RELEASE artifacts only: the debug build is ASan/UBSan-instrumented
   1645 # and pulls in __asan_*/__ubsan_* externals that are not part of the shipped
   1646 # library's dependency surface.
   1647 LIB_DEPS_RELEASE_DIR = build/release
   1648 LIB_DEPS_AR     = $(LIB_DEPS_RELEASE_DIR)/libkit.a
   1649 LIB_DEPS_ACTUAL = $(LIB_DEPS_RELEASE_DIR)/libkit.deps.txt
   1650 LIB_RELOC       = $(LIB_DEPS_RELEASE_DIR)/libkit.reloc.o
   1651 LIB_RELOC_BAD   = $(LIB_DEPS_RELEASE_DIR)/libkit.reloc.bad-symbols.txt
   1652 
   1653 test-lib-deps:
   1654 	@$(MAKE) lib RELEASE=1
   1655 	@mkdir -p $(dir $(LIB_DEPS_ACTUAL))
   1656 	@python3 scripts/lib_external_deps.py $(LIB_DEPS_AR) > $(LIB_DEPS_ACTUAL)
   1657 	@diff -u scripts/lib_deps.allowlist $(LIB_DEPS_ACTUAL) \
   1658 	    || { echo "libkit.a external symbol set drifted from scripts/lib_deps.allowlist"; exit 1; }
   1659 	@python3 scripts/lib_reloc_defined_prefixes.py $(LIB_DEPS_AR) \
   1660 	    --output $(LIB_RELOC) --ar $(AR) --cc $(CC) > $(LIB_RELOC_BAD)
   1661 	@test ! -s $(LIB_RELOC_BAD) \
   1662 	    || { echo "libkit relocatable link exposes non-public symbols"; cat $(LIB_RELOC_BAD); exit 1; }