Auto merge of #108293 - Jarcho:mut_analyses, r=eholk

Take MIR dataflow analyses by mutable reference

The main motivation here is any analysis requiring dynamically sized scratch memory to work. One concrete example would be pointer target tracking, where tracking the results of a dereference can result in multiple possible targets. This leads to processing multi-level dereferences requiring the ability to handle a changing number of potential targets per step. A (simplified) function for this would be `fn apply_deref(potential_targets: &mut Vec<Target>)` which would use the scratch space contained in the analysis to send arguments and receive the results.

The alternative to this would be to wrap everything in a `RefCell`, which is what `MaybeRequiresStorage` currently does. This comes with a small perf cost and loses the compiler's guarantee that we don't try to take multiple borrows at the same time.

For the implementation:
* `AnalysisResults` is an unfortunate requirement to avoid an unconstrained type parameter error.
* `CloneAnalysis` could just be `Clone` instead, but that would result in more work than is required to have multiple cursors over the same result set.
* `ResultsVisitor` now takes the results type on in each function as there's no other way to have access to the analysis without cloning it. This could use an associated type rather than a type parameter, but the current approach makes it easier to not care about the type when it's not necessary.
* `MaybeRequiresStorage` now no longer uses a `RefCell`, but the graphviz formatter now does. It could be removed, but that would require even more changes and doesn't really seem necessary.
This commit is contained in:
bors 2023-06-08 23:58:44 +00:00
commit 68c8fdaac0
19 changed files with 491 additions and 288 deletions

View File

@ -59,7 +59,7 @@ macro_rules! impl_visitable {
}
fn reconstruct_before_statement_effect(
&self,
&mut self,
state: &mut Self::FlowState,
stmt: &mir::Statement<'tcx>,
loc: Location,
@ -69,7 +69,7 @@ macro_rules! impl_visitable {
}
fn reconstruct_statement_effect(
&self,
&mut self,
state: &mut Self::FlowState,
stmt: &mir::Statement<'tcx>,
loc: Location,
@ -79,7 +79,7 @@ macro_rules! impl_visitable {
}
fn reconstruct_before_terminator_effect(
&self,
&mut self,
state: &mut Self::FlowState,
term: &mir::Terminator<'tcx>,
loc: Location,
@ -89,7 +89,7 @@ macro_rules! impl_visitable {
}
fn reconstruct_terminator_effect(
&self,
&mut self,
state: &mut Self::FlowState,
term: &mir::Terminator<'tcx>,
loc: Location,
@ -343,7 +343,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
type Idx = BorrowIndex;
fn before_statement_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_statement: &mir::Statement<'tcx>,
location: Location,
@ -352,7 +352,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
}
fn statement_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
stmt: &mir::Statement<'tcx>,
location: Location,
@ -400,7 +400,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
}
fn before_terminator_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@ -409,7 +409,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
}
fn terminator_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
_location: Location,
@ -426,7 +426,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
}
fn call_return_effect(
&self,
&mut self,
_trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,

View File

@ -368,7 +368,7 @@ fn do_mir_borrowck<'tcx>(
// Compute and report region errors, if any.
mbcx.report_region_errors(nll_errors);
let results = BorrowckResults {
let mut results = BorrowckResults {
ever_inits: flow_ever_inits,
uninits: flow_uninits,
borrows: flow_borrows,
@ -379,7 +379,7 @@ fn do_mir_borrowck<'tcx>(
rustc_mir_dataflow::visit_results(
body,
traversal::reverse_postorder(body).map(|(bb, _)| bb),
&results,
&mut results,
&mut mbcx,
);
@ -598,11 +598,12 @@ struct MirBorrowckCtxt<'cx, 'tcx> {
// 2. loans made in overlapping scopes do not conflict
// 3. assignments do not affect things loaned out as immutable
// 4. moves do not affect things loaned out in any way
impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> {
impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorrowckCtxt<'cx, 'tcx> {
type FlowState = Flows<'cx, 'tcx>;
fn visit_statement_before_primary_effect(
&mut self,
_results: &R,
flow_state: &Flows<'cx, 'tcx>,
stmt: &'cx Statement<'tcx>,
location: Location,
@ -672,6 +673,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
fn visit_terminator_before_primary_effect(
&mut self,
_results: &R,
flow_state: &Flows<'cx, 'tcx>,
term: &'cx Terminator<'tcx>,
loc: Location,
@ -782,6 +784,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
fn visit_terminator_after_primary_effect(
&mut self,
_results: &R,
flow_state: &Flows<'cx, 'tcx>,
term: &'cx Terminator<'tcx>,
loc: Location,

View File

@ -337,7 +337,7 @@ where
Q: Qualif,
{
fn apply_statement_effect(
&self,
&mut self,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@ -346,7 +346,7 @@ where
}
fn apply_terminator_effect(
&self,
&mut self,
state: &mut Self::Domain,
terminator: &mir::Terminator<'tcx>,
location: Location,
@ -355,7 +355,7 @@ where
}
fn apply_call_return_effect(
&self,
&mut self,
state: &mut Self::Domain,
block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,

View File

@ -1,18 +1,60 @@
//! Random access inspection of the results of a dataflow analysis.
use crate::framework::BitSetExt;
use crate::{framework::BitSetExt, CloneAnalysis};
use std::borrow::Borrow;
use std::borrow::{Borrow, BorrowMut};
use std::cmp::Ordering;
#[cfg(debug_assertions)]
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::{self, BasicBlock, Location};
use super::{Analysis, Direction, Effect, EffectIndex, Results};
use super::{Analysis, Direction, Effect, EffectIndex, EntrySets, Results, ResultsCloned};
// `AnalysisResults` is needed as an impl such as the following has an unconstrained type
// parameter:
// ```
// impl<'tcx, A, E, R> ResultsCursor<'_, 'tcx, A, R>
// where
// A: Analysis<'tcx>,
// E: Borrow<EntrySets<'tcx, A>>,
// R: Results<'tcx, A, E>,
// {}
// ```
/// A type representing the analysis results consumed by a `ResultsCursor`.
pub trait AnalysisResults<'tcx, A>: BorrowMut<Results<'tcx, A, Self::EntrySets>>
where
A: Analysis<'tcx>,
{
/// The type containing the entry sets for this `Results` type.
///
/// Should be either `EntrySets<'tcx, A>` or `&EntrySets<'tcx, A>`.
type EntrySets: Borrow<EntrySets<'tcx, A>>;
}
impl<'tcx, A, E> AnalysisResults<'tcx, A> for Results<'tcx, A, E>
where
A: Analysis<'tcx>,
E: Borrow<EntrySets<'tcx, A>>,
{
type EntrySets = E;
}
impl<'a, 'tcx, A, E> AnalysisResults<'tcx, A> for &'a mut Results<'tcx, A, E>
where
A: Analysis<'tcx>,
E: Borrow<EntrySets<'tcx, A>>,
{
type EntrySets = E;
}
/// A `ResultsCursor` that borrows the underlying `Results`.
pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>;
pub type ResultsRefCursor<'res, 'mir, 'tcx, A> =
ResultsCursor<'mir, 'tcx, A, &'res mut Results<'tcx, A>>;
/// A `ResultsCursor` which uses a cloned `Analysis` while borrowing the underlying `Results`. This
/// allows multiple cursors over the same `Results`.
pub type ResultsClonedCursor<'res, 'mir, 'tcx, A> =
ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>>;
/// Allows random access inspection of the results of a dataflow analysis.
///
@ -45,7 +87,38 @@ where
impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
where
A: Analysis<'tcx>,
R: Borrow<Results<'tcx, A>>,
{
/// Returns the dataflow state at the current location.
pub fn get(&self) -> &A::Domain {
&self.state
}
/// Returns the body this analysis was run on.
pub fn body(&self) -> &'mir mir::Body<'tcx> {
self.body
}
/// Unwraps this cursor, returning the underlying `Results`.
pub fn into_results(self) -> R {
self.results
}
}
impl<'res, 'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>>
where
A: Analysis<'tcx> + CloneAnalysis,
{
/// Creates a new cursor over the same `Results`. Note that the cursor's position is *not*
/// copied.
pub fn new_cursor(&self) -> Self {
Self::new(self.body, self.results.reclone_analysis())
}
}
impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
where
A: Analysis<'tcx>,
R: AnalysisResults<'tcx, A>,
{
/// Returns a new cursor that can inspect `results`.
pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
@ -74,8 +147,13 @@ where
}
/// Returns the underlying `Results`.
pub fn results(&self) -> &Results<'tcx, A> {
&self.results.borrow()
pub fn results(&mut self) -> &Results<'tcx, A, R::EntrySets> {
self.results.borrow()
}
/// Returns the underlying `Results`.
pub fn mut_results(&mut self) -> &mut Results<'tcx, A, R::EntrySets> {
self.results.borrow_mut()
}
/// Returns the `Analysis` used to generate the underlying `Results`.
@ -83,9 +161,14 @@ where
&self.results.borrow().analysis
}
/// Returns the dataflow state at the current location.
pub fn get(&self) -> &A::Domain {
&self.state
/// Returns the `Analysis` used to generate the underlying `Results`.
pub fn mut_analysis(&mut self) -> &mut A {
&mut self.results.borrow_mut().analysis
}
/// Returns both the dataflow state at the current location and the `Analysis`.
pub fn get_with_analysis(&mut self) -> (&A::Domain, &mut A) {
(&self.state, &mut self.results.borrow_mut().analysis)
}
/// Resets the cursor to hold the entry set for the given basic block.
@ -97,7 +180,7 @@ where
#[cfg(debug_assertions)]
assert!(self.reachable_blocks.contains(block));
self.state.clone_from(&self.results.borrow().entry_set_for_block(block));
self.state.clone_from(self.results.borrow().entry_set_for_block(block));
self.pos = CursorPosition::block_entry(block);
self.state_needs_reset = false;
}
@ -186,7 +269,7 @@ where
)
};
let analysis = &self.results.borrow().analysis;
let analysis = &mut self.results.borrow_mut().analysis;
let target_effect_index = effect.at_index(target.statement_index);
A::Direction::apply_effects_in_range(
@ -205,8 +288,8 @@ where
///
/// This can be used, e.g., to apply the call return effect directly to the cursor without
/// creating an extra copy of the dataflow state.
pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) {
f(&self.results.borrow().analysis, &mut self.state);
pub fn apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain)) {
f(&mut self.results.borrow_mut().analysis, &mut self.state);
self.state_needs_reset = true;
}
}
@ -215,7 +298,6 @@ impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
where
A: crate::GenKillAnalysis<'tcx>,
A::Domain: BitSetExt<A::Idx>,
R: Borrow<Results<'tcx, A>>,
{
pub fn contains(&self, elem: A::Idx) -> bool {
self.get().contains(elem)

View File

@ -16,7 +16,7 @@ pub trait Direction {
///
/// `effects.start()` must precede or equal `effects.end()` in this direction.
fn apply_effects_in_range<'tcx, A>(
analysis: &A,
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@ -25,7 +25,7 @@ pub trait Direction {
A: Analysis<'tcx>;
fn apply_effects_in_block<'tcx, A>(
analysis: &A,
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@ -33,7 +33,7 @@ pub trait Direction {
A: Analysis<'tcx>;
fn gen_kill_effects_in_block<'tcx, A>(
analysis: &A,
analysis: &mut A,
trans: &mut GenKillSet<A::Idx>,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@ -44,13 +44,13 @@ pub trait Direction {
state: &mut F,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
results: &R,
vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
results: &mut R,
vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>,
) where
R: ResultsVisitable<'tcx, FlowState = F>;
fn join_state_into_successors_of<'tcx, A>(
analysis: &A,
analysis: &mut A,
tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
@ -67,7 +67,7 @@ impl Direction for Backward {
const IS_FORWARD: bool = false;
fn apply_effects_in_block<'tcx, A>(
analysis: &A,
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@ -87,7 +87,7 @@ impl Direction for Backward {
}
fn gen_kill_effects_in_block<'tcx, A>(
analysis: &A,
analysis: &mut A,
trans: &mut GenKillSet<A::Idx>,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@ -107,7 +107,7 @@ impl Direction for Backward {
}
fn apply_effects_in_range<'tcx, A>(
analysis: &A,
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@ -187,36 +187,36 @@ impl Direction for Backward {
state: &mut F,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
results: &R,
vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
results: &mut R,
vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>,
) where
R: ResultsVisitable<'tcx, FlowState = F>,
{
results.reset_to_block_entry(state, block);
vis.visit_block_end(&state, block_data, block);
vis.visit_block_end(results, &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);
vis.visit_terminator_before_primary_effect(results, state, term, loc);
results.reconstruct_terminator_effect(state, term, loc);
vis.visit_terminator_after_primary_effect(state, term, loc);
vis.visit_terminator_after_primary_effect(results, 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);
vis.visit_statement_before_primary_effect(results, state, stmt, loc);
results.reconstruct_statement_effect(state, stmt, loc);
vis.visit_statement_after_primary_effect(state, stmt, loc);
vis.visit_statement_after_primary_effect(results, state, stmt, loc);
}
vis.visit_block_start(state, block_data, block);
vis.visit_block_start(results, state, block_data, block);
}
fn join_state_into_successors_of<'tcx, A>(
analysis: &A,
analysis: &mut A,
_tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
@ -319,7 +319,7 @@ impl Direction for Forward {
const IS_FORWARD: bool = true;
fn apply_effects_in_block<'tcx, A>(
analysis: &A,
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@ -339,7 +339,7 @@ impl Direction for Forward {
}
fn gen_kill_effects_in_block<'tcx, A>(
analysis: &A,
analysis: &mut A,
trans: &mut GenKillSet<A::Idx>,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@ -359,7 +359,7 @@ impl Direction for Forward {
}
fn apply_effects_in_range<'tcx, A>(
analysis: &A,
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@ -435,35 +435,35 @@ impl Direction for Forward {
state: &mut F,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
results: &R,
vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
results: &mut R,
vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>,
) where
R: ResultsVisitable<'tcx, FlowState = F>,
{
results.reset_to_block_entry(state, block);
vis.visit_block_start(state, block_data, block);
vis.visit_block_start(results, 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);
vis.visit_statement_before_primary_effect(results, state, stmt, loc);
results.reconstruct_statement_effect(state, stmt, loc);
vis.visit_statement_after_primary_effect(state, stmt, loc);
vis.visit_statement_after_primary_effect(results, 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);
vis.visit_terminator_before_primary_effect(results, state, term, loc);
results.reconstruct_terminator_effect(state, term, loc);
vis.visit_terminator_after_primary_effect(state, term, loc);
vis.visit_terminator_after_primary_effect(results, state, term, loc);
vis.visit_block_end(state, block_data, block);
vis.visit_block_end(results, state, block_data, block);
}
fn join_state_into_successors_of<'tcx, A>(
analysis: &A,
analysis: &mut A,
_tcx: TyCtxt<'tcx>,
_body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,

View File

@ -5,7 +5,9 @@ use crate::errors::{
};
use crate::framework::BitSetExt;
use std::borrow::Borrow;
use std::ffi::OsString;
use std::marker::PhantomData;
use std::path::PathBuf;
use rustc_ast as ast;
@ -22,54 +24,108 @@ use rustc_span::symbol::{sym, Symbol};
use super::fmt::DebugWithContext;
use super::graphviz;
use super::{
visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice,
ResultsCursor, ResultsVisitor,
visit_results, Analysis, AnalysisDomain, CloneAnalysis, Direction, GenKill, GenKillAnalysis,
GenKillSet, JoinSemiLattice, ResultsClonedCursor, ResultsCursor, ResultsRefCursor,
ResultsVisitor,
};
pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as AnalysisDomain<'tcx>>::Domain>;
/// A dataflow analysis that has converged to fixpoint.
pub struct Results<'tcx, A>
pub struct Results<'tcx, A, E = EntrySets<'tcx, A>>
where
A: Analysis<'tcx>,
{
pub analysis: A,
pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>,
pub(super) entry_sets: E,
pub(super) _marker: PhantomData<&'tcx ()>,
}
impl<'tcx, A> Results<'tcx, A>
/// `Results` type with a cloned `Analysis` and borrowed entry sets.
pub type ResultsCloned<'res, 'tcx, A> = Results<'tcx, A, &'res EntrySets<'tcx, A>>;
impl<'tcx, A, E> Results<'tcx, A, E>
where
A: Analysis<'tcx>,
E: Borrow<EntrySets<'tcx, A>>,
{
/// Creates a `ResultsCursor` that can inspect these `Results`.
pub fn into_results_cursor<'mir>(
self,
body: &'mir mir::Body<'tcx>,
) -> ResultsCursor<'mir, 'tcx, A> {
) -> ResultsCursor<'mir, 'tcx, A, Self> {
ResultsCursor::new(body, self)
}
/// Gets the dataflow state for the given block.
pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
&self.entry_sets[block]
&self.entry_sets.borrow()[block]
}
pub fn visit_with<'mir>(
&self,
&mut self,
body: &'mir mir::Body<'tcx>,
blocks: impl IntoIterator<Item = BasicBlock>,
vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
vis: &mut impl ResultsVisitor<'mir, 'tcx, Self, FlowState = A::Domain>,
) {
visit_results(body, blocks, self, vis)
}
pub fn visit_reachable_with<'mir>(
&self,
&mut self,
body: &'mir mir::Body<'tcx>,
vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
vis: &mut impl ResultsVisitor<'mir, 'tcx, Self, FlowState = A::Domain>,
) {
let blocks = mir::traversal::reachable(body);
visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
}
}
impl<'tcx, A> Results<'tcx, A>
where
A: Analysis<'tcx>,
{
/// Creates a `ResultsCursor` that can inspect these `Results`.
pub fn as_results_cursor<'a, 'mir>(
&'a mut self,
body: &'mir mir::Body<'tcx>,
) -> ResultsRefCursor<'a, 'mir, 'tcx, A> {
ResultsCursor::new(body, self)
}
}
impl<'tcx, A> Results<'tcx, A>
where
A: Analysis<'tcx> + CloneAnalysis,
{
/// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets.
pub fn clone_analysis(&self) -> ResultsCloned<'_, 'tcx, A> {
Results {
analysis: self.analysis.clone_analysis(),
entry_sets: &self.entry_sets,
_marker: PhantomData,
}
}
/// Creates a `ResultsCursor` that can inspect these `Results`.
pub fn cloned_results_cursor<'mir>(
&self,
body: &'mir mir::Body<'tcx>,
) -> ResultsClonedCursor<'_, 'mir, 'tcx, A> {
self.clone_analysis().into_results_cursor(body)
}
}
impl<'res, 'tcx, A> Results<'tcx, A, &'res EntrySets<'tcx, A>>
where
A: Analysis<'tcx> + CloneAnalysis,
{
/// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets.
pub fn reclone_analysis(&self) -> Self {
Results {
analysis: self.analysis.clone_analysis(),
entry_sets: self.entry_sets,
_marker: PhantomData,
}
}
}
/// A solver for dataflow problems.
pub struct Engine<'a, 'tcx, A>
@ -98,7 +154,7 @@ where
T: Idx,
{
/// Creates a new `Engine` to solve a gen-kill dataflow problem.
pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self {
pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, mut analysis: A) -> Self {
// If there are no back-edges in the control-flow graph, we only ever need to apply the
// transfer function for each block exactly once (assuming that we process blocks in RPO).
//
@ -114,7 +170,7 @@ where
for (block, block_data) in body.basic_blocks.iter_enumerated() {
let trans = &mut trans_for_block[block];
A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data);
A::Direction::gen_kill_effects_in_block(&mut analysis, trans, block, block_data);
}
let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
@ -171,7 +227,13 @@ where
A::Domain: DebugWithContext<A>,
{
let Engine {
analysis, body, mut entry_sets, tcx, apply_trans_for_block, pass_name, ..
mut analysis,
body,
mut entry_sets,
tcx,
apply_trans_for_block,
pass_name,
..
} = self;
let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_none(body.basic_blocks.len());
@ -203,11 +265,13 @@ where
// 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(&analysis, &mut state, bb, bb_data),
None => {
A::Direction::apply_effects_in_block(&mut analysis, &mut state, bb, bb_data)
}
}
A::Direction::join_state_into_successors_of(
&analysis,
&mut analysis,
tcx,
body,
&mut state,
@ -221,9 +285,9 @@ where
);
}
let results = Results { analysis, entry_sets };
let mut results = Results { analysis, entry_sets, _marker: PhantomData };
let res = write_graphviz_results(tcx, &body, &results, pass_name);
let res = write_graphviz_results(tcx, body, &mut results, pass_name);
if let Err(e) = res {
error!("Failed to write graphviz dataflow results: {}", e);
}
@ -239,7 +303,7 @@ where
fn write_graphviz_results<'tcx, A>(
tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
results: &Results<'tcx, A>,
results: &mut Results<'tcx, A>,
pass_name: Option<&'static str>,
) -> std::io::Result<()>
where

View File

@ -1,6 +1,7 @@
//! A helpful diagram for debugging dataflow problems.
use std::borrow::Cow;
use std::cell::RefCell;
use std::sync::OnceLock;
use std::{io, ops, str};
@ -28,23 +29,27 @@ impl OutputStyle {
}
}
pub struct Formatter<'a, 'tcx, A>
pub struct Formatter<'res, 'mir, 'tcx, A>
where
A: Analysis<'tcx>,
{
body: &'a Body<'tcx>,
results: &'a Results<'tcx, A>,
body: &'mir Body<'tcx>,
results: RefCell<&'res mut Results<'tcx, A>>,
style: OutputStyle,
reachable: BitSet<BasicBlock>,
}
impl<'a, 'tcx, A> Formatter<'a, 'tcx, A>
impl<'res, 'mir, 'tcx, A> Formatter<'res, 'mir, 'tcx, A>
where
A: Analysis<'tcx>,
{
pub fn new(body: &'a Body<'tcx>, results: &'a Results<'tcx, A>, style: OutputStyle) -> Self {
pub fn new(
body: &'mir Body<'tcx>,
results: &'res mut Results<'tcx, A>,
style: OutputStyle,
) -> Self {
let reachable = mir::traversal::reachable_as_bitset(body);
Formatter { body, results, style, reachable }
Formatter { body, results: results.into(), style, reachable }
}
}
@ -64,7 +69,7 @@ fn dataflow_successors(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> {
.collect()
}
impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, 'tcx, A>
impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, '_, 'tcx, A>
where
A: Analysis<'tcx>,
A::Domain: DebugWithContext<A>,
@ -83,13 +88,14 @@ where
fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
let mut label = Vec::new();
let mut results = self.results.borrow_mut();
let mut fmt = BlockFormatter {
results: ResultsRefCursor::new(self.body, self.results),
results: results.as_results_cursor(self.body),
style: self.style,
bg: Background::Light,
};
fmt.write_node_label(&mut label, self.body, *block).unwrap();
fmt.write_node_label(&mut label, *block).unwrap();
dot::LabelText::html(String::from_utf8(label).unwrap())
}
@ -103,7 +109,7 @@ where
}
}
impl<'a, 'tcx, A> dot::GraphWalk<'a> for Formatter<'a, 'tcx, A>
impl<'mir, 'tcx, A> dot::GraphWalk<'mir> for Formatter<'_, 'mir, 'tcx, A>
where
A: Analysis<'tcx>,
{
@ -137,16 +143,16 @@ where
}
}
struct BlockFormatter<'a, 'tcx, A>
struct BlockFormatter<'res, 'mir, 'tcx, A>
where
A: Analysis<'tcx>,
{
results: ResultsRefCursor<'a, 'a, 'tcx, A>,
results: ResultsRefCursor<'res, 'mir, 'tcx, A>,
bg: Background,
style: OutputStyle,
}
impl<'a, 'tcx, A> BlockFormatter<'a, 'tcx, A>
impl<'res, 'mir, 'tcx, A> BlockFormatter<'res, 'mir, 'tcx, A>
where
A: Analysis<'tcx>,
A::Domain: DebugWithContext<A>,
@ -159,12 +165,7 @@ where
bg
}
fn write_node_label(
&mut self,
w: &mut impl io::Write,
body: &'a Body<'tcx>,
block: BasicBlock,
) -> io::Result<()> {
fn write_node_label(&mut self, w: &mut impl io::Write, block: BasicBlock) -> io::Result<()> {
// Sample output:
// +-+-----------------------------------------------+
// A | bb4 |
@ -215,11 +216,11 @@ where
self.write_row_with_full_state(w, "", "(on start)")?;
// D + E: Statement and terminator transfer functions
self.write_statements_and_terminator(w, body, block)?;
self.write_statements_and_terminator(w, block)?;
// F: State at end of block
let terminator = body[block].terminator();
let terminator = self.results.body()[block].terminator();
// Write the full dataflow state immediately after the terminator if it differs from the
// state at block entry.
@ -389,10 +390,14 @@ where
fn write_statements_and_terminator(
&mut self,
w: &mut impl io::Write,
body: &'a Body<'tcx>,
block: BasicBlock,
) -> io::Result<()> {
let diffs = StateDiffCollector::run(body, block, self.results.results(), self.style);
let diffs = StateDiffCollector::run(
self.results.body(),
block,
self.results.mut_results(),
self.style,
);
let mut diffs_before = diffs.before.map(|v| v.into_iter());
let mut diffs_after = diffs.after.into_iter();
@ -401,7 +406,7 @@ where
if A::Direction::IS_FORWARD { it.next().unwrap() } else { it.next_back().unwrap() }
};
for (i, statement) in body[block].statements.iter().enumerate() {
for (i, statement) in self.results.body()[block].statements.iter().enumerate() {
let statement_str = format!("{statement:?}");
let index_str = format!("{i}");
@ -423,7 +428,7 @@ where
assert!(diffs_after.is_empty());
assert!(diffs_before.as_ref().map_or(true, ExactSizeIterator::is_empty));
let terminator = body[block].terminator();
let terminator = self.results.body()[block].terminator();
let mut terminator_str = String::new();
terminator.kind.fmt_head(&mut terminator_str).unwrap();
@ -492,29 +497,24 @@ where
}
}
struct StateDiffCollector<'a, 'tcx, A>
where
A: Analysis<'tcx>,
{
analysis: &'a A,
prev_state: A::Domain,
struct StateDiffCollector<D> {
prev_state: D,
before: Option<Vec<String>>,
after: Vec<String>,
}
impl<'a, 'tcx, A> StateDiffCollector<'a, 'tcx, A>
where
A: Analysis<'tcx>,
A::Domain: DebugWithContext<A>,
{
fn run(
body: &'a mir::Body<'tcx>,
impl<D> StateDiffCollector<D> {
fn run<'tcx, A>(
body: &mir::Body<'tcx>,
block: BasicBlock,
results: &'a Results<'tcx, A>,
results: &mut Results<'tcx, A>,
style: OutputStyle,
) -> Self {
) -> Self
where
A: Analysis<'tcx, Domain = D>,
D: DebugWithContext<A>,
{
let mut collector = StateDiffCollector {
analysis: &results.analysis,
prev_state: results.analysis.bottom_value(body),
after: vec![],
before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]),
@ -525,7 +525,7 @@ where
}
}
impl<'a, 'tcx, A> ResultsVisitor<'a, 'tcx> for StateDiffCollector<'a, 'tcx, A>
impl<'tcx, A> ResultsVisitor<'_, 'tcx, Results<'tcx, A>> for StateDiffCollector<A::Domain>
where
A: Analysis<'tcx>,
A::Domain: DebugWithContext<A>,
@ -534,6 +534,7 @@ where
fn visit_block_start(
&mut self,
_results: &Results<'tcx, A>,
state: &Self::FlowState,
_block_data: &mir::BasicBlockData<'tcx>,
_block: BasicBlock,
@ -545,6 +546,7 @@ where
fn visit_block_end(
&mut self,
_results: &Results<'tcx, A>,
state: &Self::FlowState,
_block_data: &mir::BasicBlockData<'tcx>,
_block: BasicBlock,
@ -556,45 +558,49 @@ where
fn visit_statement_before_primary_effect(
&mut self,
results: &Results<'tcx, A>,
state: &Self::FlowState,
_statement: &mir::Statement<'tcx>,
_location: Location,
) {
if let Some(before) = self.before.as_mut() {
before.push(diff_pretty(state, &self.prev_state, self.analysis));
before.push(diff_pretty(state, &self.prev_state, &results.analysis));
self.prev_state.clone_from(state)
}
}
fn visit_statement_after_primary_effect(
&mut self,
results: &Results<'tcx, A>,
state: &Self::FlowState,
_statement: &mir::Statement<'tcx>,
_location: Location,
) {
self.after.push(diff_pretty(state, &self.prev_state, self.analysis));
self.after.push(diff_pretty(state, &self.prev_state, &results.analysis));
self.prev_state.clone_from(state)
}
fn visit_terminator_before_primary_effect(
&mut self,
results: &Results<'tcx, A>,
state: &Self::FlowState,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
) {
if let Some(before) = self.before.as_mut() {
before.push(diff_pretty(state, &self.prev_state, self.analysis));
before.push(diff_pretty(state, &self.prev_state, &results.analysis));
self.prev_state.clone_from(state)
}
}
fn visit_terminator_after_primary_effect(
&mut self,
results: &Results<'tcx, A>,
state: &Self::FlowState,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
) {
self.after.push(diff_pretty(state, &self.prev_state, self.analysis));
self.after.push(diff_pretty(state, &self.prev_state, &results.analysis));
self.prev_state.clone_from(state)
}
}

View File

@ -45,9 +45,9 @@ pub mod graphviz;
pub mod lattice;
mod visitor;
pub use self::cursor::{ResultsCursor, ResultsRefCursor};
pub use self::cursor::{ResultsClonedCursor, ResultsCursor, ResultsRefCursor};
pub use self::direction::{Backward, Direction, Forward};
pub use self::engine::{Engine, Results};
pub use self::engine::{Engine, EntrySets, Results, ResultsCloned};
pub use self::lattice::{JoinSemiLattice, MeetSemiLattice};
pub use self::visitor::{visit_results, ResultsVisitable, ResultsVisitor};
@ -146,7 +146,7 @@ pub trait AnalysisDomain<'tcx> {
pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
/// Updates the current dataflow state with the effect of evaluating a statement.
fn apply_statement_effect(
&self,
&mut self,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@ -159,7 +159,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
/// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule,
/// analyses should not implement this without also implementing `apply_statement_effect`.
fn apply_before_statement_effect(
&self,
&mut self,
_state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
_location: Location,
@ -173,7 +173,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
/// `InitializedPlaces` analyses, the return place for a function call is not marked as
/// initialized here.
fn apply_terminator_effect(
&self,
&mut self,
state: &mut Self::Domain,
terminator: &mir::Terminator<'tcx>,
location: Location,
@ -186,7 +186,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
/// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule,
/// analyses should not implement this without also implementing `apply_terminator_effect`.
fn apply_before_terminator_effect(
&self,
&mut self,
_state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
@ -201,7 +201,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
/// This is separate from `apply_terminator_effect` to properly track state across unwind
/// edges.
fn apply_call_return_effect(
&self,
&mut self,
state: &mut Self::Domain,
block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@ -214,7 +214,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
///
/// By default, no effects happen.
fn apply_yield_resume_effect(
&self,
&mut self,
_state: &mut Self::Domain,
_resume_block: BasicBlock,
_resume_place: mir::Place<'tcx>,
@ -235,7 +235,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
/// engine doesn't need to clone the exit state for a block unless
/// `SwitchIntEdgeEffects::apply` is actually called.
fn apply_switch_int_edge_effects(
&self,
&mut self,
_block: BasicBlock,
_discr: &mir::Operand<'tcx>,
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
@ -269,6 +269,21 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
}
}
/// Defines an `Analysis` which can be cloned for use in multiple `ResultsCursor`s or
/// `ResultsVisitor`s. Note this need not be a full clone, only enough of one to be used with a new
/// `ResultsCursor` or `ResultsVisitor`
pub trait CloneAnalysis {
fn clone_analysis(&self) -> Self;
}
impl<'tcx, A> CloneAnalysis for A
where
A: Analysis<'tcx> + Copy,
{
fn clone_analysis(&self) -> Self {
*self
}
}
/// A gen/kill dataflow problem.
///
/// Each method in this trait has a corresponding one in `Analysis`. However, these methods only
@ -282,7 +297,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
/// See `Analysis::apply_statement_effect`.
fn statement_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
statement: &mir::Statement<'tcx>,
location: Location,
@ -290,7 +305,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
/// See `Analysis::apply_before_statement_effect`.
fn before_statement_effect(
&self,
&mut self,
_trans: &mut impl GenKill<Self::Idx>,
_statement: &mir::Statement<'tcx>,
_location: Location,
@ -299,7 +314,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
/// See `Analysis::apply_terminator_effect`.
fn terminator_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
location: Location,
@ -307,7 +322,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
/// See `Analysis::apply_before_terminator_effect`.
fn before_terminator_effect(
&self,
&mut self,
_trans: &mut impl GenKill<Self::Idx>,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
@ -318,7 +333,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
/// See `Analysis::apply_call_return_effect`.
fn call_return_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@ -326,7 +341,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
/// See `Analysis::apply_yield_resume_effect`.
fn yield_resume_effect(
&self,
&mut self,
_trans: &mut impl GenKill<Self::Idx>,
_resume_block: BasicBlock,
_resume_place: mir::Place<'tcx>,
@ -335,7 +350,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
/// See `Analysis::apply_switch_int_edge_effects`.
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
&self,
&mut self,
_block: BasicBlock,
_discr: &mir::Operand<'tcx>,
_edge_effects: &mut impl SwitchIntEdgeEffects<G>,
@ -349,7 +364,7 @@ where
A::Domain: GenKill<A::Idx> + BitSetExt<A::Idx>,
{
fn apply_statement_effect(
&self,
&mut self,
state: &mut A::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@ -358,7 +373,7 @@ where
}
fn apply_before_statement_effect(
&self,
&mut self,
state: &mut A::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@ -367,7 +382,7 @@ where
}
fn apply_terminator_effect(
&self,
&mut self,
state: &mut A::Domain,
terminator: &mir::Terminator<'tcx>,
location: Location,
@ -376,7 +391,7 @@ where
}
fn apply_before_terminator_effect(
&self,
&mut self,
state: &mut A::Domain,
terminator: &mir::Terminator<'tcx>,
location: Location,
@ -387,7 +402,7 @@ where
/* Edge-specific effects */
fn apply_call_return_effect(
&self,
&mut self,
state: &mut A::Domain,
block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@ -396,7 +411,7 @@ where
}
fn apply_yield_resume_effect(
&self,
&mut self,
state: &mut A::Domain,
resume_block: BasicBlock,
resume_place: mir::Place<'tcx>,
@ -405,7 +420,7 @@ where
}
fn apply_switch_int_edge_effects(
&self,
&mut self,
block: BasicBlock,
discr: &mir::Operand<'tcx>,
edge_effects: &mut impl SwitchIntEdgeEffects<A::Domain>,

View File

@ -179,7 +179,7 @@ impl<'tcx, D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> {
impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
fn apply_statement_effect(
&self,
&mut self,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
location: Location,
@ -189,7 +189,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
}
fn apply_before_statement_effect(
&self,
&mut self,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
location: Location,
@ -199,7 +199,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
}
fn apply_terminator_effect(
&self,
&mut self,
state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@ -209,7 +209,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
}
fn apply_before_terminator_effect(
&self,
&mut self,
state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@ -219,7 +219,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
}
fn apply_call_return_effect(
&self,
&mut self,
_state: &mut Self::Domain,
_block: BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
@ -266,7 +266,8 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) {
let body = analysis.body;
let mut cursor =
Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body);
Results { entry_sets: analysis.mock_entry_sets(), analysis, _marker: PhantomData }
.into_results_cursor(body);
cursor.allow_unreachable();

View File

@ -1,16 +1,18 @@
use std::borrow::Borrow;
use rustc_middle::mir::{self, BasicBlock, Location};
use super::{Analysis, Direction, Results};
use super::{Analysis, Direction, EntrySets, Results};
/// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the
/// dataflow state at that location.
pub fn visit_results<'mir, 'tcx, F, V>(
pub fn visit_results<'mir, 'tcx, F, R>(
body: &'mir mir::Body<'tcx>,
blocks: impl IntoIterator<Item = BasicBlock>,
results: &V,
vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
results: &mut R,
vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>,
) where
V: ResultsVisitable<'tcx, FlowState = F>,
R: ResultsVisitable<'tcx, FlowState = F>,
{
let mut state = results.new_flow_state(body);
@ -22,15 +24,18 @@ pub fn visit_results<'mir, 'tcx, F, V>(
assert!(reachable_blocks.contains(block));
let block_data = &body[block];
V::Direction::visit_results_in_block(&mut state, block, block_data, results, vis);
R::Direction::visit_results_in_block(&mut state, block, block_data, results, vis);
}
}
pub trait ResultsVisitor<'mir, 'tcx> {
/// A visitor over the results of an `Analysis`. The type parameter `R` is the results type being
/// visited.
pub trait ResultsVisitor<'mir, 'tcx, R> {
type FlowState;
fn visit_block_start(
&mut self,
_results: &R,
_state: &Self::FlowState,
_block_data: &'mir mir::BasicBlockData<'tcx>,
_block: BasicBlock,
@ -41,6 +46,7 @@ pub trait ResultsVisitor<'mir, 'tcx> {
/// its `statement_effect`.
fn visit_statement_before_primary_effect(
&mut self,
_results: &R,
_state: &Self::FlowState,
_statement: &'mir mir::Statement<'tcx>,
_location: Location,
@ -51,6 +57,7 @@ pub trait ResultsVisitor<'mir, 'tcx> {
/// statement applied to `state`.
fn visit_statement_after_primary_effect(
&mut self,
_results: &R,
_state: &Self::FlowState,
_statement: &'mir mir::Statement<'tcx>,
_location: Location,
@ -61,6 +68,7 @@ pub trait ResultsVisitor<'mir, 'tcx> {
/// its `terminator_effect`.
fn visit_terminator_before_primary_effect(
&mut self,
_results: &R,
_state: &Self::FlowState,
_terminator: &'mir mir::Terminator<'tcx>,
_location: Location,
@ -73,6 +81,7 @@ pub trait ResultsVisitor<'mir, 'tcx> {
/// The `call_return_effect` (if one exists) will *not* be applied to `state`.
fn visit_terminator_after_primary_effect(
&mut self,
_results: &R,
_state: &Self::FlowState,
_terminator: &'mir mir::Terminator<'tcx>,
_location: Location,
@ -81,6 +90,7 @@ pub trait ResultsVisitor<'mir, 'tcx> {
fn visit_block_end(
&mut self,
_results: &R,
_state: &Self::FlowState,
_block_data: &'mir mir::BasicBlockData<'tcx>,
_block: BasicBlock,
@ -105,37 +115,38 @@ pub trait ResultsVisitable<'tcx> {
fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock);
fn reconstruct_before_statement_effect(
&self,
&mut self,
state: &mut Self::FlowState,
statement: &mir::Statement<'tcx>,
location: Location,
);
fn reconstruct_statement_effect(
&self,
&mut self,
state: &mut Self::FlowState,
statement: &mir::Statement<'tcx>,
location: Location,
);
fn reconstruct_before_terminator_effect(
&self,
&mut self,
state: &mut Self::FlowState,
terminator: &mir::Terminator<'tcx>,
location: Location,
);
fn reconstruct_terminator_effect(
&self,
&mut self,
state: &mut Self::FlowState,
terminator: &mir::Terminator<'tcx>,
location: Location,
);
}
impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A>
impl<'tcx, A, E> ResultsVisitable<'tcx> for Results<'tcx, A, E>
where
A: Analysis<'tcx>,
E: Borrow<EntrySets<'tcx, A>>,
{
type FlowState = A::Domain;
@ -146,11 +157,11 @@ where
}
fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) {
state.clone_from(&self.entry_set_for_block(block));
state.clone_from(self.entry_set_for_block(block));
}
fn reconstruct_before_statement_effect(
&self,
&mut self,
state: &mut Self::FlowState,
stmt: &mir::Statement<'tcx>,
loc: Location,
@ -159,7 +170,7 @@ where
}
fn reconstruct_statement_effect(
&self,
&mut self,
state: &mut Self::FlowState,
stmt: &mir::Statement<'tcx>,
loc: Location,
@ -168,7 +179,7 @@ where
}
fn reconstruct_before_terminator_effect(
&self,
&mut self,
state: &mut Self::FlowState,
term: &mir::Terminator<'tcx>,
loc: Location,
@ -177,7 +188,7 @@ where
}
fn reconstruct_terminator_effect(
&self,
&mut self,
state: &mut Self::FlowState,
term: &mir::Terminator<'tcx>,
loc: Location,

View File

@ -10,6 +10,7 @@ use rustc_middle::mir::*;
/// At present, this is used as a very limited form of alias analysis. For example,
/// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for
/// immovable generators.
#[derive(Clone, Copy)]
pub struct MaybeBorrowedLocals;
impl MaybeBorrowedLocals {
@ -36,7 +37,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
type Idx = Local;
fn statement_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
statement: &mir::Statement<'tcx>,
location: Location,
@ -45,7 +46,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
}
fn terminator_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
location: Location,
@ -54,7 +55,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
}
fn call_return_effect(
&self,
&mut self,
_trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,

View File

@ -21,6 +21,7 @@ use crate::{Analysis, AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKi
/// [`MaybeBorrowedLocals`]: super::MaybeBorrowedLocals
/// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs
/// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis
#[derive(Clone, Copy)]
pub struct MaybeLiveLocals;
impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals {
@ -43,7 +44,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
type Idx = Local;
fn statement_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
statement: &mir::Statement<'tcx>,
location: Location,
@ -52,7 +53,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
}
fn terminator_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
location: Location,
@ -61,7 +62,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
}
fn call_return_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@ -74,7 +75,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
}
fn yield_resume_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_resume_block: mir::BasicBlock,
resume_place: mir::Place<'tcx>,
@ -215,6 +216,7 @@ impl DefUse {
/// This is basically written for dead store elimination and nothing else.
///
/// All of the caveats of `MaybeLiveLocals` apply.
#[derive(Clone, Copy)]
pub struct MaybeTransitiveLiveLocals<'a> {
always_live: &'a BitSet<Local>,
}
@ -247,7 +249,7 @@ impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a> {
impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
fn apply_statement_effect(
&self,
&mut self,
trans: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@ -282,7 +284,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
}
fn apply_terminator_effect(
&self,
&mut self,
trans: &mut Self::Domain,
terminator: &mir::Terminator<'tcx>,
location: Location,
@ -291,7 +293,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
}
fn apply_call_return_effect(
&self,
&mut self,
trans: &mut Self::Domain,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@ -304,7 +306,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
}
fn apply_yield_resume_effect(
&self,
&mut self,
trans: &mut Self::Domain,
_resume_block: mir::BasicBlock,
resume_place: mir::Place<'tcx>,

View File

@ -306,7 +306,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
type Idx = MovePathIndex;
fn statement_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
statement: &mir::Statement<'tcx>,
location: Location,
@ -329,7 +329,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
}
fn terminator_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
location: Location,
@ -351,7 +351,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
}
fn call_return_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@ -372,7 +372,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
}
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
&self,
&mut self,
block: mir::BasicBlock,
discr: &mir::Operand<'tcx>,
edge_effects: &mut impl SwitchIntEdgeEffects<G>,
@ -442,7 +442,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
type Idx = MovePathIndex;
fn statement_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_statement: &mir::Statement<'tcx>,
location: Location,
@ -456,7 +456,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
}
fn terminator_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@ -467,7 +467,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
}
fn call_return_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@ -488,7 +488,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
}
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
&self,
&mut self,
block: mir::BasicBlock,
discr: &mir::Operand<'tcx>,
edge_effects: &mut impl SwitchIntEdgeEffects<G>,
@ -562,7 +562,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
type Idx = MovePathIndex;
fn statement_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_statement: &mir::Statement<'tcx>,
location: Location,
@ -573,7 +573,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
}
fn terminator_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@ -584,7 +584,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
}
fn call_return_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@ -627,7 +627,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
#[instrument(skip(self, trans), level = "debug")]
fn statement_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
stmt: &mir::Statement<'tcx>,
location: Location,
@ -651,7 +651,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
#[instrument(skip(self, trans, _terminator), level = "debug")]
fn terminator_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@ -672,7 +672,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
}
fn call_return_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
block: mir::BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,

View File

@ -1,10 +1,9 @@
pub use super::*;
use crate::{CallReturnPlaces, GenKill, Results, ResultsRefCursor};
use crate::{CallReturnPlaces, GenKill, ResultsClonedCursor};
use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use std::borrow::Cow;
use std::cell::RefCell;
#[derive(Clone)]
pub struct MaybeStorageLive<'a> {
@ -17,6 +16,12 @@ impl<'a> MaybeStorageLive<'a> {
}
}
impl crate::CloneAnalysis for MaybeStorageLive<'_> {
fn clone_analysis(&self) -> Self {
self.clone()
}
}
impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> {
type Domain = BitSet<Local>;
@ -43,7 +48,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
type Idx = Local;
fn statement_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
stmt: &mir::Statement<'tcx>,
_: Location,
@ -56,7 +61,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
}
fn terminator_effect(
&self,
&mut self,
_trans: &mut impl GenKill<Self::Idx>,
_: &mir::Terminator<'tcx>,
_: Location,
@ -65,7 +70,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
}
fn call_return_effect(
&self,
&mut self,
_trans: &mut impl GenKill<Self::Idx>,
_block: BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
@ -110,7 +115,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
type Idx = Local;
fn statement_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
stmt: &mir::Statement<'tcx>,
_: Location,
@ -123,7 +128,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
}
fn terminator_effect(
&self,
&mut self,
_trans: &mut impl GenKill<Self::Idx>,
_: &mir::Terminator<'tcx>,
_: Location,
@ -132,7 +137,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
}
fn call_return_effect(
&self,
&mut self,
_trans: &mut impl GenKill<Self::Idx>,
_block: BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
@ -141,28 +146,28 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
}
}
type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;
type BorrowedLocalsResults<'res, 'mir, 'tcx> =
ResultsClonedCursor<'res, 'mir, 'tcx, MaybeBorrowedLocals>;
/// Dataflow analysis that determines whether each local requires storage at a
/// given location; i.e. whether its storage can go away without being observed.
pub struct MaybeRequiresStorage<'mir, 'tcx> {
body: &'mir Body<'tcx>,
borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
pub struct MaybeRequiresStorage<'res, 'mir, 'tcx> {
borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>,
}
impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
pub fn new(
body: &'mir Body<'tcx>,
borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>,
) -> Self {
MaybeRequiresStorage {
body,
borrowed_locals: RefCell::new(ResultsRefCursor::new(&body, borrowed_locals)),
}
impl<'res, 'mir, 'tcx> MaybeRequiresStorage<'res, 'mir, 'tcx> {
pub fn new(borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>) -> Self {
MaybeRequiresStorage { borrowed_locals }
}
}
impl<'mir, 'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
impl crate::CloneAnalysis for MaybeRequiresStorage<'_, '_, '_> {
fn clone_analysis(&self) -> Self {
Self { borrowed_locals: self.borrowed_locals.new_cursor() }
}
}
impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
type Domain = BitSet<Local>;
const NAME: &'static str = "requires_storage";
@ -181,17 +186,17 @@ impl<'mir, 'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx
}
}
impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
type Idx = Local;
fn before_statement_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
stmt: &mir::Statement<'tcx>,
loc: Location,
) {
// If a place is borrowed in a statement, it needs storage for that statement.
self.borrowed_locals.borrow().analysis().statement_effect(trans, stmt, loc);
self.borrowed_locals.mut_analysis().statement_effect(trans, stmt, loc);
match &stmt.kind {
StatementKind::StorageDead(l) => trans.kill(*l),
@ -218,7 +223,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
}
fn statement_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_: &mir::Statement<'tcx>,
loc: Location,
@ -229,13 +234,13 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
}
fn before_terminator_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
loc: Location,
) {
// If a place is borrowed in a terminator, it needs storage for that terminator.
self.borrowed_locals.borrow().analysis().terminator_effect(trans, terminator, loc);
self.borrowed_locals.mut_analysis().terminator_effect(trans, terminator, loc);
match &terminator.kind {
TerminatorKind::Call { destination, .. } => {
@ -282,7 +287,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
}
fn terminator_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
loc: Location,
@ -321,7 +326,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
}
fn call_return_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@ -330,7 +335,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
}
fn yield_resume_effect(
&self,
&mut self,
trans: &mut impl GenKill<Self::Idx>,
_resume_block: BasicBlock,
resume_place: mir::Place<'tcx>,
@ -339,28 +344,28 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
}
}
impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
impl<'tcx> MaybeRequiresStorage<'_, '_, 'tcx> {
/// Kill locals that are fully moved and have not been borrowed.
fn check_for_move(&self, trans: &mut impl GenKill<Local>, loc: Location) {
let mut visitor = MoveVisitor { trans, borrowed_locals: &self.borrowed_locals };
visitor.visit_location(&self.body, loc);
fn check_for_move(&mut self, trans: &mut impl GenKill<Local>, loc: Location) {
let body = self.borrowed_locals.body();
let mut visitor = MoveVisitor { trans, borrowed_locals: &mut self.borrowed_locals };
visitor.visit_location(body, loc);
}
}
struct MoveVisitor<'a, 'mir, 'tcx, T> {
borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
struct MoveVisitor<'a, 'res, 'mir, 'tcx, T> {
borrowed_locals: &'a mut BorrowedLocalsResults<'res, 'mir, 'tcx>,
trans: &'a mut T,
}
impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T>
impl<'tcx, T> Visitor<'tcx> for MoveVisitor<'_, '_, '_, 'tcx, T>
where
T: GenKill<Local>,
{
fn visit_local(&mut self, local: Local, context: PlaceContext, loc: Location) {
if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
let mut borrowed_locals = self.borrowed_locals.borrow_mut();
borrowed_locals.seek_before_primary_effect(loc);
if !borrowed_locals.contains(local) {
self.borrowed_locals.seek_before_primary_effect(loc);
if !self.borrowed_locals.contains(local) {
self.trans.kill(local);
}
}

View File

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

View File

@ -17,7 +17,7 @@ use crate::impls::{
use crate::move_paths::{HasMoveData, MoveData};
use crate::move_paths::{LookupResult, MovePathIndex};
use crate::MoveDataParamEnv;
use crate::{Analysis, JoinSemiLattice, Results, ResultsCursor};
use crate::{Analysis, JoinSemiLattice, ResultsCursor};
pub struct SanityCheck;
@ -42,7 +42,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
.into_engine(tcx, body)
.iterate_to_fixpoint();
sanity_check_via_rustc_peek(tcx, body, &flow_inits);
sanity_check_via_rustc_peek(tcx, flow_inits.into_results_cursor(body));
}
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() {
@ -50,7 +50,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
.into_engine(tcx, body)
.iterate_to_fixpoint();
sanity_check_via_rustc_peek(tcx, body, &flow_uninits);
sanity_check_via_rustc_peek(tcx, flow_uninits.into_results_cursor(body));
}
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_definite_init).is_some() {
@ -58,13 +58,13 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
.into_engine(tcx, body)
.iterate_to_fixpoint();
sanity_check_via_rustc_peek(tcx, body, &flow_def_inits);
sanity_check_via_rustc_peek(tcx, flow_def_inits.into_results_cursor(body));
}
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() {
let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint();
sanity_check_via_rustc_peek(tcx, body, &flow_liveness);
sanity_check_via_rustc_peek(tcx, flow_liveness.into_results_cursor(body));
}
if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() {
@ -91,17 +91,14 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
/// errors are not intended to be used for unit tests.)
pub fn sanity_check_via_rustc_peek<'tcx, A>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
results: &Results<'tcx, A>,
mut cursor: ResultsCursor<'_, 'tcx, A>,
) where
A: RustcPeekAt<'tcx>,
{
let def_id = body.source.def_id();
let def_id = cursor.body().source.def_id();
debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id);
let mut cursor = ResultsCursor::new(body, results);
let peek_calls = body.basic_blocks.iter_enumerated().filter_map(|(bb, block_data)| {
let peek_calls = cursor.body().basic_blocks.iter_enumerated().filter_map(|(bb, block_data)| {
PeekCall::from_terminator(tcx, block_data.terminator()).map(|call| (bb, block_data, call))
});
@ -132,8 +129,8 @@ pub fn sanity_check_via_rustc_peek<'tcx, A>(
) => {
let loc = Location { block: bb, statement_index };
cursor.seek_before_primary_effect(loc);
let state = cursor.get();
results.analysis.peek_at(tcx, *place, state, call);
let (state, analysis) = cursor.get_with_analysis();
analysis.peek_at(tcx, *place, state, call);
}
_ => {

View File

@ -343,7 +343,7 @@ where
T: ValueAnalysis<'tcx>,
{
fn apply_statement_effect(
&self,
&mut self,
state: &mut Self::Domain,
statement: &Statement<'tcx>,
_location: Location,
@ -354,7 +354,7 @@ where
}
fn apply_terminator_effect(
&self,
&mut self,
state: &mut Self::Domain,
terminator: &Terminator<'tcx>,
_location: Location,
@ -365,7 +365,7 @@ where
}
fn apply_call_return_effect(
&self,
&mut self,
state: &mut Self::Domain,
_block: BasicBlock,
return_places: crate::CallReturnPlaces<'_, 'tcx>,
@ -376,7 +376,7 @@ where
}
fn apply_switch_int_edge_effects(
&self,
&mut self,
_block: BasicBlock,
discr: &Operand<'tcx>,
apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,

View File

@ -10,8 +10,12 @@ use rustc_middle::mir::visit::{MutVisitor, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_mir_dataflow::value_analysis::{Map, State, TrackElem, ValueAnalysis, ValueOrPlace};
use rustc_mir_dataflow::{lattice::FlatSet, Analysis, ResultsVisitor, SwitchIntEdgeEffects};
use rustc_mir_dataflow::value_analysis::{
Map, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace,
};
use rustc_mir_dataflow::{
lattice::FlatSet, Analysis, Results, ResultsVisitor, SwitchIntEdgeEffects,
};
use rustc_span::DUMMY_SP;
use rustc_target::abi::{Align, FieldIdx, VariantIdx};
@ -52,11 +56,11 @@ impl<'tcx> MirPass<'tcx> for DataflowConstProp {
// Perform the actual dataflow analysis.
let analysis = ConstAnalysis::new(tcx, body, map);
let results = debug_span!("analyze")
let mut results = debug_span!("analyze")
.in_scope(|| analysis.wrap().into_engine(tcx, body).iterate_to_fixpoint());
// Collect results and patch the body afterwards.
let mut visitor = CollectAndPatch::new(tcx, &results.analysis.0.map);
let mut visitor = CollectAndPatch::new(tcx);
debug_span!("collect").in_scope(|| results.visit_reachable_with(body, &mut visitor));
debug_span!("patch").in_scope(|| visitor.visit_body(body));
}
@ -387,9 +391,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
}
}
struct CollectAndPatch<'tcx, 'map> {
struct CollectAndPatch<'tcx> {
tcx: TyCtxt<'tcx>,
map: &'map Map,
/// For a given MIR location, this stores the values of the operands used by that location. In
/// particular, this is before the effect, such that the operands of `_1 = _1 + _2` are
@ -400,9 +403,9 @@ struct CollectAndPatch<'tcx, 'map> {
assignments: FxHashMap<Location, ScalarTy<'tcx>>,
}
impl<'tcx, 'map> CollectAndPatch<'tcx, 'map> {
fn new(tcx: TyCtxt<'tcx>, map: &'map Map) -> Self {
Self { tcx, map, before_effect: FxHashMap::default(), assignments: FxHashMap::default() }
impl<'tcx> CollectAndPatch<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> Self {
Self { tcx, before_effect: FxHashMap::default(), assignments: FxHashMap::default() }
}
fn make_operand(&self, scalar: ScalarTy<'tcx>) -> Operand<'tcx> {
@ -414,18 +417,23 @@ impl<'tcx, 'map> CollectAndPatch<'tcx, 'map> {
}
}
impl<'mir, 'tcx, 'map> ResultsVisitor<'mir, 'tcx> for CollectAndPatch<'tcx, 'map> {
impl<'mir, 'tcx>
ResultsVisitor<'mir, 'tcx, Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>>
for CollectAndPatch<'tcx>
{
type FlowState = State<FlatSet<ScalarTy<'tcx>>>;
fn visit_statement_before_primary_effect(
&mut self,
results: &Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>,
state: &Self::FlowState,
statement: &'mir Statement<'tcx>,
location: Location,
) {
match &statement.kind {
StatementKind::Assign(box (_, rvalue)) => {
OperandCollector { state, visitor: self }.visit_rvalue(rvalue, location);
OperandCollector { state, visitor: self, map: &results.analysis.0.map }
.visit_rvalue(rvalue, location);
}
_ => (),
}
@ -433,6 +441,7 @@ impl<'mir, 'tcx, 'map> ResultsVisitor<'mir, 'tcx> for CollectAndPatch<'tcx, 'map
fn visit_statement_after_primary_effect(
&mut self,
results: &Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>,
state: &Self::FlowState,
statement: &'mir Statement<'tcx>,
location: Location,
@ -441,30 +450,34 @@ impl<'mir, 'tcx, 'map> ResultsVisitor<'mir, 'tcx> for CollectAndPatch<'tcx, 'map
StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(_)))) => {
// Don't overwrite the assignment if it already uses a constant (to keep the span).
}
StatementKind::Assign(box (place, _)) => match state.get(place.as_ref(), self.map) {
FlatSet::Top => (),
FlatSet::Elem(value) => {
self.assignments.insert(location, value);
StatementKind::Assign(box (place, _)) => {
match state.get(place.as_ref(), &results.analysis.0.map) {
FlatSet::Top => (),
FlatSet::Elem(value) => {
self.assignments.insert(location, value);
}
FlatSet::Bottom => {
// This assignment is either unreachable, or an uninitialized value is assigned.
}
}
FlatSet::Bottom => {
// This assignment is either unreachable, or an uninitialized value is assigned.
}
},
}
_ => (),
}
}
fn visit_terminator_before_primary_effect(
&mut self,
results: &Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>,
state: &Self::FlowState,
terminator: &'mir Terminator<'tcx>,
location: Location,
) {
OperandCollector { state, visitor: self }.visit_terminator(terminator, location);
OperandCollector { state, visitor: self, map: &results.analysis.0.map }
.visit_terminator(terminator, location);
}
}
impl<'tcx, 'map> MutVisitor<'tcx> for CollectAndPatch<'tcx, 'map> {
impl<'tcx> MutVisitor<'tcx> for CollectAndPatch<'tcx> {
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
self.tcx
}
@ -496,14 +509,15 @@ impl<'tcx, 'map> MutVisitor<'tcx> for CollectAndPatch<'tcx, 'map> {
struct OperandCollector<'tcx, 'map, 'a> {
state: &'a State<FlatSet<ScalarTy<'tcx>>>,
visitor: &'a mut CollectAndPatch<'tcx, 'map>,
visitor: &'a mut CollectAndPatch<'tcx>,
map: &'map Map,
}
impl<'tcx, 'map, 'a> Visitor<'tcx> for OperandCollector<'tcx, 'map, 'a> {
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
match operand {
Operand::Copy(place) | Operand::Move(place) => {
match self.state.get(place.as_ref(), self.visitor.map) {
match self.state.get(place.as_ref(), self.map) {
FlatSet::Top => (),
FlatSet::Elem(value) => {
self.visitor.before_effect.insert((location, *place), value);

View File

@ -597,16 +597,15 @@ fn locals_live_across_suspend_points<'tcx>(
let borrowed_locals_results =
MaybeBorrowedLocals.into_engine(tcx, body_ref).pass_name("generator").iterate_to_fixpoint();
let mut borrowed_locals_cursor =
rustc_mir_dataflow::ResultsCursor::new(body_ref, &borrowed_locals_results);
let mut borrowed_locals_cursor = borrowed_locals_results.cloned_results_cursor(body_ref);
// Calculate the MIR locals that we actually need to keep storage around
// for.
let requires_storage_results = MaybeRequiresStorage::new(body, &borrowed_locals_results)
.into_engine(tcx, body_ref)
.iterate_to_fixpoint();
let mut requires_storage_cursor =
rustc_mir_dataflow::ResultsCursor::new(body_ref, &requires_storage_results);
let mut requires_storage_results =
MaybeRequiresStorage::new(borrowed_locals_results.cloned_results_cursor(body))
.into_engine(tcx, body_ref)
.iterate_to_fixpoint();
let mut requires_storage_cursor = requires_storage_results.as_results_cursor(body_ref);
// Calculate the liveness of MIR locals ignoring borrows.
let mut liveness = MaybeLiveLocals
@ -747,7 +746,7 @@ fn compute_storage_conflicts<'mir, 'tcx>(
body: &'mir Body<'tcx>,
saved_locals: &GeneratorSavedLocals,
always_live_locals: BitSet<Local>,
requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>,
mut requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'_, 'mir, 'tcx>>,
) -> BitMatrix<GeneratorSavedLocal, GeneratorSavedLocal> {
assert_eq!(body.local_decls.len(), saved_locals.domain_size());
@ -802,13 +801,14 @@ struct StorageConflictVisitor<'mir, 'tcx, 's> {
local_conflicts: BitMatrix<Local, Local>,
}
impl<'mir, 'tcx> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx>
impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
for StorageConflictVisitor<'mir, 'tcx, '_>
{
type FlowState = BitSet<Local>;
fn visit_statement_before_primary_effect(
&mut self,
_results: &R,
state: &Self::FlowState,
_statement: &'mir Statement<'tcx>,
loc: Location,
@ -818,6 +818,7 @@ impl<'mir, 'tcx> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx>
fn visit_terminator_before_primary_effect(
&mut self,
_results: &R,
state: &Self::FlowState,
_terminator: &'mir Terminator<'tcx>,
loc: Location,