o1_remat.sh (4742B)
1 #!/usr/bin/env bash 2 # Structural check for -O1 rematerialization (doc/plan/O1.md W2). 3 # 4 # At a use of a spilled value whose single def is cheaper to recompute than to 5 # reload, kit recomputes at the use instead of reloading from the slot, and 6 # drops the spill store entirely when every use rematerializes. The conservative 7 # v1 set is input-less producers whose recompute is <= reload cost: 8 # - IR_LOAD_IMM whose immediate is <= 2 instructions (movz / movz;movk); 9 # - IR_ADDR_OF of a local (one `add` off the stable frame base). 10 # 11 # The probe drives a small marker constant (KMARK, one movz) and the address of 12 # a local (one `add xN, x29, #off`) to spill: both are low-frequency but live 13 # across two trailing calls, while sixteen high-frequency array values win every 14 # callee-save register. After W2 each spilled use of the constant / address must 15 # RECOMPUTE (a `movz #KMARK` / a repeated `add xN, x29, #off`) rather than reload 16 # from a slot, and the spill store must be gone (no `str` of the recomputed 17 # value into a frame slot it is never read from). 18 # 19 # Pinned on aarch64 (the reference backend) where the mnemonics are stable. 20 set -euo pipefail 21 22 ROOT="$(cd "$(dirname "$0")/../.." && pwd)" 23 KIT="${KIT:-$ROOT/build/kit}" 24 WORK="$ROOT/build/test/opt/o1_remat" 25 mkdir -p "$WORK" 26 27 SRC="$WORK/case.c" 28 cat > "$SRC" <<'EOF' 29 extern void use_ptr(int *); 30 extern void sink(long); 31 extern void barrier(void); 32 33 /* KMARK = 0x4321 is a single-movz constant; &loc is one add off the frame base. 34 * Both are low-frequency but live across the two trailing calls, so the 35 * sixteen high-frequency array values displace them from the callee-saves and 36 * they spill. W2 should recompute both at their uses instead of reloading. */ 37 long remat_probe(long *arr) { 38 int loc; 39 const long KMARK = 0x4321; 40 long a=arr[0],b=arr[1],c=arr[2],d=arr[3],e=arr[4],f=arr[5],g=arr[6],h=arr[7]; 41 long i=arr[8],j=arr[9],k=arr[10],l=arr[11],m=arr[12],o=arr[13],p=arr[14],q=arr[15]; 42 barrier(); 43 long s = a*a+b*b+c*c+d*d+e*e+f*f+g*g+h*h+i*i+j*j+k*k+l*l+m*m+o*o+p*p+q*q; 44 s += a*b+b*c+c*d+d*e+e*f+f*g+g*h+h*i+i*j+j*k+k*l+l*m+m*o+o*p+p*q+q*a; 45 s += a+b+c+d+e+f+g+h+i+j+k+l+m+o+p+q; 46 use_ptr(&loc); /* use 1: address of a local as a call arg */ 47 sink(KMARK); /* use 2: the constant as a call arg */ 48 use_ptr(&loc); /* use 3: the address again */ 49 return s + KMARK; /* use 4: the constant again */ 50 } 51 EOF 52 53 OBJ="$WORK/case.o" 54 "$KIT" cc -target aarch64-linux-gnu -O1 -std=c11 -c "$SRC" \ 55 -o "$OBJ" > "$WORK/cc.out" 2>&1 56 "$KIT" objdump -d "$OBJ" > "$WORK/dis.out" 2>&1 57 58 BODY="$(awk ' 59 /<remat_probe>:/ { in_fn = 1; next } 60 in_fn && /^[0-9a-f]+ </ { exit } 61 in_fn { print } 62 ' "$WORK/dis.out")" 63 64 fail() { 65 printf 'o1_remat FAILED: %s\n' "$1" >&2 66 printf ' --- remat_probe disassembly ---\n' >&2 67 printf '%s\n' "$BODY" | sed 's/^/ | /' >&2 68 exit 1 69 } 70 71 [ -n "$BODY" ] || fail "remat_probe not found in disassembly" 72 73 # (1) Constant rematerialization: KMARK (0x4321) is recomputed at each of its 74 # two uses. A reload-based baseline would materialize it once and reload via 75 # ldr; remat emits the `movz #0x4321` again at every use -> count >= 2. 76 movz_n="$(printf '%s\n' "$BODY" | grep -cE 'movz[ \t]+x[0-9]+, 0x4321' || true)" 77 [ "$movz_n" -ge 2 ] || fail "constant KMARK not rematerialized (movz 0x4321 x$movz_n, expected >=2)" 78 79 # (2) Address rematerialization: the same `add xN, x29, #off` recompute appears 80 # at both address uses. Find the most common add-off-frame-base offset and 81 # require it to recur (a single def would appear once). 82 addr_off="$(printf '%s\n' "$BODY" \ 83 | grep -oE 'add x[0-9]+, x29, #[0-9]+$' \ 84 | grep -oE '#[0-9]+$' | sort | uniq -c | sort -rn | head -1 | awk '{print $2}')" 85 [ -n "$addr_off" ] || fail "no 'add xN, x29, #off' (addr_of local) recompute found" 86 addr_n="$(printf '%s\n' "$BODY" | grep -cE "add x[0-9]+, x29, ${addr_off}\$" || true)" 87 [ "$addr_n" -ge 2 ] || fail "local address not rematerialized (add x29,${addr_off} x$addr_n, expected >=2)" 88 89 # (3) Store-drop: the rematerialized constant and address must leave no spill 90 # store behind. With every use rematerialized there must be no `str` of the 91 # KMARK value or the recomputed address into a slot. We approximate by 92 # requiring that the marker constant is never stored: a kept spill store 93 # would be `movz xN,#0x4321 ; str xN,[x29,#..]` -- i.e. a movz immediately 94 # followed by a str of the same register. None should remain. 95 if printf '%s\n' "$BODY" | grep -A1 -E 'movz[ \t]+x[0-9]+, 0x4321' \ 96 | grep -qE 'str[ \t]+x[0-9]+, \[x29'; then 97 fail "spill store for rematerialized constant survived (movz;str)" 98 fi 99 100 printf 'o1_remat: OK (const movz x%s, addr add-off-x29 x%s, spill stores dropped)\n' \ 101 "$movz_n" "$addr_n"