toy_cross_batch.sh (5248B)
1 #!/usr/bin/env bash 2 # scripts/toy_cross_batch.sh — fast batched cross-exec of the toy suite for a 3 # single target arch + opt level. Compiles/links every case natively (fast), 4 # then runs all executables inside ONE podman container, instead of the 5 # per-case podman invocation test/toy/run.sh uses. Cuts a full-suite run from 6 # ~30 min (257 emulated container starts) to a couple of minutes. 7 # 8 # Usage: scripts/toy_cross_batch.sh <arch> <opt> [name_substr] 9 # arch: rv64 | x64 | aa64 opt: 0 | 1 10 # Env: KIT (path to kit binary, default build/kit) 11 # 12 # Checks only the X path (cross-compile + link + exec rc vs <name>.expected). 13 # Compile/link failures and nonzero/mismatched rc count as FAIL. 14 set -u 15 ROOT="$(cd "$(dirname "$0")/.." && pwd)" 16 KIT="${KIT:-$ROOT/build/kit}" 17 ARCH="${1:?arch}" 18 OPT="${2:?opt}" 19 FILTER="${3:-}" 20 CASES="$ROOT/test/toy/cases" 21 # Under the repo build dir, not /tmp: the podman VM (macOS) shares the repo tree 22 # but not /tmp, so a /tmp bind-mount fails with "statfs ...: no such file". 23 WORK="$ROOT/build/toy_cross_batch/$ARCH-O$OPT" 24 rm -rf "$WORK"; mkdir -p "$WORK" 25 26 # Per-arch image: the three platforms otherwise collide on a shared 27 # alpine:latest tag (a multi-platform pull overwrites it). Distinct local tags 28 # (localhost/alpine-{rv64,amd64,arm64}) avoid that; override via RUN_<ARCH>_IMAGE. 29 case "$ARCH" in 30 rv64) TRIPLE=riscv64-linux-gnu; PLAT=linux/riscv64; IMAGE="${RUN_RV64_IMAGE:-localhost/alpine-rv64}" ;; 31 x64) TRIPLE=x86_64-linux-gnu; PLAT=linux/amd64; IMAGE="${RUN_X64_IMAGE:-localhost/alpine-amd64}" ;; 32 aa64) TRIPLE=aarch64-linux-gnu; PLAT=linux/arm64; IMAGE="${RUN_AARCH64_IMAGE:-localhost/alpine-arm64}" ;; 33 *) echo "unknown arch $ARCH"; exit 2 ;; 34 esac 35 IMAGE="${IMAGE_OVERRIDE:-$IMAGE}" 36 37 # Build a freestanding _start.o for the target via clang (mirrors run.sh). 38 START_C="$WORK/start.c" 39 cat > "$START_C" <<'EOF' 40 extern int main(void); 41 __attribute__((noreturn)) static void do_exit(int code) { 42 #if defined(__aarch64__) 43 register long x8 __asm__("x8") = 94; register long x0 __asm__("x0") = code; 44 __asm__ volatile("svc #0" ::"r"(x8), "r"(x0) : "memory"); 45 #elif defined(__x86_64__) 46 register long rax __asm__("rax") = 231; register long rdi __asm__("rdi") = code; 47 __asm__ volatile("syscall" ::"r"(rax), "r"(rdi) : "memory"); 48 #elif defined(__riscv) && __riscv_xlen == 64 49 register long a7 __asm__("a7") = 94; register long a0 __asm__("a0") = code; 50 __asm__ volatile("ecall" ::"r"(a7), "r"(a0) : "memory"); 51 #endif 52 __builtin_unreachable(); 53 } 54 #if defined(__x86_64__) 55 __attribute__((force_align_arg_pointer)) 56 #endif 57 void _start(void) { do_exit(main()); } 58 EOF 59 if ! clang --target="$TRIPLE" -O1 -ffreestanding -fno-stack-protector -fno-PIC \ 60 -fno-pie -c "$START_C" -o "$WORK/start.o" 2>"$WORK/start.err"; then 61 echo "FATAL: could not build start.o for $TRIPLE"; cat "$WORK/start.err"; exit 2 62 fi 63 64 PASS=0; FAIL=0; SKIP=0; FAILED=() 65 RUNLIST="$WORK/runlist.txt"; : > "$RUNLIST" 66 declare -A EXPECT 67 68 for src in "$CASES"/*.toy; do 69 name="$(basename "$src" .toy)" 70 [ -n "$FILTER" ] && [[ "$name" != *"$FILTER"* ]] && continue 71 # asmnop is aa64-specific before toy asm selectors. 72 if [ "$ARCH" != "aa64" ] && grep -q 'asmnop' "$src"; then 73 SKIP=$((SKIP+1)); continue 74 fi 75 exp=0; [ -f "${src%.toy}.expected" ] && exp="$(tr -d '[:space:]' < "${src%.toy}.expected")" 76 EXPECT[$name]=$exp 77 if ! "$KIT" cc "-O$OPT" -target "$TRIPLE" -c "$src" -o "$WORK/$name.o" \ 78 >"$WORK/$name.cc.out" 2>"$WORK/$name.cc.err"; then 79 FAIL=$((FAIL+1)); FAILED+=("$name [cc] $(head -1 "$WORK/$name.cc.err")"); continue 80 fi 81 if [ -s "$WORK/$name.cc.err" ]; then 82 FAIL=$((FAIL+1)); FAILED+=("$name [cc-stderr] $(head -1 "$WORK/$name.cc.err")"); continue 83 fi 84 if ! "$KIT" ld "$WORK/$name.o" "$WORK/start.o" -o "$WORK/$name.exe" \ 85 >"$WORK/$name.ld.out" 2>"$WORK/$name.ld.err"; then 86 FAIL=$((FAIL+1)); FAILED+=("$name [ld] $(head -1 "$WORK/$name.ld.err")"); continue 87 fi 88 chmod +x "$WORK/$name.exe" 2>/dev/null || true 89 echo "$name" >> "$RUNLIST" 90 done 91 92 # Single batched container run: execute every linked exe, print "name rc". 93 # The in-container loop is written to a file (bind-mounted via $WORK) rather than 94 # inlined through nested sh/bash quoting, which is brittle and shell-dependent. 95 RESULTS="$WORK/results.txt" 96 INRUN="$WORK/run_in_container.sh" 97 cat > "$INRUN" <<EOF 98 cd "$WORK" || exit 2 99 while read n; do 100 "./\$n.exe" >/dev/null 2>"$WORK/\$n.run.err"; echo "\$n \$?" 101 done < "$RUNLIST" 102 EOF 103 if [ -s "$RUNLIST" ]; then 104 podman run --rm --pull=never --platform "$PLAT" --net=none \ 105 -v "$WORK:$WORK" -w "$WORK" "$IMAGE" sh "$INRUN" \ 106 > "$RESULTS" 2>"$WORK/podman.err" || { 107 echo "FATAL: podman batch run failed"; cat "$WORK/podman.err"; exit 2; } 108 fi 109 110 while read -r name rc; do 111 exp=${EXPECT[$name]:-0}; exp=$((exp & 255)) 112 if [ "$rc" -eq "$exp" ] && [ ! -s "$WORK/$name.run.err" ]; then 113 PASS=$((PASS+1)) 114 else 115 FAIL=$((FAIL+1)) 116 msg="rc=$rc want=$exp"; [ -s "$WORK/$name.run.err" ] && msg="$msg stderr=$(head -1 "$WORK/$name.run.err")" 117 FAILED+=("$name [run] $msg") 118 fi 119 done < "$RESULTS" 120 121 echo "==== $ARCH -O$OPT : $PASS pass, $FAIL fail, $SKIP skip ====" 122 if [ "${#FAILED[@]}" -gt 0 ]; then 123 printf '%s\n' "${FAILED[@]}" | sort 124 fi 125 [ "$FAIL" -eq 0 ]