Rollup merge of #133155 - nnethercote:yet-more-rustc_mir_dataflow-cleanups, r=cjgillot

Yet more `rustc_mir_dataflow` cleanups

A few more cleanups.

r? `@cjgillot`
This commit is contained in:
Michael Goulet 2024-11-26 12:03:41 -05:00 committed by GitHub
commit f010e2dc57
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 242 additions and 213 deletions

View File

@ -1,6 +1,7 @@
//! Random access inspection of the results of a dataflow analysis. //! Random access inspection of the results of a dataflow analysis.
use std::cmp::Ordering; use std::cmp::Ordering;
use std::ops::{Deref, DerefMut};
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
use rustc_index::bit_set::BitSet; use rustc_index::bit_set::BitSet;
@ -8,18 +9,63 @@ use rustc_middle::mir::{self, BasicBlock, Location};
use super::{Analysis, Direction, Effect, EffectIndex, Results}; use super::{Analysis, Direction, Effect, EffectIndex, Results};
/// Allows random access inspection of the results of a dataflow analysis. /// Some `ResultsCursor`s want to own a `Results`, and some want to borrow a `Results`, either
/// mutable or immutably. This type allows all of the above. It's similar to `Cow`.
pub enum ResultsHandle<'a, 'tcx, A>
where
A: Analysis<'tcx>,
{
Borrowed(&'a Results<'tcx, A>),
BorrowedMut(&'a mut Results<'tcx, A>),
Owned(Results<'tcx, A>),
}
impl<'tcx, A> Deref for ResultsHandle<'_, 'tcx, A>
where
A: Analysis<'tcx>,
{
type Target = Results<'tcx, A>;
fn deref(&self) -> &Results<'tcx, A> {
match self {
ResultsHandle::Borrowed(borrowed) => borrowed,
ResultsHandle::BorrowedMut(borrowed) => borrowed,
ResultsHandle::Owned(owned) => owned,
}
}
}
impl<'tcx, A> DerefMut for ResultsHandle<'_, 'tcx, A>
where
A: Analysis<'tcx>,
{
fn deref_mut(&mut self) -> &mut Results<'tcx, A> {
match self {
ResultsHandle::Borrowed(_borrowed) => {
panic!("tried to deref_mut a `ResultsHandle::Borrowed")
}
ResultsHandle::BorrowedMut(borrowed) => borrowed,
ResultsHandle::Owned(owned) => owned,
}
}
}
/// Allows random access inspection of the results of a dataflow analysis. Use this when you want
/// to inspect domain values only in certain locations; use `ResultsVisitor` if you want to inspect
/// domain values in many or all locations.
/// ///
/// This cursor only has linear performance within a basic block when its statements are visited in /// Because `Results` only has domain values for the entry of each basic block, these inspections
/// the same order as the `DIRECTION` of the analysis. In the worst case—when statements are /// involve some amount of domain value recomputations. This cursor only has linear performance
/// visited in *reverse* order—performance will be quadratic in the number of statements in the /// within a basic block when its statements are visited in the same order as the `DIRECTION` of
/// block. The order in which basic blocks are inspected has no impact on performance. /// the analysis. In the worst case—when statements are visited in *reverse* order—performance will
/// be quadratic in the number of statements in the block. The order in which basic blocks are
/// inspected has no impact on performance.
pub struct ResultsCursor<'mir, 'tcx, A> pub struct ResultsCursor<'mir, 'tcx, A>
where where
A: Analysis<'tcx>, A: Analysis<'tcx>,
{ {
body: &'mir mir::Body<'tcx>, body: &'mir mir::Body<'tcx>,
results: Results<'tcx, A>, results: ResultsHandle<'mir, 'tcx, A>,
state: A::Domain, state: A::Domain,
pos: CursorPosition, pos: CursorPosition,
@ -47,13 +93,8 @@ where
self.body self.body
} }
/// Unwraps this cursor, returning the underlying `Results`.
pub fn into_results(self) -> Results<'tcx, A> {
self.results
}
/// Returns a new cursor that can inspect `results`. /// Returns a new cursor that can inspect `results`.
pub fn new(body: &'mir mir::Body<'tcx>, results: Results<'tcx, A>) -> Self { pub fn new(body: &'mir mir::Body<'tcx>, results: ResultsHandle<'mir, 'tcx, A>) -> Self {
let bottom_value = results.analysis.bottom_value(body); let bottom_value = results.analysis.bottom_value(body);
ResultsCursor { ResultsCursor {
body, body,

View File

@ -9,10 +9,21 @@ use super::{Analysis, Effect, EffectIndex, Results, SwitchIntTarget};
pub trait Direction { pub trait Direction {
const IS_FORWARD: bool; const IS_FORWARD: bool;
const IS_BACKWARD: bool = !Self::IS_FORWARD; const IS_BACKWARD: bool = !Self::IS_FORWARD;
/// Applies all effects between the given `EffectIndex`s. /// Called by `iterate_to_fixpoint` during initial analysis computation.
fn apply_effects_in_block<'mir, 'tcx, A>(
analysis: &mut A,
body: &mir::Body<'tcx>,
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
propagate: impl FnMut(BasicBlock, &A::Domain),
) 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. /// `effects.start()` must precede or equal `effects.end()` in this direction.
fn apply_effects_in_range<'tcx, A>( fn apply_effects_in_range<'tcx, A>(
@ -24,15 +35,9 @@ pub trait Direction {
) where ) where
A: Analysis<'tcx>; A: Analysis<'tcx>;
fn apply_effects_in_block<'mir, 'tcx, A>( /// Called by `ResultsVisitor` to recompute the analysis domain values for
analysis: &mut A, /// all locations in a basic block (starting from the entry value stored
state: &mut A::Domain, /// in `Results`) and to visit them with `vis`.
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
) -> TerminatorEdges<'mir, 'tcx>
where
A: Analysis<'tcx>;
fn visit_results_in_block<'mir, 'tcx, A>( fn visit_results_in_block<'mir, 'tcx, A>(
state: &mut A::Domain, state: &mut A::Domain,
block: BasicBlock, block: BasicBlock,
@ -41,16 +46,6 @@ pub trait Direction {
vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
) where ) where
A: Analysis<'tcx>; A: Analysis<'tcx>;
fn join_state_into_successors_of<'tcx, A>(
analysis: &mut A,
body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
block: BasicBlock,
edges: TerminatorEdges<'_, 'tcx>,
propagate: impl FnMut(BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>;
} }
/// Dataflow that runs from the exit of a block (terminator), to its entry (the first statement). /// Dataflow that runs from the exit of a block (terminator), to its entry (the first statement).
@ -61,23 +56,84 @@ impl Direction for Backward {
fn apply_effects_in_block<'mir, 'tcx, A>( fn apply_effects_in_block<'mir, 'tcx, A>(
analysis: &mut A, analysis: &mut A,
body: &mir::Body<'tcx>,
state: &mut A::Domain, state: &mut A::Domain,
block: BasicBlock, block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>, block_data: &'mir mir::BasicBlockData<'tcx>,
) -> TerminatorEdges<'mir, 'tcx> mut propagate: impl FnMut(BasicBlock, &A::Domain),
where ) where
A: Analysis<'tcx>, A: Analysis<'tcx>,
{ {
let terminator = block_data.terminator(); let terminator = block_data.terminator();
let location = Location { block, statement_index: block_data.statements.len() }; let location = Location { block, statement_index: block_data.statements.len() };
analysis.apply_before_terminator_effect(state, terminator, location); analysis.apply_before_terminator_effect(state, terminator, location);
let edges = analysis.apply_terminator_effect(state, terminator, location); analysis.apply_terminator_effect(state, terminator, location);
for (statement_index, statement) in block_data.statements.iter().enumerate().rev() { for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
let location = Location { block, statement_index }; let location = Location { block, statement_index };
analysis.apply_before_statement_effect(state, statement, location); analysis.apply_before_statement_effect(state, statement, location);
analysis.apply_statement_effect(state, statement, location); analysis.apply_statement_effect(state, statement, location);
} }
edges
let exit_state = state;
for pred in body.basic_blocks.predecessors()[block].iter().copied() {
match body[pred].terminator().kind {
// Apply terminator-specific edge effects.
//
// FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally.
mir::TerminatorKind::Call { destination, target: Some(dest), .. }
if dest == block =>
{
let mut tmp = exit_state.clone();
analysis.apply_call_return_effect(
&mut tmp,
pred,
CallReturnPlaces::Call(destination),
);
propagate(pred, &tmp);
}
mir::TerminatorKind::InlineAsm { ref targets, ref operands, .. }
if targets.contains(&block) =>
{
let mut tmp = exit_state.clone();
analysis.apply_call_return_effect(
&mut tmp,
pred,
CallReturnPlaces::InlineAsm(operands),
);
propagate(pred, &tmp);
}
mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == block => {
let mut tmp = exit_state.clone();
analysis.apply_call_return_effect(
&mut tmp,
resume,
CallReturnPlaces::Yield(resume_arg),
);
propagate(pred, &tmp);
}
mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
let mut applier = BackwardSwitchIntEdgeEffectsApplier {
body,
pred,
exit_state,
block,
propagate: &mut propagate,
effects_applied: false,
};
analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);
if !applier.effects_applied {
propagate(pred, exit_state)
}
}
_ => propagate(pred, exit_state),
}
}
} }
fn apply_effects_in_range<'tcx, A>( fn apply_effects_in_range<'tcx, A>(
@ -170,7 +226,6 @@ impl Direction for Backward {
vis.visit_block_end(state); vis.visit_block_end(state);
// Terminator
let loc = Location { block, statement_index: block_data.statements.len() }; let loc = Location { block, statement_index: block_data.statements.len() };
let term = block_data.terminator(); let term = block_data.terminator();
results.analysis.apply_before_terminator_effect(state, term, loc); results.analysis.apply_before_terminator_effect(state, term, loc);
@ -188,82 +243,13 @@ impl Direction for Backward {
vis.visit_block_start(state); vis.visit_block_start(state);
} }
fn join_state_into_successors_of<'tcx, A>(
analysis: &mut A,
body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
bb: BasicBlock,
_edges: TerminatorEdges<'_, 'tcx>,
mut propagate: impl FnMut(BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>,
{
for pred in body.basic_blocks.predecessors()[bb].iter().copied() {
match body[pred].terminator().kind {
// Apply terminator-specific edge effects.
//
// FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally.
mir::TerminatorKind::Call { destination, target: Some(dest), .. } if dest == bb => {
let mut tmp = exit_state.clone();
analysis.apply_call_return_effect(
&mut tmp,
pred,
CallReturnPlaces::Call(destination),
);
propagate(pred, &tmp);
}
mir::TerminatorKind::InlineAsm { ref targets, ref operands, .. }
if targets.contains(&bb) =>
{
let mut tmp = exit_state.clone();
analysis.apply_call_return_effect(
&mut tmp,
pred,
CallReturnPlaces::InlineAsm(operands),
);
propagate(pred, &tmp);
}
mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == bb => {
let mut tmp = exit_state.clone();
analysis.apply_call_return_effect(
&mut tmp,
resume,
CallReturnPlaces::Yield(resume_arg),
);
propagate(pred, &tmp);
}
mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
let mut applier = BackwardSwitchIntEdgeEffectsApplier {
body,
pred,
exit_state,
bb,
propagate: &mut propagate,
effects_applied: false,
};
analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);
if !applier.effects_applied {
propagate(pred, exit_state)
}
}
_ => propagate(pred, exit_state),
}
}
}
} }
struct BackwardSwitchIntEdgeEffectsApplier<'mir, 'tcx, D, F> { struct BackwardSwitchIntEdgeEffectsApplier<'mir, 'tcx, D, F> {
body: &'mir mir::Body<'tcx>, body: &'mir mir::Body<'tcx>,
pred: BasicBlock, pred: BasicBlock,
exit_state: &'mir mut D, exit_state: &'mir mut D,
bb: BasicBlock, block: BasicBlock,
propagate: &'mir mut F, propagate: &'mir mut F,
effects_applied: bool, effects_applied: bool,
} }
@ -276,8 +262,8 @@ where
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) { fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
assert!(!self.effects_applied); assert!(!self.effects_applied);
let values = &self.body.basic_blocks.switch_sources()[&(self.bb, self.pred)]; let values = &self.body.basic_blocks.switch_sources()[&(self.block, self.pred)];
let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.bb }); let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.block });
let mut tmp = None; let mut tmp = None;
for target in targets { for target in targets {
@ -298,11 +284,12 @@ impl Direction for Forward {
fn apply_effects_in_block<'mir, 'tcx, A>( fn apply_effects_in_block<'mir, 'tcx, A>(
analysis: &mut A, analysis: &mut A,
_body: &mir::Body<'tcx>,
state: &mut A::Domain, state: &mut A::Domain,
block: BasicBlock, block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>, block_data: &'mir mir::BasicBlockData<'tcx>,
) -> TerminatorEdges<'mir, 'tcx> mut propagate: impl FnMut(BasicBlock, &A::Domain),
where ) where
A: Analysis<'tcx>, A: Analysis<'tcx>,
{ {
for (statement_index, statement) in block_data.statements.iter().enumerate() { for (statement_index, statement) in block_data.statements.iter().enumerate() {
@ -313,7 +300,53 @@ impl Direction for Forward {
let terminator = block_data.terminator(); let terminator = block_data.terminator();
let location = Location { block, statement_index: block_data.statements.len() }; let location = Location { block, statement_index: block_data.statements.len() };
analysis.apply_before_terminator_effect(state, terminator, location); analysis.apply_before_terminator_effect(state, terminator, location);
analysis.apply_terminator_effect(state, terminator, location) let edges = analysis.apply_terminator_effect(state, terminator, location);
let exit_state = state;
match edges {
TerminatorEdges::None => {}
TerminatorEdges::Single(target) => propagate(target, exit_state),
TerminatorEdges::Double(target, unwind) => {
propagate(target, exit_state);
propagate(unwind, exit_state);
}
TerminatorEdges::AssignOnReturn { return_, cleanup, place } => {
// This must be done *first*, otherwise the unwind path will see the assignments.
if let Some(cleanup) = cleanup {
propagate(cleanup, exit_state);
}
if !return_.is_empty() {
analysis.apply_call_return_effect(exit_state, block, place);
for &target in return_ {
propagate(target, exit_state);
}
}
}
TerminatorEdges::SwitchInt { targets, discr } => {
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
exit_state,
targets,
propagate,
effects_applied: false,
};
analysis.apply_switch_int_edge_effects(block, discr, &mut applier);
let ForwardSwitchIntEdgeEffectsApplier {
exit_state,
mut propagate,
effects_applied,
..
} = applier;
if !effects_applied {
for target in targets.all_targets() {
propagate(*target, exit_state);
}
}
}
}
} }
fn apply_effects_in_range<'tcx, A>( fn apply_effects_in_range<'tcx, A>(
@ -351,7 +384,8 @@ impl Direction for Forward {
let statement = &block_data.statements[from.statement_index]; let statement = &block_data.statements[from.statement_index];
analysis.apply_statement_effect(state, statement, location); analysis.apply_statement_effect(state, statement, location);
// If we only needed to apply the after effect of the statement at `idx`, we are done. // If we only needed to apply the after effect of the statement at `idx`, we are
// done.
if from == to { if from == to {
return; return;
} }
@ -419,62 +453,6 @@ impl Direction for Forward {
vis.visit_block_end(state); vis.visit_block_end(state);
} }
fn join_state_into_successors_of<'tcx, A>(
analysis: &mut A,
_body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
bb: BasicBlock,
edges: TerminatorEdges<'_, 'tcx>,
mut propagate: impl FnMut(BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>,
{
match edges {
TerminatorEdges::None => {}
TerminatorEdges::Single(target) => propagate(target, exit_state),
TerminatorEdges::Double(target, unwind) => {
propagate(target, exit_state);
propagate(unwind, exit_state);
}
TerminatorEdges::AssignOnReturn { return_, cleanup, place } => {
// This must be done *first*, otherwise the unwind path will see the assignments.
if let Some(cleanup) = cleanup {
propagate(cleanup, exit_state);
}
if !return_.is_empty() {
analysis.apply_call_return_effect(exit_state, bb, place);
for &target in return_ {
propagate(target, exit_state);
}
}
}
TerminatorEdges::SwitchInt { targets, discr } => {
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
exit_state,
targets,
propagate,
effects_applied: false,
};
analysis.apply_switch_int_edge_effects(bb, discr, &mut applier);
let ForwardSwitchIntEdgeEffectsApplier {
exit_state,
mut propagate,
effects_applied,
..
} = applier;
if !effects_applied {
for target in targets.all_targets() {
propagate(*target, exit_state);
}
}
}
}
}
} }
struct ForwardSwitchIntEdgeEffectsApplier<'mir, D, F> { struct ForwardSwitchIntEdgeEffectsApplier<'mir, D, F> {

View File

@ -47,20 +47,16 @@ where
{ {
pub(crate) fn new( pub(crate) fn new(
body: &'mir Body<'tcx>, body: &'mir Body<'tcx>,
results: Results<'tcx, A>, results: &'mir Results<'tcx, A>,
style: OutputStyle, style: OutputStyle,
) -> Self { ) -> Self {
let reachable = mir::traversal::reachable_as_bitset(body); let reachable = mir::traversal::reachable_as_bitset(body);
Formatter { cursor: results.into_results_cursor(body).into(), style, reachable } Formatter { cursor: results.as_results_cursor(body).into(), style, reachable }
} }
fn body(&self) -> &'mir Body<'tcx> { fn body(&self) -> &'mir Body<'tcx> {
self.cursor.borrow().body() self.cursor.borrow().body()
} }
pub(crate) fn into_results(self) -> Results<'tcx, A> {
self.cursor.into_inner().into_results()
}
} }
/// A pair of a basic block and an index into that basic blocks `successors`. /// A pair of a basic block and an index into that basic blocks `successors`.

View File

@ -8,8 +8,9 @@
//! The `impls` module contains several examples of dataflow analyses. //! The `impls` module contains several examples of dataflow analyses.
//! //!
//! Then call `iterate_to_fixpoint` on your type that impls `Analysis` to get a `Results`. From //! Then call `iterate_to_fixpoint` on your type that impls `Analysis` to get a `Results`. From
//! there, you can use a `ResultsCursor` to inspect the fixpoint solution to your dataflow problem, //! there, you can use a `ResultsCursor` to inspect the fixpoint solution to your dataflow problem
//! or implement the `ResultsVisitor` interface and use `visit_results`. The following example uses //! (good for inspecting a small number of locations), or implement the `ResultsVisitor` interface
//! and use `visit_results` (good for inspecting many or all locations). The following example uses
//! the `ResultsCursor` approach. //! the `ResultsCursor` approach.
//! //!
//! ```ignore (cross-crate-imports) //! ```ignore (cross-crate-imports)
@ -278,22 +279,17 @@ pub trait Analysis<'tcx> {
// every iteration. // every iteration.
let mut state = self.bottom_value(body); let mut state = self.bottom_value(body);
while let Some(bb) = dirty_queue.pop() { while let Some(bb) = dirty_queue.pop() {
let bb_data = &body[bb];
// Set the state to the entry state of the block. // Set the state to the entry state of the block.
// This is equivalent to `state = entry_sets[bb].clone()`, // This is equivalent to `state = entry_sets[bb].clone()`,
// but it saves an allocation, thus improving compile times. // but it saves an allocation, thus improving compile times.
state.clone_from(&entry_sets[bb]); state.clone_from(&entry_sets[bb]);
// Apply the block transfer function, using the cached one if it exists. Self::Direction::apply_effects_in_block(
let edges = Self::Direction::apply_effects_in_block(&mut self, &mut state, bb, bb_data);
Self::Direction::join_state_into_successors_of(
&mut self, &mut self,
body, body,
&mut state, &mut state,
bb, bb,
edges, &body[bb],
|target: BasicBlock, state: &Self::Domain| { |target: BasicBlock, state: &Self::Domain| {
let set_changed = entry_sets[target].join(state); let set_changed = entry_sets[target].join(state);
if set_changed { if set_changed {
@ -306,14 +302,13 @@ pub trait Analysis<'tcx> {
let results = Results { analysis: self, entry_sets }; let results = Results { analysis: self, entry_sets };
if tcx.sess.opts.unstable_opts.dump_mir_dataflow { if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
let (res, results) = write_graphviz_results(tcx, body, results, pass_name); let res = write_graphviz_results(tcx, body, &results, pass_name);
if let Err(e) = res { if let Err(e) = res {
error!("Failed to write graphviz dataflow results: {}", e); error!("Failed to write graphviz dataflow results: {}", e);
} }
results
} else {
results
} }
results
} }
} }

View File

@ -17,10 +17,13 @@ use super::{Analysis, ResultsCursor, ResultsVisitor, graphviz, visit_results};
use crate::errors::{ use crate::errors::{
DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter, DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
}; };
use crate::framework::cursor::ResultsHandle;
pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>; pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>;
/// A dataflow analysis that has converged to fixpoint. /// A dataflow analysis that has converged to fixpoint. It only holds the domain values at the
/// entry of each basic block. Domain values in other parts of the block are recomputed on the fly
/// by visitors (i.e. `ResultsCursor`, or `ResultsVisitor` impls).
#[derive(Clone)] #[derive(Clone)]
pub struct Results<'tcx, A> pub struct Results<'tcx, A>
where where
@ -34,12 +37,30 @@ impl<'tcx, A> Results<'tcx, A>
where where
A: Analysis<'tcx>, A: Analysis<'tcx>,
{ {
/// Creates a `ResultsCursor` that can inspect these `Results`. /// Creates a `ResultsCursor` that can inspect these `Results`. Immutably borrows the `Results`,
/// which is appropriate when the `Results` is used outside the cursor.
pub fn as_results_cursor<'mir>(
&'mir self,
body: &'mir mir::Body<'tcx>,
) -> ResultsCursor<'mir, 'tcx, A> {
ResultsCursor::new(body, ResultsHandle::Borrowed(self))
}
/// Creates a `ResultsCursor` that can mutate these `Results`. Mutably borrows the `Results`,
/// which is appropriate when the `Results` is used outside the cursor.
pub fn as_results_cursor_mut<'mir>(
&'mir mut self,
body: &'mir mir::Body<'tcx>,
) -> ResultsCursor<'mir, 'tcx, A> {
ResultsCursor::new(body, ResultsHandle::BorrowedMut(self))
}
/// Creates a `ResultsCursor` that takes ownership of the `Results`.
pub fn into_results_cursor<'mir>( pub fn into_results_cursor<'mir>(
self, self,
body: &'mir mir::Body<'tcx>, body: &'mir mir::Body<'tcx>,
) -> ResultsCursor<'mir, 'tcx, A> { ) -> ResultsCursor<'mir, 'tcx, A> {
ResultsCursor::new(body, self) ResultsCursor::new(body, ResultsHandle::Owned(self))
} }
/// Gets the dataflow state for the given block. /// Gets the dataflow state for the given block.
@ -74,9 +95,9 @@ where
pub(super) fn write_graphviz_results<'tcx, A>( pub(super) fn write_graphviz_results<'tcx, A>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>, body: &mir::Body<'tcx>,
results: Results<'tcx, A>, results: &Results<'tcx, A>,
pass_name: Option<&'static str>, pass_name: Option<&'static str>,
) -> (std::io::Result<()>, Results<'tcx, A>) ) -> std::io::Result<()>
where where
A: Analysis<'tcx>, A: Analysis<'tcx>,
A::Domain: DebugWithContext<A>, A::Domain: DebugWithContext<A>,
@ -87,7 +108,7 @@ where
let def_id = body.source.def_id(); let def_id = body.source.def_id();
let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else { let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else {
// Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse` // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
return (Ok(()), results); return Ok(());
}; };
let file = try { let file = try {
@ -104,12 +125,12 @@ where
create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
} }
_ => return (Ok(()), results), _ => return Ok(()),
} }
}; };
let mut file = match file { let mut file = match file {
Ok(f) => f, Ok(f) => f,
Err(e) => return (Err(e), results), Err(e) => return Err(e),
}; };
let style = match attrs.formatter { let style = match attrs.formatter {
@ -132,7 +153,7 @@ where
file.write_all(&buf)?; file.write_all(&buf)?;
}; };
(lhs, graphviz.into_results()) lhs
} }
#[derive(Default)] #[derive(Default)]

View File

@ -26,7 +26,9 @@ pub fn visit_results<'mir, 'tcx, A>(
} }
} }
/// A visitor over the results of an `Analysis`. /// A visitor over the results of an `Analysis`. Use this when you want to inspect domain values in
/// many or all locations; use `ResultsCursor` if you want to inspect domain values only in certain
/// locations.
pub trait ResultsVisitor<'mir, 'tcx, A> pub trait ResultsVisitor<'mir, 'tcx, A>
where where
A: Analysis<'tcx>, A: Analysis<'tcx>,

View File

@ -15,7 +15,7 @@ use crate::{Analysis, GenKill};
pub struct MaybeBorrowedLocals; pub struct MaybeBorrowedLocals;
impl MaybeBorrowedLocals { impl MaybeBorrowedLocals {
pub(super) fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T> { pub(super) fn transfer_function<'a, T>(trans: &'a mut T) -> TransferFunction<'a, T> {
TransferFunction { trans } TransferFunction { trans }
} }
} }
@ -39,7 +39,7 @@ impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals {
statement: &Statement<'tcx>, statement: &Statement<'tcx>,
location: Location, location: Location,
) { ) {
self.transfer_function(trans).visit_statement(statement, location); Self::transfer_function(trans).visit_statement(statement, location);
} }
fn apply_terminator_effect<'mir>( fn apply_terminator_effect<'mir>(
@ -48,7 +48,7 @@ impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals {
terminator: &'mir Terminator<'tcx>, terminator: &'mir Terminator<'tcx>,
location: Location, location: Location,
) -> TerminatorEdges<'mir, 'tcx> { ) -> TerminatorEdges<'mir, 'tcx> {
self.transfer_function(trans).visit_terminator(terminator, location); Self::transfer_function(trans).visit_terminator(terminator, location);
terminator.edges() terminator.edges()
} }
} }

View File

@ -135,7 +135,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
loc: Location, loc: Location,
) { ) {
// If a place is borrowed in a statement, it needs storage for that statement. // If a place is borrowed in a statement, it needs storage for that statement.
self.borrowed_locals.mut_analysis().apply_statement_effect(trans, stmt, loc); MaybeBorrowedLocals::transfer_function(trans).visit_statement(stmt, loc);
match &stmt.kind { match &stmt.kind {
StatementKind::StorageDead(l) => trans.kill(*l), StatementKind::StorageDead(l) => trans.kill(*l),
@ -180,10 +180,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
loc: Location, loc: Location,
) { ) {
// If a place is borrowed in a terminator, it needs storage for that terminator. // If a place is borrowed in a terminator, it needs storage for that terminator.
self.borrowed_locals MaybeBorrowedLocals::transfer_function(trans).visit_terminator(terminator, loc);
.mut_analysis()
.transfer_function(trans)
.visit_terminator(terminator, loc);
match &terminator.kind { match &terminator.kind {
TerminatorKind::Call { destination, .. } => { TerminatorKind::Call { destination, .. } => {

View File

@ -676,12 +676,11 @@ fn locals_live_across_suspend_points<'tcx>(
let mut borrowed_locals_cursor = borrowed_locals_results.clone().into_results_cursor(body); let mut borrowed_locals_cursor = borrowed_locals_results.clone().into_results_cursor(body);
// Calculate the MIR locals that we actually need to keep storage around // Calculate the MIR locals that we need to keep storage around for.
// for. let mut requires_storage_results =
let mut requires_storage_cursor =
MaybeRequiresStorage::new(borrowed_locals_results.into_results_cursor(body)) MaybeRequiresStorage::new(borrowed_locals_results.into_results_cursor(body))
.iterate_to_fixpoint(tcx, body, None) .iterate_to_fixpoint(tcx, body, None);
.into_results_cursor(body); let mut requires_storage_cursor = requires_storage_results.as_results_cursor_mut(body);
// Calculate the liveness of MIR locals ignoring borrows. // Calculate the liveness of MIR locals ignoring borrows.
let mut liveness = let mut liveness =
@ -754,7 +753,7 @@ fn locals_live_across_suspend_points<'tcx>(
body, body,
&saved_locals, &saved_locals,
always_live_locals.clone(), always_live_locals.clone(),
requires_storage_cursor.into_results(), requires_storage_results,
); );
LivenessInfo { LivenessInfo {