2022-10-08 23:47:59 +01:00
|
|
|
use rustc_middle::mir::{self, BasicBlock, Location, SwitchTargets, UnwindAction};
|
2020-09-26 13:12:51 -07:00
|
|
|
use rustc_middle::ty::TyCtxt;
|
2020-03-22 12:09:40 -07:00
|
|
|
use std::ops::RangeInclusive;
|
|
|
|
|
|
|
|
|
|
use super::visitor::{ResultsVisitable, ResultsVisitor};
|
2021-08-30 01:23:33 +01:00
|
|
|
use super::{
|
|
|
|
|
Analysis, CallReturnPlaces, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget,
|
|
|
|
|
};
|
2020-03-22 12:09:40 -07:00
|
|
|
|
|
|
|
|
pub trait Direction {
|
2022-06-05 00:00:00 +00:00
|
|
|
const IS_FORWARD: bool;
|
2020-03-22 12:09:40 -07:00
|
|
|
|
2022-06-05 00:00:00 +00:00
|
|
|
const IS_BACKWARD: bool = !Self::IS_FORWARD;
|
2020-03-22 12:09:40 -07:00
|
|
|
|
|
|
|
|
/// Applies all effects between the given `EffectIndex`s.
|
|
|
|
|
///
|
|
|
|
|
/// `effects.start()` must precede or equal `effects.end()` in this direction.
|
2021-12-14 12:02:45 -05:00
|
|
|
fn apply_effects_in_range<'tcx, A>(
|
2020-03-22 12:09:40 -07:00
|
|
|
analysis: &A,
|
2020-08-27 23:02:46 -07:00
|
|
|
state: &mut A::Domain,
|
2020-03-22 12:09:40 -07:00
|
|
|
block: BasicBlock,
|
|
|
|
|
block_data: &mir::BasicBlockData<'tcx>,
|
|
|
|
|
effects: RangeInclusive<EffectIndex>,
|
|
|
|
|
) where
|
|
|
|
|
A: Analysis<'tcx>;
|
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
fn apply_effects_in_block<'tcx, A>(
|
2020-03-22 12:09:40 -07:00
|
|
|
analysis: &A,
|
2020-08-27 23:02:46 -07:00
|
|
|
state: &mut A::Domain,
|
2020-03-22 12:09:40 -07:00
|
|
|
block: BasicBlock,
|
|
|
|
|
block_data: &mir::BasicBlockData<'tcx>,
|
|
|
|
|
) where
|
|
|
|
|
A: Analysis<'tcx>;
|
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
fn gen_kill_effects_in_block<'tcx, A>(
|
2020-03-22 12:09:40 -07:00
|
|
|
analysis: &A,
|
|
|
|
|
trans: &mut GenKillSet<A::Idx>,
|
|
|
|
|
block: BasicBlock,
|
|
|
|
|
block_data: &mir::BasicBlockData<'tcx>,
|
|
|
|
|
) where
|
|
|
|
|
A: GenKillAnalysis<'tcx>;
|
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
fn visit_results_in_block<'mir, 'tcx, F, R>(
|
2020-03-22 12:09:40 -07:00
|
|
|
state: &mut F,
|
|
|
|
|
block: BasicBlock,
|
|
|
|
|
block_data: &'mir mir::BasicBlockData<'tcx>,
|
|
|
|
|
results: &R,
|
|
|
|
|
vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
|
|
|
|
|
) where
|
|
|
|
|
R: ResultsVisitable<'tcx, FlowState = F>;
|
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
fn join_state_into_successors_of<'tcx, A>(
|
2020-03-22 12:09:40 -07:00
|
|
|
analysis: &A,
|
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
|
body: &mir::Body<'tcx>,
|
2020-08-27 23:02:46 -07:00
|
|
|
exit_state: &mut A::Domain,
|
2020-03-22 12:09:40 -07:00
|
|
|
block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
|
2020-08-27 23:02:46 -07:00
|
|
|
propagate: impl FnMut(BasicBlock, &A::Domain),
|
2020-03-22 12:09:40 -07:00
|
|
|
) where
|
|
|
|
|
A: Analysis<'tcx>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Dataflow that runs from the exit of a block (the terminator), to its entry (the first statement).
|
|
|
|
|
pub struct Backward;
|
|
|
|
|
|
|
|
|
|
impl Direction for Backward {
|
2022-06-05 00:00:00 +00:00
|
|
|
const IS_FORWARD: bool = false;
|
2020-03-22 12:09:40 -07:00
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
fn apply_effects_in_block<'tcx, A>(
|
2020-03-22 12:09:40 -07:00
|
|
|
analysis: &A,
|
2020-08-27 23:02:46 -07:00
|
|
|
state: &mut A::Domain,
|
2020-03-22 12:09:40 -07:00
|
|
|
block: BasicBlock,
|
|
|
|
|
block_data: &mir::BasicBlockData<'tcx>,
|
|
|
|
|
) where
|
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
|
{
|
|
|
|
|
let terminator = block_data.terminator();
|
|
|
|
|
let location = Location { block, statement_index: block_data.statements.len() };
|
|
|
|
|
analysis.apply_before_terminator_effect(state, terminator, location);
|
|
|
|
|
analysis.apply_terminator_effect(state, terminator, location);
|
|
|
|
|
|
|
|
|
|
for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
|
|
|
|
|
let location = Location { block, statement_index };
|
|
|
|
|
analysis.apply_before_statement_effect(state, statement, location);
|
|
|
|
|
analysis.apply_statement_effect(state, statement, location);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
fn gen_kill_effects_in_block<'tcx, A>(
|
2020-03-22 12:09:40 -07:00
|
|
|
analysis: &A,
|
|
|
|
|
trans: &mut GenKillSet<A::Idx>,
|
|
|
|
|
block: BasicBlock,
|
|
|
|
|
block_data: &mir::BasicBlockData<'tcx>,
|
|
|
|
|
) where
|
|
|
|
|
A: GenKillAnalysis<'tcx>,
|
|
|
|
|
{
|
|
|
|
|
let terminator = block_data.terminator();
|
|
|
|
|
let location = Location { block, statement_index: block_data.statements.len() };
|
|
|
|
|
analysis.before_terminator_effect(trans, terminator, location);
|
|
|
|
|
analysis.terminator_effect(trans, terminator, location);
|
|
|
|
|
|
|
|
|
|
for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
|
|
|
|
|
let location = Location { block, statement_index };
|
|
|
|
|
analysis.before_statement_effect(trans, statement, location);
|
|
|
|
|
analysis.statement_effect(trans, statement, location);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
fn apply_effects_in_range<'tcx, A>(
|
2020-03-22 12:09:40 -07:00
|
|
|
analysis: &A,
|
2020-08-27 23:02:46 -07:00
|
|
|
state: &mut A::Domain,
|
2020-03-22 12:09:40 -07:00
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
// 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::Before {
|
|
|
|
|
analysis.apply_before_terminator_effect(state, terminator, location);
|
|
|
|
|
if to == Effect::Before.at_index(terminator_index) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
analysis.apply_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_statement_effect(state, statement, location);
|
|
|
|
|
if to == Effect::Primary.at_index(from.statement_index) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
from.statement_index - 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Effect::Before => 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_before_statement_effect(state, statement, location);
|
|
|
|
|
analysis.apply_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_before_statement_effect(state, statement, location);
|
|
|
|
|
|
|
|
|
|
if to.effect == Effect::Before {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
analysis.apply_statement_effect(state, statement, location);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
fn visit_results_in_block<'mir, 'tcx, F, R>(
|
2020-03-22 12:09:40 -07:00
|
|
|
state: &mut F,
|
|
|
|
|
block: BasicBlock,
|
|
|
|
|
block_data: &'mir mir::BasicBlockData<'tcx>,
|
|
|
|
|
results: &R,
|
|
|
|
|
vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
|
|
|
|
|
) where
|
|
|
|
|
R: ResultsVisitable<'tcx, FlowState = F>,
|
|
|
|
|
{
|
|
|
|
|
results.reset_to_block_entry(state, block);
|
|
|
|
|
|
|
|
|
|
vis.visit_block_end(&state, block_data, block);
|
|
|
|
|
|
|
|
|
|
// Terminator
|
|
|
|
|
let loc = Location { block, statement_index: block_data.statements.len() };
|
|
|
|
|
let term = block_data.terminator();
|
|
|
|
|
results.reconstruct_before_terminator_effect(state, term, loc);
|
|
|
|
|
vis.visit_terminator_before_primary_effect(state, term, loc);
|
|
|
|
|
results.reconstruct_terminator_effect(state, term, loc);
|
|
|
|
|
vis.visit_terminator_after_primary_effect(state, term, loc);
|
|
|
|
|
|
|
|
|
|
for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
|
|
|
|
|
let loc = Location { block, statement_index };
|
|
|
|
|
results.reconstruct_before_statement_effect(state, stmt, loc);
|
|
|
|
|
vis.visit_statement_before_primary_effect(state, stmt, loc);
|
|
|
|
|
results.reconstruct_statement_effect(state, stmt, loc);
|
|
|
|
|
vis.visit_statement_after_primary_effect(state, stmt, loc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vis.visit_block_start(state, block_data, block);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
fn join_state_into_successors_of<'tcx, A>(
|
2020-03-22 12:09:40 -07:00
|
|
|
analysis: &A,
|
|
|
|
|
_tcx: TyCtxt<'tcx>,
|
|
|
|
|
body: &mir::Body<'tcx>,
|
2020-08-27 23:02:46 -07:00
|
|
|
exit_state: &mut A::Domain,
|
2020-03-22 12:09:40 -07:00
|
|
|
(bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
|
2020-08-27 23:02:46 -07:00
|
|
|
mut propagate: impl FnMut(BasicBlock, &A::Domain),
|
2020-03-22 12:09:40 -07:00
|
|
|
) where
|
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
|
{
|
2022-07-05 00:00:00 +00:00
|
|
|
for pred in body.basic_blocks.predecessors()[bb].iter().copied() {
|
2020-03-22 12:09:40 -07:00
|
|
|
match body[pred].terminator().kind {
|
|
|
|
|
// Apply terminator-specific edge effects.
|
|
|
|
|
//
|
|
|
|
|
// FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally.
|
2022-04-16 09:27:54 -04:00
|
|
|
mir::TerminatorKind::Call { destination, target: Some(dest), .. } if dest == bb => {
|
2021-08-30 01:23:33 +01:00
|
|
|
let mut tmp = exit_state.clone();
|
|
|
|
|
analysis.apply_call_return_effect(
|
|
|
|
|
&mut tmp,
|
|
|
|
|
pred,
|
2022-04-16 09:27:54 -04:00
|
|
|
CallReturnPlaces::Call(destination),
|
2021-08-30 01:23:33 +01:00
|
|
|
);
|
|
|
|
|
propagate(pred, &tmp);
|
|
|
|
|
}
|
2022-03-19 12:04:59 -04:00
|
|
|
|
2021-08-30 01:23:33 +01:00
|
|
|
mir::TerminatorKind::InlineAsm {
|
|
|
|
|
destination: Some(dest), ref operands, ..
|
2020-03-22 12:09:40 -07:00
|
|
|
} if dest == bb => {
|
|
|
|
|
let mut tmp = exit_state.clone();
|
2021-08-30 01:23:33 +01:00
|
|
|
analysis.apply_call_return_effect(
|
|
|
|
|
&mut tmp,
|
|
|
|
|
pred,
|
|
|
|
|
CallReturnPlaces::InlineAsm(operands),
|
|
|
|
|
);
|
2020-03-22 12:09:40 -07:00
|
|
|
propagate(pred, &tmp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == bb => {
|
|
|
|
|
let mut tmp = exit_state.clone();
|
|
|
|
|
analysis.apply_yield_resume_effect(&mut tmp, resume, resume_arg);
|
|
|
|
|
propagate(pred, &tmp);
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-03 16:03:27 -08:00
|
|
|
mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
|
2022-03-19 12:04:59 -04:00
|
|
|
let mut applier = BackwardSwitchIntEdgeEffectsApplier {
|
2022-05-08 00:00:00 +00:00
|
|
|
body,
|
2022-03-19 12:04:59 -04:00
|
|
|
pred,
|
|
|
|
|
exit_state,
|
|
|
|
|
bb,
|
|
|
|
|
propagate: &mut propagate,
|
|
|
|
|
effects_applied: false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);
|
|
|
|
|
|
2022-03-27 10:58:55 -07:00
|
|
|
if !applier.effects_applied {
|
2022-03-19 12:04:59 -04:00
|
|
|
propagate(pred, exit_state)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:09:40 -07:00
|
|
|
_ => propagate(pred, exit_state),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-22 12:48:19 -07:00
|
|
|
struct BackwardSwitchIntEdgeEffectsApplier<'a, 'tcx, D, F> {
|
|
|
|
|
body: &'a mir::Body<'tcx>,
|
2022-03-19 12:04:59 -04:00
|
|
|
pred: BasicBlock,
|
|
|
|
|
exit_state: &'a mut D,
|
|
|
|
|
bb: BasicBlock,
|
|
|
|
|
propagate: &'a mut F,
|
|
|
|
|
effects_applied: bool,
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-22 12:48:19 -07:00
|
|
|
impl<D, F> super::SwitchIntEdgeEffects<D> for BackwardSwitchIntEdgeEffectsApplier<'_, '_, D, F>
|
2022-03-19 12:04:59 -04:00
|
|
|
where
|
|
|
|
|
D: Clone,
|
|
|
|
|
F: FnMut(BasicBlock, &D),
|
|
|
|
|
{
|
|
|
|
|
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
|
|
|
|
|
assert!(!self.effects_applied);
|
|
|
|
|
|
2022-07-05 00:00:00 +00:00
|
|
|
let values = &self.body.basic_blocks.switch_sources()[&(self.bb, self.pred)];
|
2022-05-08 00:00:00 +00:00
|
|
|
let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.bb });
|
2022-03-25 21:11:49 -04:00
|
|
|
|
|
|
|
|
let mut tmp = None;
|
|
|
|
|
for target in targets {
|
|
|
|
|
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
|
|
|
|
|
apply_edge_effect(tmp, target);
|
|
|
|
|
(self.propagate)(self.pred, tmp);
|
|
|
|
|
}
|
2022-03-19 12:04:59 -04:00
|
|
|
|
|
|
|
|
self.effects_applied = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:09:40 -07:00
|
|
|
/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
|
|
|
|
|
pub struct Forward;
|
|
|
|
|
|
|
|
|
|
impl Direction for Forward {
|
2022-06-05 00:00:00 +00:00
|
|
|
const IS_FORWARD: bool = true;
|
2020-03-22 12:09:40 -07:00
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
fn apply_effects_in_block<'tcx, A>(
|
2020-03-22 12:09:40 -07:00
|
|
|
analysis: &A,
|
2020-08-27 23:02:46 -07:00
|
|
|
state: &mut A::Domain,
|
2020-03-22 12:09:40 -07:00
|
|
|
block: BasicBlock,
|
|
|
|
|
block_data: &mir::BasicBlockData<'tcx>,
|
|
|
|
|
) where
|
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
|
{
|
|
|
|
|
for (statement_index, statement) in block_data.statements.iter().enumerate() {
|
|
|
|
|
let location = Location { block, statement_index };
|
|
|
|
|
analysis.apply_before_statement_effect(state, statement, location);
|
|
|
|
|
analysis.apply_statement_effect(state, statement, location);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let terminator = block_data.terminator();
|
|
|
|
|
let location = Location { block, statement_index: block_data.statements.len() };
|
|
|
|
|
analysis.apply_before_terminator_effect(state, terminator, location);
|
|
|
|
|
analysis.apply_terminator_effect(state, terminator, location);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
fn gen_kill_effects_in_block<'tcx, A>(
|
2020-03-22 12:09:40 -07:00
|
|
|
analysis: &A,
|
|
|
|
|
trans: &mut GenKillSet<A::Idx>,
|
|
|
|
|
block: BasicBlock,
|
|
|
|
|
block_data: &mir::BasicBlockData<'tcx>,
|
|
|
|
|
) where
|
|
|
|
|
A: GenKillAnalysis<'tcx>,
|
|
|
|
|
{
|
|
|
|
|
for (statement_index, statement) in block_data.statements.iter().enumerate() {
|
|
|
|
|
let location = Location { block, statement_index };
|
|
|
|
|
analysis.before_statement_effect(trans, statement, location);
|
|
|
|
|
analysis.statement_effect(trans, statement, location);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let terminator = block_data.terminator();
|
|
|
|
|
let location = Location { block, statement_index: block_data.statements.len() };
|
|
|
|
|
analysis.before_terminator_effect(trans, terminator, location);
|
|
|
|
|
analysis.terminator_effect(trans, terminator, location);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
fn apply_effects_in_range<'tcx, A>(
|
2020-03-22 12:09:40 -07:00
|
|
|
analysis: &A,
|
2020-08-27 23:02:46 -07:00
|
|
|
state: &mut A::Domain,
|
2020-03-22 12:09:40 -07:00
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
// 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::Before => 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_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_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_before_statement_effect(state, statement, location);
|
|
|
|
|
analysis.apply_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_before_terminator_effect(state, terminator, location);
|
|
|
|
|
|
|
|
|
|
if to.effect == Effect::Primary {
|
|
|
|
|
analysis.apply_terminator_effect(state, terminator, location);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
let statement = &block_data.statements[to.statement_index];
|
|
|
|
|
analysis.apply_before_statement_effect(state, statement, location);
|
|
|
|
|
|
|
|
|
|
if to.effect == Effect::Primary {
|
|
|
|
|
analysis.apply_statement_effect(state, statement, location);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
fn visit_results_in_block<'mir, 'tcx, F, R>(
|
2020-03-22 12:09:40 -07:00
|
|
|
state: &mut F,
|
|
|
|
|
block: BasicBlock,
|
|
|
|
|
block_data: &'mir mir::BasicBlockData<'tcx>,
|
|
|
|
|
results: &R,
|
|
|
|
|
vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
|
|
|
|
|
) where
|
|
|
|
|
R: ResultsVisitable<'tcx, FlowState = F>,
|
|
|
|
|
{
|
|
|
|
|
results.reset_to_block_entry(state, block);
|
|
|
|
|
|
|
|
|
|
vis.visit_block_start(state, block_data, block);
|
|
|
|
|
|
|
|
|
|
for (statement_index, stmt) in block_data.statements.iter().enumerate() {
|
|
|
|
|
let loc = Location { block, statement_index };
|
|
|
|
|
results.reconstruct_before_statement_effect(state, stmt, loc);
|
|
|
|
|
vis.visit_statement_before_primary_effect(state, stmt, loc);
|
|
|
|
|
results.reconstruct_statement_effect(state, stmt, loc);
|
|
|
|
|
vis.visit_statement_after_primary_effect(state, stmt, loc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let loc = Location { block, statement_index: block_data.statements.len() };
|
|
|
|
|
let term = block_data.terminator();
|
|
|
|
|
results.reconstruct_before_terminator_effect(state, term, loc);
|
|
|
|
|
vis.visit_terminator_before_primary_effect(state, term, loc);
|
|
|
|
|
results.reconstruct_terminator_effect(state, term, loc);
|
|
|
|
|
vis.visit_terminator_after_primary_effect(state, term, loc);
|
|
|
|
|
|
|
|
|
|
vis.visit_block_end(state, block_data, block);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
fn join_state_into_successors_of<'tcx, A>(
|
2020-03-22 12:09:40 -07:00
|
|
|
analysis: &A,
|
2020-09-26 13:12:51 -07:00
|
|
|
_tcx: TyCtxt<'tcx>,
|
|
|
|
|
_body: &mir::Body<'tcx>,
|
2020-08-27 23:02:46 -07:00
|
|
|
exit_state: &mut A::Domain,
|
2020-03-22 12:09:40 -07:00
|
|
|
(bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
|
2020-08-27 23:02:46 -07:00
|
|
|
mut propagate: impl FnMut(BasicBlock, &A::Domain),
|
2020-03-22 12:09:40 -07:00
|
|
|
) where
|
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
|
{
|
|
|
|
|
use mir::TerminatorKind::*;
|
|
|
|
|
match bb_data.terminator().kind {
|
|
|
|
|
Return | Resume | Abort | GeneratorDrop | Unreachable => {}
|
|
|
|
|
|
|
|
|
|
Goto { target } => propagate(target, exit_state),
|
|
|
|
|
|
2022-10-08 23:47:59 +01:00
|
|
|
Assert { target, unwind, expected: _, msg: _, cond: _ }
|
2020-06-10 09:56:54 +02:00
|
|
|
| Drop { target, unwind, place: _ }
|
2020-03-22 12:09:40 -07:00
|
|
|
| FalseUnwind { real_target: target, unwind } => {
|
2022-10-08 23:47:59 +01:00
|
|
|
if let UnwindAction::Cleanup(unwind) = unwind {
|
Remove dead unwinds before drop elaboration
As a part of drop elaboration, we identify dead unwinds, i.e., unwind
edges on a drop terminators which are known to be unreachable, because
there is no need to drop anything.
Previously, the data flow framework was informed about the dead unwinds,
and it assumed those edges are absent from MIR. Unfortunately, the data
flow framework wasn't consistent in maintaining this assumption.
In particular, if a block was reachable only through a dead unwind edge,
its state was propagated to other blocks still. This became an issue in
the context of change removes DropAndReplace terminator, since it
introduces initialization into cleanup blocks.
To avoid this issue, remove unreachable unwind edges before the drop
elaboration, and elaborate only blocks that remain reachable.
2023-01-05 10:15:33 +01:00
|
|
|
propagate(unwind, exit_state);
|
2020-03-22 12:09:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
propagate(target, exit_state);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-02 09:15:24 +02:00
|
|
|
FalseEdge { real_target, imaginary_target } => {
|
2020-03-22 12:09:40 -07:00
|
|
|
propagate(real_target, exit_state);
|
|
|
|
|
propagate(imaginary_target, exit_state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Yield { resume: target, drop, resume_arg, value: _ } => {
|
|
|
|
|
if let Some(drop) = drop {
|
|
|
|
|
propagate(drop, exit_state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
analysis.apply_yield_resume_effect(exit_state, target, resume_arg);
|
|
|
|
|
propagate(target, exit_state);
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-16 09:27:54 -04:00
|
|
|
Call {
|
2022-10-08 23:47:59 +01:00
|
|
|
unwind,
|
2022-04-16 09:27:54 -04:00
|
|
|
destination,
|
|
|
|
|
target,
|
|
|
|
|
func: _,
|
|
|
|
|
args: _,
|
|
|
|
|
from_hir_call: _,
|
|
|
|
|
fn_span: _,
|
|
|
|
|
} => {
|
2022-10-08 23:47:59 +01:00
|
|
|
if let UnwindAction::Cleanup(unwind) = unwind {
|
Remove dead unwinds before drop elaboration
As a part of drop elaboration, we identify dead unwinds, i.e., unwind
edges on a drop terminators which are known to be unreachable, because
there is no need to drop anything.
Previously, the data flow framework was informed about the dead unwinds,
and it assumed those edges are absent from MIR. Unfortunately, the data
flow framework wasn't consistent in maintaining this assumption.
In particular, if a block was reachable only through a dead unwind edge,
its state was propagated to other blocks still. This became an issue in
the context of change removes DropAndReplace terminator, since it
introduces initialization into cleanup blocks.
To avoid this issue, remove unreachable unwind edges before the drop
elaboration, and elaborate only blocks that remain reachable.
2023-01-05 10:15:33 +01:00
|
|
|
propagate(unwind, exit_state);
|
2020-03-22 12:09:40 -07:00
|
|
|
}
|
|
|
|
|
|
2022-04-16 09:27:54 -04:00
|
|
|
if let Some(target) = target {
|
2020-03-22 12:09:40 -07:00
|
|
|
// N.B.: This must be done *last*, otherwise the unwind path will see the call
|
|
|
|
|
// return effect.
|
2021-08-30 01:23:33 +01:00
|
|
|
analysis.apply_call_return_effect(
|
|
|
|
|
exit_state,
|
|
|
|
|
bb,
|
2022-04-16 09:27:54 -04:00
|
|
|
CallReturnPlaces::Call(destination),
|
2021-08-30 01:23:33 +01:00
|
|
|
);
|
2020-03-22 12:09:40 -07:00
|
|
|
propagate(target, exit_state);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-30 01:23:33 +01:00
|
|
|
InlineAsm {
|
|
|
|
|
template: _,
|
|
|
|
|
ref operands,
|
|
|
|
|
options: _,
|
|
|
|
|
line_spans: _,
|
|
|
|
|
destination,
|
2022-10-08 23:47:59 +01:00
|
|
|
unwind,
|
2021-08-30 01:23:33 +01:00
|
|
|
} => {
|
2022-10-08 23:47:59 +01:00
|
|
|
if let UnwindAction::Cleanup(unwind) = unwind {
|
Remove dead unwinds before drop elaboration
As a part of drop elaboration, we identify dead unwinds, i.e., unwind
edges on a drop terminators which are known to be unreachable, because
there is no need to drop anything.
Previously, the data flow framework was informed about the dead unwinds,
and it assumed those edges are absent from MIR. Unfortunately, the data
flow framework wasn't consistent in maintaining this assumption.
In particular, if a block was reachable only through a dead unwind edge,
its state was propagated to other blocks still. This became an issue in
the context of change removes DropAndReplace terminator, since it
introduces initialization into cleanup blocks.
To avoid this issue, remove unreachable unwind edges before the drop
elaboration, and elaborate only blocks that remain reachable.
2023-01-05 10:15:33 +01:00
|
|
|
propagate(unwind, exit_state);
|
2021-08-30 01:23:33 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-14 18:17:50 +00:00
|
|
|
if let Some(target) = destination {
|
2021-08-30 01:23:33 +01:00
|
|
|
// N.B.: This must be done *last*, otherwise the unwind path will see the call
|
|
|
|
|
// return effect.
|
|
|
|
|
analysis.apply_call_return_effect(
|
|
|
|
|
exit_state,
|
|
|
|
|
bb,
|
|
|
|
|
CallReturnPlaces::InlineAsm(operands),
|
|
|
|
|
);
|
2020-02-14 18:17:50 +00:00
|
|
|
propagate(target, exit_state);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-03 16:03:27 -08:00
|
|
|
SwitchInt { ref targets, ref discr } => {
|
2022-03-19 12:04:59 -04:00
|
|
|
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
|
2020-09-26 13:12:51 -07:00
|
|
|
exit_state,
|
2020-10-10 17:36:04 +02:00
|
|
|
targets,
|
2020-09-26 13:12:51 -07:00
|
|
|
propagate,
|
|
|
|
|
effects_applied: false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
analysis.apply_switch_int_edge_effects(bb, discr, &mut applier);
|
|
|
|
|
|
2022-03-19 12:04:59 -04:00
|
|
|
let ForwardSwitchIntEdgeEffectsApplier {
|
|
|
|
|
exit_state,
|
|
|
|
|
mut propagate,
|
|
|
|
|
effects_applied,
|
|
|
|
|
..
|
2020-09-26 13:12:51 -07:00
|
|
|
} = applier;
|
|
|
|
|
|
|
|
|
|
if !effects_applied {
|
2020-10-10 17:36:04 +02:00
|
|
|
for target in targets.all_targets() {
|
|
|
|
|
propagate(*target, exit_state);
|
2020-03-22 12:09:40 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-19 12:04:59 -04:00
|
|
|
struct ForwardSwitchIntEdgeEffectsApplier<'a, D, F> {
|
2020-09-26 13:12:51 -07:00
|
|
|
exit_state: &'a mut D,
|
2020-10-11 01:14:12 +02:00
|
|
|
targets: &'a SwitchTargets,
|
2020-09-26 13:12:51 -07:00
|
|
|
propagate: F,
|
|
|
|
|
|
|
|
|
|
effects_applied: bool,
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-19 12:04:59 -04:00
|
|
|
impl<D, F> super::SwitchIntEdgeEffects<D> for ForwardSwitchIntEdgeEffectsApplier<'_, D, F>
|
2020-09-26 13:12:51 -07:00
|
|
|
where
|
|
|
|
|
D: Clone,
|
|
|
|
|
F: FnMut(BasicBlock, &D),
|
|
|
|
|
{
|
|
|
|
|
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
|
|
|
|
|
assert!(!self.effects_applied);
|
|
|
|
|
|
|
|
|
|
let mut tmp = None;
|
2020-10-10 17:36:04 +02:00
|
|
|
for (value, target) in self.targets.iter() {
|
2020-09-26 13:12:51 -07:00
|
|
|
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
|
|
|
|
|
apply_edge_effect(tmp, SwitchIntTarget { value: Some(value), target });
|
|
|
|
|
(self.propagate)(target, tmp);
|
2020-03-22 12:09:40 -07:00
|
|
|
}
|
|
|
|
|
|
2020-09-26 13:12:51 -07:00
|
|
|
// Once we get to the final, "otherwise" branch, there is no need to preserve `exit_state`,
|
|
|
|
|
// so pass it directly to `apply_edge_effect` to save a clone of the dataflow state.
|
2020-10-10 17:36:04 +02:00
|
|
|
let otherwise = self.targets.otherwise();
|
2020-09-26 13:12:51 -07:00
|
|
|
apply_edge_effect(self.exit_state, SwitchIntTarget { value: None, target: otherwise });
|
|
|
|
|
(self.propagate)(otherwise, self.exit_state);
|
|
|
|
|
|
|
|
|
|
self.effects_applied = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// An analogue of `Option::get_or_insert_with` that stores a clone of `val` into `opt`, but uses
|
|
|
|
|
/// the more efficient `clone_from` if `opt` was `Some`.
|
|
|
|
|
///
|
|
|
|
|
/// Returns a mutable reference to the new clone that resides in `opt`.
|
|
|
|
|
//
|
|
|
|
|
// FIXME: Figure out how to express this using `Option::clone_from`, or maybe lift it into the
|
|
|
|
|
// standard library?
|
2021-12-14 12:02:45 -05:00
|
|
|
fn opt_clone_from_or_clone<'a, T: Clone>(opt: &'a mut Option<T>, val: &T) -> &'a mut T {
|
2020-09-26 13:12:51 -07:00
|
|
|
if opt.is_some() {
|
|
|
|
|
let ret = opt.as_mut().unwrap();
|
|
|
|
|
ret.clone_from(val);
|
|
|
|
|
ret
|
|
|
|
|
} else {
|
|
|
|
|
*opt = Some(val.clone());
|
|
|
|
|
opt.as_mut().unwrap()
|
2020-03-22 12:09:40 -07:00
|
|
|
}
|
|
|
|
|
}
|