kit

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

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:
Ascripts/perf_identity_gate.sh | 167+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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