Skip to content

fix(vcr-ra): local promotion must never cause a compile failure (#474)#475

Merged
avrabe merged 1 commit into
mainfrom
vcr-ra/474-promotion-exhaustion-fallback
Jun 24, 2026
Merged

fix(vcr-ra): local promotion must never cause a compile failure (#474)#475
avrabe merged 1 commit into
mainfrom
vcr-ra/474-promotion-exhaustion-fallback

Conversation

@avrabe

@avrabe avrabe commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Fixes #474 — a v0.14.0 regression: default-on i32 local promotion turns a working compile into a hard register exhaustion skip on dense functions (fine on v0.12.0). Promotion pins eligible locals into callee-saved r4-r8, halving the operand-stack temp pool; past a density threshold the allocator can't recover.

Fix

arm_backend.rs::select_direct: factor the exhaustion-recovery ladder over a local_promote flag. Run it with promotion first (so every function that compiles today is bit-identical), then — only if it still ends in register exhaustion — fall back to the promotion-off ladder automatically. That's exactly what the SYNTH_NO_LOCAL_PROMOTE=1 workaround does by hand, now automatic. Promotion is an optimization; it must never be the reason a function fails to compile. Keys on "register exhaustion" generally, covering both the single-register and i64-spill-pool exhaustion classes promotion can trigger.

Verified

  • Frozen byte gate green — every currently-compiling fixture stays bit-identical (the fallback is reached only by functions that exhaust with promotion).
  • Generic repro (scripts/repro/promotion_exhaustion_fallback.wat): fails without the fix (register exhaustion: i64 spill-slot pool exhausted), compiles with it, .text byte-identical to the SYNTH_NO_LOCAL_PROMOTE=1 build (proving the fallback fired and landed on the v0.12.0 frame-slot path).
  • New CI regression test promotion_never_causes_compile_failure_474.

I couldn't obtain gale's exact control_step_packed, so the end-to-end rescue is demonstrated on the generic fixture (same promotion-induced-exhaustion class); gale offered to re-confirm on control_step_packed / G474RE once this lands.

Bug-fix release candidate (v0.15.1).

Refs #474, #462, #242

🤖 Generated with Claude Code

v0.14.0 made i32 local promotion default-on. Promotion pins eligible locals into
callee-saved r4-r8, halving the operand-stack temp pool; on a dense function that
tips register allocation past what it can recover, turning a working compile into
a hard "register exhaustion" skip — a regression from v0.12.0 (reported on a real
engine-control function: compiled on 0.12.0, skipped on 0.14.0).

Fix (arm_backend.rs `select_direct`): factor the exhaustion-recovery ladder over a
`local_promote` flag and run it with promotion FIRST — so every function that
compiles today is bit-identical (frozen byte gate stays green) — then, only if it
still ends in register exhaustion, fall back to the promotion-off ladder
automatically. That is exactly what the `SYNTH_NO_LOCAL_PROMOTE=1` workaround does
by hand, now automatic. Promotion is an optimization; it must never be the *reason*
a function fails to compile. The fallback keys on "register exhaustion" generally,
so it covers both the single-register and i64-spill-pool exhaustion classes
promotion can trigger.

Verified:
- frozen byte gate green — every currently-compiling fixture bit-identical (the
  fallback is reached ONLY by functions that exhaust WITH promotion);
- the generic repro (promotion_exhaustion_fallback.wat) FAILS to compile without
  the fix ("register exhaustion: i64 spill-slot pool exhausted") and COMPILES with
  it, .text byte-identical to the SYNTH_NO_LOCAL_PROMOTE=1 build (proving the
  fallback fired and landed on the v0.12.0 frame-slot path);
- new CI regression test promotion_never_causes_compile_failure_474.

Refs #474, #462, #242

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.90909% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/synth-backend/src/arm_backend.rs 90.90% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe avrabe merged commit 8a87cf6 into main Jun 24, 2026
15 checks passed
@avrabe avrabe deleted the vcr-ra/474-promotion-exhaustion-fallback branch June 24, 2026 21:19
avrabe added a commit that referenced this pull request Jun 24, 2026
Roadmap artifact, no codegen change. The north star (#242) replaces synth's
single-pass register allocator with a verified one; the clearest spec of what that
allocator must do is the failure-recovery ladder the current allocator has accreted
(arm_backend.rs select_direct): base attempt -> optimized-decline (#120) ->
spill-on-exhaustion (#242 3b-lite) -> param frame-backing (#204) -> promotion-off
fallback (#474/#475). Each rung is reached only after a recoverable register
exhaustion, so the base-attempt corpus is untouched (frozen gate green).

Maps each rung to the issue that added it and the failure it catches, ties fixtures
to rungs (only rung 4 / promotion_exhaustion_fallback is confirmed load-bearing;
the rest are by design intent pending a per-rung counter), and states the VCR-RA
acceptance implication: a correct-by-construction allocator with spilling collapses
rungs 2-4 into "allocation succeeds" — the ladder then survives only as a
never-firing defense-in-depth assertion.

Refs #242, #474

Co-authored-by: Claude Opus 4.8 <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

1 participant