Repository navigation
gc: a deleted write barrier passes every runtime probe — only a static IR assertion can catch it #8185
Description
A deleted GC write barrier passes every runtime probe we have
Found while validating #8183. A release build with the write barrier deleted from the dynamic-key IC's new reference-store arm passes the entire behavioural matrix:
- old→young edge fixtures
PERRY_GC_FORCE_EVACUATE=1 PERRY_GC_VERIFY_EVACUATION=1PERRY_GEN_GC=0PERRY_GC_ZEAL=1+ from-space protection at depth 400
Byte-identical output, exit 0, on both the gap fixture and a larger adversarial one. The static IR test was the only thing that said no.
Why this matters more than one PR
CLAUDE.md's rooting-invariant guidance says the runtime instruments are the detector for the class the static checker cannot see (a runtime-side cache of a heap pointer). This is the mirror image and it is not written down anywhere: for a missing write barrier, the runtime instruments are the ones that cannot see it, and the static IR assertion is the only detector.
The reason is structural. A missing barrier does not corrupt anything at the moment of the store. It leaves a remembered-set entry unwritten, so a later minor collection fails to trace an old→young edge and drops a live object. Whether that happens depends on the object surviving to old-gen, the child staying young, and a minor landing in that window — none of which a short fixture reliably produces, and FORCE_EVACUATE/VERIFY_EVACUATION do not manufacture it either because they verify rewriting, not remembering.
This is the same shape as CLAUDE.md's hazard 4 — "the gate runs but its subject never did" — except the subject here is a negative: the absence of a barrier cannot be observed by running the program.
Consequences worth acting on
- Any PR that adds or moves a store on a GC-managed slot needs a static IR assertion, not a behavioural test, as its barrier evidence. perf(codegen): store reference values inline in the dynamic-key write IC (#8108) #8183 has one (
br i1into the arm, plus barrier/layout-note/addref presence); it should be the template. - A green GC-canary run is not evidence a barrier is present. Several PRs in this campaign have cited
FORCE_EVACUATE + VERIFY_EVACUATIONas barrier evidence. That inference is invalid — it is evidence about rewriting, which is a different property. - Worth checking whether
scripts/gc_store_site_inventory.pycan be extended to assert barrier presence per store site, rather than only inventorying the sites. If it can, this becomes a gate instead of a review convention.
Provenance
Sabotage arms run against #8183, all four caught by its two IR tests: drop the write barrier; drop the layout note + string addref; route reference values back to put.dynic.slow; leak a barrier into the scalar arm. The third initially passed — the arm became dead IR — which is why that PR added a br i1-into-the-arm assertion. That near-miss is the same lesson twice in one PR: presence of code is not proof it is reached, and absence of a crash is not proof a barrier exists.
Activity
perf(codegen): put the static write PIC's GC bookkeeping behind one l…
fix(codegen): audit the ctor-prologue store and give its stem a witne…
test(codegen): IR witnesses for the idxset.recv_global / recv_capture…
perf(codegen): inline array store/push lanes for module-global receiv…