kit

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

run.sh (21787B)


      1 #!/usr/bin/env bash
      2 # test/wasm/run.sh — Wasm frontend end-to-end tests, on the shared corpus
      3 # harness (test/lib/kit_corpus.sh). Lanes (KIT_TEST_PATHS, default WDNOJE):
      4 #   W  wat2wasm (wasm-tool)
      5 #   D  kit run (JIT)            — wat + wasm inputs
      6 #   N  kit run --no-jit (interp; SKIPs ops it does not implement)
      7 #   O  kit cc -c (object out)   — wat + wasm inputs
      8 #   J  jit-runner against the obj — wat + wasm inputs
      9 #   E  link + native exec (synchronous exec_target_run)
     10 #   C  --emit=c + host cc + exec  (opt-in; C-source backend, doc/CBACKEND.md)
     11 # Plus three diagnostic corpora (trap/err/meta) when any of W/D/O is enabled.
     12 # All lane hooks write only under KIT_WORK and record via kit_*, so the runner is
     13 # parallel-safe; KIT_WASM_PARALLEL flips dispatch.
     14 set -u
     15 
     16 ROOT=$(cd "$(dirname "$0")/../.." && pwd)
     17 export KIT_LIB_DIR="$ROOT/test/lib"
     18 . "$ROOT/test/lib/kit_corpus.sh"
     19 
     20 BUILD_DIR="$ROOT/build/test/wasm"
     21 CASES_DIR="$ROOT/test/wasm/cases"
     22 ERR_DIR="$ROOT/test/wasm/err"
     23 TRAP_DIR="$ROOT/test/wasm/trap"
     24 META_DIR="$ROOT/test/wasm/meta"
     25 KIT_BIN="${KIT:-$ROOT/build/kit}"
     26 WASM_TOOL="$ROOT/build/test/wasm-tool"
     27 JIT_RUNNER="$ROOT/build/test/jit-runner"
     28 LINK_EXE_RUNNER="$ROOT/build/test/link-exe-runner"
     29 TEST_ARCH="${KIT_TEST_ARCH:-aa64}"
     30 TEST_OBJ="${KIT_TEST_OBJ:-macho}"
     31 HOST_CC="${CC:-cc}"
     32 mkdir -p "$BUILD_DIR"
     33 
     34 PATHS="${KIT_TEST_PATHS:-WDNOJE}"
     35 case "$PATHS" in *W*) RUN_W=1;; *) RUN_W=0;; esac
     36 case "$PATHS" in *D*) RUN_D=1;; *) RUN_D=0;; esac
     37 case "$PATHS" in *N*) RUN_N=1;; *) RUN_N=0;; esac
     38 case "$PATHS" in *O*) RUN_O=1;; *) RUN_O=0;; esac
     39 case "$PATHS" in *J*) RUN_J=1;; *) RUN_J=0;; esac
     40 case "$PATHS" in *E*) RUN_E=1;; *) RUN_E=0;; esac
     41 case "$PATHS" in *C*) RUN_C=1;; *) RUN_C=0;; esac
     42 
     43 case "$TEST_ARCH" in
     44   aa64|aarch64|arm64) TEST_ARCH=aa64; EXEC_ARCH="aarch64" ;;
     45   x64|x86_64|amd64)   TEST_ARCH=x64;  EXEC_ARCH="x64" ;;
     46   rv64|riscv64)       TEST_ARCH=rv64; EXEC_ARCH="rv64" ;;
     47   *)                  TEST_ARCH=aa64; EXEC_ARCH="aarch64" ;;
     48 esac
     49 host_arch=$(uname -m); host_matches=0
     50 case "$TEST_ARCH:$host_arch" in
     51   aa64:arm64|aa64:aarch64) host_matches=1 ;;
     52   x64:x86_64) host_matches=1 ;;
     53 esac
     54 case "$TEST_OBJ" in
     55   macho) EXEC_OS="macos"
     56     case "$TEST_ARCH" in
     57       aa64) target_triple="aarch64-macos"; clang_triple="arm64-apple-macos" ;;
     58       x64)  target_triple="x86_64-macos";  clang_triple="x86_64-apple-macos" ;;
     59       *)    target_triple="aarch64-macos"; clang_triple="arm64-apple-macos" ;;
     60     esac ;;
     61   elf) EXEC_OS="linux"
     62     case "$TEST_ARCH" in
     63       aa64) target_triple="aarch64-linux"; clang_triple="aarch64-linux-gnu" ;;
     64       x64)  target_triple="x86_64-linux";  clang_triple="x86_64-linux-gnu" ;;
     65       rv64) target_triple="riscv64-linux"; clang_triple="riscv64-linux-gnu" ;;
     66       *)    target_triple="aarch64-linux"; clang_triple="aarch64-linux-gnu" ;;
     67     esac ;;
     68   *) target_triple="aarch64-macos"; clang_triple="arm64-apple-macos"; EXEC_OS="macos" ;;
     69 esac
     70 EXEC_TAG="$EXEC_ARCH-$EXEC_OS"
     71 export KIT_TEST_ARCH="$TEST_ARCH" KIT_TEST_OBJ="$TEST_OBJ"
     72 WASM_RUN_TEST_IMPORTS=(--wasm-imports=test)
     73 
     74 have_wasm_tool=0;  [ -x "$WASM_TOOL" ] && have_wasm_tool=1
     75 have_jit_runner=0; [ -x "$JIT_RUNNER" ] && [ "$host_matches" -eq 1 ] && have_jit_runner=1
     76 have_link_runner=0;[ -x "$LINK_EXE_RUNNER" ] && have_link_runner=1
     77 
     78 # exec_target wiring (lane E runs SYNCHRONOUSLY via exec_target_run).
     79 have_qemu=0; have_podman=0; is_aarch64=0
     80 QEMU_BIN="$(command -v qemu-aarch64-static 2>/dev/null || command -v qemu-aarch64 2>/dev/null || true)"
     81 [ -n "$QEMU_BIN" ] && have_qemu=1
     82 command -v podman >/dev/null 2>&1 && have_podman=1
     83 { [ "$host_arch" = "aarch64" ] || [ "$host_arch" = "arm64" ]; } && is_aarch64=1
     84 EXEC_TARGET_MOUNT_ROOT="$BUILD_DIR"
     85 export have_qemu have_podman is_aarch64 QEMU_BIN EXEC_TARGET_MOUNT_ROOT
     86 . "$ROOT/test/lib/exec_target.sh"
     87 
     88 WASM_START_OBJ="$BUILD_DIR/start-wasm.o"
     89 have_wasm_start_obj=0
     90 if clang --target="$clang_triple" -ffreestanding -fno-stack-protector \
     91     -fno-builtin -nostdlib -c "$ROOT/test/wasm/harness/start_wasm.c" \
     92     -o "$WASM_START_OBJ" >"$BUILD_DIR/start-wasm.out" 2>"$BUILD_DIR/start-wasm.err"; then
     93   have_wasm_start_obj=1
     94 fi
     95 
     96 MACHO_DSO_ARGS=()
     97 if [ "$TEST_OBJ" = "macho" ] && command -v xcrun >/dev/null 2>&1; then
     98   sdk="$(xcrun --show-sdk-path 2>/dev/null || true)"
     99   if [ -n "$sdk" ] && [ -f "$sdk/usr/lib/libSystem.tbd" ]; then
    100     MACHO_DSO_ARGS=(--dso "$sdk/usr/lib/libSystem.tbd")
    101   fi
    102 fi
    103 
    104 # Path C wrapper: bridges the emitted C's test_main(instance) to int main(void).
    105 C_WRAPPER_SRC="$BUILD_DIR/wasm_c_wrapper.c"
    106 C_WRAPPER_OBJ="$BUILD_DIR/wasm_c_wrapper.o"
    107 have_c_wrapper=0
    108 if [ "$RUN_C" -eq 1 ]; then
    109   cat > "$C_WRAPPER_SRC" <<'EOF'
    110 #include <stdint.h>
    111 #include <stdlib.h>
    112 #include <string.h>
    113 extern void __kit_wasm_init(void *);
    114 extern int32_t test_main(void *);
    115 
    116 /* Canned host import, mirroring jit_runner.c / start_wasm.c / driver/run.c. */
    117 static int32_t test_host_add(void *inst, int32_t a, int32_t b) {
    118   (void)inst;
    119   return a + b;
    120 }
    121 
    122 /* Import descriptors emitted by lang/wasm/cg.c. The C lane compiles for the
    123  * native target (aarch64/x86_64-*), so this is the 64-bit layout used by
    124  * start_wasm.c, with full module/field name pointers — match by name and bind
    125  * the canned host function into the instance import slot before
    126  * __kit_wasm_init. Weak DEFAULTS (not weak externs): a module with imports
    127  * emits its own strong __kit_wasm_imports/__kit_wasm_nimports that override
    128  * these; a module with none falls back to nimports=0. (A weak *undefined* ref
    129  * would be rejected by the Mach-O static linker.) */
    130 typedef struct {
    131   const char *module;
    132   const char *field;
    133   unsigned int kind;
    134   unsigned int desc_index;
    135   unsigned int slot_offset;
    136   unsigned int reserved;
    137 } WasmImportDesc;
    138 __attribute__((weak)) const unsigned int __kit_wasm_nimports = 0;
    139 __attribute__((weak)) const WasmImportDesc __kit_wasm_imports[1] = {{0, 0, 0, 0, 0, 0}};
    140 
    141 static int bind_canned_imports(void *instance) {
    142   for (unsigned int i = 0; i < __kit_wasm_nimports; ++i) {
    143     const WasmImportDesc *d = &__kit_wasm_imports[i];
    144     if (d->kind == 0 && strcmp(d->module, "env") == 0 &&
    145         strcmp(d->field, "host_add") == 0)
    146       *(void **)((unsigned char *)instance + d->slot_offset) =
    147           (void *)(uintptr_t)test_host_add;
    148     else
    149       return 0;
    150   }
    151   return 1;
    152 }
    153 
    154 typedef struct { unsigned char *data; unsigned long long pages;
    155   unsigned long long max_pages; unsigned int flags; } WasmStartMemoryPrefix;
    156 typedef struct {
    157   unsigned long long offset;
    158   unsigned long long min_pages;
    159   unsigned long long max_pages;
    160   unsigned int flags;
    161   unsigned int reserved;
    162 } WasmStartMemoryLayout;
    163 extern const unsigned long long __kit_wasm_instance_size;
    164 extern const unsigned int __kit_wasm_nmemories;
    165 __attribute__((weak)) const WasmStartMemoryLayout
    166     __kit_wasm_memory_layouts[1] = {{0, 0, 0, 0, 0}};
    167 
    168 #define WASM_START_PAGE_SIZE (64ull * 1024ull)
    169 #define WASM_START_MAX_INSTANCE_SIZE (64ull * 1024ull * 1024ull)
    170 #define WASM_START_MAX_TOTAL_MEMORY_SIZE (1024ull * 1024ull * 1024ull)
    171 
    172 static int setup_instance(void **instance_out) {
    173   unsigned long long instance_size = __kit_wasm_instance_size ?
    174       __kit_wasm_instance_size : 1ull;
    175   unsigned long long total_memory = 0;
    176   void *instance;
    177   if (instance_size > WASM_START_MAX_INSTANCE_SIZE ||
    178       instance_size > (unsigned long long)SIZE_MAX)
    179     return 0;
    180   instance = calloc(1, (size_t)instance_size);
    181   if (!instance) return 0;
    182   for (unsigned int i = 0; i < __kit_wasm_nmemories; ++i) {
    183     const WasmStartMemoryLayout *ml = &__kit_wasm_memory_layouts[i];
    184     WasmStartMemoryPrefix *rec;
    185     unsigned long long bytes;
    186     void *memory = NULL;
    187     if (ml->max_pages < ml->min_pages) return 0;
    188     if (ml->max_pages > (~0ull / WASM_START_PAGE_SIZE)) return 0;
    189     bytes = ml->max_pages * WASM_START_PAGE_SIZE;
    190     if (bytes > WASM_START_MAX_TOTAL_MEMORY_SIZE ||
    191         total_memory > WASM_START_MAX_TOTAL_MEMORY_SIZE - bytes ||
    192         bytes > (unsigned long long)SIZE_MAX)
    193       return 0;
    194     if (ml->offset > instance_size ||
    195         instance_size - ml->offset < sizeof(WasmStartMemoryPrefix))
    196       return 0;
    197     total_memory += bytes;
    198     if (bytes) {
    199       memory = calloc(1, (size_t)bytes);
    200       if (!memory) return 0;
    201     }
    202     rec = (WasmStartMemoryPrefix *)((unsigned char *)instance + ml->offset);
    203     rec->data = (unsigned char *)memory;
    204   }
    205   *instance_out = instance;
    206   return 1;
    207 }
    208 
    209 int main(void) {
    210   void *instance = NULL;
    211   if (!setup_instance(&instance)) return 1;
    212   /* After the memory prefix (which can overlap import slots for memory-less
    213    * modules), before init (which consumes the bound slots). */
    214   if (!bind_canned_imports(instance)) return 1;
    215   __kit_wasm_init(instance);
    216   return (int)test_main(instance);
    217 }
    218 EOF
    219   $HOST_CC -std=gnu99 -c "$C_WRAPPER_SRC" -o "$C_WRAPPER_OBJ" \
    220       2>"$BUILD_DIR/wasm_c_wrapper.err" && have_c_wrapper=1
    221 fi
    222 
    223 # ---- per-result oracle helpers (KIT_WORK-confined -> parallel-safe) ---------
    224 _wf_base() { printf '%s/%s' "$KIT_WORK" "$(printf '%s' "$1" | tr '/ ' '__')"; }
    225 wf_rc() {            # LABEL EXPECTED CMD...
    226   local label=$1 exp=$2 b; b=$(_wf_base "$1"); shift 2
    227   "$@" >"$b.out" 2>"$b.err"; local rc=$?
    228   if [ "$rc" -eq "$exp" ]; then kit_pass "$label"; else kit_fail "$label" "expected $exp got $rc"; fi
    229 }
    230 wf_rc_interp() {     # LABEL EXPECTED CMD...  (--no-jit; SKIP on "interp: ..not supported")
    231   local label=$1 exp=$2 b; b=$(_wf_base "$1"); shift 2
    232   "$@" >"$b.out" 2>"$b.err"; local rc=$? miss
    233   miss=$(grep -oE 'interp: .*not supported' "$b.err" 2>/dev/null | head -n1 || true)
    234   if [ -n "$miss" ]; then kit_skip "$label" "$miss"
    235   elif [ "$rc" -eq "$exp" ]; then kit_pass "$label"
    236   else kit_fail "$label" "expected $exp got $rc"; fi
    237 }
    238 wf_zero() {          # LABEL CMD...
    239   local label=$1 b; b=$(_wf_base "$1"); shift
    240   if "$@" >"$b.out" 2>"$b.err"; then kit_pass "$label"; else kit_fail "$label"; fi
    241 }
    242 wf_fail() {          # LABEL CMD...  (expect nonzero)
    243   local label=$1 b; b=$(_wf_base "$1"); shift
    244   if "$@" >"$b.out" 2>"$b.err"; then kit_fail "$label" "expected failure"; else kit_pass "$label"; fi
    245 }
    246 wf_fail_grep() {     # LABEL SUBSTR CMD...  (expect failure + stderr contains SUBSTR)
    247   local label=$1 want=$2 b; b=$(_wf_base "$1"); shift 2
    248   if "$@" >"$b.out" 2>"$b.err"; then kit_fail "$label" "expected failure"
    249   elif grep -F "$want" "$b.err" >/dev/null 2>&1; then kit_pass "$label"
    250   else kit_fail "$label" "missing diagnostic: $want"; fi
    251 }
    252 
    253 # Build the wat->wasm intermediate once per case (idempotent). Sets WASM.
    254 wasm_build() {
    255   WASM="$KIT_WORK/$KIT_BASE.wasm"
    256   [ -f "$WASM" ] && return 0
    257   [ "$have_wasm_tool" -eq 1 ] || return 1
    258   "$WASM_TOOL" --wat2wasm "$KIT_SRC" "$WASM" >"$KIT_WORK/_w.out" 2>"$KIT_WORK/_w.err"
    259 }
    260 # Ensure the wat/wasm .o exists (O lane builds it; J/E build on demand if absent).
    261 _obj_wat()  { local o="$KIT_WORK/$KIT_BASE.wat.o";  [ -f "$o" ] || "$KIT_BIN" cc -target "$target_triple" -c "$KIT_SRC" -o "$o" >/dev/null 2>&1; printf '%s' "$o"; }
    262 _obj_wasm() { local o="$KIT_WORK/$KIT_BASE.wasm.o"; wasm_build || return 1; [ -f "$o" ] || "$KIT_BIN" cc -target "$target_triple" -c "$WASM" -o "$o" >/dev/null 2>&1; printf '%s' "$o"; }
    263 
    264 # ---- cases-corpus lanes ----------------------------------------------------
    265 kit_lane_W() {
    266   if [ "$have_wasm_tool" -eq 1 ]; then wf_zero "$KIT_NAME/W" "$WASM_TOOL" --wat2wasm "$KIT_SRC" "$KIT_WORK/$KIT_BASE.wasm"
    267   else kit_skip "$KIT_NAME/W" "no wasm-tool"; fi
    268 }
    269 kit_lane_D() {
    270   wf_rc "$KIT_NAME/D-wat" "$KIT_EXPECTED" "$KIT_BIN" run "${WASM_RUN_TEST_IMPORTS[@]}" -e test_main "$KIT_SRC"
    271   if wasm_build; then wf_rc "$KIT_NAME/D-wasm" "$KIT_EXPECTED" "$KIT_BIN" run "${WASM_RUN_TEST_IMPORTS[@]}" -e test_main "$WASM"
    272   else kit_skip "$KIT_NAME/D-wasm" "no wasm-tool"; fi
    273 }
    274 kit_lane_N() {
    275   wf_rc_interp "$KIT_NAME/N-wat" "$KIT_EXPECTED" "$KIT_BIN" run --no-jit "${WASM_RUN_TEST_IMPORTS[@]}" -e test_main "$KIT_SRC"
    276   if wasm_build; then wf_rc_interp "$KIT_NAME/N-wasm" "$KIT_EXPECTED" "$KIT_BIN" run --no-jit "${WASM_RUN_TEST_IMPORTS[@]}" -e test_main "$WASM"
    277   else kit_skip "$KIT_NAME/N-wasm" "no wasm-tool"; fi
    278 }
    279 kit_lane_O() {
    280   wf_zero "$KIT_NAME/O-wat" "$KIT_BIN" cc -target "$target_triple" -c "$KIT_SRC" -o "$KIT_WORK/$KIT_BASE.wat.o"
    281   if wasm_build; then wf_zero "$KIT_NAME/O-wasm" "$KIT_BIN" cc -target "$target_triple" -c "$WASM" -o "$KIT_WORK/$KIT_BASE.wasm.o"
    282   else kit_skip "$KIT_NAME/O-wasm" "no wasm-tool"; fi
    283 }
    284 kit_lane_J() {
    285   if [ "$have_jit_runner" -eq 0 ]; then kit_skip "$KIT_NAME/J" "host arch does not match target or no jit-runner"; return; fi
    286   wf_rc "$KIT_NAME/J-wat-obj" "$KIT_EXPECTED" env KIT_TEST_ARCH="$TEST_ARCH" KIT_TEST_OBJ="$TEST_OBJ" "$JIT_RUNNER" "$(_obj_wat)"
    287   local wo; if wo=$(_obj_wasm); then wf_rc "$KIT_NAME/J-wasm-obj" "$KIT_EXPECTED" env KIT_TEST_ARCH="$TEST_ARCH" KIT_TEST_OBJ="$TEST_OBJ" "$JIT_RUNNER" "$wo"
    288   else kit_skip "$KIT_NAME/J-wasm-obj" "no wasm-tool"; fi
    289 }
    290 kit_lane_E() {
    291   if [ "$have_link_runner" -eq 0 ] || [ "$have_wasm_start_obj" -eq 0 ]; then
    292     kit_skip "$KIT_NAME/E" "requires link runner and wasm start.o"; return
    293   fi
    294   local exe="$KIT_WORK/$KIT_BASE.exe"
    295   if "$LINK_EXE_RUNNER" -o "$exe" "${MACHO_DSO_ARGS[@]}" "$(_obj_wat)" "$WASM_START_OBJ" \
    296       >"$KIT_WORK/link.out" 2>"$KIT_WORK/link.err"; then
    297     if exec_target_supported "$EXEC_TAG"; then
    298       exec_target_run "$EXEC_TAG" "$exe" "$KIT_WORK/exec.out" "$KIT_WORK/exec.err"
    299       if [ "$RUN_RC" -eq "$KIT_EXPECTED" ]; then kit_pass "$KIT_NAME/E"
    300       else kit_fail "$KIT_NAME/E" "expected $KIT_EXPECTED got $RUN_RC"; fi
    301     else kit_skip "$KIT_NAME/E" "no execution support for $TEST_ARCH"; fi
    302   else kit_fail "$KIT_NAME/E" "link failed"; fi
    303 }
    304 kit_lane_C() {
    305   if [ "$have_c_wrapper" -eq 0 ]; then kit_skip "$KIT_NAME/C" "host CC wrapper unavailable"; return; fi
    306   if [ "$host_matches" -eq 0 ]; then kit_skip "$KIT_NAME/C" "host arch != $TEST_ARCH (C target is target-locked)"; return; fi
    307   local c_src="$KIT_WORK/$KIT_BASE.kit.c" c_bin="$KIT_WORK/$KIT_BASE.cbackend.bin" miss
    308   if ! "$KIT_BIN" cc -target "$target_triple" --emit=c "$KIT_SRC" -o "$c_src" \
    309         >"$KIT_WORK/c.emit.out" 2>"$KIT_WORK/c.emit.err"; then
    310     miss=$(grep -oE 'C target: .*(not implemented|not yet supported)' "$KIT_WORK/c.emit.err" 2>/dev/null | head -n1 || true)
    311     if [ -n "$miss" ]; then kit_skip "$KIT_NAME/C" "$miss"; else kit_fail "$KIT_NAME/C" "kit cc --emit=c failed"; fi
    312     return
    313   fi
    314   if ! $HOST_CC -std=gnu99 -Wno-main-return-type "$c_src" "$C_WRAPPER_OBJ" -o "$c_bin" \
    315         >"$KIT_WORK/c.cc.out" 2>"$KIT_WORK/c.cc.err"; then
    316     kit_fail "$KIT_NAME/C" "host cc rejected emitted source"; return
    317   fi
    318   "$c_bin" >"$KIT_WORK/c.run.out" 2>"$KIT_WORK/c.run.err"; local rc=$?
    319   if [ "$rc" -eq "$KIT_EXPECTED" ]; then kit_pass "$KIT_NAME/C"; else kit_fail "$KIT_NAME/C" "expected $KIT_EXPECTED got $rc"; fi
    320 }
    321 
    322 # ---- diagnostic-corpus lanes ----------------------------------------------
    323 kit_lane_T() {        # trap: build wasm (expect 0), then run expecting a trap (nonzero)
    324   if [ "$have_wasm_tool" -eq 1 ]; then wf_zero "trap/$KIT_BASE/W" "$WASM_TOOL" --wat2wasm "$KIT_SRC" "$KIT_WORK/$KIT_BASE.wasm"
    325   else kit_skip "trap/$KIT_BASE/W" "no wasm-tool"; return; fi
    326   wf_fail "trap/$KIT_BASE/D-wat"  "$KIT_BIN" run -e test_main "$KIT_SRC"
    327   wf_fail "trap/$KIT_BASE/D-wasm" "$KIT_BIN" run -e test_main "$KIT_WORK/$KIT_BASE.wasm"
    328 }
    329 kit_lane_ERR() {      # err: cc must fail
    330   wf_fail "err/$KIT_BASE/cc" "$KIT_BIN" cc -target "$target_triple" -c "$KIT_SRC" -o "$KIT_WORK/$KIT_BASE.o"
    331 }
    332 kit_lane_META() {     # meta: build wasm, then native cc per the expect whitelist
    333   wf_zero "meta/$KIT_BASE/W" "$WASM_TOOL" --wat2wasm "$KIT_SRC" "$KIT_WORK/$KIT_BASE.wasm"
    334   if [ "$KIT_BASE" = "type_custom" ] || [ "$KIT_BASE" = "import_table_global_start" ]; then
    335     wf_zero "meta/$KIT_BASE/native" "$KIT_BIN" cc -target "$target_triple" -c "$KIT_WORK/$KIT_BASE.wasm" -o "$KIT_WORK/$KIT_BASE.o"
    336   else
    337     wf_fail "meta/$KIT_BASE/native" "$KIT_BIN" cc -target "$target_triple" -c "$KIT_WORK/$KIT_BASE.wasm" -o "$KIT_WORK/$KIT_BASE.o"
    338   fi
    339 }
    340 
    341 # ---- drive the corpora -----------------------------------------------------
    342 printf 'test-wasm-front target=%s obj=%s\n' "$target_triple" "$TEST_OBJ"
    343 
    344 # cases corpus — active lanes in PATHS order.
    345 CASE_LANES=
    346 [ "$RUN_W" -eq 1 ] && CASE_LANES="$CASE_LANES W"
    347 [ "$RUN_D" -eq 1 ] && CASE_LANES="$CASE_LANES D"
    348 [ "$RUN_N" -eq 1 ] && CASE_LANES="$CASE_LANES N"
    349 [ "$RUN_O" -eq 1 ] && CASE_LANES="$CASE_LANES O"
    350 [ "$RUN_J" -eq 1 ] && CASE_LANES="$CASE_LANES J"
    351 [ "$RUN_E" -eq 1 ] && CASE_LANES="$CASE_LANES E"
    352 [ "$RUN_C" -eq 1 ] && CASE_LANES="$CASE_LANES C"
    353 PAR="${KIT_WASM_PARALLEL:-1}"
    354 KIT_LABEL=test-wasm-front KIT_BUILD_DIR="$BUILD_DIR/cases" \
    355   KIT_CORPUS_GLOBS="$CASES_DIR/*.wat" KIT_CORPUS_EXT=wat KIT_SIDECAR_DIR="$CASES_DIR" \
    356   KIT_LANES="$CASE_LANES" KIT_OPT_LEVELS="" KIT_TUPLES="$EXEC_TAG" \
    357   KIT_EXPECTED_EXT=.expect KIT_TARGETS_EXT="" KIT_PARALLELIZABLE="$PAR" \
    358   kit_corpus_run
    359 
    360 # trap/err/meta are diagnostics corpora, orthogonal to the C-emit path.
    361 RUN_DIAG=0
    362 { [ "$RUN_W" -eq 1 ] || [ "$RUN_D" -eq 1 ] || [ "$RUN_O" -eq 1 ]; } && RUN_DIAG=1
    363 
    364 if [ "$RUN_DIAG" -eq 1 ]; then
    365   KIT_LABEL=test-wasm-front KIT_BUILD_DIR="$BUILD_DIR/trap" \
    366     KIT_CORPUS_GLOBS="$TRAP_DIR/*.wat" KIT_CORPUS_EXT=wat KIT_SIDECAR_DIR="$TRAP_DIR" \
    367     KIT_LANES="T" KIT_OPT_LEVELS="" KIT_TUPLES="$EXEC_TAG" KIT_TARGETS_EXT="" \
    368     KIT_PARALLELIZABLE="$PAR" kit_corpus_run
    369 
    370   KIT_LABEL=test-wasm-front KIT_BUILD_DIR="$BUILD_DIR/err" \
    371     KIT_CORPUS_GLOBS="$ERR_DIR/*.wat" KIT_CORPUS_EXT=wat KIT_SIDECAR_DIR="$ERR_DIR" \
    372     KIT_LANES="ERR" KIT_OPT_LEVELS="" KIT_TUPLES="$EXEC_TAG" KIT_TARGETS_EXT="" \
    373     KIT_PARALLELIZABLE="$PAR" kit_corpus_run
    374 
    375   # Inline diagnostic cases (not file-glob corpora).
    376   KIT_WORK="$BUILD_DIR/err-inline"; rm -rf "$KIT_WORK"; mkdir -p "$KIT_WORK"
    377   KIT_NAME=err KIT_BASE=err
    378   wf_fail_grep "err/stack_underflow/loc" \
    379     "$ERR_DIR/stack_underflow.wat:3:5: fatal: wasm: operand stack underflow" \
    380     "$KIT_BIN" cc -target "$target_triple" -c "$ERR_DIR/stack_underflow.wat" -o "$KIT_WORK/su-loc.o"
    381 
    382   if [ "$have_wasm_tool" -eq 1 ]; then
    383     KIT_LABEL=test-wasm-front KIT_BUILD_DIR="$BUILD_DIR/meta" \
    384       KIT_CORPUS_GLOBS="$META_DIR/*.wat" KIT_CORPUS_EXT=wat KIT_SIDECAR_DIR="$META_DIR" \
    385       KIT_LANES="META" KIT_OPT_LEVELS="" KIT_TUPLES="$EXEC_TAG" KIT_TARGETS_EXT="" \
    386       KIT_PARALLELIZABLE="$PAR" kit_corpus_run
    387   else
    388     kit_skip "meta" "no wasm-tool"
    389   fi
    390 
    391   printf '\000asm\001\000\000\000\001\005\001\140' > "$KIT_WORK/malformed-section.wasm"
    392   wf_fail "err/malformed-section/wasm" "$KIT_BIN" cc -target "$target_triple" -c "$KIT_WORK/malformed-section.wasm" -o "$KIT_WORK/bad-section.o"
    393   printf '\000asm\001\000\000\000\001\200\200\200\200\020' > "$KIT_WORK/malformed-leb.wasm"
    394   wf_fail "err/malformed-leb/wasm" "$KIT_BIN" cc -target "$target_triple" -c "$KIT_WORK/malformed-leb.wasm" -o "$KIT_WORK/bad-leb.o"
    395   # Positive: the bulk-memory DataCount section (id 12) is spec-placed between
    396   # Element (9) and Code (10), so a standard module's section ids run
    397   # ...,12,10,11 — not monotonic. Toolchains (clang/wasm-ld, emscripten) emit it
    398   # whenever passive data segments or memory.init are used; the decoder must
    399   # accept it rather than report "sections out of order". Module below:
    400   # type ()->(), one func {end}, memory 1, DataCount 1, code, active data @0.
    401   printf '\000asm\001\000\000\000\001\004\001\140\000\000\003\002\001\000\005\003\001\000\001\014\001\001\012\004\001\002\000\013\013\007\001\000\101\000\013\001\252' > "$KIT_WORK/datacount-order.wasm"
    402   wf_zero "decode/datacount-order/wasm" "$KIT_BIN" cc -target "$target_triple" -c "$KIT_WORK/datacount-order.wasm" -o "$KIT_WORK/datacount-order.o"
    403 fi
    404 
    405 # ---- multi-source wasm (cross-TU call + cross-TU data reference) -----------
    406 if [ "$RUN_D" -eq 1 ] || [ "$RUN_N" -eq 1 ]; then
    407   KIT_WORK="$BUILD_DIR/multi-tu"; rm -rf "$KIT_WORK"; mkdir -p "$KIT_WORK"
    408   cat > "$KIT_WORK/mtu_helper.c" <<'EOF'
    409 int g_count = 0;
    410 int add_and_count(int a, int b) { g_count++; return a + b; }
    411 EOF
    412   cat > "$KIT_WORK/mtu_main.c" <<'EOF'
    413 extern int g_count;
    414 int add_and_count(int a, int b);
    415 int test_main(void) {
    416   int r = add_and_count(3, 4);
    417   return (r == 7 && g_count == 1) ? 0 : 1;
    418 }
    419 EOF
    420   _mtu_wasm="$KIT_WORK/multi_tu.wasm"
    421   if "$KIT_BIN" build-obj -target wasm32-none \
    422       "$KIT_WORK/mtu_main.c" "$KIT_WORK/mtu_helper.c" \
    423       -o "$_mtu_wasm" >"$KIT_WORK/build.out" 2>"$KIT_WORK/build.err"; then
    424     if [ "$RUN_N" -eq 1 ]; then
    425       wf_rc_interp "multi-tu/N" 0 \
    426         "$KIT_BIN" run --no-jit -e test_main "$_mtu_wasm"
    427     fi
    428     if [ "$RUN_D" -eq 1 ]; then
    429       wf_rc "multi-tu/D" 0 \
    430         "$KIT_BIN" run -e test_main "$_mtu_wasm"
    431     fi
    432   else
    433     kit_fail "multi-tu/build" "build-obj failed"
    434   fi
    435 fi
    436 
    437 # ---- wasm build-exe: DCE from entry point, runnable via kit run -------------
    438 if [ "$RUN_D" -eq 1 ] || [ "$RUN_N" -eq 1 ]; then
    439   KIT_WORK="$BUILD_DIR/wasm-exe"; rm -rf "$KIT_WORK"; mkdir -p "$KIT_WORK"
    440   cat > "$KIT_WORK/exe_add.c" <<'EOF'
    441 int add(int a, int b) { return a + b; }
    442 int dead_fn(void) { return 99; }
    443 EOF
    444   cat > "$KIT_WORK/exe_main.c" <<'EOF'
    445 int add(int a, int b);
    446 int test_main(void) { return add(2, 3) == 5 ? 0 : 1; }
    447 EOF
    448   _wasm_exe="$KIT_WORK/out.wasm"
    449   if "$KIT_BIN" build-exe -target wasm32-none \
    450       "$KIT_WORK/exe_main.c" "$KIT_WORK/exe_add.c" \
    451       -e test_main -o "$_wasm_exe" \
    452       >"$KIT_WORK/build.out" 2>"$KIT_WORK/build.err"; then
    453     [ "$RUN_D" -eq 1 ] && wf_rc "wasm-exe/D" 0 \
    454         "$KIT_BIN" run -e test_main "$_wasm_exe"
    455     [ "$RUN_N" -eq 1 ] && wf_rc_interp "wasm-exe/N" 0 \
    456         "$KIT_BIN" run --no-jit -e test_main "$_wasm_exe"
    457     [ "$RUN_D" -eq 1 ] && wf_rc "wasm-exe/D-autoentry" 0 \
    458         "$KIT_BIN" run "$_wasm_exe"
    459   else
    460     kit_fail "wasm-exe/build" "build-exe failed"
    461   fi
    462 fi
    463 
    464 kit_summary test-wasm-front
    465 kit_exit