Repository navigation
feat: interference graph — VCR-RA-001 colouring input (#242) - #269
Merged
Merged
Conversation
#242) 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>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
avrabe
added a commit
that referenced
this pull request
Jun 5, 2026
…on (#242) (#270) Complete the analysis→decision core of the allocator on top of the merged interference graph (#269): color_graph(graph, k) -> ColorResult via Chaitin/Briggs with optimistic spill. 1. SIMPLIFY — repeatedly remove a node of degree < k onto a stack (it can always be coloured after its neighbours, which use ≤ k−1 colours). 2. SPILL — if every remaining node has degree ≥ k, optimistically push the highest-degree one (Briggs: often still colourable in practice). 3. SELECT — pop in reverse, assign the lowest colour no neighbour uses; a node that finds no free colour in 0..k is an actual spill. Returns Colored(BTreeMap<Reg,colour>) on success or Spilled(BTreeSet<Reg>) with the nodes a real allocator would rewrite to stack slots and retry. This is precisely the decision synth's single-pass allocator CANNOT make — it hard-fails on register exhaustion instead of spilling, which is what forces the reciprocal-mult cost-gate (#209/v0.11.20). Soundness: pure algorithm over the graph, 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 colouring units (empty, 2-colour a path, K4 spills at k=3, K4 colours at k=4, k=0 spills). Extended the real-selector-output test to assert actual codegen is colourable within the R0–R8 pool (k=9) and the returned colouring is valid (adjacent nodes differ). Remaining for VCR-RA-001: spill-code insertion -> precolouring reserved regs -> ABI-exit-liveness union -> virtual-reg selector output -> oracle-gated wiring (the step that finally removes the hard-fail). 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 (side-by-side, unwired)
Builds directly on the just-merged CFG-liveness substrate (#268) with the next allocator piece. No codegen path touched.
What
liveness::interference_graph()derives per-instruction liveness fromcfg_liveness(walking each block backward from itslive_out) and constructs the undirected "cannot share a physical register" graph a graph-colouring allocator consumes.Construction (standard, precise):
Umull'srdlo/rdhiget distinct registers).add r3, r1, r2the inputs die at the add, sor3does not interfere withr1/r2and may reuse one of their registers — a naive "all operands interfere" graph would forbid that reuse.InterferenceGraphexposesinterferes/neighbors/degree/nodes/edge_count. (degreeis the Chaitin/Briggs simplify criterion:degree < k⇒k-colourable.)Soundness
Returns
Noneexactly whencfg_livenessdeclines (out-of-scope construct), so a non-Nonegraph is complete for the whole function. Pure analysis, zero non-test callers, no emitted-byte path touched ⇒ every frozen differential fixture (control_step0x00210a55, flight_algo0x07FDF307, divseam) is bit-identical by construction.Tests
Verification
cargo test -p synth-synthesis(297 lib + suites) green · clippy clean · fmt clean.Remaining for VCR-RA-001: graph-colouring (simplify/select) → spill/reload → ABI-exit-liveness union → the oracle-gated wiring step.
🤖 Generated with Claude Code