run.sh (29013B)
1 #!/usr/bin/env bash 2 # test/parse/run.sh — file-driven C-parser test harness, on the shared corpus 3 # harness (test/lib/kit_corpus.sh). 4 # 5 # For each test/parse/cases/*.c, runs up to six lanes (KIT_TEST_PATHS, 6 # default DREJ — C and W are opt-in): 7 # 8 # D in-process JIT — parse-runner --jit FILE.c → exit code matches 9 # expected. No file I/O. Host arch must match cross 10 # target. 11 # R ELF roundtrip — parse-runner --emit + kit-roundtrip + readelf 12 # normalize diff. Validates emitter+reader fidelity. 13 # E exec via qemu — parse-runner --emit + start.o → link-exe-runner → 14 # qemu/podman → exit code. Cross-host friendly. Deferred 15 # to a batched exec_target flush (kit_queue_e). 16 # J jit-via-file — parse-runner --emit + jit-runner. Host arch must match 17 # cross target. 18 # C emit-c host — parse-runner --emit-c + host cc + test_main wrapper, 19 # run native. Validates the --emit=c C-source backend. 20 # Host arch must match cross target. opt=0 only. Cases 21 # that hit an unimplemented C-target method are reported 22 # as SKIP (not FAIL) so phased backend rollout is 23 # tolerated. 24 # W wasm roundtrip — kit cc -target wasm32-none -c case.c -> .wasm, then 25 # kit run -e test_main on it (the lang/wasm frontend 26 # re-lowers to native CG, JITs, calls test_main). 27 # Exercises the Wasm CGTarget (C -> wasm). Opt-in and 28 # opt=0 only; host arch must match cross target (the 29 # re-lowering JITs for the host). Phased-rollout panics 30 # ("wasm: ... not yet implemented") report SKIP. 31 # 32 # Reuses the test/link harness binaries (kit-roundtrip, link-exe-runner, 33 # jit-runner) and test/link/harness/start.c verbatim. 34 # 35 # Sidecar conventions (each missing file uses the documented default): 36 # <name>.expected — integer; default 0. Compared mod 256 to test_main. 37 # <name>.skip — single-line reason. Whole-case skip on every arch. 38 # <name>.<arch>.skip— single-line reason; whole-case skip on that arch only 39 # (e.g. asm_01_grammar.rv64.skip). 40 # <name>.cbackend.skip — single-line reason; opts the case out of lane C only. 41 # <name>.wasm.skip — single-line reason; opts the case out of lane W only. 42 # Skips are treated as failure unless KIT_TEST_ALLOW_SKIP=1 (matching the 43 # rest of the test suite). 44 # 45 # Filtering: 46 # ./run.sh [name_filter] [paths] 47 # name_filter substring match against case basename 48 # paths subset of "DREJCW" (default "DREJ" — C and W opt-in) 49 # Equivalent env vars: KIT_TEST_FILTER, KIT_TEST_PATHS. 50 # 51 # Optimization levels: 52 # KIT_OPT_LEVELS="0 1" whitespace-separated levels to test. 53 # KIT_OPT_LEVEL=1 compatibility shorthand for one level. 54 # Default is "0 1". 55 # 56 # Parallelism: 57 # default run in parallel with a capped CPU-count default. 58 # KIT_TEST_JOBS=N run up to N cases concurrently. 59 # KIT_PARSE_PARALLEL=0 force serial dispatch. 60 # KIT_CORPUS_TRACE=1 force serial dispatch and print each item/lane before 61 # running it. 62 # All lane hooks write only under KIT_WORK and record via kit_*, so the runner is 63 # parallel-safe by construction. 64 65 set -u 66 67 ROOT="$(cd "$(dirname "$0")/../.." && pwd)" 68 export KIT_LIB_DIR="$ROOT/test/lib" 69 . "$ROOT/test/lib/kit_corpus.sh" 70 71 TEST_DIR="$ROOT/test/parse" 72 LINK_TEST_DIR="$ROOT/test/link" 73 BUILD_DIR="$ROOT/build/test" 74 LIB_AR="$ROOT/build/libkit.a" 75 76 KIT="${KIT:-$ROOT/build/kit}" 77 PARSE_RUNNER="$BUILD_DIR/parse-runner" 78 ROUNDTRIP_BIN="$BUILD_DIR/kit-roundtrip" 79 LINK_EXE_RUNNER="$BUILD_DIR/link-exe-runner" 80 JIT_RUNNER="$BUILD_DIR/jit-runner" 81 NORMALIZE="$ROOT/test/elf/normalize.py" 82 83 # KIT_TEST_ARCH selects the cross-target. Default aa64 preserves the 84 # pre-multiarch behavior. The C runners read the same env via 85 # test/lib/kit_test_target.h. 86 KIT_TEST_ARCH="${KIT_TEST_ARCH:-aa64}" 87 case "$KIT_TEST_ARCH" in 88 aa64|aarch64|arm64) TEST_ARCH=aa64; CLANG_TRIPLE=aarch64-linux-gnu; EXEC_ARCH=aarch64 ;; 89 x64|x86_64|amd64) TEST_ARCH=x64; CLANG_TRIPLE=x86_64-linux-gnu; EXEC_ARCH=x64 ;; 90 rv64|riscv64) TEST_ARCH=rv64; CLANG_TRIPLE=riscv64-linux-gnu; EXEC_ARCH=rv64 ;; 91 # rv32 is freestanding: the E lane runs bare-metal under qemu-system-riscv32 92 # via exec_bare (rv32), not exec_target's qemu-user path. CLANG_TRIPLE is 93 # only for clang probes; the kit target comes from KIT_TEST_ARCH. 94 rv32|riscv32) TEST_ARCH=rv32; CLANG_TRIPLE=riscv32-unknown-elf; EXEC_ARCH=rv32 ;; 95 # arm32 is freestanding too: the E lane runs bare-metal under qemu-system-arm 96 # via exec_bare (arm32), mirroring rv32. 97 arm32|arm) TEST_ARCH=arm32; CLANG_TRIPLE=arm-none-eabi; EXEC_ARCH=arm32 ;; 98 *) printf 'unknown KIT_TEST_ARCH=%s\n' "$KIT_TEST_ARCH" >&2; exit 2 ;; 99 esac 100 export KIT_TEST_ARCH 101 102 case "$TEST_ARCH" in 103 aa64) RT_AR="$ROOT/build/rt/aarch64-linux/libkit_rt.a" ;; 104 x64) RT_AR="$ROOT/build/rt/x86_64-linux/libkit_rt.a" ;; 105 rv64) RT_AR="$ROOT/build/rt/riscv64-linux/libkit_rt.a" ;; 106 rv32) RT_AR="$ROOT/build/rt/riscv32-elf-hardfloat/libkit_rt.a" ;; # used by exec_bare (rv32) 107 arm32) RT_AR="$ROOT/build/rt/arm-eabi-thumb2/libkit_rt.a" ;; # used by exec_bare (arm32) 108 esac 109 RT_LINK_ARGS=() 110 if [ -f "$RT_AR" ]; then 111 RT_LINK_ARGS=(--archive "$RT_AR") 112 fi 113 114 CLANG_TARGET="--target=$CLANG_TRIPLE" 115 CC="${CC:-cc}" 116 117 FILTER="${1:-${KIT_TEST_FILTER:-}}" 118 PATHS="${2:-${KIT_TEST_PATHS:-DREJ}}" 119 export KIT_TEST_FILTER="$FILTER" 120 if [ -n "${KIT_OPT_LEVELS:-}" ]; then 121 OPT_LEVELS="$KIT_OPT_LEVELS" 122 elif [ -n "${KIT_OPT_LEVEL:-}" ]; then 123 OPT_LEVELS="$KIT_OPT_LEVEL" 124 else 125 OPT_LEVELS="0 1" 126 fi 127 for opt in $OPT_LEVELS; do 128 case "$opt" in 129 0|1|2) ;; 130 *) printf 'parse: invalid opt level %s in KIT_OPT_LEVELS\n' "$opt" >&2; exit 2 ;; 131 esac 132 done 133 case "$PATHS" in *D*) RUN_D=1;; *) RUN_D=0;; esac 134 case "$PATHS" in *R*) RUN_R=1;; *) RUN_R=0;; esac 135 case "$PATHS" in *E*) RUN_E=1;; *) RUN_E=0;; esac 136 case "$PATHS" in *J*) RUN_J=1;; *) RUN_J=0;; esac 137 case "$PATHS" in *C*) RUN_C=1;; *) RUN_C=0;; esac 138 case "$PATHS" in *W*) RUN_W=1;; *) RUN_W=0;; esac 139 140 mkdir -p "$BUILD_DIR" "$BUILD_DIR/parse" 141 142 # ---- tool detection (mirrors test/cg/run.sh) ------------------------------- 143 144 have_clang_cross=0 145 have_readelf=0 146 have_python3=0 147 have_qemu=0 148 have_podman=0 149 have_roundtrip=0 150 have_exe_runner=0 151 have_jit_runner=0 152 is_aarch64=0 153 154 if clang $CLANG_TARGET -c -x c - -o /dev/null < /dev/null 2>/dev/null; then 155 have_clang_cross=1 156 fi 157 command -v llvm-readelf >/dev/null 2>&1 && have_readelf=1 158 command -v readelf >/dev/null 2>&1 && have_readelf=1 159 command -v python3 >/dev/null 2>&1 && have_python3=1 160 161 QEMU_BIN="$(command -v qemu-aarch64-static 2>/dev/null || command -v qemu-aarch64 2>/dev/null || true)" 162 [ -n "$QEMU_BIN" ] && have_qemu=1 163 command -v podman >/dev/null 2>&1 && have_podman=1 164 165 arch_raw="$(uname -m 2>/dev/null || true)" 166 { [ "$arch_raw" = "aarch64" ] || [ "$arch_raw" = "arm64" ]; } && is_aarch64=1 167 168 # Host object format for path C: the emitted C is target-locked, so the 169 # C-target must use the host's obj format (controls ELF-vs-Mach-O choices 170 # like `__attribute__((alias))` vs a thunk fallback). 171 case "$(uname -s 2>/dev/null)" in 172 Darwin) HOST_OBJ_FMT=macho ;; 173 *) HOST_OBJ_FMT=elf ;; 174 esac 175 176 # is_native_target=1 when the cross-target arch matches the host arch. 177 # Required for in-process JIT (path D) and the jit-runner (path J). 178 is_native_target=0 179 case "$TEST_ARCH" in 180 aa64) [ $is_aarch64 -eq 1 ] && is_native_target=1 ;; 181 x64) { [ "$arch_raw" = "x86_64" ] || [ "$arch_raw" = "amd64" ]; } && is_native_target=1 ;; 182 rv64) [ "$arch_raw" = "riscv64" ] && is_native_target=1 ;; 183 esac 184 185 READELF_BIN="$(command -v llvm-readelf 2>/dev/null || command -v readelf 2>/dev/null || true)" 186 187 # Shared per-arch exec helper — see test/lib/exec_target.sh. 188 EXEC_TARGET_MOUNT_ROOT="$BUILD_DIR" 189 export have_qemu have_podman is_aarch64 QEMU_BIN EXEC_TARGET_MOUNT_ROOT 190 # shellcheck source=../lib/exec_target.sh 191 source "$ROOT/test/lib/exec_target.sh" 192 193 # rv32 is freestanding: the E lane runs bare-metal under qemu-system-riscv32 via 194 # the exec_bare backend (sourced by exec_target.sh). 195 if [ "$TEST_ARCH" = "rv32" ]; then 196 # The parse corpus's entry is test_main() (path C bridges main->test_main). 197 if exec_bare_setup rv32 "$BUILD_DIR/rv32" test_main; then RV32_BARE_OK=1; else RV32_BARE_OK=0; fi 198 fi 199 if [ "$TEST_ARCH" = "arm32" ]; then 200 if exec_bare_setup arm32 "$BUILD_DIR/arm32" test_main; then ARM32_BARE_OK=1; else ARM32_BARE_OK=0; fi 201 fi 202 203 # ---- harness binaries ------------------------------------------------------ 204 205 printf 'Checking harness...\n' 206 207 if [ ! -f "$LIB_AR" ]; then 208 printf ' FATAL: %s not found — run "make lib" first\n' "$LIB_AR" >&2 209 exit 1 210 fi 211 212 # parse-runner 213 if [ -x "$PARSE_RUNNER" ]; then 214 printf ' found parse-runner\n' 215 else 216 printf ' FATAL parse-runner missing — run "make build/test/parse-runner"\n' >&2 217 exit 1 218 fi 219 220 # kit-roundtrip — for path R. 221 if [ -x "$ROUNDTRIP_BIN" ]; then 222 have_roundtrip=1 223 printf ' found kit-roundtrip\n' 224 else 225 printf ' warn kit-roundtrip missing — path R will skip\n' >&2 226 fi 227 228 # link-exe-runner — for path E. 229 if [ -x "$LINK_EXE_RUNNER" ]; then 230 have_exe_runner=1 231 printf ' found link-exe-runner\n' 232 else 233 printf ' warn link-exe-runner missing — path E will skip\n' >&2 234 fi 235 236 # jit-runner — for path J. Only when host arch matches the cross-target. 237 if [ $is_native_target -eq 1 ]; then 238 if [ -x "$JIT_RUNNER" ]; then 239 have_jit_runner=1 240 printf ' found jit-runner\n' 241 else 242 printf ' warn jit-runner missing — path J will skip\n' >&2 243 fi 244 fi 245 246 # Cached start.o — build once for the harness run rather than per case. 247 # This harness is routinely run for several architectures at once (for 248 # example, independent cross-test workers). Keep the cached startup object 249 # architecture-owned: sharing parse_start.o lets one worker replace another's 250 # object between the probe above and the per-case link below. 251 START_OBJ="$BUILD_DIR/parse_start.$TEST_ARCH.o" 252 have_start_obj=0 253 if [ $have_clang_cross -eq 1 ]; then 254 if clang $CLANG_TARGET -O1 -ffreestanding -fno-stack-protector \ 255 -fno-PIC -fno-pie \ 256 -c "$LINK_TEST_DIR/harness/start.c" -o "$START_OBJ" 2>/dev/null; then 257 have_start_obj=1 258 fi 259 fi 260 261 # Cached test_main main-wrapper.o — used by path C to link the emitted C 262 # source against a host-cc-compiled main() that returns test_main()'s value. 263 # Phase 1 C target only supports native host arch (no cross-emit), so this 264 # wrapper is built with the host CC, not the cross clang. 265 C_WRAPPER_SRC="$BUILD_DIR/parse_c_wrapper.c" 266 C_WRAPPER_OBJ="$BUILD_DIR/parse_c_wrapper.o" 267 have_c_wrapper=0 268 if [ ! -f "$C_WRAPPER_SRC" ] || [ ! -s "$C_WRAPPER_SRC" ]; then 269 cat > "$C_WRAPPER_SRC" <<'EOF' 270 /* Generated by test/parse/run.sh — bridges main() to test_main() for path C. */ 271 extern int test_main(void); 272 int main(void) { return test_main(); } 273 EOF 274 fi 275 if $CC -std=c11 -c "$C_WRAPPER_SRC" -o "$C_WRAPPER_OBJ" 2>/dev/null; then 276 have_c_wrapper=1 277 printf ' built c-wrapper\n' 278 else 279 printf ' warn c-wrapper (host CC failed)\n' >&2 280 fi 281 282 # Probe whether the host C compiler treats `long double` as 128-bit 283 # (IEEE binary128). The ldbl128_* fixtures early-return 0 unless 284 # __LDBL_MANT_DIG__ == 113, and the kit target the C path uses 285 # (host arch + host OS) matches the host compiler, so a mismatch means 286 # the test cannot exercise its 128-bit code path and silently turns 287 # into a return-0 — which then fails the non-zero expected. Skip path C 288 # for these tests when the host doesn't provide 128-bit ldbl. 289 HOST_LDBL128=0 290 LDBL_PROBE_SRC="$BUILD_DIR/parse_ldbl_probe.c" 291 LDBL_PROBE_BIN="$BUILD_DIR/parse_ldbl_probe" 292 cat > "$LDBL_PROBE_SRC" <<'EOF' 293 int main(void) { return __LDBL_MANT_DIG__ == 113 ? 0 : 1; } 294 EOF 295 if $CC -std=c11 "$LDBL_PROBE_SRC" -o "$LDBL_PROBE_BIN" 2>/dev/null \ 296 && "$LDBL_PROBE_BIN" 2>/dev/null; then 297 HOST_LDBL128=1 298 fi 299 300 # ---- per-lane oracle hooks (KIT_WORK-confined -> parallel-safe) ------------- 301 # Each hook records via kit_pass/kit_fail/kit_skip (or kit_queue_e for E). The 302 # expected exit code is KIT_EXPECTED (the engine read <name>.expected); compared 303 # mod 256 to the program's exit code. 304 305 # Build the .o the R/E/J lanes share, once per (case,opt). Sets PARSE_OBJ. 306 # Returns 0 on success, 1 on failure. 307 # 308 # The original harness emitted the .o ONCE up front (before R/E/J) and, on a 309 # failure, recorded a single FAIL "<name>/emit" and skipped R/E/J entirely. To 310 # preserve that exact verdict (one FAIL, not one per lane), the first lane that 311 # needs the object reports "<name>/emit"; a failure marker makes every later 312 # lane in the same item return silently without re-reporting. 313 _parse_emit_obj() { 314 PARSE_OBJ="$KIT_WORK/$KIT_BASE.o" 315 [ -f "$PARSE_OBJ" ] && return 0 316 [ -f "$KIT_WORK/.emit.failed" ] && return 1 317 if ! KIT_OPT_LEVEL="$KIT_OPT" "$PARSE_RUNNER" --emit "$KIT_SRC" "$PARSE_OBJ" \ 318 2>"$KIT_WORK/emit.err"; then 319 : > "$KIT_WORK/.emit.failed" 320 kit_fail "$KIT_NAME/emit" "parse-runner --emit failed; see $KIT_WORK/emit.err" 321 return 1 322 fi 323 return 0 324 } 325 326 kit_lane_D() { 327 if [ $is_native_target -eq 0 ]; then 328 kit_skip "$KIT_NAME/D" "host arch != $TEST_ARCH (no native JIT)" 329 return 330 fi 331 local exp_byte t0 dt d_rc 332 exp_byte=$(( KIT_EXPECTED & 0xff )) 333 t0=$(kit_now_ms) 334 KIT_OPT_LEVEL="$KIT_OPT" "$PARSE_RUNNER" --jit "$KIT_SRC" \ 335 >"$KIT_WORK/d.out" 2>"$KIT_WORK/d.err" 336 d_rc=$? 337 dt=$(( $(kit_now_ms) - t0 )) 338 kit_time D "$dt" 339 if [ "$d_rc" -eq "$exp_byte" ]; then 340 kit_pass "$KIT_NAME/D (${dt}ms)" 341 else 342 kit_fail "$KIT_NAME/D" "expected $exp_byte got $d_rc, ${dt}ms" 343 fi 344 } 345 346 kit_lane_R() { 347 if [ $have_roundtrip -ne 1 ] || [ $have_readelf -ne 1 ] || [ $have_python3 -ne 1 ]; then 348 kit_skip "$KIT_NAME/R" "missing roundtrip/readelf/python3" 349 return 350 fi 351 _parse_emit_obj || return 352 local t0 dt rt r_ok r_msg 353 t0=$(kit_now_ms) 354 rt="$KIT_WORK/$KIT_BASE.rt.o" 355 r_ok=1; r_msg="" 356 if ! "$ROUNDTRIP_BIN" "$PARSE_OBJ" "$rt" 2>"$KIT_WORK/rt.err"; then 357 r_ok=0; r_msg="roundtrip failed" 358 else 359 "$READELF_BIN" -aW "$PARSE_OBJ" | python3 "$NORMALIZE" >"$KIT_WORK/golden.norm" 2>/dev/null 360 "$READELF_BIN" -aW "$rt" | python3 "$NORMALIZE" >"$KIT_WORK/rt.norm" 2>/dev/null 361 diff -u "$KIT_WORK/golden.norm" "$KIT_WORK/rt.norm" >"$KIT_WORK/r.diff" 2>&1 || r_ok=0 362 fi 363 dt=$(( $(kit_now_ms) - t0 )) 364 kit_time R "$dt" 365 if [ $r_ok -eq 1 ]; then kit_pass "$KIT_NAME/R (${dt}ms)" 366 else kit_fail "$KIT_NAME/R" "${r_msg} ${dt}ms"; fi 367 } 368 369 kit_lane_E() { 370 # The ldbl128_* execution fixtures assert IEEE binary128 semantics. x64 371 # targets use either x87 extended precision (SysV/macOS) or double-aliased 372 # long double (Win64), so their deliberate return-0 guard cannot match the 373 # nonzero binary128 oracle. Keep the layout/macro probe, whose expected 374 # result is already zero, and classify the semantic fixtures as inapplicable. 375 if [ "$TEST_ARCH" = "x64" ] && \ 376 [[ "$KIT_BASE" == ldbl128_* ]] && \ 377 [[ "$KIT_BASE" != ldbl128_01_* ]]; then 378 kit_skip "$KIT_NAME/E" "x64 long double is not IEEE binary128" 379 return 380 fi 381 # rv32: freestanding bare-metal. parse-runner --emit -> kit ld with a startup 382 # that calls main() and reports its return via a SiFive finisher -> run under 383 # qemu-system-riscv32 (test/lib/exec_bare.sh). The qemu exit equals 384 # main()'s return, so the corpus rc==expected oracle applies. Gaps stay RED. 385 if [ "$TEST_ARCH" = "rv32" ]; then 386 local exp_byte rc reason t0 dt run_rc 387 if [ "${RV32_BARE_OK:-0}" -ne 1 ]; then 388 kit_skip "$KIT_NAME/E" "no rv32 runner (qemu-system-riscv32)" 389 return 390 fi 391 _parse_emit_obj || return 392 exp_byte=$(( KIT_EXPECTED & 0xff )) 393 t0=$(kit_now_ms) 394 reason="$(exec_bare_run rv32 "$PARSE_OBJ" "$KIT_WORK" "$KIT_WORK/exec.rc")" 395 run_rc=$? 396 dt=$(( $(kit_now_ms) - t0 )) 397 kit_time E "$dt" 398 if [ "$run_rc" -eq 2 ]; then kit_fail "$KIT_NAME/E" "$reason, ${dt}ms"; return; fi 399 rc="$(cat "$KIT_WORK/exec.rc" 2>/dev/null || echo 99)" 400 if [ "$rc" -eq "$exp_byte" ]; then kit_pass "$KIT_NAME/E (${dt}ms)" 401 else kit_fail "$KIT_NAME/E" "expected $exp_byte got $rc (qemu-system-riscv32), ${dt}ms"; fi 402 return 403 fi 404 if [ "$TEST_ARCH" = "arm32" ]; then 405 # arm32: freestanding bare-metal, same shape as rv32 but under 406 # qemu-system-arm (ARM semihosting exit). Gaps stay RED. 407 local exp_byte rc reason t0 dt run_rc 408 if [ "${ARM32_BARE_OK:-0}" -ne 1 ]; then 409 kit_skip "$KIT_NAME/E" "no arm32 runner (qemu-system-arm)" 410 return 411 fi 412 _parse_emit_obj || return 413 exp_byte=$(( KIT_EXPECTED & 0xff )) 414 t0=$(kit_now_ms) 415 reason="$(exec_bare_run arm32 "$PARSE_OBJ" "$KIT_WORK" "$KIT_WORK/exec.rc")" 416 run_rc=$? 417 dt=$(( $(kit_now_ms) - t0 )) 418 kit_time E "$dt" 419 if [ "$run_rc" -eq 2 ]; then kit_fail "$KIT_NAME/E" "$reason, ${dt}ms"; return; fi 420 rc="$(cat "$KIT_WORK/exec.rc" 2>/dev/null || echo 99)" 421 if [ "$rc" -eq "$exp_byte" ]; then kit_pass "$KIT_NAME/E (${dt}ms)" 422 else kit_fail "$KIT_NAME/E" "expected $exp_byte got $rc (qemu-system-arm), ${dt}ms"; fi 423 return 424 fi 425 if [ $have_exe_runner -ne 1 ] || [ $have_clang_cross -ne 1 ] || [ $have_start_obj -ne 1 ]; then 426 kit_skip "$KIT_NAME/E" "no link-exe-runner, $TEST_ARCH clang, or start.o" 427 return 428 fi 429 _parse_emit_obj || return 430 local t0 dt exe exp_byte 431 exp_byte=$(( KIT_EXPECTED & 0xff )) 432 t0=$(kit_now_ms) 433 exe="$KIT_WORK/linked.exe" 434 if ! "$LINK_EXE_RUNNER" -o "$exe" "$PARSE_OBJ" "$START_OBJ" "${RT_LINK_ARGS[@]}" \ 435 >"$KIT_WORK/exec_link.out" 2>"$KIT_WORK/exec_link.err"; then 436 dt=$(( $(kit_now_ms) - t0 )) 437 kit_time E "$dt" 438 kit_fail "$KIT_NAME/E" "link failed, ${dt}ms" 439 elif exec_target_supported "$EXEC_ARCH"; then 440 dt=$(( $(kit_now_ms) - t0 )) 441 kit_time E "$dt" 442 # Deferred batched exec: the engine flush runs the exe and verifies 443 # rc == exp_byte (both masked & 255) at the end. 444 kit_queue_e "$KIT_NAME/E (link ${dt}ms)" "$exe" \ 445 "$KIT_WORK/exec.out" "$KIT_WORK/exec.err" "$KIT_WORK/exec.rc" \ 446 "$exp_byte" "$EXEC_ARCH" 447 else 448 dt=$(( $(kit_now_ms) - t0 )) 449 kit_time E "$dt" 450 kit_skip "$KIT_NAME/E" "no runner for $EXEC_ARCH" 451 fi 452 } 453 454 kit_lane_J() { 455 if [ $have_jit_runner -ne 1 ]; then 456 kit_skip "$KIT_NAME/J" "no jit-runner (host arch != $TEST_ARCH)" 457 return 458 fi 459 _parse_emit_obj || return 460 local t0 dt j_rc exp_byte 461 exp_byte=$(( KIT_EXPECTED & 0xff )) 462 t0=$(kit_now_ms) 463 "$JIT_RUNNER" "$PARSE_OBJ" "${RT_LINK_ARGS[@]}" >"$KIT_WORK/jit.out" 2>"$KIT_WORK/jit.err" 464 j_rc=$? 465 dt=$(( $(kit_now_ms) - t0 )) 466 kit_time J "$dt" 467 if [ "$j_rc" -eq "$exp_byte" ]; then 468 kit_pass "$KIT_NAME/J (${dt}ms)" 469 else 470 kit_fail "$KIT_NAME/J" "expected $exp_byte got $j_rc, ${dt}ms" 471 fi 472 } 473 474 # Path C: --emit=c + host cc + run. Phase 1 of the C-source backend only 475 # handles a slice of the CGTarget vtable (doc/CBACKEND.md). Cases that hit an 476 # unimplemented method panic, surfaced here as SKIP so the pass/fail signal 477 # reflects the implemented surface. The .cbackend.skip sidecar (handled by the 478 # engine via the per-lane sidecar with LANE=cbackend) opts a case out of C only. 479 kit_lane_C() { 480 # Per-case opt-out for path C: <name>.cbackend.skip. The engine's per-lane 481 # sidecar check keys on the lane id ("C"), so the cbackend-named sidecar is 482 # handled here instead. 483 local reason 484 if reason=$(kit_skip_sidecar "$KIT_SIDECAR_DIR" "$KIT_BASE" "" cbackend); then 485 kit_skip "$KIT_NAME/C" "$reason" 486 return 487 fi 488 # ldbl128_* tests assert 128-bit long-double semantics. Skip them on 489 # path C when the host C compiler doesn't provide 128-bit ldbl (the 490 # test's `if (__LDBL_MANT_DIG__ != 113) return 0;` early-out can't be 491 # reconciled with a non-zero expected). ldbl128_01_* are exempt. 492 if [ $HOST_LDBL128 -eq 0 ] && \ 493 [[ "$KIT_BASE" == ldbl128_* ]] && \ 494 [[ "$KIT_BASE" != ldbl128_01_* ]]; then 495 kit_skip "$KIT_NAME/C" "host long double is not 128-bit" 496 return 497 fi 498 # Mach-O's static linker rejects unresolved weak undef refs that aren't 499 # backed by a dylib. ELF lets them resolve to 0 at link time, which is 500 # what the test expects. 501 if [ "$HOST_OBJ_FMT" = "macho" ] && [[ "$KIT_BASE" == attr_p2_08_weak_undef ]]; then 502 kit_skip "$KIT_NAME/C" "Mach-O static link rejects weak undef ref without dylib" 503 return 504 fi 505 # File-scope asm that defines C-visible symbols re-emits verbatim, so it 506 # defines the bare name (global_x). On Mach-O the C reference picks up the 507 # leading-underscore (_global_x), so the link can't resolve — a name-mangling 508 # mismatch the C backend can't bridge without parsing the opaque asm. ELF has 509 # no such prefix, so the emitted C links and runs there. 510 # asm_04_register_callee_saved hits the same wall: its file-scope asm defines 511 # write_saved_reg/read_saved_reg as bare names, but the C calls reference the 512 # underscored _write_saved_reg/_read_saved_reg on Mach-O, so the link fails. 513 # Verified otherwise-correct: underscoring the asm labels links clean under 514 # -Wall -Wextra -Werror and returns the expected 77. 515 if [ "$HOST_OBJ_FMT" = "macho" ] && \ 516 { [[ "$KIT_BASE" == asm_02_file_scope ]] || \ 517 [[ "$KIT_BASE" == asm_04_register_callee_saved ]]; }; then 518 kit_skip "$KIT_NAME/C" "Mach-O underscores C symbol refs; verbatim file-scope asm defines the bare name" 519 return 520 fi 521 # The C backend replays IR as portable C and has no arch register model, so 522 # it cannot bind asm operands to machine register classes (aa64 "x"/"y", 523 # rv64 "cr"/"cf"); cg/asm.c correctly rejects them. Native-only feature. 524 if [[ "$KIT_BASE" == cg_native_inline_asm_machine_constraints ]]; then 525 kit_skip "$KIT_NAME/C" "C backend has no machine register model for arch asm constraints" 526 return 527 fi 528 if [ $have_c_wrapper -eq 0 ]; then 529 kit_skip "$KIT_NAME/C" "no c-wrapper (host CC failed)" 530 return 531 fi 532 if [ $is_native_target -eq 0 ]; then 533 kit_skip "$KIT_NAME/C" "host arch != $TEST_ARCH (C target is target-locked)" 534 return 535 fi 536 local t0 dt c_src c_bin c_rc missing exp_byte 537 exp_byte=$(( KIT_EXPECTED & 0xff )) 538 t0=$(kit_now_ms) 539 c_src="$KIT_WORK/$KIT_BASE.kit.c" 540 c_bin="$KIT_WORK/$KIT_BASE.cbackend.bin" 541 # Emitted C is target-locked, so we override KIT_TEST_OBJ to the host's 542 # object format for the --emit-c invocation — otherwise ELF-only constructs 543 # like __attribute__((alias("x"))) leak into source compiled by a 544 # Mach-O-targeting host cc and fail at compile time. 545 if ! KIT_TEST_OBJ="$HOST_OBJ_FMT" "$PARSE_RUNNER" \ 546 --emit-c "$KIT_SRC" "$c_src" \ 547 >"$KIT_WORK/c.emit.out" 2>"$KIT_WORK/c.emit.err"; then 548 dt=$(( $(kit_now_ms) - t0 )) 549 kit_time C "$dt" 550 # Recognize "C target: ... not implemented" and "... not yet supported" 551 # as phased-rollout skips. Anything else is a real failure. 552 missing=$(grep -oE 'C target: .*(not implemented|not yet supported)' \ 553 "$KIT_WORK/c.emit.err" 2>/dev/null | head -n1 || true) 554 if [ -n "$missing" ]; then 555 kit_skip "$KIT_NAME/C" "$missing" 556 else 557 kit_fail "$KIT_NAME/C" "parse-runner --emit-c failed; see $KIT_WORK/c.emit.err" 558 fi 559 elif ! $CC -std=c11 -Wall -Wextra -Werror "$c_src" "$C_WRAPPER_OBJ" -o "$c_bin" \ 560 >"$KIT_WORK/c.cc.out" 2>"$KIT_WORK/c.cc.err"; then 561 dt=$(( $(kit_now_ms) - t0 )) 562 kit_time C "$dt" 563 kit_fail "$KIT_NAME/C" "host cc rejected emitted source; see $KIT_WORK/c.cc.err" 564 else 565 "$c_bin" >"$KIT_WORK/c.run.out" 2>"$KIT_WORK/c.run.err" 566 c_rc=$? 567 dt=$(( $(kit_now_ms) - t0 )) 568 kit_time C "$dt" 569 if [ "$c_rc" -eq "$exp_byte" ]; then 570 kit_pass "$KIT_NAME/C (${dt}ms)" 571 else 572 kit_fail "$KIT_NAME/C" "expected $exp_byte got $c_rc, ${dt}ms" 573 fi 574 fi 575 } 576 577 # Path W: cc -target wasm32-none + kit run. Compile the case straight to a 578 # .wasm via the Wasm CGTarget, then run it with `kit run -e test_main` (the 579 # lang/wasm frontend re-lowers to native CG, JITs it, and calls test_main). 580 # Target-agnostic like C, but the re-lowering JITs for the host, so it only 581 # runs when the host arch matches the cross target. opt=0 only. Phased-rollout 582 # panics surface as SKIP. The .wasm.skip sidecar (engine per-lane, LANE=wasm) 583 # opts a case out of W only. 584 kit_lane_W() { 585 # Per-case opt-out for path W: <name>.wasm.skip. The engine's per-lane 586 # sidecar check keys on the lane id ("W"), so the wasm-named sidecar is 587 # handled here instead. 588 local reason 589 if reason=$(kit_skip_sidecar "$KIT_SIDECAR_DIR" "$KIT_BASE" "" wasm); then 590 kit_skip "$KIT_NAME/W" "$reason" 591 return 592 fi 593 if [ $is_native_target -eq 0 ]; then 594 kit_skip "$KIT_NAME/W" "host arch != $TEST_ARCH (no native JIT for re-lowering)" 595 return 596 fi 597 local t0 dt wasm w_cc_err w_run_err w_rc w_missing exp_byte 598 exp_byte=$(( KIT_EXPECTED & 0xff )) 599 wasm="$KIT_WORK/$KIT_BASE.wasm" 600 w_cc_err="$KIT_WORK/w.cc.err" 601 w_run_err="$KIT_WORK/w.run.err" 602 t0=$(kit_now_ms) 603 if ! "$KIT" cc -O0 -target wasm32-none -c "$KIT_SRC" -o "$wasm" \ 604 >"$KIT_WORK/w.cc.out" 2>"$w_cc_err"; then 605 dt=$(( $(kit_now_ms) - t0 )); kit_time W "$dt" 606 w_missing=$(grep -oE 'wasm(32 ABI| target)?: .*(not yet implemented|not (yet )?supported|unsupported [a-z_0-9]+|max [0-9]+ supported|supported in v1)' \ 607 "$w_cc_err" 2>/dev/null | head -n1 || true) 608 if [ -n "$w_missing" ]; then 609 kit_skip "$KIT_NAME/W" "$w_missing" 610 else 611 kit_fail "$KIT_NAME/W" "cc -target wasm32-none failed; see $w_cc_err" 612 fi 613 else 614 # Validate by exit code only (like D/J/C). cc stderr is not a failure 615 # on its own: legitimate non-fatal diagnostics such as `#warning` print 616 # there while compilation succeeds. 617 "$KIT" run -e test_main "$wasm" >"$KIT_WORK/w.run.out" 2>"$w_run_err" 618 w_rc=$? 619 dt=$(( $(kit_now_ms) - t0 )); kit_time W "$dt" 620 if [ "$w_rc" -eq "$exp_byte" ]; then 621 kit_pass "$KIT_NAME/W (${dt}ms)" 622 else 623 kit_fail "$KIT_NAME/W" "expected $exp_byte got $w_rc, ${dt}ms" 624 fi 625 fi 626 } 627 628 # ---- drive the corpus ------------------------------------------------------ 629 630 # Active lanes in DREJCW order. 631 LANES= 632 [ $RUN_D -eq 1 ] && LANES="$LANES D" 633 [ $RUN_R -eq 1 ] && LANES="$LANES R" 634 [ $RUN_E -eq 1 ] && LANES="$LANES E" 635 [ $RUN_J -eq 1 ] && LANES="$LANES J" 636 [ $RUN_C -eq 1 ] && LANES="$LANES C" 637 [ $RUN_W -eq 1 ] && LANES="$LANES W" 638 639 # Paths C/W force opt_level=0 internally; running them at every requested opt 640 # level would duplicate identical work. When C and/or W are the ONLY enabled 641 # lanes, collapse the opt axis to "0" so the matrix isn't padded with items 642 # that produce no records. (The engine already gates C/W to opt 0 via 643 # KIT_OPT0ONLY; this just trims the redundant opt=1 expansion.) 644 CASE_OPT_LEVELS="$OPT_LEVELS" 645 if [ $RUN_D -eq 0 ] && [ $RUN_R -eq 0 ] && [ $RUN_E -eq 0 ] && [ $RUN_J -eq 0 ] \ 646 && { [ $RUN_C -eq 1 ] || [ $RUN_W -eq 1 ]; }; then 647 CASE_OPT_LEVELS="0" 648 fi 649 650 PAR="${KIT_PARSE_PARALLEL:-1}" 651 652 printf 'test-parse-ok target=%s obj=%s arch=%s\n' "$CLANG_TRIPLE" "$HOST_OBJ_FMT" "$TEST_ARCH" 653 654 KIT_LABEL=test-parse-ok KIT_BUILD_DIR="$BUILD_DIR/parse" \ 655 KIT_CORPUS_GLOBS="$TEST_DIR/cases/*.c" KIT_CORPUS_EXT=c KIT_SIDECAR_DIR="$TEST_DIR/cases" \ 656 KIT_LANES="$LANES" KIT_OPT_LEVELS="$CASE_OPT_LEVELS" KIT_TUPLES="$TEST_ARCH-noobj" \ 657 KIT_OPT0ONLY="C W" KIT_TARGETS_EXT="" KIT_PARALLELIZABLE="$PAR" \ 658 kit_corpus_run 659 660 # Treat skips (cross-target without a runner, the data-model-bound .skip 661 # sidecars — i128_*/ldbl128_* under ILP32, long-double-aliased targets, ...) as 662 # non-fatal, matching the toy runner: only a real FAIL (wrong exit code) or an 663 # XPASS (stale skip) gates the exit. Per-lane opt-ins like KIT_TEST_ALLOW_SKIP=1 664 # are now redundant but harmless. 665 KIT_SKIP_IS_FAILURE=0 666 kit_summary test-parse-ok 667 kit_exit