o1p_rider.sh (10824B)
1 #!/usr/bin/env bash 2 # Structural + correctness guards for the two -O1 operand-rider folds from 3 # doc/plan/O1-PATTERNS.md §2 (L7, L8). Both extend the optimizer Operand model 4 # with a rider field consumed only by a backend that advertises the matching 5 # NativeTarget capability hook (aa64 today; x64/rv64 keep the unfolded form): 6 # 7 # L7 shift rider on a register ALU operand 8 # a + (b<<k) -> add xD,xB,xS,lsl #k (no separate `lsl`) 9 # src/opt/pass_combine.c try_fold_shift_into_alu + aa64 aa_binop 10 # 11 # L8 extend rider on a load/store index operand 12 # a[i] (signed int i) -> ldr wD,[xB, wI, sxtw #2] (no sxtw+add) 13 # src/opt/pass_combine.c try_addr_synth (L8 block) + aa64 aa_emit_mem 14 # 15 # Each section is GREEN-only against the candidate kit ($KIT): it asserts the 16 # folded idiom is PRESENT and the unfolded scaffolding (separate lsl / sxtw+add) 17 # is GONE, that a multiply-used producer is NOT folded (negative case), and that 18 # the program computes the identical correct result at -O0 and -O1. When a 19 # baseline ($KIT_BASE) is present the structural assertions are additionally 20 # checked RED on it (the baseline must still emit the unfolded form). The folded 21 # forms are aa64-specific, so the structural disasm checks only run on an 22 # arm64/Darwin host; correctness runs wherever the host can execute the output. 23 set -uo pipefail 24 25 ROOT="$(cd "$(dirname "$0")/../.." && pwd)" 26 KIT="${KIT:-$ROOT/build/kit}" 27 KIT_BASE="${KIT_BASE:-$ROOT/build/kit_base}" 28 WORK="$ROOT/build/test/opt/o1p_rider" 29 mkdir -p "$WORK" 30 SYS="$(xcrun --sdk macosx --show-sdk-path 2>/dev/null || echo)" 31 32 # Structural disasm checks only make sense for the aa64 folded forms; gate them 33 # on an arm64 host (the only place this worktree natively runs the output too). 34 HOST_ARCH="$(uname -m 2>/dev/null || echo unknown)" 35 do_struct=1 36 case "$HOST_ARCH" in 37 arm64 | aarch64) ;; 38 *) do_struct=0 ;; 39 esac 40 41 fail() { 42 printf 'o1p_rider FAILED: %s\n' "$1" >&2 43 shift || true 44 for f in "$@"; do 45 printf ' --- %s ---\n' "$f" >&2 46 sed 's/^/ | /' "$f" >&2 47 done 48 exit 1 49 } 50 51 have_base=1 52 [ -x "$KIT_BASE" ] || { 53 have_base=0 54 printf 'o1p_rider: note: no %s — RED baseline checks skipped (GREEN-only)\n' \ 55 "$KIT_BASE" >&2 56 } 57 58 # count_re FILE REGEX -> number of matching disasm lines 59 count_re() { grep -cE "$2" "$1" || true; } 60 61 # ---------------------------------------------------------------------------- 62 # L7 — fold a single-use shift into the consuming ALU op 63 # ---------------------------------------------------------------------------- 64 echo "== L7 shift-into-ALU ==" 65 cat > "$WORK/l7.c" <<'EOF' 66 long add_shl(long a, long b) { return a + (b << 3); } 67 long sub_shl(long a, long b) { return a - (b << 2); } 68 long and_shl(long a, long b) { return a & (b << 1); } 69 EOF 70 cat > "$WORK/l7_neg.c" <<'EOF' 71 extern long g; 72 /* shift result used twice (stored AND added): must NOT fold; lsl stays. */ 73 long multi_use(long a, long b) { long s = b << 3; g = s; return a + s; } 74 EOF 75 76 if [ "$do_struct" = 1 ] && [ -n "$SYS" ]; then 77 "$KIT" cc -O1 --sysroot "$SYS" -c "$WORK/l7.c" -o "$WORK/l7.o" \ 78 > "$WORK/l7.cc" 2>&1 || fail "L7 candidate compile failed" "$WORK/l7.cc" 79 "$KIT" objdump -d "$WORK/l7.o" > "$WORK/l7.dis" 2>&1 80 # GREEN: at least three shifted-register ALU ops (add/sub/and ...,lsl #k), 81 # and zero standalone `lsl xN,xM,#k` left behind. 82 SR="$(count_re "$WORK/l7.dis" '\b(add|sub|and|orr|eor)\b[^;]*,[[:space:]]*lsl[[:space:]]+#[1-4]\b')" 83 # A STANDALONE lsl is the instruction mnemonic (`lsl xN,xM,#k`), distinct from 84 # the `lsl #k` shift modifier inside a shifted-register ALU op. Match the 85 # mnemonic position (after the tab, immediately followed by a register). 86 LSL="$(count_re "$WORK/l7.dis" $'\tlsl[[:space:]]+[wx][0-9]+,')" 87 [ "$SR" -ge 3 ] || fail "L7: expected >=3 shifted-reg ALU ops, got $SR" "$WORK/l7.dis" 88 [ "$LSL" -eq 0 ] || fail "L7: standalone lsl should be folded away, got $LSL" "$WORK/l7.dis" 89 printf ' L7: shifted-reg ALU ops=%s, standalone lsl=%s (GREEN)\n' "$SR" "$LSL" 90 91 # Negative: multiply-used shift keeps its lsl and a plain add. 92 "$KIT" cc -O1 --sysroot "$SYS" -c "$WORK/l7_neg.c" -o "$WORK/l7n.o" \ 93 > "$WORK/l7n.cc" 2>&1 || fail "L7 neg compile failed" "$WORK/l7n.cc" 94 "$KIT" objdump -d "$WORK/l7n.o" > "$WORK/l7n.dis" 2>&1 95 NEG_LSL="$(count_re "$WORK/l7n.dis" $'\tlsl[[:space:]]+[wx][0-9]+,')" 96 NEG_SR="$(count_re "$WORK/l7n.dis" '\badd\b[^;]*,[[:space:]]*lsl[[:space:]]+#[1-4]\b')" 97 [ "$NEG_LSL" -ge 1 ] || fail "L7 neg: multiply-used lsl must remain, got $NEG_LSL" "$WORK/l7n.dis" 98 [ "$NEG_SR" -eq 0 ] || fail "L7 neg: must NOT fold a multiply-used shift, got $NEG_SR" "$WORK/l7n.dis" 99 printf ' L7 neg: multiply-used lsl kept=%s, folded=%s (correctly NOT folded)\n' \ 100 "$NEG_LSL" "$NEG_SR" 101 102 if [ "$have_base" = 1 ]; then 103 "$KIT_BASE" cc -O1 --sysroot "$SYS" -c "$WORK/l7.c" -o "$WORK/l7.base.o" \ 104 > "$WORK/l7.base.cc" 2>&1 || fail "L7 baseline compile failed" "$WORK/l7.base.cc" 105 "$KIT_BASE" objdump -d "$WORK/l7.base.o" > "$WORK/l7.base.dis" 2>&1 106 BSR="$(count_re "$WORK/l7.base.dis" '\b(add|sub|and|orr|eor)\b[^;]*,[[:space:]]*lsl[[:space:]]+#[1-4]\b')" 107 [ "$BSR" -eq 0 ] || fail "L7 RED precondition: baseline already folds (sr=$BSR)" "$WORK/l7.base.dis" 108 printf ' L7: baseline shifted-reg ALU ops=%s (RED) -> candidate=%s (GREEN)\n' "$BSR" "$SR" 109 fi 110 else 111 printf ' L7: structural disasm checks skipped (non-aa64 host or no SDK)\n' 112 fi 113 114 # ---------------------------------------------------------------------------- 115 # L8 — fold a sxtw/uxtw index into the load/store addressing mode 116 # ---------------------------------------------------------------------------- 117 echo "== L8 extend-into-addr ==" 118 cat > "$WORK/l8.c" <<'EOF' 119 int load_signed(int* a, int i) { return a[i]; } 120 void store_signed(long* a, int i, long v) { a[i] = v; } 121 unsigned load_unsigned(unsigned* a, unsigned ui) { return a[ui]; } 122 EOF 123 cat > "$WORK/l8_neg.c" <<'EOF' 124 extern long g; 125 /* widened index used twice (stored AND used as index): the sxtw must remain. */ 126 long shared(long* a, int i) { long w = (long)i; g = w; return a[w]; } 127 EOF 128 129 if [ "$do_struct" = 1 ] && [ -n "$SYS" ]; then 130 "$KIT" cc -O1 --sysroot "$SYS" -c "$WORK/l8.c" -o "$WORK/l8.o" \ 131 > "$WORK/l8.cc" 2>&1 || fail "L8 candidate compile failed" "$WORK/l8.cc" 132 "$KIT" objdump -d "$WORK/l8.o" > "$WORK/l8.dis" 2>&1 133 # GREEN: the load/store carry the folded extended-register index, and there 134 # is no standalone `sxtw`/`uxtw` left to widen it. 135 FOLD="$(count_re "$WORK/l8.dis" '\b(ldr|str)\b[^;]*\[[^]]*,[[:space:]]*w[0-9]+,[[:space:]]*(sxtw|uxtw)[[:space:]]+#[0-3]\]')" 136 # Standalone sxtw/uxtw is the mnemonic `sxtw xN,wM` — distinct from the 137 # `sxtw #k` extend modifier inside a folded addressing mode. 138 STX="$(count_re "$WORK/l8.dis" $'\t(sxtw|uxtw)[[:space:]]+[wx][0-9]+,')" 139 [ "$FOLD" -ge 2 ] || fail "L8: expected >=2 folded sxtw/uxtw addressing modes, got $FOLD" "$WORK/l8.dis" 140 [ "$STX" -eq 0 ] || fail "L8: standalone sxtw/uxtw should be folded away, got $STX" "$WORK/l8.dis" 141 printf ' L8: folded extend addressing modes=%s, standalone sxtw/uxtw=%s (GREEN)\n' "$FOLD" "$STX" 142 143 # Negative: a widened value reused beyond the index keeps its sxtw. 144 "$KIT" cc -O1 --sysroot "$SYS" -c "$WORK/l8_neg.c" -o "$WORK/l8n.o" \ 145 > "$WORK/l8n.cc" 2>&1 || fail "L8 neg compile failed" "$WORK/l8n.cc" 146 "$KIT" objdump -d "$WORK/l8n.o" > "$WORK/l8n.dis" 2>&1 147 NEG_SXTW="$(count_re "$WORK/l8n.dis" $'\tsxtw[[:space:]]+x[0-9]+,')" 148 [ "$NEG_SXTW" -ge 1 ] || fail "L8 neg: reused sxtw must remain, got $NEG_SXTW" "$WORK/l8n.dis" 149 printf ' L8 neg: reused sxtw kept=%s (the stored widened value stays live)\n' "$NEG_SXTW" 150 151 if [ "$have_base" = 1 ]; then 152 "$KIT_BASE" cc -O1 --sysroot "$SYS" -c "$WORK/l8.c" -o "$WORK/l8.base.o" \ 153 > "$WORK/l8.base.cc" 2>&1 || fail "L8 baseline compile failed" "$WORK/l8.base.cc" 154 "$KIT_BASE" objdump -d "$WORK/l8.base.o" > "$WORK/l8.base.dis" 2>&1 155 BFOLD="$(count_re "$WORK/l8.base.dis" '\b(ldr|str)\b[^;]*\[[^]]*,[[:space:]]*w[0-9]+,[[:space:]]*(sxtw|uxtw)[[:space:]]+#[0-3]\]')" 156 [ "$BFOLD" -eq 0 ] || fail "L8 RED precondition: baseline already folds (fold=$BFOLD)" "$WORK/l8.base.dis" 157 printf ' L8: baseline folded addressing modes=%s (RED) -> candidate=%s (GREEN)\n' "$BFOLD" "$FOLD" 158 fi 159 else 160 printf ' L8: structural disasm checks skipped (non-aa64 host or no SDK)\n' 161 fi 162 163 # ---------------------------------------------------------------------------- 164 # Correctness: O0 must equal O1, exercising both folds (incl. negative index 165 # sign-extension and the multiply-used / value-reused non-fold paths). 166 # ---------------------------------------------------------------------------- 167 echo "== rider correctness (O0 == O1) ==" 168 cat > "$WORK/run.c" <<'EOF' 169 long g; 170 long add_shl(long a, long b) { return a + (b << 3); } 171 long sub_shl(long a, long b) { return a - (b << 2); } 172 long multi_use(long a, long b) { long s = b << 3; g = s; return a + s; } 173 long load_neg(long* a, int i) { return a[i]; } /* sxtw: i may be negative */ 174 unsigned load_u(unsigned* a, unsigned ui) { return a[ui]; } 175 long shared(long* a, int i) { long w = (long)i; g = w; return a[w]; } 176 int sum(int* a, int n) { int s = 0; for (int i = 0; i < n; i++) s += a[i] + (i << 2); return s; } 177 178 int main(void) { 179 long la[5] = {7, 8, 9, 11, 13}; 180 unsigned ua[5] = {100, 200, 300, 400, 500}; 181 int ia[6] = {1, 2, 3, 4, 5, 6}; 182 long acc = 0; 183 acc += add_shl(100, 5); /* 140 */ 184 acc += sub_shl(100, 5); /* 80 */ 185 acc += multi_use(100, 5); /* 140; g=40 */ 186 acc += g; /* +40 */ 187 acc += load_neg(la + 2, -2); /* la[0] = 7 (negative index) */ 188 acc += (long)load_u(ua, 3); /* 400 */ 189 acc += shared(la, 4); /* la[4]=13; g=4 */ 190 acc += g; /* +4 */ 191 acc += sum(ia, 6); /* 21 + 60 = 81 */ 192 /* total = 140+80+140+40+7+400+13+4+81 = 905 */ 193 return (int)(acc - 905); /* 0 on success */ 194 } 195 EOF 196 if [ -n "$SYS" ]; then 197 "$KIT" cc -O0 --sysroot "$SYS" "$WORK/run.c" -o "$WORK/run_o0" -lc \ 198 > "$WORK/run0.cc" 2>&1 || fail "rider O0 link failed" "$WORK/run0.cc" 199 "$KIT" cc -O1 --sysroot "$SYS" "$WORK/run.c" -o "$WORK/run_o1" -lc \ 200 > "$WORK/run1.cc" 2>&1 || fail "rider O1 link failed" "$WORK/run1.cc" 201 if [ "$do_struct" = 1 ]; then 202 "$WORK/run_o0"; rc0=$? 203 "$WORK/run_o1"; rc1=$? 204 [ "$rc0" -eq 0 ] || fail "rider O0 run wrong result rc=$rc0" 205 [ "$rc1" -eq 0 ] || fail "rider O1 run wrong result rc=$rc1" 206 [ "$rc0" -eq "$rc1" ] || fail "rider O0 ($rc0) != O1 ($rc1)" 207 printf ' rider: O0==O1 (rc=%s) — both folds correct incl. negative index\n' "$rc0" 208 else 209 printf ' rider: built O0/O1 OK; exec skipped (non-aa64 host)\n' 210 fi 211 else 212 printf ' rider: correctness skipped (no SDK)\n' 213 fi 214 215 echo "o1p_rider: OK"