commit f7a46307b63b2cbb7a3420afcb8acf2b9545fefd
parent b66e4c71e9981f1f2b7dcefb06bacbc836c35c3d
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 11 Jun 2026 07:19:14 -0700
perf(bench): add byte-identical identity gate for perf changes
scripts/perf_identity_gate.sh compiles the synthetic bench corpus + a
line-splice battery + diagnostics with a golden and a candidate kit and
asserts byte-identical output across every segment: -O0/-O1 objects, -g
DWARF, -S asm, -E text, diagnostics (stderr), and the linked executable.
This is the gate for the perf work (PERF.md invariant: the output diff,
not the sanitizer, catches premature arena reuse / uninit reads).
Diffstat:
1 file changed, 167 insertions(+), 0 deletions(-)
diff --git a/scripts/perf_identity_gate.sh b/scripts/perf_identity_gate.sh
@@ -0,0 +1,167 @@
+#!/usr/bin/env bash
+# perf_identity_gate.sh — prove a candidate kit produces BYTE-IDENTICAL output to a
+# golden kit across every pipeline segment. The gate for all perf changes
+# (PERF.md invariant): ASan can't see a premature arena reuse or an uninit read,
+# but the output diff can.
+#
+# Usage: GOLDEN=path/to/golden/kit CAND=path/to/candidate/kit bash scripts/perf_identity_gate.sh
+# optional: GATE_DIR (work dir, default build/perf-gate/work)
+# GATE_TARGET (kit -target, default host)
+# GATE_KEEP=1 (keep work dir on success)
+#
+# Covers: -O0/-O1 objects, -g DWARF, -S asm, -E preprocessor text, diagnostics
+# (stderr), the linked executable, and a backslash-newline line-splice battery
+# (mid-token / mid-string / leading / trailing / consecutive + a diagnostic whose
+# line crosses a splice — the lexer-specific gate from PERF.md).
+#
+# Exit 0 iff every category is byte-identical. Prints a per-category PASS/FAIL table.
+set -u
+
+GOLDEN="${GOLDEN:?set GOLDEN=path/to/golden/kit}"
+CAND="${CAND:?set CAND=path/to/candidate/kit}"
+GEN="${GEN:-scripts/cc_bench_gen.py}"
+GATE_DIR="${GATE_DIR:-build/perf-gate/work}"
+TARGET_FLAG=""
+[ -n "${GATE_TARGET:-}" ] && TARGET_FLAG="-target ${GATE_TARGET}"
+
+GOLDEN="$(cd "$(dirname "$GOLDEN")" && pwd)/$(basename "$GOLDEN")"
+CAND="$(cd "$(dirname "$CAND")" && pwd)/$(basename "$CAND")"
+
+# Sysroot for linking the host libc (match cc_bench.sh).
+SYSROOT="${KIT_SYSROOT:-}"
+if [ -z "$SYSROOT" ] && [ "$(uname -s)" = "Darwin" ] && command -v xcrun >/dev/null 2>&1; then
+ SYSROOT="$(xcrun --show-sdk-path 2>/dev/null || true)"
+fi
+SYSROOT_ARGS=(); [ -n "$SYSROOT" ] && SYSROOT_ARGS=(--sysroot "$SYSROOT")
+
+rm -rf "$GATE_DIR"; mkdir -p "$GATE_DIR"
+ROOT="$(pwd)"
+GEN="$ROOT/$GEN"
+
+fail=0
+declare -a ROWS
+
+record() { # name result
+ ROWS+=("$2 $1")
+ [ "$2" = FAIL ] && fail=1
+}
+
+# diff one output pair, recording PASS/FAIL
+cmpfile() { # name a b
+ if cmp -s "$2" "$3"; then record "$1" PASS; else record "$1" FAIL; fi
+}
+
+# Run both kits via the `cc` driver, compare a produced artifact (rel path in cwd).
+# args: name outpath -- ccargs...
+run_pair() { # name outrel ccargs...
+ local name="$1" outrel="$2"; shift 2
+ ( cd "$GATE_DIR" && "$GOLDEN" cc $TARGET_FLAG "$@" >/dev/null 2>g_$name.err; echo $? >g_$name.rc; [ -f "$outrel" ] && cp "$outrel" "g_$name.out" )
+ ( cd "$GATE_DIR" && "$CAND" cc $TARGET_FLAG "$@" >/dev/null 2>c_$name.err; echo $? >c_$name.rc; [ -f "$outrel" ] && cp "$outrel" "c_$name.out" )
+ cmpfile "$name.rc" "$GATE_DIR/g_$name.rc" "$GATE_DIR/c_$name.rc"
+ [ -f "$GATE_DIR/g_$name.out" ] && cmpfile "$name.obj" "$GATE_DIR/g_$name.out" "$GATE_DIR/c_$name.out"
+}
+
+# Run both kits via `cc` capturing STDOUT (for -E/-S) or STDERR (diagnostics).
+run_capture() { # name stream(out|err) ccargs...
+ local name="$1" stream="$2"; shift 2
+ if [ "$stream" = out ]; then
+ ( cd "$GATE_DIR" && "$GOLDEN" cc $TARGET_FLAG "$@" >g_$name.cap 2>/dev/null; echo $? >g_$name.rc )
+ ( cd "$GATE_DIR" && "$CAND" cc $TARGET_FLAG "$@" >c_$name.cap 2>/dev/null; echo $? >c_$name.rc )
+ else
+ ( cd "$GATE_DIR" && "$GOLDEN" cc $TARGET_FLAG "$@" >/dev/null 2>g_$name.cap; echo $? >g_$name.rc )
+ ( cd "$GATE_DIR" && "$CAND" cc $TARGET_FLAG "$@" >/dev/null 2>c_$name.cap; echo $? >c_$name.rc )
+ fi
+ cmpfile "$name.cap" "$GATE_DIR/g_$name.cap" "$GATE_DIR/c_$name.cap"
+ cmpfile "$name.rc" "$GATE_DIR/g_$name.rc" "$GATE_DIR/c_$name.rc"
+}
+
+# ---- 1. compile + preprocess axes (the synthetic corpus) -------------------
+# n's chosen to exercise each path broadly while staying fast for repeated runs.
+gen_axis() { python3 "$GEN" --axis "$1" --n "$2" --out "$GATE_DIR/$3" >/dev/null 2>&1; }
+
+# compile axes -> object (-O0, -O1, -g), asm (-S)
+for spec in "fn-count:1500:fn" "body-size:8000:body" "global-decl:3000:glob" \
+ "type-decl:1500:type" "locals-per-fn:3000:loc" "ref-density:3000:ref"; do
+ IFS=: read -r ax n tag <<<"$spec"
+ gen_axis "$ax" "$n" "$tag"
+ src="$tag/gen.c"
+ run_pair "${tag}_O0" "$tag/gen.o" -O0 -c "$src" -o "$tag/gen.o"
+ run_pair "${tag}_O1" "$tag/gen1.o" -O1 -c "$src" -o "$tag/gen1.o"
+ run_pair "${tag}_g" "$tag/geng.o" -O0 -g -c "$src" -o "$tag/geng.o"
+ run_capture "${tag}_S" out -O0 -S "$src" -o -
+done
+
+# preprocess axes -> -E text
+for spec in "pp-macro:2000:ppm" "pp-include:32:inc"; do
+ IFS=: read -r ax n tag <<<"$spec"
+ gen_axis "$ax" "$n" "$tag"
+ ( cd "$GATE_DIR/$tag" && "$GOLDEN" cc $TARGET_FLAG -E gen.c >../g_${tag}E.cap 2>/dev/null )
+ ( cd "$GATE_DIR/$tag" && "$CAND" cc $TARGET_FLAG -E gen.c >../c_${tag}E.cap 2>/dev/null )
+ cmpfile "${tag}E" "$GATE_DIR/g_${tag}E.cap" "$GATE_DIR/c_${tag}E.cap"
+done
+
+# ---- 2. line-splice battery (lexer-specific gate) --------------------------
+cat >"$GATE_DIR/splice.c" <<'EOF'
+/* mid-token splice */
+int ab\
+cd = 1;
+/* mid-string splice */
+const char *s = "he\
+llo";
+/* leading-continuation */
+\
+int x = 2;
+/* trailing whitespace after backslash is NOT a splice; keep plain */
+int y = 3;
+/* consecutive continuations */
+int z\
+\
+zz = 4;
+#define CAT(a,b) a##b
+int CAT(foo,\
+bar) = 5;
+EOF
+run_pair "splice_O0" splice.o -O0 -c splice.c -o splice.o
+run_capture "spliceE" out -E splice.c -o -
+
+# diagnostic whose line number falls across a splice (error on the post-splice line)
+cat >"$GATE_DIR/splicediag.c" <<'EOF'
+int good = 1;
+int wi\
+dgets = undefined_symbol_here;
+EOF
+run_capture "splice_diag" err -O0 -c splicediag.c -o /dev/null
+
+# ---- 3. general diagnostics (stderr text) ----------------------------------
+cat >"$GATE_DIR/diag.c" <<'EOF'
+int f(void) { return q; } /* undeclared */
+void g(void) { int x = "str"; } /* int from ptr */
+struct S { int a; }; struct S; int h(struct T*); /* incomplete */
+int dup; int dup; /* redef-ok (tentative) */
+int bad(void) { 1 + ; } /* parse error */
+EOF
+run_capture "diag" err -O0 -c diag.c -o /dev/null
+
+# ---- 4. linker (prebuild objects with golden; link with each; cmp exe) -----
+gen_axis obj-count 6 link
+( cd "$GATE_DIR/link"
+ for c in obj_*.c main.c; do "$GOLDEN" cc $TARGET_FLAG -O0 -c "$c" -o "${c%.c}.o" >/dev/null 2>&1; done
+ "$GOLDEN" ld $TARGET_FLAG obj_*.o main.o -l c "${SYSROOT_ARGS[@]}" -o g.exe >/dev/null 2>&1; echo $? >../g_link.rc
+ "$CAND" ld $TARGET_FLAG obj_*.o main.o -l c "${SYSROOT_ARGS[@]}" -o c.exe >/dev/null 2>&1; echo $? >../c_link.rc )
+cmpfile "link.exe" "$GATE_DIR/link/g.exe" "$GATE_DIR/link/c.exe"
+cmpfile "link.rc" "$GATE_DIR/g_link.rc" "$GATE_DIR/c_link.rc"
+
+# ---- report ----------------------------------------------------------------
+echo "=== perf identity gate ==="
+echo "golden: $GOLDEN"
+echo "cand: $CAND"
+printf '%s\n' "${ROWS[@]}" | sort
+echo "---"
+if [ "$fail" = 0 ]; then
+ echo "RESULT: PASS (byte-identical across all categories)"
+ [ -n "${GATE_KEEP:-}" ] || rm -rf "$GATE_DIR"
+ exit 0
+else
+ echo "RESULT: FAIL — diffs remain in $GATE_DIR (g_*/c_* pairs)"
+ exit 1
+fi