run.sh (26235B)
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_rv32_bare.sh, 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 *) printf 'unknown KIT_TEST_ARCH=%s\n' "$KIT_TEST_ARCH" >&2; exit 2 ;; 96 esac 97 export KIT_TEST_ARCH 98 99 case "$TEST_ARCH" in 100 aa64) RT_AR="$ROOT/build/rt/aarch64-linux/libkit_rt.a" ;; 101 x64) RT_AR="$ROOT/build/rt/x86_64-linux/libkit_rt.a" ;; 102 rv64) RT_AR="$ROOT/build/rt/riscv64-linux/libkit_rt.a" ;; 103 rv32) RT_AR="$ROOT/build/rt/riscv32-elf-hardfloat/libkit_rt.a" ;; # used by exec_rv32_bare 104 esac 105 RT_LINK_ARGS=() 106 if [ -f "$RT_AR" ]; then 107 RT_LINK_ARGS=(--archive "$RT_AR") 108 fi 109 110 CLANG_TARGET="--target=$CLANG_TRIPLE" 111 CC="${CC:-cc}" 112 113 FILTER="${1:-${KIT_TEST_FILTER:-}}" 114 PATHS="${2:-${KIT_TEST_PATHS:-DREJ}}" 115 export KIT_TEST_FILTER="$FILTER" 116 if [ -n "${KIT_OPT_LEVELS:-}" ]; then 117 OPT_LEVELS="$KIT_OPT_LEVELS" 118 elif [ -n "${KIT_OPT_LEVEL:-}" ]; then 119 OPT_LEVELS="$KIT_OPT_LEVEL" 120 else 121 OPT_LEVELS="0 1" 122 fi 123 for opt in $OPT_LEVELS; do 124 case "$opt" in 125 0|1|2) ;; 126 *) printf 'parse: invalid opt level %s in KIT_OPT_LEVELS\n' "$opt" >&2; exit 2 ;; 127 esac 128 done 129 case "$PATHS" in *D*) RUN_D=1;; *) RUN_D=0;; esac 130 case "$PATHS" in *R*) RUN_R=1;; *) RUN_R=0;; esac 131 case "$PATHS" in *E*) RUN_E=1;; *) RUN_E=0;; esac 132 case "$PATHS" in *J*) RUN_J=1;; *) RUN_J=0;; esac 133 case "$PATHS" in *C*) RUN_C=1;; *) RUN_C=0;; esac 134 case "$PATHS" in *W*) RUN_W=1;; *) RUN_W=0;; esac 135 136 mkdir -p "$BUILD_DIR" "$BUILD_DIR/parse" 137 138 # ---- tool detection (mirrors test/cg/run.sh) ------------------------------- 139 140 have_clang_cross=0 141 have_readelf=0 142 have_python3=0 143 have_qemu=0 144 have_podman=0 145 have_roundtrip=0 146 have_exe_runner=0 147 have_jit_runner=0 148 is_aarch64=0 149 150 if clang $CLANG_TARGET -c -x c - -o /dev/null < /dev/null 2>/dev/null; then 151 have_clang_cross=1 152 fi 153 command -v llvm-readelf >/dev/null 2>&1 && have_readelf=1 154 command -v readelf >/dev/null 2>&1 && have_readelf=1 155 command -v python3 >/dev/null 2>&1 && have_python3=1 156 157 QEMU_BIN="$(command -v qemu-aarch64-static 2>/dev/null || command -v qemu-aarch64 2>/dev/null || true)" 158 [ -n "$QEMU_BIN" ] && have_qemu=1 159 command -v podman >/dev/null 2>&1 && have_podman=1 160 161 arch_raw="$(uname -m 2>/dev/null || true)" 162 { [ "$arch_raw" = "aarch64" ] || [ "$arch_raw" = "arm64" ]; } && is_aarch64=1 163 164 # Host object format for path C: the emitted C is target-locked, so the 165 # C-target must use the host's obj format (controls ELF-vs-Mach-O choices 166 # like `__attribute__((alias))` vs a thunk fallback). 167 case "$(uname -s 2>/dev/null)" in 168 Darwin) HOST_OBJ_FMT=macho ;; 169 *) HOST_OBJ_FMT=elf ;; 170 esac 171 172 # is_native_target=1 when the cross-target arch matches the host arch. 173 # Required for in-process JIT (path D) and the jit-runner (path J). 174 is_native_target=0 175 case "$TEST_ARCH" in 176 aa64) [ $is_aarch64 -eq 1 ] && is_native_target=1 ;; 177 x64) { [ "$arch_raw" = "x86_64" ] || [ "$arch_raw" = "amd64" ]; } && is_native_target=1 ;; 178 rv64) [ "$arch_raw" = "riscv64" ] && is_native_target=1 ;; 179 esac 180 181 READELF_BIN="$(command -v llvm-readelf 2>/dev/null || command -v readelf 2>/dev/null || true)" 182 183 # Shared per-arch exec helper — see test/lib/exec_target.sh. 184 EXEC_TARGET_MOUNT_ROOT="$BUILD_DIR" 185 export have_qemu have_podman is_aarch64 QEMU_BIN EXEC_TARGET_MOUNT_ROOT 186 # shellcheck source=../lib/exec_target.sh 187 source "$ROOT/test/lib/exec_target.sh" 188 189 # rv32 is freestanding: the E lane runs bare-metal under qemu-system-riscv32. 190 if [ "$TEST_ARCH" = "rv32" ]; then 191 # The parse corpus's entry is test_main() (path C bridges main->test_main). 192 RV32_BARE_ENTRY=test_main 193 export RV32_BARE_ENTRY 194 # shellcheck source=../lib/exec_rv32_bare.sh 195 . "$ROOT/test/lib/exec_rv32_bare.sh" 196 rv32_bare_setup "$BUILD_DIR/rv32" 197 fi 198 199 # ---- harness binaries ------------------------------------------------------ 200 201 printf 'Checking harness...\n' 202 203 if [ ! -f "$LIB_AR" ]; then 204 printf ' FATAL: %s not found — run "make lib" first\n' "$LIB_AR" >&2 205 exit 1 206 fi 207 208 # parse-runner 209 if [ -x "$PARSE_RUNNER" ]; then 210 printf ' found parse-runner\n' 211 else 212 printf ' FATAL parse-runner missing — run "make build/test/parse-runner"\n' >&2 213 exit 1 214 fi 215 216 # kit-roundtrip — for path R. 217 if [ -x "$ROUNDTRIP_BIN" ]; then 218 have_roundtrip=1 219 printf ' found kit-roundtrip\n' 220 else 221 printf ' warn kit-roundtrip missing — path R will skip\n' >&2 222 fi 223 224 # link-exe-runner — for path E. 225 if [ -x "$LINK_EXE_RUNNER" ]; then 226 have_exe_runner=1 227 printf ' found link-exe-runner\n' 228 else 229 printf ' warn link-exe-runner missing — path E will skip\n' >&2 230 fi 231 232 # jit-runner — for path J. Only when host arch matches the cross-target. 233 if [ $is_native_target -eq 1 ]; then 234 if [ -x "$JIT_RUNNER" ]; then 235 have_jit_runner=1 236 printf ' found jit-runner\n' 237 else 238 printf ' warn jit-runner missing — path J will skip\n' >&2 239 fi 240 fi 241 242 # Cached start.o — build once for the harness run rather than per case. 243 START_OBJ="$BUILD_DIR/parse_start.o" 244 have_start_obj=0 245 if [ $have_clang_cross -eq 1 ]; then 246 if clang $CLANG_TARGET -O1 -ffreestanding -fno-stack-protector \ 247 -fno-PIC -fno-pie \ 248 -c "$LINK_TEST_DIR/harness/start.c" -o "$START_OBJ" 2>/dev/null; then 249 have_start_obj=1 250 fi 251 fi 252 253 # Cached test_main main-wrapper.o — used by path C to link the emitted C 254 # source against a host-cc-compiled main() that returns test_main()'s value. 255 # Phase 1 C target only supports native host arch (no cross-emit), so this 256 # wrapper is built with the host CC, not the cross clang. 257 C_WRAPPER_SRC="$BUILD_DIR/parse_c_wrapper.c" 258 C_WRAPPER_OBJ="$BUILD_DIR/parse_c_wrapper.o" 259 have_c_wrapper=0 260 if [ ! -f "$C_WRAPPER_SRC" ] || [ ! -s "$C_WRAPPER_SRC" ]; then 261 cat > "$C_WRAPPER_SRC" <<'EOF' 262 /* Generated by test/parse/run.sh — bridges main() to test_main() for path C. */ 263 extern int test_main(void); 264 int main(void) { return test_main(); } 265 EOF 266 fi 267 if $CC -std=c11 -c "$C_WRAPPER_SRC" -o "$C_WRAPPER_OBJ" 2>/dev/null; then 268 have_c_wrapper=1 269 printf ' built c-wrapper\n' 270 else 271 printf ' warn c-wrapper (host CC failed)\n' >&2 272 fi 273 274 # Probe whether the host C compiler treats `long double` as 128-bit 275 # (IEEE binary128). The ldbl128_* fixtures early-return 0 unless 276 # __LDBL_MANT_DIG__ == 113, and the kit target the C path uses 277 # (host arch + host OS) matches the host compiler, so a mismatch means 278 # the test cannot exercise its 128-bit code path and silently turns 279 # into a return-0 — which then fails the non-zero expected. Skip path C 280 # for these tests when the host doesn't provide 128-bit ldbl. 281 HOST_LDBL128=0 282 LDBL_PROBE_SRC="$BUILD_DIR/parse_ldbl_probe.c" 283 LDBL_PROBE_BIN="$BUILD_DIR/parse_ldbl_probe" 284 cat > "$LDBL_PROBE_SRC" <<'EOF' 285 int main(void) { return __LDBL_MANT_DIG__ == 113 ? 0 : 1; } 286 EOF 287 if $CC -std=c11 "$LDBL_PROBE_SRC" -o "$LDBL_PROBE_BIN" 2>/dev/null \ 288 && "$LDBL_PROBE_BIN" 2>/dev/null; then 289 HOST_LDBL128=1 290 fi 291 292 # ---- per-lane oracle hooks (KIT_WORK-confined -> parallel-safe) ------------- 293 # Each hook records via kit_pass/kit_fail/kit_skip (or kit_queue_e for E). The 294 # expected exit code is KIT_EXPECTED (the engine read <name>.expected); compared 295 # mod 256 to the program's exit code. 296 297 # Build the .o the R/E/J lanes share, once per (case,opt). Sets PARSE_OBJ. 298 # Returns 0 on success, 1 on failure. 299 # 300 # The original harness emitted the .o ONCE up front (before R/E/J) and, on a 301 # failure, recorded a single FAIL "<name>/emit" and skipped R/E/J entirely. To 302 # preserve that exact verdict (one FAIL, not one per lane), the first lane that 303 # needs the object reports "<name>/emit"; a failure marker makes every later 304 # lane in the same item return silently without re-reporting. 305 _parse_emit_obj() { 306 PARSE_OBJ="$KIT_WORK/$KIT_BASE.o" 307 [ -f "$PARSE_OBJ" ] && return 0 308 [ -f "$KIT_WORK/.emit.failed" ] && return 1 309 if ! KIT_OPT_LEVEL="$KIT_OPT" "$PARSE_RUNNER" --emit "$KIT_SRC" "$PARSE_OBJ" \ 310 2>"$KIT_WORK/emit.err"; then 311 : > "$KIT_WORK/.emit.failed" 312 kit_fail "$KIT_NAME/emit" "parse-runner --emit failed; see $KIT_WORK/emit.err" 313 return 1 314 fi 315 return 0 316 } 317 318 kit_lane_D() { 319 if [ $is_native_target -eq 0 ]; then 320 kit_skip "$KIT_NAME/D" "host arch != $TEST_ARCH (no native JIT)" 321 return 322 fi 323 local exp_byte t0 dt d_rc 324 exp_byte=$(( KIT_EXPECTED & 0xff )) 325 t0=$(kit_now_ms) 326 KIT_OPT_LEVEL="$KIT_OPT" "$PARSE_RUNNER" --jit "$KIT_SRC" \ 327 >"$KIT_WORK/d.out" 2>"$KIT_WORK/d.err" 328 d_rc=$? 329 dt=$(( $(kit_now_ms) - t0 )) 330 kit_time D "$dt" 331 if [ "$d_rc" -eq "$exp_byte" ]; then 332 kit_pass "$KIT_NAME/D (${dt}ms)" 333 else 334 kit_fail "$KIT_NAME/D" "expected $exp_byte got $d_rc, ${dt}ms" 335 fi 336 } 337 338 kit_lane_R() { 339 if [ $have_roundtrip -ne 1 ] || [ $have_readelf -ne 1 ] || [ $have_python3 -ne 1 ]; then 340 kit_skip "$KIT_NAME/R" "missing roundtrip/readelf/python3" 341 return 342 fi 343 _parse_emit_obj || return 344 local t0 dt rt r_ok r_msg 345 t0=$(kit_now_ms) 346 rt="$KIT_WORK/$KIT_BASE.rt.o" 347 r_ok=1; r_msg="" 348 if ! "$ROUNDTRIP_BIN" "$PARSE_OBJ" "$rt" 2>"$KIT_WORK/rt.err"; then 349 r_ok=0; r_msg="roundtrip failed" 350 else 351 "$READELF_BIN" -aW "$PARSE_OBJ" | python3 "$NORMALIZE" >"$KIT_WORK/golden.norm" 2>/dev/null 352 "$READELF_BIN" -aW "$rt" | python3 "$NORMALIZE" >"$KIT_WORK/rt.norm" 2>/dev/null 353 diff -u "$KIT_WORK/golden.norm" "$KIT_WORK/rt.norm" >"$KIT_WORK/r.diff" 2>&1 || r_ok=0 354 fi 355 dt=$(( $(kit_now_ms) - t0 )) 356 kit_time R "$dt" 357 if [ $r_ok -eq 1 ]; then kit_pass "$KIT_NAME/R (${dt}ms)" 358 else kit_fail "$KIT_NAME/R" "${r_msg} ${dt}ms"; fi 359 } 360 361 kit_lane_E() { 362 # rv32: freestanding bare-metal. parse-runner --emit -> kit ld with a startup 363 # that calls main() and reports its return via a SiFive finisher -> run under 364 # qemu-system-riscv32 (test/lib/exec_rv32_bare.sh). The qemu exit equals 365 # main()'s return, so the corpus rc==expected oracle applies. Gaps stay RED. 366 if [ "$TEST_ARCH" = "rv32" ]; then 367 local exp_byte rc reason t0 dt run_rc 368 if [ "${RV32_BARE_OK:-0}" -ne 1 ]; then 369 kit_skip "$KIT_NAME/E" "no rv32 runner (qemu-system-riscv32)" 370 return 371 fi 372 _parse_emit_obj || return 373 exp_byte=$(( KIT_EXPECTED & 0xff )) 374 t0=$(kit_now_ms) 375 reason="$(rv32_bare_run "$PARSE_OBJ" "$KIT_WORK" "$KIT_WORK/exec.rc")" 376 run_rc=$? 377 dt=$(( $(kit_now_ms) - t0 )) 378 kit_time E "$dt" 379 if [ "$run_rc" -eq 2 ]; then kit_fail "$KIT_NAME/E" "$reason, ${dt}ms"; return; fi 380 rc="$(cat "$KIT_WORK/exec.rc" 2>/dev/null || echo 99)" 381 if [ "$rc" -eq "$exp_byte" ]; then kit_pass "$KIT_NAME/E (${dt}ms)" 382 else kit_fail "$KIT_NAME/E" "expected $exp_byte got $rc (qemu-system-riscv32), ${dt}ms"; fi 383 return 384 fi 385 if [ $have_exe_runner -ne 1 ] || [ $have_clang_cross -ne 1 ] || [ $have_start_obj -ne 1 ]; then 386 kit_skip "$KIT_NAME/E" "no link-exe-runner, $TEST_ARCH clang, or start.o" 387 return 388 fi 389 _parse_emit_obj || return 390 local t0 dt exe exp_byte 391 exp_byte=$(( KIT_EXPECTED & 0xff )) 392 t0=$(kit_now_ms) 393 exe="$KIT_WORK/linked.exe" 394 if ! "$LINK_EXE_RUNNER" -o "$exe" "$PARSE_OBJ" "$START_OBJ" "${RT_LINK_ARGS[@]}" \ 395 >"$KIT_WORK/exec_link.out" 2>"$KIT_WORK/exec_link.err"; then 396 dt=$(( $(kit_now_ms) - t0 )) 397 kit_time E "$dt" 398 kit_fail "$KIT_NAME/E" "link failed, ${dt}ms" 399 elif exec_target_supported "$EXEC_ARCH"; then 400 dt=$(( $(kit_now_ms) - t0 )) 401 kit_time E "$dt" 402 # Deferred batched exec: the engine flush runs the exe and verifies 403 # rc == exp_byte (both masked & 255) at the end. 404 kit_queue_e "$KIT_NAME/E (link ${dt}ms)" "$exe" \ 405 "$KIT_WORK/exec.out" "$KIT_WORK/exec.err" "$KIT_WORK/exec.rc" \ 406 "$exp_byte" "$EXEC_ARCH" 407 else 408 dt=$(( $(kit_now_ms) - t0 )) 409 kit_time E "$dt" 410 kit_skip "$KIT_NAME/E" "no runner for $EXEC_ARCH" 411 fi 412 } 413 414 kit_lane_J() { 415 if [ $have_jit_runner -ne 1 ]; then 416 kit_skip "$KIT_NAME/J" "no jit-runner (host arch != $TEST_ARCH)" 417 return 418 fi 419 _parse_emit_obj || return 420 local t0 dt j_rc exp_byte 421 exp_byte=$(( KIT_EXPECTED & 0xff )) 422 t0=$(kit_now_ms) 423 "$JIT_RUNNER" "$PARSE_OBJ" "${RT_LINK_ARGS[@]}" >"$KIT_WORK/jit.out" 2>"$KIT_WORK/jit.err" 424 j_rc=$? 425 dt=$(( $(kit_now_ms) - t0 )) 426 kit_time J "$dt" 427 if [ "$j_rc" -eq "$exp_byte" ]; then 428 kit_pass "$KIT_NAME/J (${dt}ms)" 429 else 430 kit_fail "$KIT_NAME/J" "expected $exp_byte got $j_rc, ${dt}ms" 431 fi 432 } 433 434 # Path C: --emit=c + host cc + run. Phase 1 of the C-source backend only 435 # handles a slice of the CGTarget vtable (doc/CBACKEND.md). Cases that hit an 436 # unimplemented method panic, surfaced here as SKIP so the pass/fail signal 437 # reflects the implemented surface. The .cbackend.skip sidecar (handled by the 438 # engine via the per-lane sidecar with LANE=cbackend) opts a case out of C only. 439 kit_lane_C() { 440 # Per-case opt-out for path C: <name>.cbackend.skip. The engine's per-lane 441 # sidecar check keys on the lane id ("C"), so the cbackend-named sidecar is 442 # handled here instead. 443 local reason 444 if reason=$(kit_skip_sidecar "$KIT_SIDECAR_DIR" "$KIT_BASE" "" cbackend); then 445 kit_skip "$KIT_NAME/C" "$reason" 446 return 447 fi 448 # ldbl128_* tests assert 128-bit long-double semantics. Skip them on 449 # path C when the host C compiler doesn't provide 128-bit ldbl (the 450 # test's `if (__LDBL_MANT_DIG__ != 113) return 0;` early-out can't be 451 # reconciled with a non-zero expected). ldbl128_01_* are exempt. 452 if [ $HOST_LDBL128 -eq 0 ] && \ 453 [[ "$KIT_BASE" == ldbl128_* ]] && \ 454 [[ "$KIT_BASE" != ldbl128_01_* ]]; then 455 kit_skip "$KIT_NAME/C" "host long double is not 128-bit" 456 return 457 fi 458 # Mach-O's static linker rejects unresolved weak undef refs that aren't 459 # backed by a dylib. ELF lets them resolve to 0 at link time, which is 460 # what the test expects. 461 if [ "$HOST_OBJ_FMT" = "macho" ] && [[ "$KIT_BASE" == attr_p2_08_weak_undef ]]; then 462 kit_skip "$KIT_NAME/C" "Mach-O static link rejects weak undef ref without dylib" 463 return 464 fi 465 # File-scope asm that defines C-visible symbols re-emits verbatim, so it 466 # defines the bare name (global_x). On Mach-O the C reference picks up the 467 # leading-underscore (_global_x), so the link can't resolve — a name-mangling 468 # mismatch the C backend can't bridge without parsing the opaque asm. ELF has 469 # no such prefix, so the emitted C links and runs there. 470 # asm_04_register_callee_saved hits the same wall: its file-scope asm defines 471 # write_saved_reg/read_saved_reg as bare names, but the C calls reference the 472 # underscored _write_saved_reg/_read_saved_reg on Mach-O, so the link fails. 473 # Verified otherwise-correct: underscoring the asm labels links clean under 474 # -Wall -Wextra -Werror and returns the expected 77. 475 if [ "$HOST_OBJ_FMT" = "macho" ] && \ 476 { [[ "$KIT_BASE" == asm_02_file_scope ]] || \ 477 [[ "$KIT_BASE" == asm_04_register_callee_saved ]]; }; then 478 kit_skip "$KIT_NAME/C" "Mach-O underscores C symbol refs; verbatim file-scope asm defines the bare name" 479 return 480 fi 481 if [ $have_c_wrapper -eq 0 ]; then 482 kit_skip "$KIT_NAME/C" "no c-wrapper (host CC failed)" 483 return 484 fi 485 if [ $is_native_target -eq 0 ]; then 486 kit_skip "$KIT_NAME/C" "host arch != $TEST_ARCH (C target is target-locked)" 487 return 488 fi 489 local t0 dt c_src c_bin c_rc missing exp_byte 490 exp_byte=$(( KIT_EXPECTED & 0xff )) 491 t0=$(kit_now_ms) 492 c_src="$KIT_WORK/$KIT_BASE.kit.c" 493 c_bin="$KIT_WORK/$KIT_BASE.cbackend.bin" 494 # Emitted C is target-locked, so we override KIT_TEST_OBJ to the host's 495 # object format for the --emit-c invocation — otherwise ELF-only constructs 496 # like __attribute__((alias("x"))) leak into source compiled by a 497 # Mach-O-targeting host cc and fail at compile time. 498 if ! KIT_TEST_OBJ="$HOST_OBJ_FMT" "$PARSE_RUNNER" \ 499 --emit-c "$KIT_SRC" "$c_src" \ 500 >"$KIT_WORK/c.emit.out" 2>"$KIT_WORK/c.emit.err"; then 501 dt=$(( $(kit_now_ms) - t0 )) 502 kit_time C "$dt" 503 # Recognize "C target: ... not implemented" and "... not yet supported" 504 # as phased-rollout skips. Anything else is a real failure. 505 missing=$(grep -oE 'C target: .*(not implemented|not yet supported)' \ 506 "$KIT_WORK/c.emit.err" 2>/dev/null | head -n1 || true) 507 if [ -n "$missing" ]; then 508 kit_skip "$KIT_NAME/C" "$missing" 509 else 510 kit_fail "$KIT_NAME/C" "parse-runner --emit-c failed; see $KIT_WORK/c.emit.err" 511 fi 512 elif ! $CC -std=c11 -Wall -Wextra -Werror "$c_src" "$C_WRAPPER_OBJ" -o "$c_bin" \ 513 >"$KIT_WORK/c.cc.out" 2>"$KIT_WORK/c.cc.err"; then 514 dt=$(( $(kit_now_ms) - t0 )) 515 kit_time C "$dt" 516 kit_fail "$KIT_NAME/C" "host cc rejected emitted source; see $KIT_WORK/c.cc.err" 517 else 518 "$c_bin" >"$KIT_WORK/c.run.out" 2>"$KIT_WORK/c.run.err" 519 c_rc=$? 520 dt=$(( $(kit_now_ms) - t0 )) 521 kit_time C "$dt" 522 if [ "$c_rc" -eq "$exp_byte" ]; then 523 kit_pass "$KIT_NAME/C (${dt}ms)" 524 else 525 kit_fail "$KIT_NAME/C" "expected $exp_byte got $c_rc, ${dt}ms" 526 fi 527 fi 528 } 529 530 # Path W: cc -target wasm32-none + kit run. Compile the case straight to a 531 # .wasm via the Wasm CGTarget, then run it with `kit run -e test_main` (the 532 # lang/wasm frontend re-lowers to native CG, JITs it, and calls test_main). 533 # Target-agnostic like C, but the re-lowering JITs for the host, so it only 534 # runs when the host arch matches the cross target. opt=0 only. Phased-rollout 535 # panics surface as SKIP. The .wasm.skip sidecar (engine per-lane, LANE=wasm) 536 # opts a case out of W only. 537 kit_lane_W() { 538 # Per-case opt-out for path W: <name>.wasm.skip. The engine's per-lane 539 # sidecar check keys on the lane id ("W"), so the wasm-named sidecar is 540 # handled here instead. 541 local reason 542 if reason=$(kit_skip_sidecar "$KIT_SIDECAR_DIR" "$KIT_BASE" "" wasm); then 543 kit_skip "$KIT_NAME/W" "$reason" 544 return 545 fi 546 if [ $is_native_target -eq 0 ]; then 547 kit_skip "$KIT_NAME/W" "host arch != $TEST_ARCH (no native JIT for re-lowering)" 548 return 549 fi 550 local t0 dt wasm w_cc_err w_run_err w_rc w_missing exp_byte 551 exp_byte=$(( KIT_EXPECTED & 0xff )) 552 wasm="$KIT_WORK/$KIT_BASE.wasm" 553 w_cc_err="$KIT_WORK/w.cc.err" 554 w_run_err="$KIT_WORK/w.run.err" 555 t0=$(kit_now_ms) 556 if ! "$KIT" cc -O0 -target wasm32-none -c "$KIT_SRC" -o "$wasm" \ 557 >"$KIT_WORK/w.cc.out" 2>"$w_cc_err"; then 558 dt=$(( $(kit_now_ms) - t0 )); kit_time W "$dt" 559 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)' \ 560 "$w_cc_err" 2>/dev/null | head -n1 || true) 561 if [ -n "$w_missing" ]; then 562 kit_skip "$KIT_NAME/W" "$w_missing" 563 else 564 kit_fail "$KIT_NAME/W" "cc -target wasm32-none failed; see $w_cc_err" 565 fi 566 else 567 # Validate by exit code only (like D/J/C). cc stderr is not a failure 568 # on its own: legitimate non-fatal diagnostics such as `#warning` print 569 # there while compilation succeeds. 570 "$KIT" run -e test_main "$wasm" >"$KIT_WORK/w.run.out" 2>"$w_run_err" 571 w_rc=$? 572 dt=$(( $(kit_now_ms) - t0 )); kit_time W "$dt" 573 if [ "$w_rc" -eq "$exp_byte" ]; then 574 kit_pass "$KIT_NAME/W (${dt}ms)" 575 else 576 kit_fail "$KIT_NAME/W" "expected $exp_byte got $w_rc, ${dt}ms" 577 fi 578 fi 579 } 580 581 # ---- drive the corpus ------------------------------------------------------ 582 583 # Active lanes in DREJCW order. 584 LANES= 585 [ $RUN_D -eq 1 ] && LANES="$LANES D" 586 [ $RUN_R -eq 1 ] && LANES="$LANES R" 587 [ $RUN_E -eq 1 ] && LANES="$LANES E" 588 [ $RUN_J -eq 1 ] && LANES="$LANES J" 589 [ $RUN_C -eq 1 ] && LANES="$LANES C" 590 [ $RUN_W -eq 1 ] && LANES="$LANES W" 591 592 # Paths C/W force opt_level=0 internally; running them at every requested opt 593 # level would duplicate identical work. When C and/or W are the ONLY enabled 594 # lanes, collapse the opt axis to "0" so the matrix isn't padded with items 595 # that produce no records. (The engine already gates C/W to opt 0 via 596 # KIT_OPT0ONLY; this just trims the redundant opt=1 expansion.) 597 CASE_OPT_LEVELS="$OPT_LEVELS" 598 if [ $RUN_D -eq 0 ] && [ $RUN_R -eq 0 ] && [ $RUN_E -eq 0 ] && [ $RUN_J -eq 0 ] \ 599 && { [ $RUN_C -eq 1 ] || [ $RUN_W -eq 1 ]; }; then 600 CASE_OPT_LEVELS="0" 601 fi 602 603 PAR="${KIT_PARSE_PARALLEL:-1}" 604 605 printf 'test-parse-ok target=%s obj=%s arch=%s\n' "$CLANG_TRIPLE" "$HOST_OBJ_FMT" "$TEST_ARCH" 606 607 KIT_LABEL=test-parse-ok KIT_BUILD_DIR="$BUILD_DIR/parse" \ 608 KIT_CORPUS_GLOBS="$TEST_DIR/cases/*.c" KIT_CORPUS_EXT=c KIT_SIDECAR_DIR="$TEST_DIR/cases" \ 609 KIT_LANES="$LANES" KIT_OPT_LEVELS="$CASE_OPT_LEVELS" KIT_TUPLES="$TEST_ARCH-noobj" \ 610 KIT_OPT0ONLY="C W" KIT_TARGETS_EXT="" KIT_PARALLELIZABLE="$PAR" \ 611 kit_corpus_run 612 613 # Treat skips (cross-target without a runner, the data-model-bound .skip 614 # sidecars — i128_*/ldbl128_* under ILP32, long-double-aliased targets, ...) as 615 # non-fatal, matching the toy runner: only a real FAIL (wrong exit code) or an 616 # XPASS (stale skip) gates the exit. Per-lane opt-ins like KIT_TEST_ALLOW_SKIP=1 617 # are now redundant but harmless. 618 KIT_SKIP_IS_FAILURE=0 619 kit_summary test-parse-ok 620 kit_exit