run.sh (17059B)
1 #!/usr/bin/env bash 2 # test/asm/run.sh — file-driven assembler / disassembler test harness, on the 3 # shared corpus harness (test/lib/kit_corpus.sh). 4 # 5 # Three sub-corpora under test/asm/, one per asm-runner static-output mode. 6 # Each is a SEQUENTIAL corpus with a disjoint case set (its own kit_corpus_run, 7 # no opt axis, single arch), NOT a parallel lane within a case: 8 # 9 # encode/ <name>.s + <name>.expected.hex golden hex bytes 10 # (+ optional <name>.expected integer exit code) 11 # decode/ <name>.hex + <name>.expected.txt golden decoded text 12 # listing/ <name>.in.bin + <name>.expected.lst golden objdump-style 13 # 14 # Path matrix (6 letters, default HTLDJE): 15 # 16 # H Hex encode — encode/ only. asm-runner --encode IN.s → hex; diff 17 # vs <name>.expected.hex. Missing golden => SKIP. 18 # T Text decode — decode/ only. asm-runner --decode IN.hex → text; diff 19 # vs <name>.expected.txt. Missing golden => FAIL. 20 # L Listing — listing/ only. asm-runner --listing IN.in.bin → text; 21 # diff vs <name>.expected.lst. Missing golden => FAIL. 22 # D Direct JIT — encode/ only, when <name>.expected (integer exit) is 23 # present. asm-runner --jit IN.s → exit code matches. 24 # Host arch must match the cross-target. Missing 25 # .expected exit => SKIP. 26 # J JIT via file — encode/ only, when <name>.expected is present. 27 # asm-runner --emit + jit-runner. Host arch must match. 28 # E ELF exec — encode/ only, when <name>.expected is present. 29 # asm-runner --emit + start.o → link-exe-runner → 30 # qemu/podman → exit code. Cross-host friendly (deferred 31 # batched exec via kit_queue_e). 32 # 33 # Reuses the test/link harness binaries (link-exe-runner, jit-runner) plus 34 # test/link/harness/start.c verbatim — same convention as test/parse/run.sh. 35 # 36 # Whole-case <name>.targets carries bare arch tokens (aa64/x64/rv64 + synonyms); 37 # a case applies only when one of them matches KIT_TEST_ARCH (else SKIP-NA). 38 # Per-case <name>.skip (+ .<arch>.skip / .<lane>.skip) sidecars skip cleanly. 39 # 40 # Phase 1 (doc/ASM.md §5): asm_parse and the disasm iterator are still stubs 41 # in src/api/stubs.c. KIT_TEST_ALLOW_SKIP defaults to 1 here for the 42 # duration of phase 1 — flip to 0 (matching the rest of the suite) once the 43 # assembler / disasm iterator are real. 44 # 45 # Filtering: 46 # ./run.sh [name_filter] [paths] 47 # name_filter substring match against case basename 48 # paths subset of "HTLDJE" (default "HTLDJE") 49 # Equivalent env vars: KIT_TEST_FILTER, KIT_TEST_PATHS. 50 # 51 # Parallelism: every lane hook writes only under $KIT_WORK and records only via 52 # kit_*, so the console summary is identical serial or parallel. KIT_ASM_PARALLEL 53 # flips dispatch (default on). 54 55 set -u 56 57 ROOT="$(cd "$(dirname "$0")/../.." && pwd)" 58 TEST_DIR="$ROOT/test/asm" 59 LINK_TEST_DIR="$ROOT/test/link" 60 BUILD_DIR="$ROOT/build/test" 61 LIB_AR="$ROOT/build/libkit.a" 62 63 export KIT_LIB_DIR="$ROOT/test/lib" 64 # shellcheck source=../lib/kit_corpus.sh 65 . "$ROOT/test/lib/kit_corpus.sh" 66 67 ASM_RUNNER="$BUILD_DIR/asm-runner" 68 LINK_EXE_RUNNER="$BUILD_DIR/link-exe-runner" 69 JIT_RUNNER="$BUILD_DIR/jit-runner" 70 71 # Phase 1: ALLOW_SKIP defaults to 1 (smoke cases skip cleanly because 72 # asm_parse / kit_disasm_iter_* are still stubs). The engine's kit_exit reads 73 # KIT_TEST_ALLOW_SKIP (default 0), so export the phase-1 default of 1 unless 74 # the caller overrides it. 75 export KIT_TEST_ALLOW_SKIP="${KIT_TEST_ALLOW_SKIP:-1}" 76 77 # KIT_TEST_ARCH selects the cross-target. Default aa64 preserves the 78 # pre-multiarch behavior. The asm-runner reads the same env via 79 # test/lib/kit_test_target.h. 80 KIT_TEST_ARCH="${KIT_TEST_ARCH:-aa64}" 81 case "$KIT_TEST_ARCH" in 82 aa64|aarch64|arm64) TEST_ARCH=aa64; CLANG_TRIPLE=aarch64-linux-gnu; EXEC_ARCH=aarch64 ;; 83 x64|x86_64|amd64) TEST_ARCH=x64; CLANG_TRIPLE=x86_64-linux-gnu; EXEC_ARCH=x64 ;; 84 rv64|riscv64) TEST_ARCH=rv64; CLANG_TRIPLE=riscv64-linux-gnu; EXEC_ARCH=rv64 ;; 85 rv32|riscv32) TEST_ARCH=rv32; CLANG_TRIPLE=riscv32-unknown-elf; EXEC_ARCH=rv32 ;; 86 *) printf 'unknown KIT_TEST_ARCH=%s\n' "$KIT_TEST_ARCH" >&2; exit 2 ;; 87 esac 88 export KIT_TEST_ARCH 89 CLANG_TARGET="--target=$CLANG_TRIPLE" 90 91 # Synthetic single tuple — there is no opt axis and this harness is single-arch. 92 # Whole-case .targets applicability is computed by kit_read_case (bare arch 93 # tokens), so the engine's tuple matcher is disabled (KIT_TARGETS_EXT=""). 94 CUR_TUPLE="$TEST_ARCH-asm" 95 96 # Filtering: positional [name_filter] [paths] mirror KIT_TEST_FILTER / PATHS. 97 # The engine's discovery honors KIT_TEST_FILTER, so export the positional one. 98 FILTER="${1:-${KIT_TEST_FILTER:-}}" 99 [ -n "$FILTER" ] && export KIT_TEST_FILTER="$FILTER" 100 PATHS="${2:-${KIT_TEST_PATHS:-HTLDJE}}" 101 case "$PATHS" in *H*) RUN_H=1;; *) RUN_H=0;; esac 102 case "$PATHS" in *T*) RUN_T=1;; *) RUN_T=0;; esac 103 case "$PATHS" in *L*) RUN_L=1;; *) RUN_L=0;; esac 104 case "$PATHS" in *D*) RUN_D=1;; *) RUN_D=0;; esac 105 case "$PATHS" in *J*) RUN_J=1;; *) RUN_J=0;; esac 106 case "$PATHS" in *E*) RUN_E=1;; *) RUN_E=0;; esac 107 108 PAR="${KIT_ASM_PARALLEL:-1}" 109 110 # ---- tool detection (mirrors test/parse/run.sh) ---------------------------- 111 112 have_clang_cross=0 113 have_exe_runner=0 114 have_jit_runner=0 115 have_qemu=0 116 have_podman=0 117 is_aarch64=0 118 119 if clang $CLANG_TARGET -c -x c - -o /dev/null < /dev/null 2>/dev/null; then 120 have_clang_cross=1 121 fi 122 123 QEMU_BIN="$(command -v qemu-aarch64-static 2>/dev/null || command -v qemu-aarch64 2>/dev/null || true)" 124 [ -n "$QEMU_BIN" ] && have_qemu=1 125 command -v podman >/dev/null 2>&1 && have_podman=1 126 127 arch_raw="$(uname -m 2>/dev/null || true)" 128 { [ "$arch_raw" = "aarch64" ] || [ "$arch_raw" = "arm64" ]; } && is_aarch64=1 129 130 # is_native_target=1 when the cross-target arch matches the host arch. 131 # Required for in-process JIT (path D) and the jit-runner (path J). 132 is_native_target=0 133 case "$TEST_ARCH" in 134 aa64) [ $is_aarch64 -eq 1 ] && is_native_target=1 ;; 135 x64) { [ "$arch_raw" = "x86_64" ] || [ "$arch_raw" = "amd64" ]; } && is_native_target=1 ;; 136 rv64) [ "$arch_raw" = "riscv64" ] && is_native_target=1 ;; 137 esac 138 139 # Shared per-arch exec helper — see test/lib/exec_target.sh. Path E queues bare 140 # EXEC_ARCH tags (== <arch>-linux), so set the caller-contract knobs it reads. 141 EXEC_TARGET_MOUNT_ROOT="$BUILD_DIR" 142 export have_qemu have_podman is_aarch64 QEMU_BIN EXEC_TARGET_MOUNT_ROOT 143 # shellcheck source=../lib/exec_target.sh 144 . "$ROOT/test/lib/exec_target.sh" 145 146 # ---- harness binaries ------------------------------------------------------ 147 148 printf 'Checking harness...\n' 149 150 if [ ! -x "$ASM_RUNNER" ]; then 151 printf ' %sFATAL%s asm-runner missing — run "make test-asm"\n' \ 152 "$_CF_RED" "$_CF_RST" >&2 153 exit 1 154 fi 155 printf ' %sfound%s asm-runner\n' "$_CF_GRN" "$_CF_RST" 156 157 # link-exe-runner — for path E. 158 if [ -x "$LINK_EXE_RUNNER" ]; then 159 have_exe_runner=1 160 printf ' %sfound%s link-exe-runner\n' "$_CF_GRN" "$_CF_RST" 161 else 162 printf ' %swarn%s link-exe-runner missing; E path will skip\n' "$_CF_YEL" "$_CF_RST" 163 fi 164 165 # jit-runner — for path J. Only meaningful when host arch matches the cross-target. 166 if [ $is_native_target -eq 1 ]; then 167 if [ -x "$JIT_RUNNER" ]; then 168 have_jit_runner=1 169 printf ' %sfound%s jit-runner\n' "$_CF_GRN" "$_CF_RST" 170 else 171 printf ' %swarn%s jit-runner missing; J path will skip\n' "$_CF_YEL" "$_CF_RST" 172 fi 173 fi 174 175 # Cached start.o — same trick as parse/cg harnesses; build once for the 176 # whole run. Arch-qualified so concurrent per-arch lanes (the test-asm aggregate 177 # under `make -j`) don't race on one path with arch-specific content. 178 START_OBJ="$BUILD_DIR/asm_start.$TEST_ARCH.o" 179 have_start_obj=0 180 if [ $have_clang_cross -eq 1 ]; then 181 if clang $CLANG_TARGET -O1 -ffreestanding -fno-stack-protector \ 182 -fno-PIC -fno-pie \ 183 -c "$LINK_TEST_DIR/harness/start.c" -o "$START_OBJ" 2>/dev/null; then 184 have_start_obj=1 185 fi 186 fi 187 188 printf 'Running cases...\n' 189 190 # ---- whole-case .targets applicability (bare arch tokens) ------------------ 191 # The original .targets sidecars list bare arch names (aa64/x64/rv64 and 192 # synonyms), NOT <arch>-<obj> tuples, so the engine's kit_tuple_applicable does 193 # not apply. Read them here and emit SKIP-NA via KIT_SKIP_NA_CASE. The engine's 194 # KIT_TARGETS_EXT matcher stays disabled (KIT_TARGETS_EXT=""). 195 kit_read_case() { 196 # Reset per-case emit state (serial mode reuses this shell across cases). 197 _asm_emit_done=0 198 local targets="$KIT_SIDECAR_DIR/$KIT_BASE.targets" 199 [ -f "$targets" ] || return 0 200 local tuple 201 for tuple in $(cat "$targets"); do 202 case "$tuple:$TEST_ARCH" in 203 aa64:aa64|aarch64:aa64|arm64:aa64) return 0 ;; 204 x64:x64|x86_64:x64|amd64:x64) return 0 ;; 205 rv64:rv64|riscv64:rv64) return 0 ;; 206 rv32:rv32|riscv32:rv32) return 0 ;; 207 esac 208 done 209 KIT_SKIP_NA_CASE=1 210 } 211 KIT_READ_CASE=kit_read_case 212 213 # ---- golden-diff helper (KIT_WORK-confined) --------------------------------- 214 # kit_golden_diff LABEL EXPECTED ACTUAL : PASS on byte-exact match, else FAIL. 215 kit_golden_diff() { 216 local label="$1" expected="$2" actual="$3" 217 if diff -u "$expected" "$actual" >"$KIT_WORK/diff" 2>&1; then 218 kit_pass "$label" 219 else 220 kit_fail "$label" "golden mismatch; see $KIT_WORK/diff" 221 fi 222 } 223 224 # ---- decode corpus: lane T (text golden-diff) ------------------------------ 225 kit_lane_T() { 226 local expected="$KIT_SIDECAR_DIR/$KIT_BASE.expected.txt" 227 if [ ! -e "$expected" ]; then 228 kit_fail "$KIT_NAME/T" "missing golden $(basename "$expected")"; return 229 fi 230 local out="$KIT_WORK/out.txt" 231 if ! "$ASM_RUNNER" --decode "$KIT_SRC" "$out" >"$KIT_WORK/stdout" 2>"$KIT_WORK/stderr"; then 232 kit_fail "$KIT_NAME/T" "asm-runner --decode failed; see $KIT_WORK/stderr"; return 233 fi 234 kit_golden_diff "$KIT_NAME/T" "$expected" "$out" 235 } 236 237 # ---- listing corpus: lane L (listing golden-diff) -------------------------- 238 kit_lane_L() { 239 local expected="$KIT_SIDECAR_DIR/$KIT_BASE.expected.lst" 240 if [ ! -e "$expected" ]; then 241 kit_fail "$KIT_NAME/L" "missing golden $(basename "$expected")"; return 242 fi 243 local out="$KIT_WORK/out.lst" 244 if ! "$ASM_RUNNER" --listing "$KIT_SRC" "$out" >"$KIT_WORK/stdout" 2>"$KIT_WORK/stderr"; then 245 kit_fail "$KIT_NAME/L" "asm-runner --listing failed; see $KIT_WORK/stderr"; return 246 fi 247 kit_golden_diff "$KIT_NAME/L" "$expected" "$out" 248 } 249 250 # ---- encode corpus lanes: H / D / J / E ------------------------------------ 251 # KIT_EXPECTED carries the .expected integer exit code (0 when absent); a missing 252 # .expected file means the exec lanes (D/J/E) SKIP. Detect "has exit" via the 253 # sidecar's presence directly so a legitimate expected exit of 0 still runs. 254 _asm_has_exit() { [ -f "$KIT_SIDECAR_DIR/$KIT_BASE.expected" ]; } 255 _asm_exit_byte() { echo $(( KIT_EXPECTED & 0xff )); } 256 257 # Path H: hex encode roundtrip. Missing .expected.hex golden => SKIP. 258 kit_lane_H() { 259 local expected_hex="$KIT_SIDECAR_DIR/$KIT_BASE.expected.hex" 260 if [ ! -e "$expected_hex" ]; then 261 kit_skip "$KIT_NAME/H" "no .expected.hex golden"; return 262 fi 263 local out="$KIT_WORK/out.hex" 264 if ! "$ASM_RUNNER" --encode "$KIT_SRC" "$out" >"$KIT_WORK/h.out" 2>"$KIT_WORK/h.err"; then 265 kit_fail "$KIT_NAME/H" "asm-runner --encode failed; see $KIT_WORK/h.err"; return 266 fi 267 kit_golden_diff "$KIT_NAME/H" "$expected_hex" "$out" 268 } 269 270 # Ensure the .o needed by J/E exists. Sets ASM_OBJ to its path and returns 0 on 271 # success; on the first failure emits FAIL "$KIT_NAME/emit" (matching the 272 # original standalone emit failure record) and returns 1. Must NOT be called in 273 # a $(...) subshell — kit_fail mutates counters that a subshell would discard. 274 # D does not need a .o — asm-runner --jit does the full parse+jit in process. 275 _asm_emit_done=0 276 _asm_emit() { 277 ASM_OBJ="$KIT_WORK/$KIT_BASE.o" 278 [ -f "$ASM_OBJ" ] && return 0 279 if [ "$_asm_emit_done" -eq 1 ]; then return 1; fi 280 _asm_emit_done=1 281 if "$ASM_RUNNER" --emit "$KIT_SRC" "$ASM_OBJ" 2>"$KIT_WORK/emit.err"; then 282 return 0 283 fi 284 kit_fail "$KIT_NAME/emit" "asm-runner --emit failed; see $KIT_WORK/emit.err" 285 return 1 286 } 287 288 # Path D: in-process JIT. Missing .expected exit => SKIP. 289 kit_lane_D() { 290 if ! _asm_has_exit; then kit_skip "$KIT_NAME/D" "no .expected exit code"; return; fi 291 if [ $is_native_target -eq 0 ]; then 292 kit_skip "$KIT_NAME/D" "host arch != $TEST_ARCH (no native JIT)"; return 293 fi 294 local want; want=$(_asm_exit_byte) 295 "$ASM_RUNNER" --jit "$KIT_SRC" >"$KIT_WORK/d.out" 2>"$KIT_WORK/d.err" 296 local rc=$? 297 if [ "$rc" -eq "$want" ]; then kit_pass "$KIT_NAME/D" 298 else kit_fail "$KIT_NAME/D" "expected $want got $rc"; fi 299 } 300 301 # Path J: jit-via-file. Missing .expected exit => SKIP. 302 kit_lane_J() { 303 if ! _asm_has_exit; then kit_skip "$KIT_NAME/J" "no .expected exit code"; return; fi 304 if [ $have_jit_runner -eq 0 ]; then 305 kit_skip "$KIT_NAME/J" "no jit-runner (host arch != $TEST_ARCH)"; return 306 fi 307 _asm_emit || { kit_skip "$KIT_NAME/J" "no .o (--emit failed)"; return; } 308 local want; want=$(_asm_exit_byte) 309 "$JIT_RUNNER" "$ASM_OBJ" >"$KIT_WORK/jit.out" 2>"$KIT_WORK/jit.err" 310 local rc=$? 311 if [ "$rc" -eq "$want" ]; then kit_pass "$KIT_NAME/J" 312 else kit_fail "$KIT_NAME/J" "expected $want got $rc"; fi 313 } 314 315 # Path E: link + deferred (batched) qemu/podman exec. Missing .expected => SKIP. 316 kit_lane_E() { 317 if ! _asm_has_exit; then kit_skip "$KIT_NAME/E" "no .expected exit code"; return; fi 318 if [ $have_exe_runner -eq 0 ] || [ $have_clang_cross -eq 0 ] || [ $have_start_obj -eq 0 ]; then 319 kit_skip "$KIT_NAME/E" "no link-exe-runner, $TEST_ARCH clang, or start.o"; return 320 fi 321 _asm_emit || { kit_skip "$KIT_NAME/E" "no .o (--emit failed)"; return; } 322 local exe="$KIT_WORK/linked.exe" 323 if ! "$LINK_EXE_RUNNER" -o "$exe" "$ASM_OBJ" "$START_OBJ" \ 324 >"$KIT_WORK/exec_link.out" 2>"$KIT_WORK/exec_link.err"; then 325 kit_fail "$KIT_NAME/E" "link failed"; return 326 fi 327 if exec_target_supported "$EXEC_ARCH"; then 328 kit_queue_e "$KIT_NAME/E" "$exe" \ 329 "$KIT_WORK/exec.out" "$KIT_WORK/exec.err" "$KIT_WORK/exec.rc" \ 330 "$(_asm_exit_byte)" "$EXEC_ARCH" 331 else 332 kit_skip "$KIT_NAME/E" "no runner for $EXEC_ARCH" 333 fi 334 } 335 336 # ---- has-cases guard ------------------------------------------------------- 337 # kit_corpus_run exits 2 if a sub-corpus has zero matching cases (after the 338 # KIT_TEST_FILTER), so only invoke it when at least one case is present. 339 # _asm_have_cases GLOB EXT : mirrors the engine's discovery (basename minus 340 # .EXT, KIT_TEST_FILTER substring) and returns 0 iff a case survives. 341 _asm_have_cases() { 342 local glob="$1" ext="$2" f base 343 shopt -s nullglob 344 for f in $glob; do 345 base="$(basename "$f")"; base="${base%.$ext}" 346 if [ -n "${KIT_TEST_FILTER:-}" ]; then 347 case "$base" in *"$KIT_TEST_FILTER"*) ;; *) continue ;; esac 348 fi 349 return 0 350 done 351 return 1 352 } 353 354 # ---- drive the three sub-corpora ------------------------------------------- 355 356 # (1) encode/*.s — lanes H (golden hex), D (in-proc JIT), J (jit-runner), 357 # E (link + deferred exec). Active lanes in PATHS order. 358 ENCODE_LANES= 359 [ "$RUN_H" -eq 1 ] && ENCODE_LANES="$ENCODE_LANES H" 360 [ "$RUN_D" -eq 1 ] && ENCODE_LANES="$ENCODE_LANES D" 361 [ "$RUN_J" -eq 1 ] && ENCODE_LANES="$ENCODE_LANES J" 362 [ "$RUN_E" -eq 1 ] && ENCODE_LANES="$ENCODE_LANES E" 363 if [ -n "$ENCODE_LANES" ] && _asm_have_cases "$TEST_DIR/encode/*.s" s; then 364 KIT_LABEL=test-asm KIT_BUILD_DIR="$BUILD_DIR/asm/encode" \ 365 KIT_CORPUS_GLOBS="$TEST_DIR/encode/*.s" KIT_CORPUS_EXT=s KIT_SIDECAR_DIR="$TEST_DIR/encode" \ 366 KIT_LANES="$ENCODE_LANES" KIT_OPT_LEVELS="" KIT_TUPLES="$CUR_TUPLE" \ 367 KIT_TARGETS_EXT="" KIT_PARALLELIZABLE="$PAR" kit_corpus_run 368 fi 369 370 # (2) decode/*.hex — lane T (golden decoded text). 371 if [ "$RUN_T" -eq 1 ] && _asm_have_cases "$TEST_DIR/decode/*.hex" hex; then 372 KIT_LABEL=test-asm KIT_BUILD_DIR="$BUILD_DIR/asm/decode" \ 373 KIT_CORPUS_GLOBS="$TEST_DIR/decode/*.hex" KIT_CORPUS_EXT=hex KIT_SIDECAR_DIR="$TEST_DIR/decode" \ 374 KIT_LANES="T" KIT_OPT_LEVELS="" KIT_TUPLES="$CUR_TUPLE" \ 375 KIT_TARGETS_EXT="" KIT_PARALLELIZABLE="$PAR" kit_corpus_run 376 fi 377 378 # (3) listing/*.in.bin — lane L (golden listing). 379 if [ "$RUN_L" -eq 1 ] && _asm_have_cases "$TEST_DIR/listing/*.in.bin" in.bin; then 380 KIT_LABEL=test-asm KIT_BUILD_DIR="$BUILD_DIR/asm/listing" \ 381 KIT_CORPUS_GLOBS="$TEST_DIR/listing/*.in.bin" KIT_CORPUS_EXT=in.bin KIT_SIDECAR_DIR="$TEST_DIR/listing" \ 382 KIT_LANES="L" KIT_OPT_LEVELS="" KIT_TUPLES="$CUR_TUPLE" \ 383 KIT_TARGETS_EXT="" KIT_PARALLELIZABLE="$PAR" kit_corpus_run 384 fi 385 386 # ---- summary --------------------------------------------------------------- 387 388 kit_summary test-asm 389 kit_exit