Summary
Perry's object cache keys on source + compiler identity, but not on codegen-affecting environment variables. So a build with such a variable set silently reuses the .o from a build without it — the switch appears to do nothing, and any A/B built on it is vacuous.
Why this is worse than a stale-cache annoyance
CLAUDE.md documents PERRY_WRITE_BARRIERS=0 as a compile-time bisection tool:
PERRY_WRITE_BARRIERS=0/off/false disables codegen-emitted write barriers at compile time … for benchmark/debug bisection
It is read in codegen (crates/perry-codegen/src/expr/write_barrier.rs, codegen/helpers.rs), so it changes emitted IR. But with a warm node_modules/.cache/perry, setting it changes nothing — you get the previously-cached object, with barriers still in. The bisection tool silently lies, and you conclude "barriers aren't the cause" from an experiment that never ran.
This was found the hard way: while validating #6385 (PR #6393), the falsification run — the one meant to prove the new tests can fail — passed. That would have been a worthless green. It only went red after rm -rf node_modules/.cache/perry. A test that passes because the cache served a stale object is indistinguishable from a test that passes because the code is correct.
Repro
perry compile x.ts -o x # populates the cache
PERRY_WRITE_BARRIERS=0 perry compile x.ts -o x2 # reuses the cached .o; barriers still emitted
Compare the emitted IR (--trace llvm) for the two — identical.
Affected switches (any env var read during codegen)
(Runtime-only knobs — PERRY_GEN_GC, PERRY_GC_FORCE_EVACUATE, PERRY_GC_DIAG — are unaffected: they are read by the linked runtime at execution time, not baked into the object.)
Fix
Fold the values of all codegen-affecting env vars into the object-cache key (build_cache.rs). Ideally register them in one place so a new switch cannot be added without also entering the key — a CODEGEN_ENV_KEYS: &[&str] array consulted by both the reader and the hasher, so the two cannot drift.
A cheaper interim: have --trace llvm / a warning note when a codegen env var is set and the cache is hit, so the user knows the switch did not take effect.
Summary
Perry's object cache keys on source + compiler identity, but not on codegen-affecting environment variables. So a build with such a variable set silently reuses the
.ofrom a build without it — the switch appears to do nothing, and any A/B built on it is vacuous.Why this is worse than a stale-cache annoyance
CLAUDE.md documents
PERRY_WRITE_BARRIERS=0as a compile-time bisection tool:It is read in codegen (
crates/perry-codegen/src/expr/write_barrier.rs,codegen/helpers.rs), so it changes emitted IR. But with a warmnode_modules/.cache/perry, setting it changes nothing — you get the previously-cached object, with barriers still in. The bisection tool silently lies, and you conclude "barriers aren't the cause" from an experiment that never ran.This was found the hard way: while validating #6385 (PR #6393), the falsification run — the one meant to prove the new tests can fail — passed. That would have been a worthless green. It only went red after
rm -rf node_modules/.cache/perry. A test that passes because the cache served a stale object is indistinguishable from a test that passes because the code is correct.Repro
Compare the emitted IR (
--trace llvm) for the two — identical.Affected switches (any env var read during codegen)
PERRY_WRITE_BARRIERS— documented compile-time bisection switchPERRY_SETJMP_VOLATILE— added by perf(codegen): replace the try/catch optnone contagion with targeted setjmp volatile slots (#6385) #6393, same hazard, same reason(Runtime-only knobs —
PERRY_GEN_GC,PERRY_GC_FORCE_EVACUATE,PERRY_GC_DIAG— are unaffected: they are read by the linked runtime at execution time, not baked into the object.)Fix
Fold the values of all codegen-affecting env vars into the object-cache key (
build_cache.rs). Ideally register them in one place so a new switch cannot be added without also entering the key — aCODEGEN_ENV_KEYS: &[&str]array consulted by both the reader and the hasher, so the two cannot drift.A cheaper interim: have
--trace llvm/ a warning note when a codegen env var is set and the cache is hit, so the user knows the switch did not take effect.