From b829f17aa86deac34b7cc0b5b55c553c94e5d6d6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 5 Aug 2026 11:01:52 +1000 Subject: [PATCH 1/7] Simplify `MaybeTransitiveLiveLocals` It's mostly identical to `MaybeLiveLocals`, and we can delegate most of its operations to `MaybeLiveLocals`. Note that there was a tiny difference between `MaybeLiveLocals::apply_call_return_effect` and `MaybeTransitiveLiveLocals::apply_call_return_effect`: the former uses `state.kill(local)`, the latter used `state.remove(local)`. The two are equivalent so the difference didn't matter, but it does demonstrate the dangers of the code duplication. --- .../rustc_mir_dataflow/src/impls/liveness.rs | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index da2ea948366db..f23ef674ba6dc 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -208,7 +208,8 @@ impl DefUse { } } -/// Like `MaybeLiveLocals`, but does not mark locals as live if they are used in a dead assignment. +/// Like `MaybeLiveLocals` (and layered on top of `MaybeLiveLocals`), but does not mark locals as +/// live if they are used in a dead assignment. /// /// This is basically written for dead store elimination and nothing else. /// @@ -274,12 +275,11 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { const NAME: &'static str = "transitive liveness"; fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - // bottom = not live - DenseBitSet::new_empty(body.local_decls.len()) + MaybeLiveLocals.bottom_value(body) } - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { - // No variables are live until we observe a use + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { + MaybeLiveLocals.initialize_start_block(body, state) } fn apply_primary_statement_effect( @@ -288,6 +288,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { statement: &mir::Statement<'tcx>, location: Location, ) { + // This is the one part of `MaybeTransitiveLiveLocals` that differs from `MaybeLiveLocals`. if let Some(destination) = Self::can_be_removed_if_dead(&statement.kind, &self.always_live, &self.debuginfo_locals) && !state.contains(destination.local) @@ -295,7 +296,8 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { // This store is dead return; } - TransferFunction(state).visit_statement(statement, location); + + MaybeLiveLocals.apply_primary_statement_effect(state, statement, location); } fn apply_primary_terminator_effect( @@ -304,27 +306,15 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { terminator: &mir::Terminator<'tcx>, location: Location, ) { - TransferFunction(state).visit_terminator(terminator, location); + MaybeLiveLocals.apply_primary_terminator_effect(state, terminator, location) } fn apply_call_return_effect( &self, state: &mut Self::Domain, - _block: mir::BasicBlock, + block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, ) { - if let CallReturnPlaces::Yield(resume_place) = return_places { - YieldResumeEffect(state).visit_place( - &resume_place, - PlaceContext::MutatingUse(MutatingUseContext::Yield), - Location::START, - ) - } else { - return_places.for_each(|place| { - if let Some(local) = place.as_local() { - state.remove(local); - } - }); - } + MaybeLiveLocals.apply_call_return_effect(state, block, return_places); } } From 530c82cb96e19e367edb4c3661e762aaedfa3979 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 5 Aug 2026 13:18:30 +1000 Subject: [PATCH 2/7] Rename `TransferFunction` As `LivenessTransferFunction`. This avoids renaming it via a `use` item, which makes things clearer. --- compiler/rustc_mir_dataflow/src/impls/liveness.rs | 12 ++++++------ compiler/rustc_mir_dataflow/src/impls/mod.rs | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index f23ef674ba6dc..dafde78e91ee7 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -24,8 +24,8 @@ use crate::{Analysis, Backward, GenKill}; pub struct MaybeLiveLocals; impl MaybeLiveLocals { - pub fn transfer_function(state: &mut I) -> TransferFunction<'_, I> { - TransferFunction(state) + pub fn transfer_function(state: &mut I) -> LivenessTransferFunction<'_, I> { + LivenessTransferFunction(state) } } @@ -50,7 +50,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { statement: &mir::Statement<'tcx>, location: Location, ) { - TransferFunction(state).visit_statement(statement, location); + LivenessTransferFunction(state).visit_statement(statement, location); } fn apply_primary_terminator_effect( @@ -59,7 +59,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { terminator: &mir::Terminator<'tcx>, location: Location, ) { - TransferFunction(state).visit_terminator(terminator, location); + LivenessTransferFunction(state).visit_terminator(terminator, location); } fn apply_call_return_effect( @@ -84,9 +84,9 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { } } -pub struct TransferFunction<'a, I>(pub &'a mut I); +pub struct LivenessTransferFunction<'a, I>(pub &'a mut I); -impl<'tcx, I> Visitor<'tcx> for TransferFunction<'_, I> +impl<'tcx, I> Visitor<'tcx> for LivenessTransferFunction<'_, I> where I: GenKill, { diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index 6d573e1c00e1c..1e12e41ce1fb4 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -9,8 +9,7 @@ pub use self::initialized::{ MaybeUninitializedPlaces, MaybeUninitializedPlacesDomain, }; pub use self::liveness::{ - DefUse, MaybeLiveLocals, MaybeTransitiveLiveLocals, - TransferFunction as LivenessTransferFunction, + DefUse, LivenessTransferFunction, MaybeLiveLocals, MaybeTransitiveLiveLocals, }; pub use self::storage_liveness::{ MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive, always_storage_live_locals, From 7e95965ac1853b29b7e0aaf7858c363eb52895b8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 5 Aug 2026 13:19:15 +1000 Subject: [PATCH 3/7] Remove `MaybeLiveLocals::transfer_function` It has only two uses, and it's just a synonym for `LivenessTransferFunction`, which has more uses. --- compiler/rustc_mir_dataflow/src/impls/liveness.rs | 6 ------ compiler/rustc_mir_transform/src/dest_prop.rs | 6 +++--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index dafde78e91ee7..a82fed864400f 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -23,12 +23,6 @@ use crate::{Analysis, Backward, GenKill}; /// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis pub struct MaybeLiveLocals; -impl MaybeLiveLocals { - pub fn transfer_function(state: &mut I) -> LivenessTransferFunction<'_, I> { - LivenessTransferFunction(state) - } -} - impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { type Domain = DenseBitSet; type Direction = Backward; diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index e392f856696be..924125404a07a 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -144,7 +144,7 @@ use rustc_index::{IndexVec, newtype_index}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, VisitPlacesWith, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -use rustc_mir_dataflow::impls::{DefUse, MaybeLiveLocals}; +use rustc_mir_dataflow::impls::{DefUse, LivenessTransferFunction, MaybeLiveLocals}; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_mir_dataflow::{Analysis, EntryStates, GenKill}; use tracing::{debug, trace}; @@ -619,7 +619,7 @@ fn save_as_intervals<'tcx>( state.current = state.current + 1; debug_assert_eq!(state.current, two_step_loc(loc, Effect::Before)); - MaybeLiveLocals::transfer_function(&mut state).visit_terminator(term, loc); + LivenessTransferFunction(&mut state).visit_terminator(term, loc); for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() { let loc = Location { block, statement_index }; @@ -659,7 +659,7 @@ fn save_as_intervals<'tcx>( // the all the writes we manually marked as live in the second half of the statement. state.current = TwoStepIndex::from_u32(state.current.as_u32() + 1); debug_assert_eq!(state.current, two_step_loc(loc, Effect::Before)); - MaybeLiveLocals::transfer_function(&mut state).visit_statement(stmt, loc); + LivenessTransferFunction(&mut state).visit_statement(stmt, loc); } // Cleanup the current block for the next one. From 0ae7d22265d64302c4bde60b34ad927a5525b6a1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 5 Aug 2026 13:23:05 +1000 Subject: [PATCH 4/7] Remove an unnecessary lifetime --- compiler/rustc_mir_dataflow/src/impls/liveness.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index a82fed864400f..673d170c84789 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -228,7 +228,7 @@ impl<'a> MaybeTransitiveLiveLocals<'a> { pub fn can_be_removed_if_dead<'tcx>( stmt_kind: &StatementKind<'tcx>, always_live: &DenseBitSet, - debuginfo_locals: &'a DenseBitSet, + debuginfo_locals: &DenseBitSet, ) -> Option> { // Compute the place that we are storing to, if any let destination = match stmt_kind { From d1689e22cf97f0e9414f770ae5ec6ba50bdbbbce Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 5 Aug 2026 13:23:29 +1000 Subject: [PATCH 5/7] Fix a typo --- compiler/rustc_mir_dataflow/src/impls/liveness.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 673d170c84789..ff373e906683a 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -214,7 +214,7 @@ pub struct MaybeTransitiveLiveLocals<'a> { } impl<'a> MaybeTransitiveLiveLocals<'a> { - /// The `always_alive` set is the set of locals to which all stores should unconditionally be + /// The `always_live` set is the set of locals to which all stores should unconditionally be /// considered live. /// /// This should include at least all locals that are ever borrowed. From d7a07f0345486d4f576eab8fb435ba6be5caafbf Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 5 Aug 2026 13:27:23 +1000 Subject: [PATCH 6/7] Remove unused derives on `DefUse` --- compiler/rustc_mir_dataflow/src/impls/liveness.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index ff373e906683a..3208f876af11c 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -130,7 +130,6 @@ impl<'tcx> Visitor<'tcx> for YieldResumeEffect<'_> { } } -#[derive(Eq, PartialEq, Clone)] pub enum DefUse { /// Full write to the local. Def, From a7b542a64b43011f14ea58ce8e5c761b0f4af7c9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 5 Aug 2026 13:54:22 +1000 Subject: [PATCH 7/7] Remove unnecessary `&` sigils --- compiler/rustc_mir_dataflow/src/impls/liveness.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 3208f876af11c..5c77827a67165 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -283,7 +283,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { ) { // This is the one part of `MaybeTransitiveLiveLocals` that differs from `MaybeLiveLocals`. if let Some(destination) = - Self::can_be_removed_if_dead(&statement.kind, &self.always_live, &self.debuginfo_locals) + Self::can_be_removed_if_dead(&statement.kind, self.always_live, self.debuginfo_locals) && !state.contains(destination.local) { // This store is dead