kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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"