o1_cmp_imm.sh (3438B)
1 #!/usr/bin/env bash 2 # Structural checks for the -O1 W6 peephole tightening (O1.md W6), in 3 # src/opt/pass_combine.c (mir_combine): 4 # 5 # A. cmp-immediate folding: a small constant that reaches a compare through a 6 # register (because its load_imm was separated from the cmp by a call 7 # barrier, so it sits in a callee-saved reg) folds into the cmp's inline 8 # immediate. The constant tracker is invalidated precisely (only the regs a 9 # call clobbers), so a constant in a callee-saved register survives the 10 # call and the compare becomes `cmp wN,#k` with no `movz` of that constant. 11 # 12 # B. address-mode constant-add + offset fold: `add xN,xM,#k; ldr [xN,#j]` 13 # collapses to `ldr [xM,#k+j]` so the explicit address-build add disappears 14 # when it is otherwise dead. 15 # 16 # Both are local, target-agnostic, and keep -O1 linear. The checks pin the 17 # resulting disassembly on aarch64 (the reference backend), where the patterns 18 # have stable mnemonics. 19 set -euo pipefail 20 21 ROOT="$(cd "$(dirname "$0")/../.." && pwd)" 22 KIT="${KIT:-$ROOT/build/kit}" 23 WORK="$ROOT/build/test/opt/o1_cmp_imm" 24 mkdir -p "$WORK" 25 26 SRC="$WORK/case.c" 27 cat > "$SRC" <<'EOF' 28 extern int side(void); 29 /* A: a small constant K materialized before a call (it survives in a callee- 30 * saved register across the call), then compared. The constant must fold into 31 * the cmp's immediate -- `cmp wN,#42` -- with no surviving `movz wM,#42`. */ 32 int cmp_after_call(int x) { 33 const int K = 42; 34 side(); 35 return x > K; 36 } 37 /* B: a base+constant add feeding a plain (no-index) load. The add should fold 38 * into the load offset so no `add xN,xM,#16` address build survives -- just a 39 * single `ldr xD,[xM,#...]`. */ 40 struct S { long a, b, c; }; 41 long off_fold(struct S *p) { 42 long *q = (long *)((char *)p + 8); 43 return *q; 44 } 45 EOF 46 47 OBJ="$WORK/case.o" 48 "$KIT" cc -target aarch64-linux-gnu -O1 -std=c11 -c "$SRC" \ 49 -o "$OBJ" > "$WORK/cc.out" 2>&1 50 "$KIT" objdump -d "$OBJ" > "$WORK/dis.out" 2>&1 51 52 fn_body() { # $1 = symbol name -> stdout body 53 awk -v want="$1" ' 54 $0 ~ ("^[0-9a-f]+ <" want ">:") { in_fn = 1; next } 55 /^[0-9a-f]+ </ { in_fn = 0 } 56 in_fn { print } 57 ' "$WORK/dis.out" 58 } 59 60 fail() { 61 printf 'o1_cmp_imm FAILED: %s\n' "$1" >&2 62 printf ' --- disassembly ---\n' >&2 63 sed 's/^/ | /' "$WORK/dis.out" >&2 64 exit 1 65 } 66 67 # A: cmp_after_call must compare against the immediate (`cmp wN, #42`) and must 68 # NOT keep a `movz wM, #42` (the constant materialization the fold removes). 69 A="$(fn_body _cmp_after_call)" 70 [ -n "$A" ] || A="$(fn_body cmp_after_call)" 71 [ -n "$A" ] || fail "cmp_after_call not found" 72 printf '%s\n' "$A" | grep -Eq 'cmp[[:space:]]+w[0-9]+, #42' \ 73 || fail "expected 'cmp wN, #42' (cmp-immediate fold did not fire)" 74 if printf '%s\n' "$A" | grep -Eq 'movz[[:space:]]+w[0-9]+, 0x2a\b'; then 75 fail "constant 42 still materialized with movz (cmp-imm fold incomplete)" 76 fi 77 78 # B: off_fold must load with a single ldr and a folded offset; no `add xN,xM,#8` 79 # address build should survive for the field offset. 80 B="$(fn_body _off_fold)" 81 [ -n "$B" ] || B="$(fn_body off_fold)" 82 [ -n "$B" ] || fail "off_fold not found" 83 printf '%s\n' "$B" | grep -Eq '\bldr\b' || fail "expected a ldr in off_fold" 84 if printf '%s\n' "$B" | grep -Eq 'add[[:space:]]+x[0-9]+, x[0-9]+, #8\b'; then 85 fail "explicit 'add xN,xM,#8' address build survived (offset fold did not fire)" 86 fi 87 88 printf 'o1_cmp_imm: OK (cmp wN,#k fold across barrier; add+offset fold)\n'