use std::ops::RangeInclusive;
use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges};
use super::visitor::ResultsVisitor;
use super::{Analysis, Effect, EffectIndex, Results, SwitchIntTarget};
pub trait Direction {
const IS_FORWARD: bool;
const IS_BACKWARD: bool = !Self::IS_FORWARD;
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>;
fn apply_effects_in_range<'tcx, A>(
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
effects: RangeInclusive<EffectIndex>,
) where
A: Analysis<'tcx>;
fn visit_results_in_block<'mir, 'tcx, A>(
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
results: &mut Results<'tcx, A>,
vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
) where
A: Analysis<'tcx>;
}
pub struct Backward;
impl Direction for Backward {
const IS_FORWARD: bool = false;
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>,
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 {
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 } => {
if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
let values = &body.basic_blocks.switch_sources()[&(block, pred)];
let targets =
values.iter().map(|&value| SwitchIntTarget { value, target: block });
let mut tmp = analysis.bottom_value(body);
for target in targets {
tmp.clone_from(&exit_state);
analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, target);
propagate(pred, &tmp);
}
} else {
propagate(pred, exit_state)
}
}
_ => propagate(pred, exit_state),
}
}
}
fn apply_effects_in_range<'tcx, A>(
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
effects: RangeInclusive<EffectIndex>,
) 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));
let next_effect = match from.effect {
_ 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;
}
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,
};
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);
}
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>(
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
results: &mut Results<'tcx, A>,
vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
) where
A: Analysis<'tcx>,
{
state.clone_from(results.entry_set_for_block(block));
vis.visit_block_end(state);
let loc = Location { block, statement_index: block_data.statements.len() };
let term = block_data.terminator();
results.analysis.apply_early_terminator_effect(state, term, loc);
vis.visit_after_early_terminator_effect(results, state, term, loc);
results.analysis.apply_primary_terminator_effect(state, term, loc);
vis.visit_after_primary_terminator_effect(results, state, term, loc);
for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
let loc = Location { block, statement_index };
results.analysis.apply_early_statement_effect(state, stmt, loc);
vis.visit_after_early_statement_effect(results, state, stmt, loc);
results.analysis.apply_primary_statement_effect(state, stmt, loc);
vis.visit_after_primary_statement_effect(results, state, stmt, loc);
}
vis.visit_block_start(state);
}
}
pub struct Forward;
impl Direction for Forward {
const IS_FORWARD: bool = true;
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>,
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);
let edges = 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 } => {
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, discr) {
let mut tmp = analysis.bottom_value(body);
for (value, target) in targets.iter() {
tmp.clone_from(&exit_state);
analysis.apply_switch_int_edge_effect(
&mut data,
&mut tmp,
SwitchIntTarget { value: Some(value), target },
);
propagate(target, &tmp);
}
let otherwise = targets.otherwise();
analysis.apply_switch_int_edge_effect(&mut data, exit_state, SwitchIntTarget {
value: None,
target: otherwise,
});
propagate(otherwise, exit_state);
} else {
for target in targets.all_targets() {
propagate(*target, exit_state);
}
}
}
}
}
fn apply_effects_in_range<'tcx, A>(
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
effects: RangeInclusive<EffectIndex>,
) 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));
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 from == to {
return;
}
from.statement_index + 1
}
};
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);
}
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>(
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
results: &mut Results<'tcx, A>,
vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
) where
A: Analysis<'tcx>,
{
state.clone_from(results.entry_set_for_block(block));
vis.visit_block_start(state);
for (statement_index, stmt) in block_data.statements.iter().enumerate() {
let loc = Location { block, statement_index };
results.analysis.apply_early_statement_effect(state, stmt, loc);
vis.visit_after_early_statement_effect(results, state, stmt, loc);
results.analysis.apply_primary_statement_effect(state, stmt, loc);
vis.visit_after_primary_statement_effect(results, state, stmt, loc);
}
let loc = Location { block, statement_index: block_data.statements.len() };
let term = block_data.terminator();
results.analysis.apply_early_terminator_effect(state, term, loc);
vis.visit_after_early_terminator_effect(results, state, term, loc);
results.analysis.apply_primary_terminator_effect(state, term, loc);
vis.visit_after_primary_terminator_effect(results, state, term, loc);
vis.visit_block_end(state);
}
}