diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index 3c56999fcbdc9..63b2adceb524c 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -195,28 +195,20 @@ where debug_assert_eq!(target.block, self.pos.block); let block_data = &self.body[target.block]; - #[rustfmt::skip] - let next_effect = if A::Direction::IS_FORWARD { - self.pos.curr_effect_index.map_or_else( - || Effect::Early.at_index(0), - EffectIndex::next_in_forward_order, - ) - } else { - self.pos.curr_effect_index.map_or_else( - || Effect::Early.at_index(block_data.statements.len()), - EffectIndex::next_in_backward_order, - ) - }; - + let next_effect = self.pos.curr_effect_index.map_or_else( + || A::Direction::first_index(block_data), + |idx| A::Direction::next_index(idx), + ); let target_effect_index = effect.at_index(target.statement_index); - A::Direction::apply_effects_in_range( - &self.results.analysis, - &mut self.state, - target.block, - block_data, - next_effect..=target_effect_index, - ); + let mut idx = next_effect; + loop { + self.results.analysis.apply_effect(&mut self.state, target.block, block_data, idx); + if idx == target_effect_index { + break; + } + idx = A::Direction::next_index(idx); + } self.pos = CursorPosition { block: target.block, curr_effect_index: Some(target_effect_index) }; diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 39df33187d3a9..68c8e03de8022 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -1,5 +1,3 @@ -use std::ops::RangeInclusive; - use rustc_middle::bug; use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges}; @@ -10,6 +8,13 @@ pub trait Direction { const IS_FORWARD: bool; const IS_BACKWARD: bool = !Self::IS_FORWARD; + /// Returns the first statement index for this direction. (0 when going forward and + /// `statements.len()` when going backward.) + fn first_index(block_data: &mir::BasicBlockData<'_>) -> EffectIndex; + + /// Returns the next index for this direction. + fn next_index(idx: EffectIndex) -> EffectIndex; + /// Called by `iterate_to_fixpoint` during initial analysis computation. fn apply_effects_in_block<'mir, 'tcx, A>( analysis: &A, @@ -21,19 +26,6 @@ pub trait Direction { ) where A: Analysis<'tcx>; - /// Called by `ResultsCursor` to recompute the domain value for a location - /// in a basic block. Applies all effects between the given `EffectIndex`s. - /// - /// `effects.start()` must precede or equal `effects.end()` in this direction. - fn apply_effects_in_range<'tcx, A>( - analysis: &A, - state: &mut A::Domain, - block: BasicBlock, - block_data: &mir::BasicBlockData<'tcx>, - effects: RangeInclusive, - ) where - A: Analysis<'tcx>; - /// Called by `ResultsVisitor` to recompute the analysis domain values for /// all locations in a basic block (starting from `entry_state` and to /// visit them with `vis`. @@ -53,6 +45,18 @@ pub struct Backward; impl Direction for Backward { const IS_FORWARD: bool = false; + fn first_index(block_data: &mir::BasicBlockData<'_>) -> EffectIndex { + Effect::Early.at_index(block_data.statements.len()) + } + + /// Returns the next index for this direction. + fn next_index(idx: EffectIndex) -> EffectIndex { + match idx.effect { + Effect::Early => Effect::Primary.at_index(idx.statement_index), + Effect::Primary => Effect::Early.at_index(idx.statement_index - 1), + } + } + fn apply_effects_in_block<'mir, 'tcx, A>( analysis: &A, body: &mir::Body<'tcx>, @@ -128,83 +132,6 @@ impl Direction for Backward { } } - fn apply_effects_in_range<'tcx, A>( - analysis: &A, - state: &mut A::Domain, - block: BasicBlock, - block_data: &mir::BasicBlockData<'tcx>, - effects: RangeInclusive, - ) where - A: Analysis<'tcx>, - { - let (from, to) = (*effects.start(), *effects.end()); - let terminator_index = block_data.statements.len(); - - assert!(from.statement_index <= terminator_index); - assert!(!to.precedes_in_backward_order(from)); - - // Handle the statement (or terminator) at `from`. - - let next_effect = match from.effect { - // If we need to apply the terminator effect in all or in part, do so now. - _ if from.statement_index == terminator_index => { - let location = Location { block, statement_index: from.statement_index }; - let terminator = block_data.terminator(); - - if from.effect == Effect::Early { - analysis.apply_early_terminator_effect(state, terminator, location); - if to == Effect::Early.at_index(terminator_index) { - return; - } - } - - analysis.apply_primary_terminator_effect(state, terminator, location); - if to == Effect::Primary.at_index(terminator_index) { - return; - } - - // If `from.statement_index` is `0`, we will have hit one of the earlier comparisons - // with `to`. - from.statement_index - 1 - } - - Effect::Primary => { - let location = Location { block, statement_index: from.statement_index }; - let statement = &block_data.statements[from.statement_index]; - - analysis.apply_primary_statement_effect(state, statement, location); - if to == Effect::Primary.at_index(from.statement_index) { - return; - } - - from.statement_index - 1 - } - - Effect::Early => from.statement_index, - }; - - // Handle all statements between `first_unapplied_idx` and `to.statement_index`. - - for statement_index in (to.statement_index..next_effect).rev().map(|i| i + 1) { - let location = Location { block, statement_index }; - let statement = &block_data.statements[statement_index]; - analysis.apply_early_statement_effect(state, statement, location); - analysis.apply_primary_statement_effect(state, statement, location); - } - - // Handle the statement at `to`. - - let location = Location { block, statement_index: to.statement_index }; - let statement = &block_data.statements[to.statement_index]; - analysis.apply_early_statement_effect(state, statement, location); - - if to.effect == Effect::Early { - return; - } - - analysis.apply_primary_statement_effect(state, statement, location); - } - fn visit_results_in_block<'mir, 'tcx, A>( analysis: &A, state: &mut A::Domain, @@ -214,8 +141,6 @@ impl Direction for Backward { ) where A: Analysis<'tcx>, { - vis.visit_block_end(state); - let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); analysis.apply_early_terminator_effect(state, term, loc); @@ -230,8 +155,6 @@ impl Direction for Backward { analysis.apply_primary_statement_effect(state, stmt, loc); vis.visit_after_primary_statement_effect(analysis, state, stmt, loc); } - - vis.visit_block_start(state); } } @@ -241,6 +164,18 @@ pub struct Forward; impl Direction for Forward { const IS_FORWARD: bool = true; + fn first_index(_block_data: &mir::BasicBlockData<'_>) -> EffectIndex { + Effect::Early.at_index(0) + } + + /// Returns the next index for this direction. + fn next_index(idx: EffectIndex) -> EffectIndex { + match idx.effect { + Effect::Early => Effect::Primary.at_index(idx.statement_index), + Effect::Primary => Effect::Early.at_index(idx.statement_index + 1), + } + } + fn apply_effects_in_block<'mir, 'tcx, A>( analysis: &A, body: &mir::Body<'tcx>, @@ -310,80 +245,6 @@ impl Direction for Forward { } } - fn apply_effects_in_range<'tcx, A>( - analysis: &A, - state: &mut A::Domain, - block: BasicBlock, - block_data: &mir::BasicBlockData<'tcx>, - effects: RangeInclusive, - ) where - A: Analysis<'tcx>, - { - let (from, to) = (*effects.start(), *effects.end()); - let terminator_index = block_data.statements.len(); - - assert!(to.statement_index <= terminator_index); - assert!(!to.precedes_in_forward_order(from)); - - // If we have applied the before affect of the statement or terminator at `from` but not its - // after effect, do so now and start the loop below from the next statement. - - let first_unapplied_index = match from.effect { - Effect::Early => from.statement_index, - - Effect::Primary if from.statement_index == terminator_index => { - debug_assert_eq!(from, to); - - let location = Location { block, statement_index: terminator_index }; - let terminator = block_data.terminator(); - analysis.apply_primary_terminator_effect(state, terminator, location); - return; - } - - Effect::Primary => { - let location = Location { block, statement_index: from.statement_index }; - let statement = &block_data.statements[from.statement_index]; - analysis.apply_primary_statement_effect(state, statement, location); - - // If we only needed to apply the after effect of the statement at `idx`, we are - // done. - if from == to { - return; - } - - from.statement_index + 1 - } - }; - - // Handle all statements between `from` and `to` whose effects must be applied in full. - - for statement_index in first_unapplied_index..to.statement_index { - let location = Location { block, statement_index }; - let statement = &block_data.statements[statement_index]; - analysis.apply_early_statement_effect(state, statement, location); - analysis.apply_primary_statement_effect(state, statement, location); - } - - // Handle the statement or terminator at `to`. - - let location = Location { block, statement_index: to.statement_index }; - if to.statement_index == terminator_index { - let terminator = block_data.terminator(); - analysis.apply_early_terminator_effect(state, terminator, location); - - if to.effect == Effect::Primary { - analysis.apply_primary_terminator_effect(state, terminator, location); - } - } else { - let statement = &block_data.statements[to.statement_index]; - analysis.apply_early_statement_effect(state, statement, location); - - if to.effect == Effect::Primary { - analysis.apply_primary_statement_effect(state, statement, location); - } - } - } - fn visit_results_in_block<'mir, 'tcx, A>( analysis: &A, state: &mut A::Domain, @@ -393,8 +254,6 @@ impl Direction for Forward { ) where A: Analysis<'tcx>, { - vis.visit_block_start(state); - for (statement_index, stmt) in block_data.statements.iter().enumerate() { let loc = Location { block, statement_index }; analysis.apply_early_statement_effect(state, stmt, loc); @@ -409,7 +268,5 @@ impl Direction for Forward { vis.visit_after_early_terminator_effect(analysis, state, term, loc); analysis.apply_primary_terminator_effect(state, term, loc); vis.visit_after_primary_terminator_effect(analysis, state, term, loc); - - vis.visit_block_end(state); } } diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 6c0f2e8d73058..a95ab44c951d6 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -633,7 +633,7 @@ struct StateDiffCollector { after: Vec, } -impl StateDiffCollector { +impl StateDiffCollector { fn run<'tcx, A>( body: &Body<'tcx>, block: BasicBlock, @@ -645,7 +645,7 @@ impl StateDiffCollector { D: DebugWithContext, { let mut collector = StateDiffCollector { - prev_state: results.analysis.bottom_value(body), + prev_state: results.entry_states[block].clone(), after: vec![], before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]), }; @@ -660,18 +660,6 @@ where A: Analysis<'tcx>, A::Domain: DebugWithContext, { - fn visit_block_start(&mut self, state: &A::Domain) { - if A::Direction::IS_FORWARD { - self.prev_state.clone_from(state); - } - } - - fn visit_block_end(&mut self, state: &A::Domain) { - if A::Direction::IS_BACKWARD { - self.prev_state.clone_from(state); - } - } - fn visit_after_early_statement_effect( &mut self, analysis: &A, diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index 6445ba7ad27b6..211a1b8d05b71 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -32,13 +32,13 @@ //! //! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems -use std::cmp::Ordering; - use rustc_data_structures::work_queue::WorkQueue; use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use rustc_index::{Idx, IndexVec}; use rustc_middle::bug; -use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges, traversal}; +use rustc_middle::mir::{ + self, BasicBlock, BasicBlockData, CallReturnPlaces, Location, TerminatorEdges, traversal, +}; use rustc_middle::ty::TyCtxt; use tracing::error; @@ -125,6 +125,40 @@ pub trait Analysis<'tcx> { // `resume`). It's not obvious how to handle `yield` points in coroutines, however. fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain); + /// Given an `EffectIndex`, calls the appropriate `apply_*` method in the + /// {early,primary} x {statement,terminator} space. + /// + /// Do not override this; instead override one or more of the `apply_*` methods. + #[inline] + fn apply_effect<'mir>( + &self, + state: &mut Self::Domain, + block: BasicBlock, + block_data: &'mir BasicBlockData<'tcx>, + idx: EffectIndex, + ) { + let statement_index = idx.statement_index; + let terminator_index = block_data.statements.len(); + let loc = Location { block, statement_index }; + let is_terminator = statement_index == terminator_index; + + if !is_terminator { + let statement = &block_data.statements[statement_index]; + match idx.effect { + Effect::Early => self.apply_early_statement_effect(state, statement, loc), + Effect::Primary => self.apply_primary_statement_effect(state, statement, loc), + } + } else { + let terminator = block_data.terminator(); + match idx.effect { + Effect::Early => self.apply_early_terminator_effect(state, terminator, loc), + Effect::Primary => { + self.apply_primary_terminator_effect(state, terminator, loc); + } + } + } + } + /// Updates the current dataflow state with an "early" effect, i.e. one /// that occurs immediately before the given statement. /// @@ -402,41 +436,5 @@ pub struct EffectIndex { effect: Effect, } -impl EffectIndex { - fn next_in_forward_order(self) -> Self { - match self.effect { - Effect::Early => Effect::Primary.at_index(self.statement_index), - Effect::Primary => Effect::Early.at_index(self.statement_index + 1), - } - } - - fn next_in_backward_order(self) -> Self { - match self.effect { - Effect::Early => Effect::Primary.at_index(self.statement_index), - Effect::Primary => Effect::Early.at_index(self.statement_index - 1), - } - } - - /// Returns `true` if the effect at `self` should be applied earlier than the effect at `other` - /// in forward order. - fn precedes_in_forward_order(self, other: Self) -> bool { - let ord = self - .statement_index - .cmp(&other.statement_index) - .then_with(|| self.effect.cmp(&other.effect)); - ord == Ordering::Less - } - - /// Returns `true` if the effect at `self` should be applied earlier than the effect at `other` - /// in backward order. - fn precedes_in_backward_order(self, other: Self) -> bool { - let ord = other - .statement_index - .cmp(&self.statement_index) - .then_with(|| self.effect.cmp(&other.effect)); - ord == Ordering::Less - } -} - #[cfg(test)] mod tests; diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index dec273d39dd1a..86ea3a34ae0ea 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -139,11 +139,7 @@ impl MockAnalysis<'_, D> { SeekTarget::After(loc) => Effect::Primary.at_index(loc.statement_index), }; - let mut pos = if D::IS_FORWARD { - Effect::Early.at_index(0) - } else { - Effect::Early.at_index(self.body[block].statements.len()) - }; + let mut pos = D::first_index(&self.body[block]); loop { ret.insert(self.effect(pos)); @@ -152,11 +148,7 @@ impl MockAnalysis<'_, D> { return ret; } - if D::IS_FORWARD { - pos = pos.next_in_forward_order(); - } else { - pos = pos.next_in_backward_order(); - } + pos = D::next_index(pos); } } } diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index 46940c6ab62fc..b6827be3d8beb 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -46,8 +46,6 @@ pub trait ResultsVisitor<'tcx, A> where A: Analysis<'tcx>, { - fn visit_block_start(&mut self, _state: &A::Domain) {} - /// Called after the "early" effect of the given statement is applied to `state`. fn visit_after_early_statement_effect( &mut self, @@ -89,6 +87,4 @@ where _location: Location, ) { } - - fn visit_block_end(&mut self, _state: &A::Domain) {} }