blob: 7b577c2b9df4c47fd2defe9dc3a17fa3a8b65cd0 [file]
use rustc_middle::bug;
use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges};
use super::visitor::ResultsVisitor;
use super::{Analysis, Effect, EffectIndex, SwitchTargetIndex};
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,
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 `ResultsVisitor` to recompute the analysis domain values for
/// all locations in a basic block (starting from `entry_state` and to
/// visit them with `vis`.
fn visit_results_in_block<'mir, 'tcx, A>(
analysis: &A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
vis: &mut impl ResultsVisitor<'tcx, A>,
) where
A: Analysis<'tcx>;
}
/// Dataflow that runs from the exit of a block (terminator), to its entry (the first statement).
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>,
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
mut propagate: impl FnMut(BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>,
{
let terminator = block_data.terminator();
let location = Location { block, statement_index: block_data.statements.len() };
analysis.apply_early_terminator_effect(state, terminator, location);
analysis.apply_primary_terminator_effect(state, terminator, location);
for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
let location = Location { block, statement_index };
analysis.apply_early_statement_effect(state, statement, location);
analysis.apply_primary_statement_effect(state, statement, location);
}
let exit_state = state;
for pred in body.basic_blocks.predecessors()[block].iter().copied() {
match body[pred].terminator().kind {
// Apply terminator-specific edge effects.
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, drop, resume_arg, .. }
if resume == block || drop == Some(block) =>
{
let mut tmp = exit_state.clone();
analysis.apply_call_return_effect(
&mut tmp,
block,
CallReturnPlaces::Yield(resume_arg),
);
propagate(pred, &tmp);
}
mir::TerminatorKind::SwitchInt { ref targets, ref discr } => {
if let Some(_data) = analysis.get_switch_int_data(pred, targets, discr) {
bug!(
"SwitchInt edge effects are unsupported in backward dataflow analyses"
);
} else {
propagate(pred, exit_state)
}
}
_ => propagate(pred, exit_state),
}
}
}
fn visit_results_in_block<'mir, 'tcx, A>(
analysis: &A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
vis: &mut impl ResultsVisitor<'tcx, A>,
) where
A: Analysis<'tcx>,
{
let loc = Location { block, statement_index: block_data.statements.len() };
let term = block_data.terminator();
analysis.apply_early_terminator_effect(state, term, loc);
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);
for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
let loc = Location { block, statement_index };
analysis.apply_early_statement_effect(state, stmt, loc);
vis.visit_after_early_statement_effect(analysis, state, stmt, loc);
analysis.apply_primary_statement_effect(state, stmt, loc);
vis.visit_after_primary_statement_effect(analysis, state, stmt, loc);
}
}
}
/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
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>,
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
mut propagate: impl FnMut(BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>,
{
for (statement_index, statement) in block_data.statements.iter().enumerate() {
let location = Location { block, statement_index };
analysis.apply_early_statement_effect(state, statement, location);
analysis.apply_primary_statement_effect(state, statement, location);
}
let terminator = block_data.terminator();
let location = Location { block, statement_index: block_data.statements.len() };
analysis.apply_early_terminator_effect(state, terminator, location);
// Edges are obtained *before* calling `apply_primary_terminator_effect`.
let edges = analysis.get_terminator_edges(state, terminator, location);
analysis.apply_primary_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 } => {
if let Some(mut data) = analysis.get_switch_int_data(block, targets, discr) {
let mut tmp = analysis.bottom_value(body);
for (i, (_value, target)) in targets.iter().enumerate() {
tmp.clone_from(exit_state);
let target_idx = SwitchTargetIndex::Normal(i);
analysis.apply_switch_int_edge_effect(&mut tmp, &mut data, target_idx);
propagate(target, &tmp);
}
// Once we get to the final, "otherwise" branch, there is no need to preserve
// `exit_state`, so pass it directly to `apply_switch_int_edge_effect` to save
// a clone of the dataflow state.
analysis.apply_switch_int_edge_effect(
exit_state,
&mut data,
SwitchTargetIndex::Otherwise,
);
propagate(targets.otherwise(), exit_state);
} else {
for target in targets.all_targets() {
propagate(*target, exit_state);
}
}
}
}
}
fn visit_results_in_block<'mir, 'tcx, A>(
analysis: &A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
vis: &mut impl ResultsVisitor<'tcx, A>,
) where
A: Analysis<'tcx>,
{
for (statement_index, stmt) in block_data.statements.iter().enumerate() {
let loc = Location { block, statement_index };
analysis.apply_early_statement_effect(state, stmt, loc);
vis.visit_after_early_statement_effect(analysis, state, stmt, loc);
analysis.apply_primary_statement_effect(state, stmt, loc);
vis.visit_after_primary_statement_effect(analysis, state, stmt, loc);
}
let loc = Location { block, statement_index: block_data.statements.len() };
let term = block_data.terminator();
analysis.apply_early_terminator_effect(state, term, loc);
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);
}
}