Repository navigation
feat: CFG-aware liveness — VCR-RA-001 allocator substrate (#242) - #268
Merged
Merged
Conversation
…trate (#242) Add cfg_liveness(): builds the label-form basic-block CFG of a selected function and solves per-block live-in/live-out via the classic backward dataflow fixpoint live_in[B] = use[B] ∪ (live_out[B] − def[B]) live_out[B] = ⋃ { live_in[S] : S ∈ succ(B) } This is the interference-graph substrate the spill-capable register allocator (VCR-RA-001) reads off — the next structural step toward removing the single-pass exhaustion HARD-FAIL that forces the reciprocal-mult cost-gates (#209/v0.11.20). Scope / soundness (matches liveness.rs discipline): - leaf-only, label-form branches (B/Bhs/Blo/Bcc → Label); declines (returns None) on numeric-offset/computed/return branches, calls, and BrTable — never a partial or guessed CFG. - exit-block live_out is ∅; ABI return-register liveness is a documented external input the allocator unions in later (not under-claimed here). Pure analysis: zero non-test callers, no codegen path touched, so every frozen differential fixture (control_step 0x00210a55, flight_algo 0x07FDF307, divseam) is bit-identical BY CONSTRUCTION. Also: derive Ord/Hash for Reg (additive; needed for BTreeSet<Reg> live sets — an allocator wants register sets anyway). Tests: 4 new cfg_liveness unit tests (straight-line inputs, cross-block backward propagation over a conditional branch, single-successor unconditional branch, decline on call/offset). Extended the real-selector -output liveness test with structural CFG soundness checks (gapless block tiling, in-range successors, live-set vector lengths). Side-by-side / unwired per the north-star migration plan — wiring is a separate, fully oracle-gated step. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
avrabe
added a commit
that referenced
this pull request
Jun 5, 2026
#242) (#269) Build on the just-merged CFG-liveness substrate (#268) with the next allocator piece: interference_graph() derives per-instruction liveness from cfg_liveness (walking each block backward from its live_out) and constructs the undirected "cannot share a physical register" graph a colouring allocator consumes. Construction (standard, precise): - a register DEFINED at an instruction interferes with everything live immediately AFTER it (they coexist), and with its co-defs (so Umull's rdlo/rdhi get distinct registers). - precision payoff: in `add r3, r1, r2` the inputs die at the add, so r3 does NOT interfere with r1/r2 and may reuse one of their registers — a naive "all operands interfere" graph would forbid that reuse. InterferenceGraph exposes interferes / neighbors / degree / nodes / edge_count (degree is the Chaitin/Briggs simplify criterion: degree<k => k-colourable). Soundness: returns None exactly when cfg_liveness declines (out-of-scope construct), so a non-None graph is complete for the whole function. Pure analysis, zero non-test callers, no codegen path touched => every frozen differential fixture (control_step 0x00210a55, flight_algo 0x07FDF307, divseam) is bit-identical BY CONSTRUCTION. Tests: 5 interference units (simultaneous-live interfere, sequential reuse does not, co-defs interfere, live-across-branch, declines-when- liveness-declines). Extended the real-selector-output test to assert the graph over actual codegen is symmetric + self-loop-free. Remaining for VCR-RA-001: graph-colouring (simplify/select) -> spill/ reload -> ABI-exit-liveness union -> the oracle-gated wiring step. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
avrabe
added a commit
that referenced
this pull request
Jun 5, 2026
…it (#242) (#272) The register-allocator analysis+decision layer is merged and unwired (reg_effect → cfg_liveness #268 → interference_graph #269 → color_graph #270 → color_graph_precolored #271), all bit-identical-by-construction because nothing calls them. The wiring is categorically different: it makes the allocator drive codegen, so it CHANGES emitted bytes and needs the full differential oracle gate. This design note sequences that crossing before any of it is written: - identifies the hard-fail sites the wiring replaces (instruction_selector.rs ~362/388/609/4699/4765 — the exhaustion errors that force the cost-gates), - the frozen-behaviour invariant (control_step 0x00210A55 / flight_algo 0x07FDF307 / divseam bit-identical at every sub-step; flag default-off), - 5 oracle-gated sub-steps (verify_allocation oracle → spill-cost ranking → virtual-reg selector output flag-gated → spill-code insertion → wire-in + per-function flip where the differential proves no-regression), - the per-step oracle gate, and the non-goals (coalescing/splitting deferred). Design leads code: the next bounded, safe piece is step 1 (verify_allocation, still unwired); the rest is the planned consequential effort. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
North-star increment — VCR-RA-001 substrate (side-by-side, unwired)
First concrete code under VCR-RA-001 (SSA-based register allocator, epic #242). Lands the analysis substrate the allocator's interference graph reads off, without touching any codegen path.
What
liveness::cfg_liveness()builds the label-form basic-block CFG of a selected function and solves per-block live-in/live-out via the classic backward dataflow fixpoint:Two registers interfere iff both are live at a common point — so these per-block sets are exactly what the colouring allocator needs to replace the single-pass exhaustion hard-fail (the thing that forces the reciprocal-mult cost-gates, #209/v0.11.20).
Soundness (matches existing liveness.rs discipline)
B/Bhs/Blo/Bcc→Label). Declines (None) on numeric-offset/computed/return branches, calls, andBrTable— never a partial or guessed CFG.live_out = ∅; ABI return-register liveness is a documented external input the allocator unions in later.Why this can't regress anything
Pure analysis — zero non-test callers, no emitted-byte path touched. Every frozen differential fixture (control_step 0x00210a55, flight_algo 0x07FDF307, divseam) is bit-identical by construction. Also derives Ord/Hash for Reg (additive, needed for BTreeSet).
Tests
Verification
cargo test -p synth-synthesis (59) + -p synth-backend (all) green · clippy clean · fmt clean · workspace builds.
Wiring into codegen is a separate, fully oracle-gated step.
🤖 Generated with Claude Code