Skip to content

feat(vcr-ra): post-exhaustion code quality — the capability PR #659 named missing (#242) - #672

Merged
avrabe merged 1 commit into
mainfrom
feat/242-post-exhaustion-quality
Jul 8, 2026
Merged

feat(vcr-ra): post-exhaustion code quality — the capability PR #659 named missing (#242)#672
avrabe merged 1 commit into
mainfrom
feat/242-post-exhaustion-quality

Conversation

@avrabe

@avrabe avrabe commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

What this is

PR #659 demonstrated the VCR-VER-001 gate but held the SYNTH_SPILL_ON_EXHAUST flip on a measured i32-shape cycle regression, naming the missing capability: post-exhaustion code quality on the optimized path — allocation-time Belady spill slots (#580) were not reached by the existing cleanup passes. This PR diagnoses why, extends the passes' reach, and re-measures PR #659's cycle-proxy table. Full evidence appended to scripts/repro/vcr_ver_001_gate.md.

Root cause of the unreached slots

  1. eliminate_dead_frame_stores structurally cannot fire: next_spill_offset is fresh-monotonic, and the pass proves deadness only via a later same-slot overwrite (the feat(vcr-ra): frame-slot dead-store elimination behind SYNTH_STACK_FWD (flag-off) (#242) #515 overwrite-only discipline) — no overwrite can exist.
  2. forward_stack_reloads structurally cannot fire: the eviction store's source register is redefined immediately after the store (that is why the value was evicted).
  3. spill_rechoice_segment declined every rename: the bridge draws scratch from R4-R8 only, so R2/R3 are never touched again — and the segment-local deadness proof rejects "never touched again" as possibly-live-out. Behind that, exact SegmentTrace equality rejected any rename onto a fresh register, and guard (b) rejected count-neutral ldrmov folds.
  4. signed_div_const (the +120% outlier) was a different gap: the optimized path emitted div-by-zero + INT_MIN/−1 trap guards even for constant divisors (the direct selector elides them since v0.11.17, perf: --relocatable direct selector bypasses synth-opt — general codegen optimization (research + stats tracking) #209 Opt 1a), plus a dead spill store the unread sweep couldn't reach past the guards' resolved branches (feat(vcr-ra)!: retire inline const aliasing; SYNTH_CONST_CSE default-on (#242) #604/AUDIT: spill-realloc passes delete instructions on resolved-offset streams — same branch-overshoot hazard class as the #604 const-CSE miscompile (default-on since v0.24.0) #606 discipline).

Pass changes (all scoped, flag-off bit-identical)

Everything is reachable only on functions the #580 machinery actually shaped, via the new OptimizerBridge::spill_on_exhaust_fired scoping — a flag-on compile leaves every untouched function byte-identical (guard elision on a non-spilling function triggers a rebuild with guards reinstated). Locked by vcr_ver_001_gate_242 + the frozen suite run in both flag states.

  • scratch_dead_at: function-level R2/R3 exit-deadness (caller-saved, never return-carrying; conservative at any branch/call/unmodeled op), feeding rename_kill_def; trace equality taken modulo provably-dead exit entries; per-pair pool-pressure commit; count-neutral folds admitted when bytes strictly shrink.
  • Constant rematerialization in spill_forward_segment: a reload of a slot provably holding a single-instruction constant becomes the retargeted mov/movw; movt's RMW kills the tracked shape (the Direct selector spill rung miscompiles: ldr reads a never-stored frame slot after mul.w clobbers a live register (shipped path, wrong value) #581/fix(selector): spill rung stores before clobber — never reload a never-stored slot (#581) #582 discipline — a non-reproducible value is never treated as reproducible; the store-before-reload/is_const_materialization machinery on the direct path is untouched).
  • Bounded fixpoint (≤4 rounds) of the cleanup triple + a late elide_dead_frame once cleanup empties the frame.
  • Terminal-segment relaxed live-out pinning in range-realloc: only R0/R1 are observable past bx lr at the pre-prologue position; the VCR-RA-003 validator runs with the same exemptions (validate_segment_rewrite_exempting); restricted to reload-free segments so R2/R3 stay free for the rechoice pass.
  • Const-divisor trap-guard elision in the bridge's DivS/DivU/RemS/RemU: single-def Const scan with total-or-disabled def enumeration (a redefined vreg poisons; any unenumerable op disables the map); c=0 keeps every guard, DivS c=−1 keeps the overflow guard.

Before/after cycle proxy (scripts/repro/postex_cycle_proxy.py, new)

Dynamic instructions + data-memory accesses under unicorn, summed over the differential vectors; every run execution-matched wasmtime in both flag states.

fixture (default path) off on @ PR #659 on now ≤ +5% target
spill_on_exhaust_242 368 +30.4 % 432 (+17.4 %) missed
spill_rung_581 204 +32.4 % 222 (+8.8 %) missed
high_pressure_i32 300 +8.0 % 228 (−24.0 %) ✓ (beats the decline)
signed_div_const 75 +120 % 50 (−33.3 %) ✓ (beats the decline; fn 76 → 46 B)
i64_pair_exhaust_587 584 −5.5 % 488 (−16.4 %)
i64_spill_pool_587 1656 −1.4 % 1608 (−2.9 %)

The residual, named honestly

Two fixtures miss the bar. The residual is the allocation itself, not the cleanup: alloc_i32_scratch draws destinations from a fixed R4-R8 pool (5 regs) while the direct selector allocates over all nine of R0-R8 — the bridge evicts 5 values where Belady over 9 registers needs 1, and the post-hoc renamer recovers only what fits through the two forever-free caller-saved registers. A reload-pool widening was tried and reverted (measured strictly worse: rung +8.8 % → +14.7 % — it starves the renamer). Closing the last gap is dest allocation over the full pool, i.e. the Track-A VCR-RA/VCR-SEL allocator replacement — exactly the North-Star claim. The default-on flip stays HELD (#580) on that + gale G474RE cycles.

Gates run

  • cargo test --workspace green (574 synthesis unit tests incl. 10 new; all suites)
  • frozen anchors 10/10 + vcr_ver_001_gate_242 lock in BOTH flag states (flag-off bit-identical by construction — every extension is behind the bridge-scoped post_exhaust bool)
  • flag-on differentials PASS: r12_spill_496 (both silicon victims, flight_algo = 0x07FDF307), spill_on_exhaust_242 (8/8), i64_pair_exhaust_587 (8/8), i64_spill_pool_587, spill_rung_581; high_pressure_i32 + signed_div_const execution-verified inside the proxy harness
  • cargo fmt --check + clippy --workspace --all-targets -- -D warnings clean

Refs #242, #580, #496; the verdict this implements is PR #659's.

🤖 Generated with Claude Code

…nst-div guards (#242, the PR #659 verdict)

PR #659 held the SYNTH_SPILL_ON_EXHAUST flip on a measured i32-shape cycle
regression and named the missing capability: post-exhaustion code quality on
the optimized path. Root cause of the unreached allocation-time Belady slots:

- fresh-monotonic slots defeat the overwrite-only frame-slot DCE (#515);
- the eviction store's source is redefined immediately, defeating
  forward_stack_reloads;
- spill_rechoice's rename deadness proof is segment-local — R2/R3 are never
  touched again on bridge streams, so "possibly live-out" declined them, and
  exact SegmentTrace equality rejected any fresh-register rename;
- signed_div_const additionally paid for const-divisor trap guards the
  direct selector elides (#209 Opt 1a).

Extensions, ALL scoped to functions the #580 machinery actually shaped
(OptimizerBridge::spill_on_exhaust_fired — flag-on leaves every untouched
function byte-identical, locked by vcr_ver_001_gate_242 + frozen 10/10 run in
both flag states; flag-off is bit-for-bit the shipping pipeline):

- scratch_dead_at: function-level R2/R3 exit-deadness (conservative at any
  branch/call/unmodeled op) feeding rename_kill_def; trace equality modulo
  provably-dead exit entries; per-pair pressure commit; byte-shrinking
  count-neutral folds admitted;
- constant rematerialization of spilled single-instruction consts in
  spill_forward_segment (movt RMW kills the shape — the #582 discipline);
- bounded fixpoint of the cleanup triple + late elide_dead_frame;
- terminal-segment relaxed live-out pinning in range-realloc (only R0/R1
  observable past bx lr pre-prologue; VCR-RA-003 validator run with the same
  exemptions; reload-free segments only);
- const-divisor trap-guard elision in DivS/DivU/RemS/RemU (single-def Const
  scan, total-or-disabled def enumeration; c=0 keeps all, DivS c=-1 keeps
  the overflow guard).

Cycle proxy (scripts/repro/postex_cycle_proxy.py, wasmtime-matched):
spill_on_exhaust_242 +30.4%→+17.4%, spill_rung_581 +32.4%→+8.8%,
high_pressure_i32 +8.0%→−24.0%, signed_div_const +120%→−33.3%,
i64 pair/pool −16.4%/−2.9%. Two fixtures still miss ≤+5%: the residual is
alloc_i32_scratch's fixed R4-R8 dest pool vs the direct selector's nine
registers — the Track-A allocator replacement itself (see the gate doc's
"residual, named" section; a reload-pool widening was tried and reverted,
measured strictly worse). The flip stays HELD (#580).

Gates: cargo test --workspace green; frozen anchors 10/10 + gate lock in
BOTH flag states; r12_spill_496 / spill_on_exhaust_242 / i64_pair_exhaust_587
/ i64_spill_pool_587 / spill_rung_581 differentials PASS flag-on; fmt +
clippy -D warnings clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.51744% with 79 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/synth-synthesis/src/liveness.rs 85.01% 49 Missing ⚠️
crates/synth-synthesis/src/optimizer_bridge.rs 90.76% 30 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe
avrabe merged commit fd174e5 into main Jul 8, 2026
32 checks passed
@avrabe
avrabe deleted the feat/242-post-exhaustion-quality branch July 8, 2026 21:54
avrabe added a commit that referenced this pull request Jul 8, 2026
… — merged ahead of the tag

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
avrabe added a commit that referenced this pull request Jul 8, 2026
…ed rules, 81 bridge Qed (#673)

* chore(release): v0.36.0 — unreachable traps + sparse tables + 40 rules + 81 bridge Qed (#668/#669/#670/#671)

Pin sweep 0.35.0 -> 0.36.0 + CHANGELOG.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: fold #672 (post-exhaustion quality) into the v0.36.0 changelog — merged ahead of the tag

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant