mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-10 06:47:34 +00:00
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:
commit
f010e2dc57
@ -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,
|
||||||
|
@ -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> {
|
||||||
|
@ -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`.
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)]
|
||||||
|
@ -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>,
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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, .. } => {
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user