redundant_frame_sub.sh (3224B)
1 #!/usr/bin/env bash 2 # Structural check for the -O1 local frame-address `sub`-CSE (O1.md W1a). 3 # 4 # An `IR_ADDR_OF` of a frame-local materializes a frame-slot address into a 5 # register; under the W1.1 x29-at-bottom layout aarch64 builds it as a positive 6 # `add xN, x29, #k` off the frame base. Two back-to-back addr-of's of the *same* 7 # local recompute the identical `add xN, x29, #k`. The W1a peephole in 8 # src/opt/pass_combine.c (mir_combine) tracks the still-live earlier producer and 9 # rewrites the recompute into a register move (`mov xN, xM`), so the second 10 # address build vanishes. (Before W1.1 the build was a negative `sub xN,x29,#k`; 11 # W1.1 made frame addressing positive, but the W1a CSE still applies to the add.) 12 # 13 # This pins the resulting disassembly on aarch64 (the reference backend), where 14 # the pattern has a stable mnemonic. Each local is addressed twice as adjacent 15 # call arguments; with the peephole only ONE `add xN, x29, #k` survives per 16 # distinct constant K, and the duplicate becomes a `mov`. 17 set -euo pipefail 18 19 ROOT="$(cd "$(dirname "$0")/../.." && pwd)" 20 KIT="${KIT:-$ROOT/build/kit}" 21 WORK="$ROOT/build/test/opt/redundant_frame_sub" 22 mkdir -p "$WORK" 23 24 SRC="$WORK/case.c" 25 cat > "$SRC" <<'EOF' 26 extern void sink4(char *, char *, char *, char *); 27 /* Two distinct locals at frame offsets past stur's -256 range; each is passed 28 * twice as adjacent arguments, so the frontend records two IR_ADDR_OF of the 29 * same slot back-to-back. Without W1a each lowers to its own `sub xN,x29,#k`. */ 30 void g(void) { 31 char a[600], b[600]; 32 sink4(a, a, b, b); 33 } 34 EOF 35 36 OBJ="$WORK/case.o" 37 "$KIT" cc -target aarch64-linux-gnu -O1 -std=c11 -c "$SRC" \ 38 -o "$OBJ" > "$WORK/cc.out" 2>&1 39 "$KIT" objdump -d "$OBJ" > "$WORK/dis.out" 2>&1 40 41 fn_body() { # $1 = symbol name -> stdout body 42 awk -v want="$1" ' 43 $0 ~ ("^[0-9a-f]+ <" want ">:") { in_fn = 1; next } 44 /^[0-9a-f]+ </ { in_fn = 0 } 45 in_fn { print } 46 ' "$WORK/dis.out" 47 } 48 49 fail() { 50 printf 'redundant_frame_sub FAILED: %s\n' "$1" >&2 51 printf ' --- disassembly ---\n' >&2 52 sed 's/^/ | /' "$WORK/dis.out" >&2 53 exit 1 54 } 55 56 B="$(fn_body _g)" 57 [ -n "$B" ] || B="$(fn_body g)" 58 [ -n "$B" ] || fail "g not found in disassembly" 59 60 # For each distinct constant K appearing in `add xN, x29, #K`, the address is 61 # built exactly once; a back-to-back recompute of the same K must NOT survive. 62 # Collect the K constants and assert no duplicates. 63 KS="$(printf '%s\n' "$B" | grep -oE 'add[[:space:]]+x[0-9]+, x29, #[0-9]+' \ 64 | grep -oE '#[0-9]+$' | sort)" 65 [ -n "$KS" ] || fail "expected at least one 'add xN, x29, #k' frame-address build" 66 67 DUP="$(printf '%s\n' "$KS" | uniq -d || true)" 68 if [ -n "$DUP" ]; then 69 fail "duplicate frame-address build survived (add xN, x29, #k recomputed for: $DUP)" 70 fi 71 72 # Sanity: the de-duplicated address must be reused via a register move, so the 73 # body should contain at least one `mov xN, xM` (the CSE'd copy) — confirms the 74 # peephole rewrote the recompute rather than the case simply not arising. 75 if ! printf '%s\n' "$B" | grep -Eq 'mov[[:space:]]+x[0-9]+, x[0-9]+'; then 76 fail "expected a 'mov xN, xM' reusing the CSE'd frame address" 77 fi 78 79 printf 'redundant_frame_sub: OK (no duplicate add xN,x29,#k; address reused via mov)\n'