diff --git a/crates/rue-codegen/src/liveness.rs b/crates/rue-codegen/src/liveness.rs index 2941ac758..696863d1f 100644 --- a/crates/rue-codegen/src/liveness.rs +++ b/crates/rue-codegen/src/liveness.rs @@ -457,7 +457,6 @@ where &inst_uses, &inst_defs, live_in, - live_out, has_back_edge, ); @@ -702,8 +701,10 @@ fn materialize_live_out( /// element values before storing them); the bitset scan is then O(N²) in the /// number of live bits, whereas this path stays linear (RUE-302). /// * **Has back-edges (loops):** loop-carried values are live past their textual -/// last use, so we extend the same dense range table from the exact -/// `live_in`/`live_out` scan as well as definitions and uses. +/// last use, so we extend the same dense range table from the exact `live_in` +/// scan as well as definitions and uses. Scanning `live_out` too would be +/// redundant: `live_in = uses ∪ (live_out - defs)`, so every live-out value +/// is already either live-in or defined at that instruction. /// /// # A range is a textual interval, not liveness /// @@ -736,7 +737,6 @@ fn build_live_ranges( inst_uses: &[VRegList], inst_defs: &[VRegList], live_in: &[FixedBitSet], - live_out: &[FixedBitSet], has_back_edge: bool, ) -> IndexMap> { let mut ranges: IndexMap> = @@ -778,9 +778,6 @@ fn build_live_ranges( for vreg_idx in live_in[idx].ones() { extend(VReg::new(vreg_idx as u32), idx); } - for vreg_idx in live_out[idx].ones() { - extend(VReg::new(vreg_idx as u32), idx); - } } ranges @@ -1115,7 +1112,9 @@ mod tests { ); // The back-edge keeps v0 live through instruction 3 even though its - // last textual use is instruction 2. + // last textual use is instruction 2. This is the cyclic extent that + // must survive when range construction omits the redundant live-out + // scan. assert_eq!(info.range(VReg::new(0)), Some(&LiveRange::new(0, 3))); } diff --git a/docs/notes/post-adr-0063-cold-compiler-architecture-audit.md b/docs/notes/post-adr-0063-cold-compiler-architecture-audit.md index 7f5452b99..ba2a10094 100644 --- a/docs/notes/post-adr-0063-cold-compiler-architecture-audit.md +++ b/docs/notes/post-adr-0063-cold-compiler-architecture-audit.md @@ -1802,6 +1802,21 @@ allocation-accounted pairs are neutral at +101--320 calls (under 0.003%) and -3.3--118.6 KB requested bytes. Every compiler-work counter, source/output metric, and executable byte remains identical. +RUE-1466 removes the redundant `live_out` set-bit walk from cyclic MIR +live-range construction shared by both backends. The transfer equation +`live_in = uses ∪ (live_out - defs)` proves that every live-out register is +already either live-in or defined at the same instruction, and those two inputs +were already extending the range. Retained per-instruction liveness and debug +views keep their live-out table; only the duplicate range pass disappears. + +Across 16 balanced fixed one-worker cold Lattice pairs, retired instructions +improve by 0.1051% (0.0868% MAD). End-to-end clock is neutral at +0.7974% +(2.6807% MAD), cycles at +0.4182% (1.5224% MAD), peak RSS at -0.0454% +(0.3189% MAD), and peak footprint at -0.0619% (0.5094% MAD). Four +allocation-accounted pairs are neutral, as expected for a set-bit iteration +reduction. Every compiler-work counter, source/output metric, and executable +byte remains identical. + ## Next actions and decision boundary Authorized low-risk work: