Allow apply_terminator_effect to customize edges.
This commit is contained in:
parent
32711b2b4e
commit
5173d85043
14 changed files with 300 additions and 306 deletions
|
@ -2,12 +2,14 @@
|
||||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||||
use rustc_data_structures::fx::FxIndexMap;
|
use rustc_data_structures::fx::FxIndexMap;
|
||||||
use rustc_index::bit_set::BitSet;
|
use rustc_index::bit_set::BitSet;
|
||||||
use rustc_middle::mir::{self, BasicBlock, Body, Location, Place};
|
use rustc_middle::mir::{
|
||||||
|
self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdge,
|
||||||
|
};
|
||||||
use rustc_middle::ty::RegionVid;
|
use rustc_middle::ty::RegionVid;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
|
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
|
||||||
use rustc_mir_dataflow::ResultsVisitable;
|
use rustc_mir_dataflow::ResultsVisitable;
|
||||||
use rustc_mir_dataflow::{self, fmt::DebugWithContext, CallReturnPlaces, GenKill};
|
use rustc_mir_dataflow::{self, fmt::DebugWithContext, GenKill};
|
||||||
use rustc_mir_dataflow::{Analysis, Direction, Results};
|
use rustc_mir_dataflow::{Analysis, Direction, Results};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
@ -404,12 +406,12 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
|
||||||
self.kill_loans_out_of_scope_at_location(trans, location);
|
self.kill_loans_out_of_scope_at_location(trans, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(
|
fn terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
trans: &mut impl GenKill<Self::Idx>,
|
trans: &mut Self::Domain,
|
||||||
terminator: &mir::Terminator<'tcx>,
|
terminator: &'mir mir::Terminator<'tcx>,
|
||||||
_location: Location,
|
_location: Location,
|
||||||
) {
|
) -> TerminatorEdge<'mir, 'tcx> {
|
||||||
if let mir::TerminatorKind::InlineAsm { operands, .. } = &terminator.kind {
|
if let mir::TerminatorKind::InlineAsm { operands, .. } = &terminator.kind {
|
||||||
for op in operands {
|
for op in operands {
|
||||||
if let mir::InlineAsmOperand::Out { place: Some(place), .. }
|
if let mir::InlineAsmOperand::Out { place: Some(place), .. }
|
||||||
|
@ -419,6 +421,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
terminator.edges()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_return_effect(
|
fn call_return_effect(
|
||||||
|
|
|
@ -4,10 +4,12 @@
|
||||||
|
|
||||||
use rustc_index::bit_set::BitSet;
|
use rustc_index::bit_set::BitSet;
|
||||||
use rustc_middle::mir::visit::Visitor;
|
use rustc_middle::mir::visit::Visitor;
|
||||||
use rustc_middle::mir::{self, BasicBlock, Local, Location, Statement, StatementKind};
|
use rustc_middle::mir::{
|
||||||
|
self, BasicBlock, CallReturnPlaces, Local, Location, Statement, StatementKind, TerminatorEdge,
|
||||||
|
};
|
||||||
use rustc_mir_dataflow::fmt::DebugWithContext;
|
use rustc_mir_dataflow::fmt::DebugWithContext;
|
||||||
use rustc_mir_dataflow::JoinSemiLattice;
|
use rustc_mir_dataflow::JoinSemiLattice;
|
||||||
use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces};
|
use rustc_mir_dataflow::{Analysis, AnalysisDomain};
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
@ -345,13 +347,14 @@ where
|
||||||
self.transfer_function(state).visit_statement(statement, location);
|
self.transfer_function(state).visit_statement(statement, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_terminator_effect(
|
fn apply_terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut Self::Domain,
|
state: &mut Self::Domain,
|
||||||
terminator: &mir::Terminator<'tcx>,
|
terminator: &'mir mir::Terminator<'tcx>,
|
||||||
location: Location,
|
location: Location,
|
||||||
) {
|
) -> TerminatorEdge<'mir, 'tcx> {
|
||||||
self.transfer_function(state).visit_terminator(terminator, location);
|
self.transfer_function(state).visit_terminator(terminator, location);
|
||||||
|
terminator.edges()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_call_return_effect(
|
fn apply_call_return_effect(
|
||||||
|
|
|
@ -10,6 +10,7 @@ use std::iter;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
pub use super::query::*;
|
pub use super::query::*;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
|
#[derive(Debug, Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
|
||||||
pub struct SwitchTargets {
|
pub struct SwitchTargets {
|
||||||
|
@ -430,3 +431,108 @@ impl<'tcx> TerminatorKind<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum TerminatorEdge<'mir, 'tcx> {
|
||||||
|
/// For terminators that have no successor, like `return`.
|
||||||
|
None,
|
||||||
|
/// For terminators that a single successor, like `goto`, and `assert` without cleanup block.
|
||||||
|
Single(BasicBlock),
|
||||||
|
/// For terminators that two successors, `assert` with cleanup block and `falseEdge`.
|
||||||
|
Double(BasicBlock, BasicBlock),
|
||||||
|
/// Special action for `Yield`, `Call` and `InlineAsm` terminators.
|
||||||
|
AssignOnReturn {
|
||||||
|
return_: Option<BasicBlock>,
|
||||||
|
unwind: UnwindAction,
|
||||||
|
place: CallReturnPlaces<'mir, 'tcx>,
|
||||||
|
},
|
||||||
|
/// Special edge for `SwitchInt`.
|
||||||
|
SwitchInt { targets: &'mir SwitchTargets, discr: &'mir Operand<'tcx> },
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List of places that are written to after a successful (non-unwind) return
|
||||||
|
/// from a `Call` or `InlineAsm`.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum CallReturnPlaces<'a, 'tcx> {
|
||||||
|
Call(Place<'tcx>),
|
||||||
|
Yield(Place<'tcx>),
|
||||||
|
InlineAsm(&'a [InlineAsmOperand<'tcx>]),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> CallReturnPlaces<'_, 'tcx> {
|
||||||
|
pub fn for_each(&self, mut f: impl FnMut(Place<'tcx>)) {
|
||||||
|
match *self {
|
||||||
|
Self::Call(place) | Self::Yield(place) => f(place),
|
||||||
|
Self::InlineAsm(operands) => {
|
||||||
|
for op in operands {
|
||||||
|
match *op {
|
||||||
|
InlineAsmOperand::Out { place: Some(place), .. }
|
||||||
|
| InlineAsmOperand::InOut { out_place: Some(place), .. } => f(place),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Terminator<'tcx> {
|
||||||
|
pub fn edges(&self) -> TerminatorEdge<'_, 'tcx> {
|
||||||
|
self.kind.edges()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> TerminatorKind<'tcx> {
|
||||||
|
pub fn edges(&self) -> TerminatorEdge<'_, 'tcx> {
|
||||||
|
use TerminatorKind::*;
|
||||||
|
match *self {
|
||||||
|
Return | Resume | Terminate | GeneratorDrop | Unreachable => TerminatorEdge::None,
|
||||||
|
|
||||||
|
Goto { target } => TerminatorEdge::Single(target),
|
||||||
|
|
||||||
|
Assert { target, unwind, expected: _, msg: _, cond: _ }
|
||||||
|
| Drop { target, unwind, place: _, replace: _ }
|
||||||
|
| FalseUnwind { real_target: target, unwind } => match unwind {
|
||||||
|
UnwindAction::Cleanup(unwind) => TerminatorEdge::Double(target, unwind),
|
||||||
|
UnwindAction::Continue | UnwindAction::Terminate | UnwindAction::Unreachable => {
|
||||||
|
TerminatorEdge::Single(target)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
FalseEdge { real_target, imaginary_target } => {
|
||||||
|
TerminatorEdge::Double(real_target, imaginary_target)
|
||||||
|
}
|
||||||
|
|
||||||
|
Yield { resume: target, drop, resume_arg, value: _ } => {
|
||||||
|
TerminatorEdge::AssignOnReturn {
|
||||||
|
return_: Some(target),
|
||||||
|
unwind: drop.map_or(UnwindAction::Terminate, UnwindAction::Cleanup),
|
||||||
|
place: CallReturnPlaces::Yield(resume_arg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Call { unwind, destination, target, func: _, args: _, fn_span: _, call_source: _ } => {
|
||||||
|
TerminatorEdge::AssignOnReturn {
|
||||||
|
return_: target,
|
||||||
|
unwind,
|
||||||
|
place: CallReturnPlaces::Call(destination),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineAsm {
|
||||||
|
template: _,
|
||||||
|
ref operands,
|
||||||
|
options: _,
|
||||||
|
line_spans: _,
|
||||||
|
destination,
|
||||||
|
unwind,
|
||||||
|
} => TerminatorEdge::AssignOnReturn {
|
||||||
|
return_: destination,
|
||||||
|
unwind,
|
||||||
|
place: CallReturnPlaces::InlineAsm(operands),
|
||||||
|
},
|
||||||
|
|
||||||
|
SwitchInt { ref targets, ref discr } => TerminatorEdge::SwitchInt { targets, discr },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use rustc_middle::mir::{self, BasicBlock, Location, SwitchTargets, UnwindAction};
|
use rustc_middle::mir::{
|
||||||
use rustc_middle::ty::TyCtxt;
|
self, BasicBlock, CallReturnPlaces, Location, SwitchTargets, TerminatorEdge, UnwindAction,
|
||||||
|
};
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
use super::visitor::{ResultsVisitable, ResultsVisitor};
|
use super::visitor::{ResultsVisitable, ResultsVisitor};
|
||||||
use super::{
|
use super::{Analysis, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget};
|
||||||
Analysis, CallReturnPlaces, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait Direction {
|
pub trait Direction {
|
||||||
const IS_FORWARD: bool;
|
const IS_FORWARD: bool;
|
||||||
|
@ -24,12 +23,14 @@ pub trait Direction {
|
||||||
) where
|
) where
|
||||||
A: Analysis<'tcx>;
|
A: Analysis<'tcx>;
|
||||||
|
|
||||||
fn apply_effects_in_block<'tcx, A>(
|
fn apply_effects_in_block<'mir, 'tcx, A>(
|
||||||
analysis: &mut A,
|
analysis: &mut A,
|
||||||
state: &mut A::Domain,
|
state: &mut A::Domain,
|
||||||
block: BasicBlock,
|
block: BasicBlock,
|
||||||
block_data: &mir::BasicBlockData<'tcx>,
|
block_data: &'mir mir::BasicBlockData<'tcx>,
|
||||||
) where
|
statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
|
||||||
|
) -> TerminatorEdge<'mir, 'tcx>
|
||||||
|
where
|
||||||
A: Analysis<'tcx>;
|
A: Analysis<'tcx>;
|
||||||
|
|
||||||
fn gen_kill_effects_in_block<'tcx, A>(
|
fn gen_kill_effects_in_block<'tcx, A>(
|
||||||
|
@ -51,10 +52,10 @@ pub trait Direction {
|
||||||
|
|
||||||
fn join_state_into_successors_of<'tcx, A>(
|
fn join_state_into_successors_of<'tcx, A>(
|
||||||
analysis: &mut A,
|
analysis: &mut A,
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
body: &mir::Body<'tcx>,
|
body: &mir::Body<'tcx>,
|
||||||
exit_state: &mut A::Domain,
|
exit_state: &mut A::Domain,
|
||||||
block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
|
block: BasicBlock,
|
||||||
|
edges: TerminatorEdge<'_, 'tcx>,
|
||||||
propagate: impl FnMut(BasicBlock, &A::Domain),
|
propagate: impl FnMut(BasicBlock, &A::Domain),
|
||||||
) where
|
) where
|
||||||
A: Analysis<'tcx>;
|
A: Analysis<'tcx>;
|
||||||
|
@ -66,24 +67,30 @@ pub struct Backward;
|
||||||
impl Direction for Backward {
|
impl Direction for Backward {
|
||||||
const IS_FORWARD: bool = false;
|
const IS_FORWARD: bool = false;
|
||||||
|
|
||||||
fn apply_effects_in_block<'tcx, A>(
|
fn apply_effects_in_block<'mir, 'tcx, A>(
|
||||||
analysis: &mut A,
|
analysis: &mut A,
|
||||||
state: &mut A::Domain,
|
state: &mut A::Domain,
|
||||||
block: BasicBlock,
|
block: BasicBlock,
|
||||||
block_data: &mir::BasicBlockData<'tcx>,
|
block_data: &'mir mir::BasicBlockData<'tcx>,
|
||||||
) where
|
statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
|
||||||
|
) -> TerminatorEdge<'mir, 'tcx>
|
||||||
|
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);
|
||||||
analysis.apply_terminator_effect(state, terminator, location);
|
let edges = analysis.apply_terminator_effect(state, terminator, location);
|
||||||
|
if let Some(statement_effect) = statement_effect {
|
||||||
for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
|
statement_effect(block, state)
|
||||||
let location = Location { block, statement_index };
|
} else {
|
||||||
analysis.apply_before_statement_effect(state, statement, location);
|
for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
|
||||||
analysis.apply_statement_effect(state, statement, location);
|
let location = Location { block, statement_index };
|
||||||
|
analysis.apply_before_statement_effect(state, statement, location);
|
||||||
|
analysis.apply_statement_effect(state, statement, location);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
edges
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_kill_effects_in_block<'tcx, A>(
|
fn gen_kill_effects_in_block<'tcx, A>(
|
||||||
|
@ -94,11 +101,6 @@ impl Direction for Backward {
|
||||||
) where
|
) where
|
||||||
A: GenKillAnalysis<'tcx>,
|
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() {
|
for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
|
||||||
let location = Location { block, statement_index };
|
let location = Location { block, statement_index };
|
||||||
analysis.before_statement_effect(trans, statement, location);
|
analysis.before_statement_effect(trans, statement, location);
|
||||||
|
@ -217,10 +219,10 @@ impl Direction for Backward {
|
||||||
|
|
||||||
fn join_state_into_successors_of<'tcx, A>(
|
fn join_state_into_successors_of<'tcx, A>(
|
||||||
analysis: &mut A,
|
analysis: &mut A,
|
||||||
_tcx: TyCtxt<'tcx>,
|
|
||||||
body: &mir::Body<'tcx>,
|
body: &mir::Body<'tcx>,
|
||||||
exit_state: &mut A::Domain,
|
exit_state: &mut A::Domain,
|
||||||
(bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
|
bb: BasicBlock,
|
||||||
|
_edges: TerminatorEdge<'_, 'tcx>,
|
||||||
mut propagate: impl FnMut(BasicBlock, &A::Domain),
|
mut propagate: impl FnMut(BasicBlock, &A::Domain),
|
||||||
) where
|
) where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
|
@ -254,7 +256,11 @@ impl Direction for Backward {
|
||||||
|
|
||||||
mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == bb => {
|
mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == bb => {
|
||||||
let mut tmp = exit_state.clone();
|
let mut tmp = exit_state.clone();
|
||||||
analysis.apply_yield_resume_effect(&mut tmp, resume, resume_arg);
|
analysis.apply_call_return_effect(
|
||||||
|
&mut tmp,
|
||||||
|
resume,
|
||||||
|
CallReturnPlaces::Yield(resume_arg),
|
||||||
|
);
|
||||||
propagate(pred, &tmp);
|
propagate(pred, &tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,24 +324,30 @@ pub struct Forward;
|
||||||
impl Direction for Forward {
|
impl Direction for Forward {
|
||||||
const IS_FORWARD: bool = true;
|
const IS_FORWARD: bool = true;
|
||||||
|
|
||||||
fn apply_effects_in_block<'tcx, A>(
|
fn apply_effects_in_block<'mir, 'tcx, A>(
|
||||||
analysis: &mut A,
|
analysis: &mut A,
|
||||||
state: &mut A::Domain,
|
state: &mut A::Domain,
|
||||||
block: BasicBlock,
|
block: BasicBlock,
|
||||||
block_data: &mir::BasicBlockData<'tcx>,
|
block_data: &'mir mir::BasicBlockData<'tcx>,
|
||||||
) where
|
statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
|
||||||
|
) -> TerminatorEdge<'mir, 'tcx>
|
||||||
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
{
|
{
|
||||||
for (statement_index, statement) in block_data.statements.iter().enumerate() {
|
if let Some(statement_effect) = statement_effect {
|
||||||
let location = Location { block, statement_index };
|
statement_effect(block, state)
|
||||||
analysis.apply_before_statement_effect(state, statement, location);
|
} else {
|
||||||
analysis.apply_statement_effect(state, statement, location);
|
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 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);
|
analysis.apply_terminator_effect(state, terminator, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_kill_effects_in_block<'tcx, A>(
|
fn gen_kill_effects_in_block<'tcx, A>(
|
||||||
|
@ -351,11 +363,6 @@ impl Direction for Forward {
|
||||||
analysis.before_statement_effect(trans, statement, location);
|
analysis.before_statement_effect(trans, statement, location);
|
||||||
analysis.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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_effects_in_range<'tcx, A>(
|
fn apply_effects_in_range<'tcx, A>(
|
||||||
|
@ -464,86 +471,32 @@ impl Direction for Forward {
|
||||||
|
|
||||||
fn join_state_into_successors_of<'tcx, A>(
|
fn join_state_into_successors_of<'tcx, A>(
|
||||||
analysis: &mut A,
|
analysis: &mut A,
|
||||||
_tcx: TyCtxt<'tcx>,
|
|
||||||
_body: &mir::Body<'tcx>,
|
_body: &mir::Body<'tcx>,
|
||||||
exit_state: &mut A::Domain,
|
exit_state: &mut A::Domain,
|
||||||
(bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
|
bb: BasicBlock,
|
||||||
|
edges: TerminatorEdge<'_, 'tcx>,
|
||||||
mut propagate: impl FnMut(BasicBlock, &A::Domain),
|
mut propagate: impl FnMut(BasicBlock, &A::Domain),
|
||||||
) where
|
) where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
{
|
{
|
||||||
use mir::TerminatorKind::*;
|
match edges {
|
||||||
match bb_data.terminator().kind {
|
TerminatorEdge::None => {}
|
||||||
Return | Resume | Terminate | GeneratorDrop | Unreachable => {}
|
TerminatorEdge::Single(target) => propagate(target, exit_state),
|
||||||
|
TerminatorEdge::Double(target, unwind) => {
|
||||||
Goto { target } => propagate(target, exit_state),
|
|
||||||
|
|
||||||
Assert { target, unwind, expected: _, msg: _, cond: _ }
|
|
||||||
| Drop { target, unwind, place: _, replace: _ }
|
|
||||||
| FalseUnwind { real_target: target, unwind } => {
|
|
||||||
if let UnwindAction::Cleanup(unwind) = unwind {
|
|
||||||
propagate(unwind, exit_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
propagate(target, exit_state);
|
propagate(target, exit_state);
|
||||||
|
propagate(unwind, exit_state);
|
||||||
}
|
}
|
||||||
|
TerminatorEdge::AssignOnReturn { return_, unwind, place } => {
|
||||||
FalseEdge { real_target, imaginary_target } => {
|
// This must be done *first*, otherwise the unwind path will see the assignments.
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
Call { unwind, destination, target, func: _, args: _, call_source: _, fn_span: _ } => {
|
|
||||||
if let UnwindAction::Cleanup(unwind) = unwind {
|
if let UnwindAction::Cleanup(unwind) = unwind {
|
||||||
propagate(unwind, exit_state);
|
propagate(unwind, exit_state);
|
||||||
}
|
}
|
||||||
|
if let Some(return_) = return_ {
|
||||||
if let Some(target) = target {
|
analysis.apply_call_return_effect(exit_state, bb, place);
|
||||||
// N.B.: This must be done *last*, otherwise the unwind path will see the call
|
propagate(return_, exit_state);
|
||||||
// return effect.
|
|
||||||
analysis.apply_call_return_effect(
|
|
||||||
exit_state,
|
|
||||||
bb,
|
|
||||||
CallReturnPlaces::Call(destination),
|
|
||||||
);
|
|
||||||
propagate(target, exit_state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TerminatorEdge::SwitchInt { targets, discr } => {
|
||||||
InlineAsm {
|
|
||||||
template: _,
|
|
||||||
ref operands,
|
|
||||||
options: _,
|
|
||||||
line_spans: _,
|
|
||||||
destination,
|
|
||||||
unwind,
|
|
||||||
} => {
|
|
||||||
if let UnwindAction::Cleanup(unwind) = unwind {
|
|
||||||
propagate(unwind, exit_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(target) = destination {
|
|
||||||
// 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),
|
|
||||||
);
|
|
||||||
propagate(target, exit_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SwitchInt { ref targets, ref discr } => {
|
|
||||||
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
|
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
|
||||||
exit_state,
|
exit_state,
|
||||||
targets,
|
targets,
|
||||||
|
|
|
@ -264,19 +264,20 @@ where
|
||||||
state.clone_from(&entry_sets[bb]);
|
state.clone_from(&entry_sets[bb]);
|
||||||
|
|
||||||
// Apply the block transfer function, using the cached one if it exists.
|
// Apply the block transfer function, using the cached one if it exists.
|
||||||
match &apply_trans_for_block {
|
let edges = A::Direction::apply_effects_in_block(
|
||||||
Some(apply) => apply(bb, &mut state),
|
&mut analysis,
|
||||||
None => {
|
&mut state,
|
||||||
A::Direction::apply_effects_in_block(&mut analysis, &mut state, bb, bb_data)
|
bb,
|
||||||
}
|
bb_data,
|
||||||
}
|
apply_trans_for_block.as_deref(),
|
||||||
|
);
|
||||||
|
|
||||||
A::Direction::join_state_into_successors_of(
|
A::Direction::join_state_into_successors_of(
|
||||||
&mut analysis,
|
&mut analysis,
|
||||||
tcx,
|
|
||||||
body,
|
body,
|
||||||
&mut state,
|
&mut state,
|
||||||
(bb, bb_data),
|
bb,
|
||||||
|
edges,
|
||||||
|target: BasicBlock, state: &A::Domain| {
|
|target: BasicBlock, state: &A::Domain| {
|
||||||
let set_changed = entry_sets[target].join(state);
|
let set_changed = entry_sets[target].join(state);
|
||||||
if set_changed {
|
if set_changed {
|
||||||
|
|
|
@ -269,7 +269,11 @@ where
|
||||||
self.write_row(w, "", "(on yield resume)", |this, w, fmt| {
|
self.write_row(w, "", "(on yield resume)", |this, w, fmt| {
|
||||||
let state_on_generator_drop = this.results.get().clone();
|
let state_on_generator_drop = this.results.get().clone();
|
||||||
this.results.apply_custom_effect(|analysis, state| {
|
this.results.apply_custom_effect(|analysis, state| {
|
||||||
analysis.apply_yield_resume_effect(state, resume, resume_arg);
|
analysis.apply_call_return_effect(
|
||||||
|
state,
|
||||||
|
resume,
|
||||||
|
CallReturnPlaces::Yield(resume_arg),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
|
|
|
@ -34,7 +34,7 @@ use std::cmp::Ordering;
|
||||||
|
|
||||||
use rustc_index::bit_set::{BitSet, ChunkedBitSet, HybridBitSet};
|
use rustc_index::bit_set::{BitSet, ChunkedBitSet, HybridBitSet};
|
||||||
use rustc_index::Idx;
|
use rustc_index::Idx;
|
||||||
use rustc_middle::mir::{self, BasicBlock, Location};
|
use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdge};
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
|
|
||||||
mod cursor;
|
mod cursor;
|
||||||
|
@ -163,12 +163,12 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
|
||||||
/// in this function. That should go in `apply_call_return_effect`. For example, in the
|
/// in this function. That should go in `apply_call_return_effect`. For example, in the
|
||||||
/// `InitializedPlaces` analyses, the return place for a function call is not marked as
|
/// `InitializedPlaces` analyses, the return place for a function call is not marked as
|
||||||
/// initialized here.
|
/// initialized here.
|
||||||
fn apply_terminator_effect(
|
fn apply_terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut Self::Domain,
|
state: &mut Self::Domain,
|
||||||
terminator: &mir::Terminator<'tcx>,
|
terminator: &'mir mir::Terminator<'tcx>,
|
||||||
location: Location,
|
location: Location,
|
||||||
);
|
) -> TerminatorEdge<'mir, 'tcx>;
|
||||||
|
|
||||||
/// Updates the current dataflow state with an effect that occurs immediately *before* the
|
/// Updates the current dataflow state with an effect that occurs immediately *before* the
|
||||||
/// given terminator.
|
/// given terminator.
|
||||||
|
@ -198,20 +198,6 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
|
||||||
return_places: CallReturnPlaces<'_, 'tcx>,
|
return_places: CallReturnPlaces<'_, 'tcx>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Updates the current dataflow state with the effect of resuming from a `Yield` terminator.
|
|
||||||
///
|
|
||||||
/// This is similar to `apply_call_return_effect` in that it only takes place after the
|
|
||||||
/// generator is resumed, not when it is dropped.
|
|
||||||
///
|
|
||||||
/// By default, no effects happen.
|
|
||||||
fn apply_yield_resume_effect(
|
|
||||||
&mut self,
|
|
||||||
_state: &mut Self::Domain,
|
|
||||||
_resume_block: BasicBlock,
|
|
||||||
_resume_place: mir::Place<'tcx>,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the current dataflow state with the effect of taking a particular branch in a
|
/// Updates the current dataflow state with the effect of taking a particular branch in a
|
||||||
/// `SwitchInt` terminator.
|
/// `SwitchInt` terminator.
|
||||||
///
|
///
|
||||||
|
@ -306,12 +292,12 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See `Analysis::apply_terminator_effect`.
|
/// See `Analysis::apply_terminator_effect`.
|
||||||
fn terminator_effect(
|
fn terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
trans: &mut impl GenKill<Self::Idx>,
|
trans: &mut Self::Domain,
|
||||||
terminator: &mir::Terminator<'tcx>,
|
terminator: &'mir mir::Terminator<'tcx>,
|
||||||
location: Location,
|
location: Location,
|
||||||
);
|
) -> TerminatorEdge<'mir, 'tcx>;
|
||||||
|
|
||||||
/// See `Analysis::apply_before_terminator_effect`.
|
/// See `Analysis::apply_before_terminator_effect`.
|
||||||
fn before_terminator_effect(
|
fn before_terminator_effect(
|
||||||
|
@ -332,15 +318,6 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
|
||||||
return_places: CallReturnPlaces<'_, 'tcx>,
|
return_places: CallReturnPlaces<'_, 'tcx>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// See `Analysis::apply_yield_resume_effect`.
|
|
||||||
fn yield_resume_effect(
|
|
||||||
&mut self,
|
|
||||||
_trans: &mut impl GenKill<Self::Idx>,
|
|
||||||
_resume_block: BasicBlock,
|
|
||||||
_resume_place: mir::Place<'tcx>,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See `Analysis::apply_switch_int_edge_effects`.
|
/// See `Analysis::apply_switch_int_edge_effects`.
|
||||||
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
|
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -374,13 +351,13 @@ where
|
||||||
self.before_statement_effect(state, statement, location);
|
self.before_statement_effect(state, statement, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_terminator_effect(
|
fn apply_terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut A::Domain,
|
state: &mut A::Domain,
|
||||||
terminator: &mir::Terminator<'tcx>,
|
terminator: &'mir mir::Terminator<'tcx>,
|
||||||
location: Location,
|
location: Location,
|
||||||
) {
|
) -> TerminatorEdge<'mir, 'tcx> {
|
||||||
self.terminator_effect(state, terminator, location);
|
self.terminator_effect(state, terminator, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_before_terminator_effect(
|
fn apply_before_terminator_effect(
|
||||||
|
@ -403,15 +380,6 @@ where
|
||||||
self.call_return_effect(state, block, return_places);
|
self.call_return_effect(state, block, return_places);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_yield_resume_effect(
|
|
||||||
&mut self,
|
|
||||||
state: &mut A::Domain,
|
|
||||||
resume_block: BasicBlock,
|
|
||||||
resume_place: mir::Place<'tcx>,
|
|
||||||
) {
|
|
||||||
self.yield_resume_effect(state, resume_block, resume_place);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_switch_int_edge_effects(
|
fn apply_switch_int_edge_effects(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: BasicBlock,
|
block: BasicBlock,
|
||||||
|
@ -621,29 +589,5 @@ pub trait SwitchIntEdgeEffects<D> {
|
||||||
fn apply(&mut self, apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget));
|
fn apply(&mut self, apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List of places that are written to after a successful (non-unwind) return
|
|
||||||
/// from a `Call` or `InlineAsm`.
|
|
||||||
pub enum CallReturnPlaces<'a, 'tcx> {
|
|
||||||
Call(mir::Place<'tcx>),
|
|
||||||
InlineAsm(&'a [mir::InlineAsmOperand<'tcx>]),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> CallReturnPlaces<'_, 'tcx> {
|
|
||||||
pub fn for_each(&self, mut f: impl FnMut(mir::Place<'tcx>)) {
|
|
||||||
match *self {
|
|
||||||
Self::Call(place) => f(place),
|
|
||||||
Self::InlineAsm(operands) => {
|
|
||||||
for op in operands {
|
|
||||||
match *op {
|
|
||||||
mir::InlineAsmOperand::Out { place: Some(place), .. }
|
|
||||||
| mir::InlineAsmOperand::InOut { out_place: Some(place), .. } => f(place),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
|
@ -198,14 +198,15 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
|
||||||
assert!(state.insert(idx));
|
assert!(state.insert(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_terminator_effect(
|
fn apply_terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut Self::Domain,
|
state: &mut Self::Domain,
|
||||||
_terminator: &mir::Terminator<'tcx>,
|
terminator: &'mir mir::Terminator<'tcx>,
|
||||||
location: Location,
|
location: Location,
|
||||||
) {
|
) -> TerminatorEdge<'mir, 'tcx> {
|
||||||
let idx = self.effect(Effect::Primary.at_index(location.statement_index));
|
let idx = self.effect(Effect::Primary.at_index(location.statement_index));
|
||||||
assert!(state.insert(idx));
|
assert!(state.insert(idx));
|
||||||
|
terminator.edges()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_before_terminator_effect(
|
fn apply_before_terminator_effect(
|
||||||
|
|
|
@ -2,7 +2,6 @@ use rustc_index::bit_set::BitSet;
|
||||||
use rustc_middle::mir::visit::Visitor;
|
use rustc_middle::mir::visit::Visitor;
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
|
|
||||||
use crate::framework::CallReturnPlaces;
|
|
||||||
use crate::{AnalysisDomain, GenKill, GenKillAnalysis};
|
use crate::{AnalysisDomain, GenKill, GenKillAnalysis};
|
||||||
|
|
||||||
/// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points
|
/// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points
|
||||||
|
@ -15,7 +14,7 @@ use crate::{AnalysisDomain, GenKill, GenKillAnalysis};
|
||||||
pub struct MaybeBorrowedLocals;
|
pub struct MaybeBorrowedLocals;
|
||||||
|
|
||||||
impl MaybeBorrowedLocals {
|
impl MaybeBorrowedLocals {
|
||||||
fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T> {
|
pub(super) fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T> {
|
||||||
TransferFunction { trans }
|
TransferFunction { trans }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,13 +49,14 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
|
||||||
self.transfer_function(trans).visit_statement(statement, location);
|
self.transfer_function(trans).visit_statement(statement, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(
|
fn terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
trans: &mut impl GenKill<Self::Idx>,
|
trans: &mut Self::Domain,
|
||||||
terminator: &Terminator<'tcx>,
|
terminator: &'mir Terminator<'tcx>,
|
||||||
location: Location,
|
location: Location,
|
||||||
) {
|
) -> TerminatorEdge<'mir, 'tcx> {
|
||||||
self.transfer_function(trans).visit_terminator(terminator, location);
|
self.transfer_function(trans).visit_terminator(terminator, location);
|
||||||
|
terminator.edges()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_return_effect(
|
fn call_return_effect(
|
||||||
|
@ -69,7 +69,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
|
/// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
|
||||||
struct TransferFunction<'a, T> {
|
pub(super) struct TransferFunction<'a, T> {
|
||||||
trans: &'a mut T,
|
trans: &'a mut T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use rustc_index::bit_set::{BitSet, ChunkedBitSet};
|
use rustc_index::bit_set::{BitSet, ChunkedBitSet};
|
||||||
use rustc_index::Idx;
|
use rustc_index::Idx;
|
||||||
use rustc_middle::mir::{self, Body, Location};
|
use rustc_middle::mir::{self, Body, CallReturnPlaces, Location, TerminatorEdge};
|
||||||
use rustc_middle::ty::{self, TyCtxt};
|
use rustc_middle::ty::{self, TyCtxt};
|
||||||
|
|
||||||
use crate::drop_flag_effects_for_function_entry;
|
use crate::drop_flag_effects_for_function_entry;
|
||||||
use crate::drop_flag_effects_for_location;
|
use crate::drop_flag_effects_for_location;
|
||||||
use crate::elaborate_drops::DropFlagState;
|
use crate::elaborate_drops::DropFlagState;
|
||||||
use crate::framework::{CallReturnPlaces, SwitchIntEdgeEffects};
|
use crate::framework::SwitchIntEdgeEffects;
|
||||||
use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
|
use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
|
||||||
use crate::on_lookup_result_bits;
|
use crate::on_lookup_result_bits;
|
||||||
use crate::MoveDataParamEnv;
|
use crate::MoveDataParamEnv;
|
||||||
|
@ -318,15 +318,16 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(
|
fn terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
trans: &mut impl GenKill<Self::Idx>,
|
state: &mut Self::Domain,
|
||||||
_: &mir::Terminator<'tcx>,
|
terminator: &'mir mir::Terminator<'tcx>,
|
||||||
location: Location,
|
location: Location,
|
||||||
) {
|
) -> TerminatorEdge<'mir, 'tcx> {
|
||||||
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
|
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
|
||||||
Self::update_bits(trans, path, s)
|
Self::update_bits(state, path, s)
|
||||||
});
|
});
|
||||||
|
terminator.edges()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_return_effect(
|
fn call_return_effect(
|
||||||
|
@ -438,15 +439,16 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
|
||||||
// mutable borrow occurs. Places cannot become uninitialized through a mutable reference.
|
// mutable borrow occurs. Places cannot become uninitialized through a mutable reference.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(
|
fn terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
trans: &mut impl GenKill<Self::Idx>,
|
trans: &mut Self::Domain,
|
||||||
_terminator: &mir::Terminator<'tcx>,
|
terminator: &'mir mir::Terminator<'tcx>,
|
||||||
location: Location,
|
location: Location,
|
||||||
) {
|
) -> TerminatorEdge<'mir, 'tcx> {
|
||||||
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
|
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
|
||||||
Self::update_bits(trans, path, s)
|
Self::update_bits(trans, path, s)
|
||||||
});
|
});
|
||||||
|
terminator.edges()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_return_effect(
|
fn call_return_effect(
|
||||||
|
@ -559,15 +561,16 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(
|
fn terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
trans: &mut impl GenKill<Self::Idx>,
|
trans: &mut Self::Domain,
|
||||||
_terminator: &mir::Terminator<'tcx>,
|
terminator: &'mir mir::Terminator<'tcx>,
|
||||||
location: Location,
|
location: Location,
|
||||||
) {
|
) -> TerminatorEdge<'mir, 'tcx> {
|
||||||
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
|
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
|
||||||
Self::update_bits(trans, path, s)
|
Self::update_bits(trans, path, s)
|
||||||
})
|
});
|
||||||
|
terminator.edges()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_return_effect(
|
fn call_return_effect(
|
||||||
|
@ -640,13 +643,13 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(self, trans, _terminator), level = "debug")]
|
#[instrument(skip(self, trans, terminator), level = "debug")]
|
||||||
fn terminator_effect(
|
fn terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
trans: &mut impl GenKill<Self::Idx>,
|
trans: &mut Self::Domain,
|
||||||
_terminator: &mir::Terminator<'tcx>,
|
terminator: &'mir mir::Terminator<'tcx>,
|
||||||
location: Location,
|
location: Location,
|
||||||
) {
|
) -> TerminatorEdge<'mir, 'tcx> {
|
||||||
let (body, move_data) = (self.body, self.move_data());
|
let (body, move_data) = (self.body, self.move_data());
|
||||||
let term = body[location.block].terminator();
|
let term = body[location.block].terminator();
|
||||||
let init_loc_map = &move_data.init_loc_map;
|
let init_loc_map = &move_data.init_loc_map;
|
||||||
|
@ -660,6 +663,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
|
||||||
})
|
})
|
||||||
.copied(),
|
.copied(),
|
||||||
);
|
);
|
||||||
|
terminator.edges()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_return_effect(
|
fn call_return_effect(
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use rustc_index::bit_set::{BitSet, ChunkedBitSet};
|
use rustc_index::bit_set::{BitSet, ChunkedBitSet};
|
||||||
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
||||||
use rustc_middle::mir::{self, Local, Location, Place, StatementKind};
|
use rustc_middle::mir::{
|
||||||
|
self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdge,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{Analysis, AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKillAnalysis};
|
use crate::{Analysis, AnalysisDomain, Backward, GenKill, GenKillAnalysis};
|
||||||
|
|
||||||
/// A [live-variable dataflow analysis][liveness].
|
/// A [live-variable dataflow analysis][liveness].
|
||||||
///
|
///
|
||||||
|
@ -56,13 +58,14 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
|
||||||
TransferFunction(trans).visit_statement(statement, location);
|
TransferFunction(trans).visit_statement(statement, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(
|
fn terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
trans: &mut impl GenKill<Self::Idx>,
|
trans: &mut Self::Domain,
|
||||||
terminator: &mir::Terminator<'tcx>,
|
terminator: &'mir mir::Terminator<'tcx>,
|
||||||
location: Location,
|
location: Location,
|
||||||
) {
|
) -> TerminatorEdge<'mir, 'tcx> {
|
||||||
TransferFunction(trans).visit_terminator(terminator, location);
|
TransferFunction(trans).visit_terminator(terminator, location);
|
||||||
|
terminator.edges()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_return_effect(
|
fn call_return_effect(
|
||||||
|
@ -72,24 +75,13 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
|
||||||
return_places: CallReturnPlaces<'_, 'tcx>,
|
return_places: CallReturnPlaces<'_, 'tcx>,
|
||||||
) {
|
) {
|
||||||
return_places.for_each(|place| {
|
return_places.for_each(|place| {
|
||||||
if let Some(local) = place.as_local() {
|
YieldResumeEffect(trans).visit_place(
|
||||||
trans.kill(local);
|
&place,
|
||||||
}
|
PlaceContext::MutatingUse(MutatingUseContext::Yield),
|
||||||
|
Location::START,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn yield_resume_effect(
|
|
||||||
&mut self,
|
|
||||||
trans: &mut impl GenKill<Self::Idx>,
|
|
||||||
_resume_block: mir::BasicBlock,
|
|
||||||
resume_place: mir::Place<'tcx>,
|
|
||||||
) {
|
|
||||||
YieldResumeEffect(trans).visit_place(
|
|
||||||
&resume_place,
|
|
||||||
PlaceContext::MutatingUse(MutatingUseContext::Yield),
|
|
||||||
Location::START,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TransferFunction<'a, T>(pub &'a mut T);
|
pub struct TransferFunction<'a, T>(pub &'a mut T);
|
||||||
|
@ -99,16 +91,12 @@ where
|
||||||
T: GenKill<Local>,
|
T: GenKill<Local>,
|
||||||
{
|
{
|
||||||
fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) {
|
fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) {
|
||||||
if let PlaceContext::MutatingUse(MutatingUseContext::Yield) = context {
|
|
||||||
// The resume place is evaluated and assigned to only after generator resumes, so its
|
|
||||||
// effect is handled separately in `yield_resume_effect`.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match DefUse::for_place(*place, context) {
|
match DefUse::for_place(*place, context) {
|
||||||
Some(DefUse::Def) => {
|
Some(DefUse::Def) => {
|
||||||
if let PlaceContext::MutatingUse(
|
if let PlaceContext::MutatingUse(
|
||||||
MutatingUseContext::Call | MutatingUseContext::AsmOutput,
|
MutatingUseContext::Yield
|
||||||
|
| MutatingUseContext::Call
|
||||||
|
| MutatingUseContext::AsmOutput,
|
||||||
) = context
|
) = context
|
||||||
{
|
{
|
||||||
// For the associated terminators, this is only a `Def` when the terminator returns
|
// For the associated terminators, this is only a `Def` when the terminator returns
|
||||||
|
@ -287,13 +275,14 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
|
||||||
TransferFunction(trans).visit_statement(statement, location);
|
TransferFunction(trans).visit_statement(statement, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_terminator_effect(
|
fn apply_terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
trans: &mut Self::Domain,
|
trans: &mut Self::Domain,
|
||||||
terminator: &mir::Terminator<'tcx>,
|
terminator: &'mir mir::Terminator<'tcx>,
|
||||||
location: Location,
|
location: Location,
|
||||||
) {
|
) -> TerminatorEdge<'mir, 'tcx> {
|
||||||
TransferFunction(trans).visit_terminator(terminator, location);
|
TransferFunction(trans).visit_terminator(terminator, location);
|
||||||
|
TerminatorEdge::None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_call_return_effect(
|
fn apply_call_return_effect(
|
||||||
|
@ -303,22 +292,11 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
|
||||||
return_places: CallReturnPlaces<'_, 'tcx>,
|
return_places: CallReturnPlaces<'_, 'tcx>,
|
||||||
) {
|
) {
|
||||||
return_places.for_each(|place| {
|
return_places.for_each(|place| {
|
||||||
if let Some(local) = place.as_local() {
|
YieldResumeEffect(trans).visit_place(
|
||||||
trans.remove(local);
|
&place,
|
||||||
}
|
PlaceContext::MutatingUse(MutatingUseContext::Yield),
|
||||||
|
Location::START,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_yield_resume_effect(
|
|
||||||
&mut self,
|
|
||||||
trans: &mut Self::Domain,
|
|
||||||
_resume_block: mir::BasicBlock,
|
|
||||||
resume_place: mir::Place<'tcx>,
|
|
||||||
) {
|
|
||||||
YieldResumeEffect(trans).visit_place(
|
|
||||||
&resume_place,
|
|
||||||
PlaceContext::MutatingUse(MutatingUseContext::Yield),
|
|
||||||
Location::START,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use rustc_middle::mir::*;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use super::MaybeBorrowedLocals;
|
use super::MaybeBorrowedLocals;
|
||||||
use crate::{CallReturnPlaces, GenKill, ResultsClonedCursor};
|
use crate::{GenKill, ResultsClonedCursor};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MaybeStorageLive<'a> {
|
pub struct MaybeStorageLive<'a> {
|
||||||
|
@ -66,13 +66,14 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(
|
fn terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_trans: &mut impl GenKill<Self::Idx>,
|
_trans: &mut Self::Domain,
|
||||||
_: &Terminator<'tcx>,
|
terminator: &'mir Terminator<'tcx>,
|
||||||
_: Location,
|
_: Location,
|
||||||
) {
|
) -> TerminatorEdge<'mir, 'tcx> {
|
||||||
// Terminators have no effect
|
// Terminators have no effect
|
||||||
|
terminator.edges()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_return_effect(
|
fn call_return_effect(
|
||||||
|
@ -137,13 +138,14 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(
|
fn terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_trans: &mut impl GenKill<Self::Idx>,
|
_: &mut Self::Domain,
|
||||||
_: &Terminator<'tcx>,
|
terminator: &'mir Terminator<'tcx>,
|
||||||
_: Location,
|
_: Location,
|
||||||
) {
|
) -> TerminatorEdge<'mir, 'tcx> {
|
||||||
// Terminators have no effect
|
// Terminators have no effect
|
||||||
|
terminator.edges()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_return_effect(
|
fn call_return_effect(
|
||||||
|
@ -254,7 +256,10 @@ impl<'tcx> crate::GenKillAnalysis<'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.mut_analysis().terminator_effect(trans, terminator, loc);
|
self.borrowed_locals
|
||||||
|
.mut_analysis()
|
||||||
|
.transfer_function(trans)
|
||||||
|
.visit_terminator(terminator, loc);
|
||||||
|
|
||||||
match &terminator.kind {
|
match &terminator.kind {
|
||||||
TerminatorKind::Call { destination, .. } => {
|
TerminatorKind::Call { destination, .. } => {
|
||||||
|
@ -300,12 +305,12 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(
|
fn terminator_effect<'t>(
|
||||||
&mut self,
|
&mut self,
|
||||||
trans: &mut impl GenKill<Self::Idx>,
|
trans: &mut Self::Domain,
|
||||||
terminator: &Terminator<'tcx>,
|
terminator: &'t Terminator<'tcx>,
|
||||||
loc: Location,
|
loc: Location,
|
||||||
) {
|
) -> TerminatorEdge<'t, 'tcx> {
|
||||||
match terminator.kind {
|
match terminator.kind {
|
||||||
// For call terminators the destination requires storage for the call
|
// For call terminators the destination requires storage for the call
|
||||||
// and after the call returns successfully, but not after a panic.
|
// and after the call returns successfully, but not after a panic.
|
||||||
|
@ -337,6 +342,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.check_for_move(trans, loc);
|
self.check_for_move(trans, loc);
|
||||||
|
terminator.edges()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_return_effect(
|
fn call_return_effect(
|
||||||
|
@ -347,15 +353,6 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
|
||||||
) {
|
) {
|
||||||
return_places.for_each(|place| trans.gen(place.local));
|
return_places.for_each(|place| trans.gen(place.local));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn yield_resume_effect(
|
|
||||||
&mut self,
|
|
||||||
trans: &mut impl GenKill<Self::Idx>,
|
|
||||||
_resume_block: BasicBlock,
|
|
||||||
resume_place: Place<'tcx>,
|
|
||||||
) {
|
|
||||||
trans.gen(resume_place.local);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> MaybeRequiresStorage<'_, '_, 'tcx> {
|
impl<'tcx> MaybeRequiresStorage<'_, '_, 'tcx> {
|
||||||
|
|
|
@ -28,9 +28,9 @@ pub use self::drop_flag_effects::{
|
||||||
};
|
};
|
||||||
pub use self::framework::{
|
pub use self::framework::{
|
||||||
fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, AnalysisResults, Backward,
|
fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, AnalysisResults, Backward,
|
||||||
CallReturnPlaces, CloneAnalysis, Direction, Engine, Forward, GenKill, GenKillAnalysis,
|
CloneAnalysis, Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice,
|
||||||
JoinSemiLattice, MaybeUnreachable, Results, ResultsCloned, ResultsClonedCursor, ResultsCursor,
|
MaybeUnreachable, Results, ResultsCloned, ResultsClonedCursor, ResultsCursor, ResultsRefCursor,
|
||||||
ResultsRefCursor, ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects,
|
ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::move_paths::MoveData;
|
use self::move_paths::MoveData;
|
||||||
|
|
|
@ -47,8 +47,7 @@ use rustc_target::abi::{FieldIdx, VariantIdx};
|
||||||
|
|
||||||
use crate::lattice::{HasBottom, HasTop};
|
use crate::lattice::{HasBottom, HasTop};
|
||||||
use crate::{
|
use crate::{
|
||||||
fmt::DebugWithContext, Analysis, AnalysisDomain, CallReturnPlaces, JoinSemiLattice,
|
fmt::DebugWithContext, Analysis, AnalysisDomain, JoinSemiLattice, SwitchIntEdgeEffects,
|
||||||
SwitchIntEdgeEffects,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait ValueAnalysis<'tcx> {
|
pub trait ValueAnalysis<'tcx> {
|
||||||
|
@ -353,22 +352,23 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_terminator_effect(
|
fn apply_terminator_effect<'mir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut Self::Domain,
|
state: &mut Self::Domain,
|
||||||
terminator: &Terminator<'tcx>,
|
terminator: &'mir Terminator<'tcx>,
|
||||||
_location: Location,
|
_location: Location,
|
||||||
) {
|
) -> TerminatorEdge<'mir, 'tcx> {
|
||||||
if state.is_reachable() {
|
if state.is_reachable() {
|
||||||
self.0.handle_terminator(terminator, state);
|
self.0.handle_terminator(terminator, state);
|
||||||
}
|
}
|
||||||
|
terminator.edges()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_call_return_effect(
|
fn apply_call_return_effect(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut Self::Domain,
|
state: &mut Self::Domain,
|
||||||
_block: BasicBlock,
|
_block: BasicBlock,
|
||||||
return_places: crate::CallReturnPlaces<'_, 'tcx>,
|
return_places: CallReturnPlaces<'_, 'tcx>,
|
||||||
) {
|
) {
|
||||||
if state.is_reachable() {
|
if state.is_reachable() {
|
||||||
self.0.handle_call_return(return_places, state)
|
self.0.handle_call_return(return_places, state)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue