kit

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

o1_remat.sh (4742B)


      1 #!/usr/bin/env bash
      2 # Structural check for -O1 rematerialization (doc/plan/O1.md W2).
      3 #
      4 # At a use of a spilled value whose single def is cheaper to recompute than to
      5 # reload, kit recomputes at the use instead of reloading from the slot, and
      6 # drops the spill store entirely when every use rematerializes. The conservative
      7 # v1 set is input-less producers whose recompute is <= reload cost:
      8 #   - IR_LOAD_IMM whose immediate is <= 2 instructions (movz / movz;movk);
      9 #   - IR_ADDR_OF of a local (one `add` off the stable frame base).
     10 #
     11 # The probe drives a small marker constant (KMARK, one movz) and the address of
     12 # a local (one `add xN, x29, #off`) to spill: both are low-frequency but live
     13 # across two trailing calls, while sixteen high-frequency array values win every
     14 # callee-save register. After W2 each spilled use of the constant / address must
     15 # RECOMPUTE (a `movz #KMARK` / a repeated `add xN, x29, #off`) rather than reload
     16 # from a slot, and the spill store must be gone (no `str` of the recomputed
     17 # value into a frame slot it is never read from).
     18 #
     19 # Pinned on aarch64 (the reference backend) where the mnemonics are stable.
     20 set -euo pipefail
     21 
     22 ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
     23 KIT="${KIT:-$ROOT/build/kit}"
     24 WORK="$ROOT/build/test/opt/o1_remat"
     25 mkdir -p "$WORK"
     26 
     27 SRC="$WORK/case.c"
     28 cat > "$SRC" <<'EOF'
     29 extern void use_ptr(int *);
     30 extern void sink(long);
     31 extern void barrier(void);
     32 
     33 /* KMARK = 0x4321 is a single-movz constant; &loc is one add off the frame base.
     34  * Both are low-frequency but live across the two trailing calls, so the
     35  * sixteen high-frequency array values displace them from the callee-saves and
     36  * they spill. W2 should recompute both at their uses instead of reloading. */
     37 long remat_probe(long *arr) {
     38   int loc;
     39   const long KMARK = 0x4321;
     40   long a=arr[0],b=arr[1],c=arr[2],d=arr[3],e=arr[4],f=arr[5],g=arr[6],h=arr[7];
     41   long i=arr[8],j=arr[9],k=arr[10],l=arr[11],m=arr[12],o=arr[13],p=arr[14],q=arr[15];
     42   barrier();
     43   long s = a*a+b*b+c*c+d*d+e*e+f*f+g*g+h*h+i*i+j*j+k*k+l*l+m*m+o*o+p*p+q*q;
     44   s += a*b+b*c+c*d+d*e+e*f+f*g+g*h+h*i+i*j+j*k+k*l+l*m+m*o+o*p+p*q+q*a;
     45   s += a+b+c+d+e+f+g+h+i+j+k+l+m+o+p+q;
     46   use_ptr(&loc);    /* use 1: address of a local as a call arg */
     47   sink(KMARK);      /* use 2: the constant as a call arg */
     48   use_ptr(&loc);    /* use 3: the address again */
     49   return s + KMARK; /* use 4: the constant again */
     50 }
     51 EOF
     52 
     53 OBJ="$WORK/case.o"
     54 "$KIT" cc -target aarch64-linux-gnu -O1 -std=c11 -c "$SRC" \
     55   -o "$OBJ" > "$WORK/cc.out" 2>&1
     56 "$KIT" objdump -d "$OBJ" > "$WORK/dis.out" 2>&1
     57 
     58 BODY="$(awk '
     59   /<remat_probe>:/ { in_fn = 1; next }
     60   in_fn && /^[0-9a-f]+ </ { exit }
     61   in_fn { print }
     62 ' "$WORK/dis.out")"
     63 
     64 fail() {
     65   printf 'o1_remat FAILED: %s\n' "$1" >&2
     66   printf '  --- remat_probe disassembly ---\n' >&2
     67   printf '%s\n' "$BODY" | sed 's/^/  | /' >&2
     68   exit 1
     69 }
     70 
     71 [ -n "$BODY" ] || fail "remat_probe not found in disassembly"
     72 
     73 # (1) Constant rematerialization: KMARK (0x4321) is recomputed at each of its
     74 #     two uses. A reload-based baseline would materialize it once and reload via
     75 #     ldr; remat emits the `movz #0x4321` again at every use -> count >= 2.
     76 movz_n="$(printf '%s\n' "$BODY" | grep -cE 'movz[ \t]+x[0-9]+, 0x4321' || true)"
     77 [ "$movz_n" -ge 2 ] || fail "constant KMARK not rematerialized (movz 0x4321 x$movz_n, expected >=2)"
     78 
     79 # (2) Address rematerialization: the same `add xN, x29, #off` recompute appears
     80 #     at both address uses. Find the most common add-off-frame-base offset and
     81 #     require it to recur (a single def would appear once).
     82 addr_off="$(printf '%s\n' "$BODY" \
     83   | grep -oE 'add x[0-9]+, x29, #[0-9]+$' \
     84   | grep -oE '#[0-9]+$' | sort | uniq -c | sort -rn | head -1 | awk '{print $2}')"
     85 [ -n "$addr_off" ] || fail "no 'add xN, x29, #off' (addr_of local) recompute found"
     86 addr_n="$(printf '%s\n' "$BODY" | grep -cE "add x[0-9]+, x29, ${addr_off}\$" || true)"
     87 [ "$addr_n" -ge 2 ] || fail "local address not rematerialized (add x29,${addr_off} x$addr_n, expected >=2)"
     88 
     89 # (3) Store-drop: the rematerialized constant and address must leave no spill
     90 #     store behind. With every use rematerialized there must be no `str` of the
     91 #     KMARK value or the recomputed address into a slot. We approximate by
     92 #     requiring that the marker constant is never stored: a kept spill store
     93 #     would be `movz xN,#0x4321 ; str xN,[x29,#..]` -- i.e. a movz immediately
     94 #     followed by a str of the same register. None should remain.
     95 if printf '%s\n' "$BODY" | grep -A1 -E 'movz[ \t]+x[0-9]+, 0x4321' \
     96      | grep -qE 'str[ \t]+x[0-9]+, \[x29'; then
     97   fail "spill store for rematerialized constant survived (movz;str)"
     98 fi
     99 
    100 printf 'o1_remat: OK (const movz x%s, addr add-off-x29 x%s, spill stores dropped)\n' \
    101   "$movz_n" "$addr_n"