o1_coalesce.sh (3310B)
1 #!/usr/bin/env bash 2 # Structural checks for -O1 linear move coalescing (doc/plan/O1.md W3). 3 # 4 # A. copy_chain: a `b = a; c = b; d = c;` copy chain whose values feed a 5 # computation. The per-block combiner (mir_combine) leaves these as 6 # register-to-register moves; the linear move coalescer merges the 7 # copy-related values onto one register, so the `mov wD, wS` copies 8 # disappear. Baseline (no coalescing) emits three; coalesced emits none. 9 # 10 # B. imm_fold: `int k = 1234; int t = k; return a + t;` -- O1 may still fold a 11 # materialized immediate through a copy into its consumer (`add w0,w0,#1234`) 12 # even with the union-find now populated. Regression guard for the 13 # pass_combine.c proxy fix: coalescing keys on f->opt_o1_coalescing, not on 14 # the mere presence of f->opt_coalesce_parent, so enabling W3 must NOT 15 # silently disable the O1 immediate fold (which would re-introduce a 16 # separate movz of the constant). 17 # 18 # Pinned on aarch64 (the reference backend) where the mnemonics are stable. 19 set -euo pipefail 20 21 ROOT="$(cd "$(dirname "$0")/../.." && pwd)" 22 KIT="${KIT:-$ROOT/build/kit}" 23 WORK="$ROOT/build/test/opt/o1_coalesce" 24 mkdir -p "$WORK" 25 26 SRC="$WORK/case.c" 27 cat > "$SRC" <<'EOF' 28 /* A: a copy chain b=a; c=b; d=c whose values flow through several uses, so the 29 * frontend records IR_COPY moves the per-block combiner cannot retire. */ 30 int copies(int a) { 31 int b = a; 32 int c = b; 33 int d = c; 34 int r = b; 35 r = r + c; 36 r = r + d; 37 r = r + b * c; 38 return r; 39 } 40 /* B: an immediate assigned, copied, then used -- the O1 immediate-through-copy 41 * fold must survive the W3 union-find (no separate movz of 1234). */ 42 int imm(int a) { 43 int k = 1234; 44 int t = k; 45 return a + t; 46 } 47 EOF 48 49 OBJ="$WORK/case.o" 50 "$KIT" cc -target aarch64-linux-gnu -O1 -std=c11 -c "$SRC" \ 51 -o "$OBJ" > "$WORK/cc.out" 2>&1 52 "$KIT" objdump -d "$OBJ" > "$WORK/dis.out" 2>&1 53 54 fn_body() { # $1 = symbol name -> stdout body 55 awk -v want="$1" ' 56 $0 ~ ("^[0-9a-f]+ <" want ">:") { in_fn = 1; next } 57 /^[0-9a-f]+ </ { in_fn = 0 } 58 in_fn { print } 59 ' "$WORK/dis.out" 60 } 61 62 fail() { 63 printf 'o1_coalesce FAILED: %s\n' "$1" >&2 64 printf ' --- disassembly ---\n' >&2 65 sed 's/^/ | /' "$WORK/dis.out" >&2 66 exit 1 67 } 68 69 # A: the copy chain must leave no register-to-register `mov wD, wS` in `copies`. 70 A="$(fn_body copies)" 71 [ -n "$A" ] || A="$(fn_body _copies)" 72 [ -n "$A" ] || fail "copies not found in disassembly" 73 movs="$(printf '%s\n' "$A" | grep -cE 'mov[[:space:]]+w[0-9]+, w[0-9]+' || true)" 74 if [ "$movs" -ne 0 ]; then 75 fail "copies still has $movs reg-to-reg mov(s) (coalescing did not fire)" 76 fi 77 78 # B: the immediate fold must survive -- expect an `add wD, wS, #1234` with the 79 # constant folded into the use, and NO movz/mov materializing 1234 separately. 80 B="$(fn_body imm)" 81 [ -n "$B" ] || B="$(fn_body _imm)" 82 [ -n "$B" ] || fail "imm not found in disassembly" 83 printf '%s\n' "$B" | grep -Eq 'add[[:space:]]+w[0-9]+, w[0-9]+, #1234' \ 84 || fail "imm: immediate 1234 not folded into the add (O1 imm fold disabled)" 85 if printf '%s\n' "$B" | grep -Eq '(movz|movn|mov)[[:space:]]+w[0-9]+, #?(0x)?(1234|0x4d2)'; then 86 fail "imm: a separate materialization of 1234 survived (proxy fix regressed)" 87 fi 88 89 printf 'o1_coalesce: OK (copy chain coalesced, imm-through-copy fold intact)\n'