kit

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

perf_identity_gate.sh (6907B)


      1 #!/usr/bin/env bash
      2 # perf_identity_gate.sh — prove a candidate kit produces BYTE-IDENTICAL output to a
      3 # golden kit across every pipeline segment. The gate for all perf changes
      4 # (PERF.md invariant): ASan can't see a premature arena reuse or an uninit read,
      5 # but the output diff can.
      6 #
      7 # Usage: GOLDEN=path/to/golden/kit CAND=path/to/candidate/kit bash scripts/perf_identity_gate.sh
      8 #   optional: GATE_DIR (work dir, default build/perf-gate/work)
      9 #             GATE_TARGET (kit -target, default host)
     10 #             GATE_KEEP=1 (keep work dir on success)
     11 #
     12 # Covers: -O0/-O1 objects, -g DWARF, -S asm, -E preprocessor text, diagnostics
     13 # (stderr), the linked executable, and a backslash-newline line-splice battery
     14 # (mid-token / mid-string / leading / trailing / consecutive + a diagnostic whose
     15 # line crosses a splice — the lexer-specific gate from PERF.md).
     16 #
     17 # Exit 0 iff every category is byte-identical. Prints a per-category PASS/FAIL table.
     18 set -u
     19 
     20 GOLDEN="${GOLDEN:?set GOLDEN=path/to/golden/kit}"
     21 CAND="${CAND:?set CAND=path/to/candidate/kit}"
     22 GEN="${GEN:-scripts/cc_bench_gen.py}"
     23 GATE_DIR="${GATE_DIR:-build/perf-gate/work}"
     24 TARGET_FLAG=""
     25 [ -n "${GATE_TARGET:-}" ] && TARGET_FLAG="-target ${GATE_TARGET}"
     26 
     27 GOLDEN="$(cd "$(dirname "$GOLDEN")" && pwd)/$(basename "$GOLDEN")"
     28 CAND="$(cd "$(dirname "$CAND")" && pwd)/$(basename "$CAND")"
     29 
     30 # Sysroot for linking the host libc (match cc_bench.sh).
     31 SYSROOT="${KIT_SYSROOT:-}"
     32 if [ -z "$SYSROOT" ] && [ "$(uname -s)" = "Darwin" ] && command -v xcrun >/dev/null 2>&1; then
     33   SYSROOT="$(xcrun --show-sdk-path 2>/dev/null || true)"
     34 fi
     35 SYSROOT_ARGS=(); [ -n "$SYSROOT" ] && SYSROOT_ARGS=(--sysroot "$SYSROOT")
     36 
     37 rm -rf "$GATE_DIR"; mkdir -p "$GATE_DIR"
     38 ROOT="$(pwd)"
     39 GEN="$ROOT/$GEN"
     40 
     41 fail=0
     42 declare -a ROWS
     43 
     44 record() { # name result
     45   ROWS+=("$2  $1")
     46   [ "$2" = FAIL ] && fail=1
     47 }
     48 
     49 # diff one output pair, recording PASS/FAIL
     50 cmpfile() { # name a b
     51   if cmp -s "$2" "$3"; then record "$1" PASS; else record "$1" FAIL; fi
     52 }
     53 
     54 # Run both kits via the `cc` driver, compare a produced artifact (rel path in cwd).
     55 # args: name  outpath  -- ccargs...
     56 run_pair() { # name outrel  ccargs...
     57   local name="$1" outrel="$2"; shift 2
     58   ( cd "$GATE_DIR" && "$GOLDEN" cc $TARGET_FLAG "$@" >/dev/null 2>g_$name.err; echo $? >g_$name.rc; [ -f "$outrel" ] && cp "$outrel" "g_$name.out" )
     59   ( cd "$GATE_DIR" && "$CAND"   cc $TARGET_FLAG "$@" >/dev/null 2>c_$name.err; echo $? >c_$name.rc; [ -f "$outrel" ] && cp "$outrel" "c_$name.out" )
     60   cmpfile "$name.rc"  "$GATE_DIR/g_$name.rc"  "$GATE_DIR/c_$name.rc"
     61   [ -f "$GATE_DIR/g_$name.out" ] && cmpfile "$name.obj" "$GATE_DIR/g_$name.out" "$GATE_DIR/c_$name.out"
     62 }
     63 
     64 # Run both kits via `cc` capturing STDOUT (for -E/-S) or STDERR (diagnostics).
     65 run_capture() { # name stream(out|err) ccargs...
     66   local name="$1" stream="$2"; shift 2
     67   if [ "$stream" = out ]; then
     68     ( cd "$GATE_DIR" && "$GOLDEN" cc $TARGET_FLAG "$@" >g_$name.cap 2>/dev/null; echo $? >g_$name.rc )
     69     ( cd "$GATE_DIR" && "$CAND"   cc $TARGET_FLAG "$@" >c_$name.cap 2>/dev/null; echo $? >c_$name.rc )
     70   else
     71     ( cd "$GATE_DIR" && "$GOLDEN" cc $TARGET_FLAG "$@" >/dev/null 2>g_$name.cap; echo $? >g_$name.rc )
     72     ( cd "$GATE_DIR" && "$CAND"   cc $TARGET_FLAG "$@" >/dev/null 2>c_$name.cap; echo $? >c_$name.rc )
     73   fi
     74   cmpfile "$name.cap" "$GATE_DIR/g_$name.cap" "$GATE_DIR/c_$name.cap"
     75   cmpfile "$name.rc"  "$GATE_DIR/g_$name.rc"  "$GATE_DIR/c_$name.rc"
     76 }
     77 
     78 # ---- 1. compile + preprocess axes (the synthetic corpus) -------------------
     79 # n's chosen to exercise each path broadly while staying fast for repeated runs.
     80 gen_axis() { python3 "$GEN" --axis "$1" --n "$2" --out "$GATE_DIR/$3" >/dev/null 2>&1; }
     81 
     82 # compile axes -> object (-O0, -O1, -g), asm (-S)
     83 for spec in "fn-count:1500:fn" "body-size:8000:body" "global-decl:3000:glob" \
     84             "type-decl:1500:type" "locals-per-fn:3000:loc" "ref-density:3000:ref"; do
     85   IFS=: read -r ax n tag <<<"$spec"
     86   gen_axis "$ax" "$n" "$tag"
     87   src="$tag/gen.c"
     88   run_pair "${tag}_O0" "$tag/gen.o"  -O0 -c "$src" -o "$tag/gen.o"
     89   run_pair "${tag}_O1" "$tag/gen1.o" -O1 -c "$src" -o "$tag/gen1.o"
     90   run_pair "${tag}_g"  "$tag/geng.o" -O0 -g -c "$src" -o "$tag/geng.o"
     91   run_capture "${tag}_S" out -O0 -S "$src" -o -
     92 done
     93 
     94 # preprocess axes -> -E text
     95 for spec in "pp-macro:2000:ppm" "pp-include:32:inc"; do
     96   IFS=: read -r ax n tag <<<"$spec"
     97   gen_axis "$ax" "$n" "$tag"
     98   ( cd "$GATE_DIR/$tag" && "$GOLDEN" cc $TARGET_FLAG -E gen.c >../g_${tag}E.cap 2>/dev/null )
     99   ( cd "$GATE_DIR/$tag" && "$CAND"   cc $TARGET_FLAG -E gen.c >../c_${tag}E.cap 2>/dev/null )
    100   cmpfile "${tag}E" "$GATE_DIR/g_${tag}E.cap" "$GATE_DIR/c_${tag}E.cap"
    101 done
    102 
    103 # ---- 2. line-splice battery (lexer-specific gate) --------------------------
    104 cat >"$GATE_DIR/splice.c" <<'EOF'
    105 /* mid-token splice */
    106 int ab\
    107 cd = 1;
    108 /* mid-string splice */
    109 const char *s = "he\
    110 llo";
    111 /* leading-continuation */
    112 \
    113 int x = 2;
    114 /* trailing whitespace after backslash is NOT a splice; keep plain */
    115 int y = 3;
    116 /* consecutive continuations */
    117 int z\
    118 \
    119 zz = 4;
    120 #define CAT(a,b) a##b
    121 int CAT(foo,\
    122 bar) = 5;
    123 EOF
    124 run_pair "splice_O0" splice.o -O0 -c splice.c -o splice.o
    125 run_capture "spliceE" out -E splice.c -o -
    126 
    127 # diagnostic whose line number falls across a splice (error on the post-splice line)
    128 cat >"$GATE_DIR/splicediag.c" <<'EOF'
    129 int good = 1;
    130 int wi\
    131 dgets = undefined_symbol_here;
    132 EOF
    133 run_capture "splice_diag" err -O0 -c splicediag.c -o /dev/null
    134 
    135 # ---- 3. general diagnostics (stderr text) ----------------------------------
    136 cat >"$GATE_DIR/diag.c" <<'EOF'
    137 int f(void) { return q; }              /* undeclared */
    138 void g(void) { int x = "str"; }        /* int from ptr */
    139 struct S { int a; }; struct S; int h(struct T*); /* incomplete */
    140 int dup; int dup;                      /* redef-ok (tentative) */
    141 int bad(void) { 1 + ; }                /* parse error */
    142 EOF
    143 run_capture "diag" err -O0 -c diag.c -o /dev/null
    144 
    145 # ---- 4. linker (prebuild objects with golden; link with each; cmp exe) -----
    146 gen_axis obj-count 6 link
    147 ( cd "$GATE_DIR/link"
    148   for c in obj_*.c main.c; do "$GOLDEN" cc $TARGET_FLAG -O0 -c "$c" -o "${c%.c}.o" >/dev/null 2>&1; done
    149   "$GOLDEN" ld $TARGET_FLAG obj_*.o main.o -l c "${SYSROOT_ARGS[@]}" -o g.exe >/dev/null 2>&1; echo $? >../g_link.rc
    150   "$CAND"   ld $TARGET_FLAG obj_*.o main.o -l c "${SYSROOT_ARGS[@]}" -o c.exe >/dev/null 2>&1; echo $? >../c_link.rc )
    151 cmpfile "link.exe" "$GATE_DIR/link/g.exe" "$GATE_DIR/link/c.exe"
    152 cmpfile "link.rc"  "$GATE_DIR/g_link.rc" "$GATE_DIR/c_link.rc"
    153 
    154 # ---- report ----------------------------------------------------------------
    155 echo "=== perf identity gate ==="
    156 echo "golden: $GOLDEN"
    157 echo "cand:   $CAND"
    158 printf '%s\n' "${ROWS[@]}" | sort
    159 echo "---"
    160 if [ "$fail" = 0 ]; then
    161   echo "RESULT: PASS (byte-identical across all categories)"
    162   [ -n "${GATE_KEEP:-}" ] || rm -rf "$GATE_DIR"
    163   exit 0
    164 else
    165   echo "RESULT: FAIL — diffs remain in $GATE_DIR (g_*/c_* pairs)"
    166   exit 1
    167 fi