Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions crates/rue-codegen/src/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,6 @@ where
&inst_uses,
&inst_defs,
live_in,
live_out,
has_back_edge,
);

Expand Down Expand Up @@ -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
///
Expand Down Expand Up @@ -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<VReg, Option<LiveRange>> {
let mut ranges: IndexMap<VReg, Option<LiveRange>> =
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)));
}

Expand Down
15 changes: 15 additions & 0 deletions docs/notes/post-adr-0063-cold-compiler-architecture-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down