Allow apply_terminator_effect to customize edges.

This commit is contained in:
Camille GILLOT 2023-05-07 10:20:43 +00:00
parent 32711b2b4e
commit 5173d85043
14 changed files with 300 additions and 306 deletions

View File

@ -2,12 +2,14 @@
#![deny(rustc::diagnostic_outside_of_impl)]
use rustc_data_structures::fx::FxIndexMap;
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::TyCtxt;
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
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 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);
}
fn terminator_effect(
fn terminator_effect<'mir>(
&mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
trans: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
_location: Location,
) {
) -> TerminatorEdge<'mir, 'tcx> {
if let mir::TerminatorKind::InlineAsm { operands, .. } = &terminator.kind {
for op in operands {
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(

View File

@ -4,10 +4,12 @@
use rustc_index::bit_set::BitSet;
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::JoinSemiLattice;
use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces};
use rustc_mir_dataflow::{Analysis, AnalysisDomain};
use std::fmt;
use std::marker::PhantomData;
@ -345,13 +347,14 @@ where
self.transfer_function(state).visit_statement(statement, location);
}
fn apply_terminator_effect(
fn apply_terminator_effect<'mir>(
&mut self,
state: &mut Self::Domain,
terminator: &mir::Terminator<'tcx>,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) {
) -> TerminatorEdge<'mir, 'tcx> {
self.transfer_function(state).visit_terminator(terminator, location);
terminator.edges()
}
fn apply_call_return_effect(

View File

@ -10,6 +10,7 @@ use std::iter;
use std::slice;
pub use super::query::*;
use super::*;
#[derive(Debug, Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
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 },
}
}
}

View File

@ -1,11 +1,10 @@
use rustc_middle::mir::{self, BasicBlock, Location, SwitchTargets, UnwindAction};
use rustc_middle::ty::TyCtxt;
use rustc_middle::mir::{
self, BasicBlock, CallReturnPlaces, Location, SwitchTargets, TerminatorEdge, UnwindAction,
};
use std::ops::RangeInclusive;
use super::visitor::{ResultsVisitable, ResultsVisitor};
use super::{
Analysis, CallReturnPlaces, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget,
};
use super::{Analysis, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget};
pub trait Direction {
const IS_FORWARD: bool;
@ -24,12 +23,14 @@ pub trait Direction {
) where
A: Analysis<'tcx>;
fn apply_effects_in_block<'tcx, A>(
fn apply_effects_in_block<'mir, 'tcx, A>(
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
) where
block_data: &'mir mir::BasicBlockData<'tcx>,
statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
) -> TerminatorEdge<'mir, 'tcx>
where
A: Analysis<'tcx>;
fn gen_kill_effects_in_block<'tcx, A>(
@ -51,10 +52,10 @@ pub trait Direction {
fn join_state_into_successors_of<'tcx, A>(
analysis: &mut A,
tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
block: BasicBlock,
edges: TerminatorEdge<'_, 'tcx>,
propagate: impl FnMut(BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>;
@ -66,24 +67,30 @@ pub struct Backward;
impl Direction for Backward {
const IS_FORWARD: bool = false;
fn apply_effects_in_block<'tcx, A>(
fn apply_effects_in_block<'mir, 'tcx, A>(
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
) where
block_data: &'mir mir::BasicBlockData<'tcx>,
statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
) -> TerminatorEdge<'mir, '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);
let edges = analysis.apply_terminator_effect(state, terminator, location);
if let Some(statement_effect) = statement_effect {
statement_effect(block, state)
} else {
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);
}
}
edges
}
fn gen_kill_effects_in_block<'tcx, A>(
@ -94,11 +101,6 @@ impl Direction for Backward {
) 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);
@ -217,10 +219,10 @@ impl Direction for Backward {
fn join_state_into_successors_of<'tcx, A>(
analysis: &mut A,
_tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
(bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
bb: BasicBlock,
_edges: TerminatorEdge<'_, 'tcx>,
mut propagate: impl FnMut(BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>,
@ -254,7 +256,11 @@ impl Direction for Backward {
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);
analysis.apply_call_return_effect(
&mut tmp,
resume,
CallReturnPlaces::Yield(resume_arg),
);
propagate(pred, &tmp);
}
@ -318,24 +324,30 @@ pub struct Forward;
impl Direction for Forward {
const IS_FORWARD: bool = true;
fn apply_effects_in_block<'tcx, A>(
fn apply_effects_in_block<'mir, 'tcx, A>(
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
) where
block_data: &'mir mir::BasicBlockData<'tcx>,
statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
) -> TerminatorEdge<'mir, '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);
if let Some(statement_effect) = statement_effect {
statement_effect(block, state)
} else {
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);
analysis.apply_terminator_effect(state, terminator, location)
}
fn gen_kill_effects_in_block<'tcx, A>(
@ -351,11 +363,6 @@ impl Direction for Forward {
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);
}
fn apply_effects_in_range<'tcx, A>(
@ -464,86 +471,32 @@ impl Direction for Forward {
fn join_state_into_successors_of<'tcx, A>(
analysis: &mut A,
_tcx: TyCtxt<'tcx>,
_body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
(bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
bb: BasicBlock,
edges: TerminatorEdge<'_, 'tcx>,
mut propagate: impl FnMut(BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>,
{
use mir::TerminatorKind::*;
match bb_data.terminator().kind {
Return | Resume | Terminate | GeneratorDrop | Unreachable => {}
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);
}
match edges {
TerminatorEdge::None => {}
TerminatorEdge::Single(target) => propagate(target, exit_state),
TerminatorEdge::Double(target, unwind) => {
propagate(target, exit_state);
propagate(unwind, exit_state);
}
FalseEdge { real_target, imaginary_target } => {
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: _ } => {
TerminatorEdge::AssignOnReturn { return_, unwind, place } => {
// This must be done *first*, otherwise the unwind path will see the assignments.
if let UnwindAction::Cleanup(unwind) = unwind {
propagate(unwind, exit_state);
}
if let Some(target) = target {
// 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::Call(destination),
);
propagate(target, exit_state);
if let Some(return_) = return_ {
analysis.apply_call_return_effect(exit_state, bb, place);
propagate(return_, exit_state);
}
}
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 } => {
TerminatorEdge::SwitchInt { targets, discr } => {
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
exit_state,
targets,

View File

@ -264,19 +264,20 @@ where
state.clone_from(&entry_sets[bb]);
// Apply the block transfer function, using the cached one if it exists.
match &apply_trans_for_block {
Some(apply) => apply(bb, &mut state),
None => {
A::Direction::apply_effects_in_block(&mut analysis, &mut state, bb, bb_data)
}
}
let edges = A::Direction::apply_effects_in_block(
&mut analysis,
&mut state,
bb,
bb_data,
apply_trans_for_block.as_deref(),
);
A::Direction::join_state_into_successors_of(
&mut analysis,
tcx,
body,
&mut state,
(bb, bb_data),
bb,
edges,
|target: BasicBlock, state: &A::Domain| {
let set_changed = entry_sets[target].join(state);
if set_changed {

View File

@ -269,7 +269,11 @@ where
self.write_row(w, "", "(on yield resume)", |this, w, fmt| {
let state_on_generator_drop = this.results.get().clone();
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!(

View File

@ -34,7 +34,7 @@ use std::cmp::Ordering;
use rustc_index::bit_set::{BitSet, ChunkedBitSet, HybridBitSet};
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;
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
/// `InitializedPlaces` analyses, the return place for a function call is not marked as
/// initialized here.
fn apply_terminator_effect(
fn apply_terminator_effect<'mir>(
&mut self,
state: &mut Self::Domain,
terminator: &mir::Terminator<'tcx>,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
);
) -> TerminatorEdge<'mir, 'tcx>;
/// Updates the current dataflow state with an effect that occurs immediately *before* the
/// given terminator.
@ -198,20 +198,6 @@ pub trait Analysis<'tcx>: AnalysisDomain<'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
/// `SwitchInt` terminator.
///
@ -306,12 +292,12 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
}
/// See `Analysis::apply_terminator_effect`.
fn terminator_effect(
fn terminator_effect<'mir>(
&mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
trans: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
);
) -> TerminatorEdge<'mir, 'tcx>;
/// See `Analysis::apply_before_terminator_effect`.
fn before_terminator_effect(
@ -332,15 +318,6 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'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`.
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
&mut self,
@ -374,13 +351,13 @@ where
self.before_statement_effect(state, statement, location);
}
fn apply_terminator_effect(
fn apply_terminator_effect<'mir>(
&mut self,
state: &mut A::Domain,
terminator: &mir::Terminator<'tcx>,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) {
self.terminator_effect(state, terminator, location);
) -> TerminatorEdge<'mir, 'tcx> {
self.terminator_effect(state, terminator, location)
}
fn apply_before_terminator_effect(
@ -403,15 +380,6 @@ where
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(
&mut self,
block: BasicBlock,
@ -621,29 +589,5 @@ pub trait SwitchIntEdgeEffects<D> {
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)]
mod tests;

View File

@ -198,14 +198,15 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
assert!(state.insert(idx));
}
fn apply_terminator_effect(
fn apply_terminator_effect<'mir>(
&mut self,
state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) {
) -> TerminatorEdge<'mir, 'tcx> {
let idx = self.effect(Effect::Primary.at_index(location.statement_index));
assert!(state.insert(idx));
terminator.edges()
}
fn apply_before_terminator_effect(

View File

@ -2,7 +2,6 @@ use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
use crate::framework::CallReturnPlaces;
use crate::{AnalysisDomain, GenKill, GenKillAnalysis};
/// 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;
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 }
}
}
@ -50,13 +49,14 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
self.transfer_function(trans).visit_statement(statement, location);
}
fn terminator_effect(
fn terminator_effect<'mir>(
&mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &Terminator<'tcx>,
trans: &mut Self::Domain,
terminator: &'mir Terminator<'tcx>,
location: Location,
) {
) -> TerminatorEdge<'mir, 'tcx> {
self.transfer_function(trans).visit_terminator(terminator, location);
terminator.edges()
}
fn call_return_effect(
@ -69,7 +69,7 @@ impl<'tcx> GenKillAnalysis<'tcx> 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,
}

View File

@ -1,12 +1,12 @@
use rustc_index::bit_set::{BitSet, ChunkedBitSet};
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 crate::drop_flag_effects_for_function_entry;
use crate::drop_flag_effects_for_location;
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::on_lookup_result_bits;
use crate::MoveDataParamEnv;
@ -318,15 +318,16 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
}
}
fn terminator_effect(
fn terminator_effect<'mir>(
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_: &mir::Terminator<'tcx>,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) {
) -> TerminatorEdge<'mir, 'tcx> {
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(
@ -438,15 +439,16 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
// mutable borrow occurs. Places cannot become uninitialized through a mutable reference.
}
fn terminator_effect(
fn terminator_effect<'mir>(
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_terminator: &mir::Terminator<'tcx>,
trans: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) {
) -> TerminatorEdge<'mir, 'tcx> {
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
Self::update_bits(trans, path, s)
});
terminator.edges()
}
fn call_return_effect(
@ -559,15 +561,16 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
})
}
fn terminator_effect(
fn terminator_effect<'mir>(
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_terminator: &mir::Terminator<'tcx>,
trans: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) {
) -> TerminatorEdge<'mir, 'tcx> {
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
Self::update_bits(trans, path, s)
})
});
terminator.edges()
}
fn call_return_effect(
@ -640,13 +643,13 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
}
}
#[instrument(skip(self, trans, _terminator), level = "debug")]
fn terminator_effect(
#[instrument(skip(self, trans, terminator), level = "debug")]
fn terminator_effect<'mir>(
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_terminator: &mir::Terminator<'tcx>,
trans: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) {
) -> TerminatorEdge<'mir, 'tcx> {
let (body, move_data) = (self.body, self.move_data());
let term = body[location.block].terminator();
let init_loc_map = &move_data.init_loc_map;
@ -660,6 +663,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
})
.copied(),
);
terminator.edges()
}
fn call_return_effect(

View File

@ -1,8 +1,10 @@
use rustc_index::bit_set::{BitSet, ChunkedBitSet};
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].
///
@ -56,13 +58,14 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
TransferFunction(trans).visit_statement(statement, location);
}
fn terminator_effect(
fn terminator_effect<'mir>(
&mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
trans: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) {
) -> TerminatorEdge<'mir, 'tcx> {
TransferFunction(trans).visit_terminator(terminator, location);
terminator.edges()
}
fn call_return_effect(
@ -72,24 +75,13 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
return_places: CallReturnPlaces<'_, 'tcx>,
) {
return_places.for_each(|place| {
if let Some(local) = place.as_local() {
trans.kill(local);
}
YieldResumeEffect(trans).visit_place(
&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);
@ -99,16 +91,12 @@ where
T: GenKill<Local>,
{
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) {
Some(DefUse::Def) => {
if let PlaceContext::MutatingUse(
MutatingUseContext::Call | MutatingUseContext::AsmOutput,
MutatingUseContext::Yield
| MutatingUseContext::Call
| MutatingUseContext::AsmOutput,
) = context
{
// 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);
}
fn apply_terminator_effect(
fn apply_terminator_effect<'mir>(
&mut self,
trans: &mut Self::Domain,
terminator: &mir::Terminator<'tcx>,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) {
) -> TerminatorEdge<'mir, 'tcx> {
TransferFunction(trans).visit_terminator(terminator, location);
TerminatorEdge::None
}
fn apply_call_return_effect(
@ -303,22 +292,11 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
return_places: CallReturnPlaces<'_, 'tcx>,
) {
return_places.for_each(|place| {
if let Some(local) = place.as_local() {
trans.remove(local);
}
YieldResumeEffect(trans).visit_place(
&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,
)
}
}

View File

@ -5,7 +5,7 @@ use rustc_middle::mir::*;
use std::borrow::Cow;
use super::MaybeBorrowedLocals;
use crate::{CallReturnPlaces, GenKill, ResultsClonedCursor};
use crate::{GenKill, ResultsClonedCursor};
#[derive(Clone)]
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,
_trans: &mut impl GenKill<Self::Idx>,
_: &Terminator<'tcx>,
_trans: &mut Self::Domain,
terminator: &'mir Terminator<'tcx>,
_: Location,
) {
) -> TerminatorEdge<'mir, 'tcx> {
// Terminators have no effect
terminator.edges()
}
fn call_return_effect(
@ -137,13 +138,14 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
}
}
fn terminator_effect(
fn terminator_effect<'mir>(
&mut self,
_trans: &mut impl GenKill<Self::Idx>,
_: &Terminator<'tcx>,
_: &mut Self::Domain,
terminator: &'mir Terminator<'tcx>,
_: Location,
) {
) -> TerminatorEdge<'mir, 'tcx> {
// Terminators have no effect
terminator.edges()
}
fn call_return_effect(
@ -254,7 +256,10 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
loc: Location,
) {
// 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 {
TerminatorKind::Call { destination, .. } => {
@ -300,12 +305,12 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
}
}
fn terminator_effect(
fn terminator_effect<'t>(
&mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &Terminator<'tcx>,
trans: &mut Self::Domain,
terminator: &'t Terminator<'tcx>,
loc: Location,
) {
) -> TerminatorEdge<'t, 'tcx> {
match terminator.kind {
// For call terminators the destination requires storage for the call
// 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);
terminator.edges()
}
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));
}
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> {

View File

@ -28,9 +28,9 @@ pub use self::drop_flag_effects::{
};
pub use self::framework::{
fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, AnalysisResults, Backward,
CallReturnPlaces, CloneAnalysis, Direction, Engine, Forward, GenKill, GenKillAnalysis,
JoinSemiLattice, MaybeUnreachable, Results, ResultsCloned, ResultsClonedCursor, ResultsCursor,
ResultsRefCursor, ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects,
CloneAnalysis, Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice,
MaybeUnreachable, Results, ResultsCloned, ResultsClonedCursor, ResultsCursor, ResultsRefCursor,
ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects,
};
use self::move_paths::MoveData;

View File

@ -47,8 +47,7 @@ use rustc_target::abi::{FieldIdx, VariantIdx};
use crate::lattice::{HasBottom, HasTop};
use crate::{
fmt::DebugWithContext, Analysis, AnalysisDomain, CallReturnPlaces, JoinSemiLattice,
SwitchIntEdgeEffects,
fmt::DebugWithContext, Analysis, AnalysisDomain, JoinSemiLattice, SwitchIntEdgeEffects,
};
pub trait ValueAnalysis<'tcx> {
@ -353,22 +352,23 @@ where
}
}
fn apply_terminator_effect(
fn apply_terminator_effect<'mir>(
&mut self,
state: &mut Self::Domain,
terminator: &Terminator<'tcx>,
terminator: &'mir Terminator<'tcx>,
_location: Location,
) {
) -> TerminatorEdge<'mir, 'tcx> {
if state.is_reachable() {
self.0.handle_terminator(terminator, state);
}
terminator.edges()
}
fn apply_call_return_effect(
&mut self,
state: &mut Self::Domain,
_block: BasicBlock,
return_places: crate::CallReturnPlaces<'_, 'tcx>,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
if state.is_reachable() {
self.0.handle_call_return(return_places, state)