A benchmark workload or the self-hosted compiler that fails to build
with both checkouts now fails the gate, and so does a run in which no
program built with both. Before this, a run whose builds all failed the
same way, such as with a helper of another version, reported PASS. A
build error now prints in full under its program; its first line is
often a warning.
The zero-cost gate's I3 check (a fully typed bench workload defines no
dynamic-tier runtime symbol) runs only when someone runs the gate, and on
Darwin it never matched: Mach-O names carry a leading underscore, so no
symbol had a DYNAMIC_TIER_PREFIXES prefix.
DYNAMIC_WORKLOADS and the nm function-name reader move to
zero-cost-compare.mjs, which now drops the Mach-O underscore. A harness
test builds every bench program on Linux and fails when a workload outside
DYNAMIC_WORKLOADS links a dynamic-tier symbol, or when a listed workload
no longer links any, so the list shrinks as leaks close.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The gate read an empty symbol list when nm could not start, was killed by
a signal, or exited with a non-zero status, so the dynamic-tier symbol
check and the executable comparison passed without any symbols. nmOutput
now throws with the binary, the reason and nm's stderr.
scripts/zero-cost-gate.mjs compiles a fixed good-code set (the runtime
benchmark workloads, the self-hosted compiler, and the TypeScript corpus)
with a baseline and a candidate checkout. It requires byte-identical IR,
LLVM and release executables, and names the first differing IR function,
LLVM function or executable symbol when something changes. Programs that
match the change's pattern signature (--allow) may differ. It also checks
compile time (paired builds, bootstrap interval), binary size, optional
per-function cachegrind instruction counts, and the two-tier I3 symbol
rule: no executable gains dynamic-tier runtime symbols, and fully typed
benchmark workloads define none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>