commit d74fc43d6c6e0e04eae4ea221404d54a769acf28
parent 2dadd8b01a1000932423b27d2625bf5b2ca8098d
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 16 Jun 2026 09:38:06 -0700
test/opt: o1p_rider structural+correctness guard for L7/L8 (O1-PATTERNS)
Adds test/opt/o1p_rider.sh. L7: a+(b<<k) emits add/sub/and xD,xB,xS,lsl #k with
no standalone lsl; L8: signed/unsigned int array access emits ldr/str [xB, wI,
sxtw/uxtw #scale] with no standalone sxtw/uxtw. Both sections RED-GREEN against
$KIT_BASE when present, include a multiply-used / value-reused negative case
that must NOT fold, and an O0==O1 correctness program covering negative-index
sign-extension. Structural disasm checks gate on an arm64 host (the aa64 folded
forms); correctness builds everywhere and execs on arm64. Run via
KIT=build/kit bash test/opt/o1p_rider.sh. Not wired into mk/test.mk.
Diffstat:
| A | test/opt/o1p_rider.sh | | | 215 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 215 insertions(+), 0 deletions(-)
diff --git a/test/opt/o1p_rider.sh b/test/opt/o1p_rider.sh
@@ -0,0 +1,215 @@
+#!/usr/bin/env bash
+# Structural + correctness guards for the two -O1 operand-rider folds from
+# doc/plan/O1-PATTERNS.md §2 (L7, L8). Both extend the optimizer Operand model
+# with a rider field consumed only by a backend that advertises the matching
+# NativeTarget capability hook (aa64 today; x64/rv64 keep the unfolded form):
+#
+# L7 shift rider on a register ALU operand
+# a + (b<<k) -> add xD,xB,xS,lsl #k (no separate `lsl`)
+# src/opt/pass_combine.c try_fold_shift_into_alu + aa64 aa_binop
+#
+# L8 extend rider on a load/store index operand
+# a[i] (signed int i) -> ldr wD,[xB, wI, sxtw #2] (no sxtw+add)
+# src/opt/pass_combine.c try_addr_synth (L8 block) + aa64 aa_emit_mem
+#
+# Each section is GREEN-only against the candidate kit ($KIT): it asserts the
+# folded idiom is PRESENT and the unfolded scaffolding (separate lsl / sxtw+add)
+# is GONE, that a multiply-used producer is NOT folded (negative case), and that
+# the program computes the identical correct result at -O0 and -O1. When a
+# baseline ($KIT_BASE) is present the structural assertions are additionally
+# checked RED on it (the baseline must still emit the unfolded form). The folded
+# forms are aa64-specific, so the structural disasm checks only run on an
+# arm64/Darwin host; correctness runs wherever the host can execute the output.
+set -uo pipefail
+
+ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
+KIT="${KIT:-$ROOT/build/kit}"
+KIT_BASE="${KIT_BASE:-$ROOT/build/kit_base}"
+WORK="$ROOT/build/test/opt/o1p_rider"
+mkdir -p "$WORK"
+SYS="$(xcrun --sdk macosx --show-sdk-path 2>/dev/null || echo)"
+
+# Structural disasm checks only make sense for the aa64 folded forms; gate them
+# on an arm64 host (the only place this worktree natively runs the output too).
+HOST_ARCH="$(uname -m 2>/dev/null || echo unknown)"
+do_struct=1
+case "$HOST_ARCH" in
+ arm64 | aarch64) ;;
+ *) do_struct=0 ;;
+esac
+
+fail() {
+ printf 'o1p_rider FAILED: %s\n' "$1" >&2
+ shift || true
+ for f in "$@"; do
+ printf ' --- %s ---\n' "$f" >&2
+ sed 's/^/ | /' "$f" >&2
+ done
+ exit 1
+}
+
+have_base=1
+[ -x "$KIT_BASE" ] || {
+ have_base=0
+ printf 'o1p_rider: note: no %s — RED baseline checks skipped (GREEN-only)\n' \
+ "$KIT_BASE" >&2
+}
+
+# count_re FILE REGEX -> number of matching disasm lines
+count_re() { grep -cE "$2" "$1" || true; }
+
+# ----------------------------------------------------------------------------
+# L7 — fold a single-use shift into the consuming ALU op
+# ----------------------------------------------------------------------------
+echo "== L7 shift-into-ALU =="
+cat > "$WORK/l7.c" <<'EOF'
+long add_shl(long a, long b) { return a + (b << 3); }
+long sub_shl(long a, long b) { return a - (b << 2); }
+long and_shl(long a, long b) { return a & (b << 1); }
+EOF
+cat > "$WORK/l7_neg.c" <<'EOF'
+extern long g;
+/* shift result used twice (stored AND added): must NOT fold; lsl stays. */
+long multi_use(long a, long b) { long s = b << 3; g = s; return a + s; }
+EOF
+
+if [ "$do_struct" = 1 ] && [ -n "$SYS" ]; then
+ "$KIT" cc -O1 --sysroot "$SYS" -c "$WORK/l7.c" -o "$WORK/l7.o" \
+ > "$WORK/l7.cc" 2>&1 || fail "L7 candidate compile failed" "$WORK/l7.cc"
+ "$KIT" objdump -d "$WORK/l7.o" > "$WORK/l7.dis" 2>&1
+ # GREEN: at least three shifted-register ALU ops (add/sub/and ...,lsl #k),
+ # and zero standalone `lsl xN,xM,#k` left behind.
+ SR="$(count_re "$WORK/l7.dis" '\b(add|sub|and|orr|eor)\b[^;]*,[[:space:]]*lsl[[:space:]]+#[1-4]\b')"
+ # A STANDALONE lsl is the instruction mnemonic (`lsl xN,xM,#k`), distinct from
+ # the `lsl #k` shift modifier inside a shifted-register ALU op. Match the
+ # mnemonic position (after the tab, immediately followed by a register).
+ LSL="$(count_re "$WORK/l7.dis" $'\tlsl[[:space:]]+[wx][0-9]+,')"
+ [ "$SR" -ge 3 ] || fail "L7: expected >=3 shifted-reg ALU ops, got $SR" "$WORK/l7.dis"
+ [ "$LSL" -eq 0 ] || fail "L7: standalone lsl should be folded away, got $LSL" "$WORK/l7.dis"
+ printf ' L7: shifted-reg ALU ops=%s, standalone lsl=%s (GREEN)\n' "$SR" "$LSL"
+
+ # Negative: multiply-used shift keeps its lsl and a plain add.
+ "$KIT" cc -O1 --sysroot "$SYS" -c "$WORK/l7_neg.c" -o "$WORK/l7n.o" \
+ > "$WORK/l7n.cc" 2>&1 || fail "L7 neg compile failed" "$WORK/l7n.cc"
+ "$KIT" objdump -d "$WORK/l7n.o" > "$WORK/l7n.dis" 2>&1
+ NEG_LSL="$(count_re "$WORK/l7n.dis" $'\tlsl[[:space:]]+[wx][0-9]+,')"
+ NEG_SR="$(count_re "$WORK/l7n.dis" '\badd\b[^;]*,[[:space:]]*lsl[[:space:]]+#[1-4]\b')"
+ [ "$NEG_LSL" -ge 1 ] || fail "L7 neg: multiply-used lsl must remain, got $NEG_LSL" "$WORK/l7n.dis"
+ [ "$NEG_SR" -eq 0 ] || fail "L7 neg: must NOT fold a multiply-used shift, got $NEG_SR" "$WORK/l7n.dis"
+ printf ' L7 neg: multiply-used lsl kept=%s, folded=%s (correctly NOT folded)\n' \
+ "$NEG_LSL" "$NEG_SR"
+
+ if [ "$have_base" = 1 ]; then
+ "$KIT_BASE" cc -O1 --sysroot "$SYS" -c "$WORK/l7.c" -o "$WORK/l7.base.o" \
+ > "$WORK/l7.base.cc" 2>&1 || fail "L7 baseline compile failed" "$WORK/l7.base.cc"
+ "$KIT_BASE" objdump -d "$WORK/l7.base.o" > "$WORK/l7.base.dis" 2>&1
+ BSR="$(count_re "$WORK/l7.base.dis" '\b(add|sub|and|orr|eor)\b[^;]*,[[:space:]]*lsl[[:space:]]+#[1-4]\b')"
+ [ "$BSR" -eq 0 ] || fail "L7 RED precondition: baseline already folds (sr=$BSR)" "$WORK/l7.base.dis"
+ printf ' L7: baseline shifted-reg ALU ops=%s (RED) -> candidate=%s (GREEN)\n' "$BSR" "$SR"
+ fi
+else
+ printf ' L7: structural disasm checks skipped (non-aa64 host or no SDK)\n'
+fi
+
+# ----------------------------------------------------------------------------
+# L8 — fold a sxtw/uxtw index into the load/store addressing mode
+# ----------------------------------------------------------------------------
+echo "== L8 extend-into-addr =="
+cat > "$WORK/l8.c" <<'EOF'
+int load_signed(int* a, int i) { return a[i]; }
+void store_signed(long* a, int i, long v) { a[i] = v; }
+unsigned load_unsigned(unsigned* a, unsigned ui) { return a[ui]; }
+EOF
+cat > "$WORK/l8_neg.c" <<'EOF'
+extern long g;
+/* widened index used twice (stored AND used as index): the sxtw must remain. */
+long shared(long* a, int i) { long w = (long)i; g = w; return a[w]; }
+EOF
+
+if [ "$do_struct" = 1 ] && [ -n "$SYS" ]; then
+ "$KIT" cc -O1 --sysroot "$SYS" -c "$WORK/l8.c" -o "$WORK/l8.o" \
+ > "$WORK/l8.cc" 2>&1 || fail "L8 candidate compile failed" "$WORK/l8.cc"
+ "$KIT" objdump -d "$WORK/l8.o" > "$WORK/l8.dis" 2>&1
+ # GREEN: the load/store carry the folded extended-register index, and there
+ # is no standalone `sxtw`/`uxtw` left to widen it.
+ FOLD="$(count_re "$WORK/l8.dis" '\b(ldr|str)\b[^;]*\[[^]]*,[[:space:]]*w[0-9]+,[[:space:]]*(sxtw|uxtw)[[:space:]]+#[0-3]\]')"
+ # Standalone sxtw/uxtw is the mnemonic `sxtw xN,wM` — distinct from the
+ # `sxtw #k` extend modifier inside a folded addressing mode.
+ STX="$(count_re "$WORK/l8.dis" $'\t(sxtw|uxtw)[[:space:]]+[wx][0-9]+,')"
+ [ "$FOLD" -ge 2 ] || fail "L8: expected >=2 folded sxtw/uxtw addressing modes, got $FOLD" "$WORK/l8.dis"
+ [ "$STX" -eq 0 ] || fail "L8: standalone sxtw/uxtw should be folded away, got $STX" "$WORK/l8.dis"
+ printf ' L8: folded extend addressing modes=%s, standalone sxtw/uxtw=%s (GREEN)\n' "$FOLD" "$STX"
+
+ # Negative: a widened value reused beyond the index keeps its sxtw.
+ "$KIT" cc -O1 --sysroot "$SYS" -c "$WORK/l8_neg.c" -o "$WORK/l8n.o" \
+ > "$WORK/l8n.cc" 2>&1 || fail "L8 neg compile failed" "$WORK/l8n.cc"
+ "$KIT" objdump -d "$WORK/l8n.o" > "$WORK/l8n.dis" 2>&1
+ NEG_SXTW="$(count_re "$WORK/l8n.dis" $'\tsxtw[[:space:]]+x[0-9]+,')"
+ [ "$NEG_SXTW" -ge 1 ] || fail "L8 neg: reused sxtw must remain, got $NEG_SXTW" "$WORK/l8n.dis"
+ printf ' L8 neg: reused sxtw kept=%s (the stored widened value stays live)\n' "$NEG_SXTW"
+
+ if [ "$have_base" = 1 ]; then
+ "$KIT_BASE" cc -O1 --sysroot "$SYS" -c "$WORK/l8.c" -o "$WORK/l8.base.o" \
+ > "$WORK/l8.base.cc" 2>&1 || fail "L8 baseline compile failed" "$WORK/l8.base.cc"
+ "$KIT_BASE" objdump -d "$WORK/l8.base.o" > "$WORK/l8.base.dis" 2>&1
+ BFOLD="$(count_re "$WORK/l8.base.dis" '\b(ldr|str)\b[^;]*\[[^]]*,[[:space:]]*w[0-9]+,[[:space:]]*(sxtw|uxtw)[[:space:]]+#[0-3]\]')"
+ [ "$BFOLD" -eq 0 ] || fail "L8 RED precondition: baseline already folds (fold=$BFOLD)" "$WORK/l8.base.dis"
+ printf ' L8: baseline folded addressing modes=%s (RED) -> candidate=%s (GREEN)\n' "$BFOLD" "$FOLD"
+ fi
+else
+ printf ' L8: structural disasm checks skipped (non-aa64 host or no SDK)\n'
+fi
+
+# ----------------------------------------------------------------------------
+# Correctness: O0 must equal O1, exercising both folds (incl. negative index
+# sign-extension and the multiply-used / value-reused non-fold paths).
+# ----------------------------------------------------------------------------
+echo "== rider correctness (O0 == O1) =="
+cat > "$WORK/run.c" <<'EOF'
+long g;
+long add_shl(long a, long b) { return a + (b << 3); }
+long sub_shl(long a, long b) { return a - (b << 2); }
+long multi_use(long a, long b) { long s = b << 3; g = s; return a + s; }
+long load_neg(long* a, int i) { return a[i]; } /* sxtw: i may be negative */
+unsigned load_u(unsigned* a, unsigned ui) { return a[ui]; }
+long shared(long* a, int i) { long w = (long)i; g = w; return a[w]; }
+int sum(int* a, int n) { int s = 0; for (int i = 0; i < n; i++) s += a[i] + (i << 2); return s; }
+
+int main(void) {
+ long la[5] = {7, 8, 9, 11, 13};
+ unsigned ua[5] = {100, 200, 300, 400, 500};
+ int ia[6] = {1, 2, 3, 4, 5, 6};
+ long acc = 0;
+ acc += add_shl(100, 5); /* 140 */
+ acc += sub_shl(100, 5); /* 80 */
+ acc += multi_use(100, 5); /* 140; g=40 */
+ acc += g; /* +40 */
+ acc += load_neg(la + 2, -2); /* la[0] = 7 (negative index) */
+ acc += (long)load_u(ua, 3); /* 400 */
+ acc += shared(la, 4); /* la[4]=13; g=4 */
+ acc += g; /* +4 */
+ acc += sum(ia, 6); /* 21 + 60 = 81 */
+ /* total = 140+80+140+40+7+400+13+4+81 = 905 */
+ return (int)(acc - 905); /* 0 on success */
+}
+EOF
+if [ -n "$SYS" ]; then
+ "$KIT" cc -O0 --sysroot "$SYS" "$WORK/run.c" -o "$WORK/run_o0" -lc \
+ > "$WORK/run0.cc" 2>&1 || fail "rider O0 link failed" "$WORK/run0.cc"
+ "$KIT" cc -O1 --sysroot "$SYS" "$WORK/run.c" -o "$WORK/run_o1" -lc \
+ > "$WORK/run1.cc" 2>&1 || fail "rider O1 link failed" "$WORK/run1.cc"
+ if [ "$do_struct" = 1 ]; then
+ "$WORK/run_o0"; rc0=$?
+ "$WORK/run_o1"; rc1=$?
+ [ "$rc0" -eq 0 ] || fail "rider O0 run wrong result rc=$rc0"
+ [ "$rc1" -eq 0 ] || fail "rider O1 run wrong result rc=$rc1"
+ [ "$rc0" -eq "$rc1" ] || fail "rider O0 ($rc0) != O1 ($rc1)"
+ printf ' rider: O0==O1 (rc=%s) — both folds correct incl. negative index\n' "$rc0"
+ else
+ printf ' rider: built O0/O1 OK; exec skipped (non-aa64 host)\n'
+ fi
+else
+ printf ' rider: correctness skipped (no SDK)\n'
+fi
+
+echo "o1p_rider: OK"