Rollup merge of #140234 - nnethercote:separate-Analysis-and-Results, r=davidtwco
Separate dataflow analysis and results `Analysis` gets put into `Results` with `EntryStates`, by `iterate_to_fixpoint`. This has two problems: - `Results` is passed various places where only `Analysis` is needed. - `EntryStates` is passed around mutably everywhere even though it is immutable. This commit mostly separates `Analysis` from `Results` and fixes these two problems. r? `@davidtwco`
This commit is contained in:
@@ -47,7 +47,7 @@ use rustc_mir_dataflow::impls::{
|
||||
use rustc_mir_dataflow::move_paths::{
|
||||
InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
|
||||
};
|
||||
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
|
||||
use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor, visit_results};
|
||||
use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
|
||||
use rustc_span::{ErrorGuaranteed, Span, Symbol};
|
||||
use smallvec::SmallVec;
|
||||
@@ -461,11 +461,13 @@ fn do_mir_borrowck<'tcx>(
|
||||
// Compute and report region errors, if any.
|
||||
mbcx.report_region_errors(nll_errors);
|
||||
|
||||
let mut flow_results = get_flow_results(tcx, body, &move_data, &borrow_set, ®ioncx);
|
||||
let (mut flow_analysis, flow_entry_states) =
|
||||
get_flow_results(tcx, body, &move_data, &borrow_set, ®ioncx);
|
||||
visit_results(
|
||||
body,
|
||||
traversal::reverse_postorder(body).map(|(bb, _)| bb),
|
||||
&mut flow_results,
|
||||
&mut flow_analysis,
|
||||
&flow_entry_states,
|
||||
&mut mbcx,
|
||||
);
|
||||
|
||||
@@ -525,7 +527,7 @@ fn get_flow_results<'a, 'tcx>(
|
||||
move_data: &'a MoveData<'tcx>,
|
||||
borrow_set: &'a BorrowSet<'tcx>,
|
||||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
) -> Results<'tcx, Borrowck<'a, 'tcx>> {
|
||||
) -> (Borrowck<'a, 'tcx>, Results<BorrowckDomain>) {
|
||||
// We compute these three analyses individually, but them combine them into
|
||||
// a single results so that `mbcx` can visit them all together.
|
||||
let borrows = Borrows::new(tcx, body, regioncx, borrow_set).iterate_to_fixpoint(
|
||||
@@ -550,14 +552,14 @@ fn get_flow_results<'a, 'tcx>(
|
||||
ever_inits: ever_inits.analysis,
|
||||
};
|
||||
|
||||
assert_eq!(borrows.entry_states.len(), uninits.entry_states.len());
|
||||
assert_eq!(borrows.entry_states.len(), ever_inits.entry_states.len());
|
||||
let entry_states: EntryStates<'_, Borrowck<'_, '_>> =
|
||||
itertools::izip!(borrows.entry_states, uninits.entry_states, ever_inits.entry_states)
|
||||
assert_eq!(borrows.results.len(), uninits.results.len());
|
||||
assert_eq!(borrows.results.len(), ever_inits.results.len());
|
||||
let results: Results<_> =
|
||||
itertools::izip!(borrows.results, uninits.results, ever_inits.results)
|
||||
.map(|(borrows, uninits, ever_inits)| BorrowckDomain { borrows, uninits, ever_inits })
|
||||
.collect();
|
||||
|
||||
Results { analysis, entry_states }
|
||||
(analysis, results)
|
||||
}
|
||||
|
||||
pub(crate) struct BorrowckInferCtxt<'tcx> {
|
||||
@@ -705,7 +707,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
|
||||
impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, '_, 'tcx> {
|
||||
fn visit_after_early_statement_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
|
||||
_analysis: &mut Borrowck<'a, 'tcx>,
|
||||
state: &BorrowckDomain,
|
||||
stmt: &Statement<'tcx>,
|
||||
location: Location,
|
||||
@@ -781,7 +783,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a,
|
||||
|
||||
fn visit_after_early_terminator_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
|
||||
_analysis: &mut Borrowck<'a, 'tcx>,
|
||||
state: &BorrowckDomain,
|
||||
term: &Terminator<'tcx>,
|
||||
loc: Location,
|
||||
@@ -901,7 +903,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a,
|
||||
|
||||
fn visit_after_primary_terminator_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
|
||||
_analysis: &mut Borrowck<'a, 'tcx>,
|
||||
state: &BorrowckDomain,
|
||||
term: &Terminator<'tcx>,
|
||||
loc: Location,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Random access inspection of the results of a dataflow analysis.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
@@ -9,38 +10,30 @@ use rustc_middle::mir::{self, BasicBlock, Location};
|
||||
|
||||
use super::{Analysis, Direction, Effect, EffectIndex, Results};
|
||||
|
||||
/// Some `ResultsCursor`s want to own a `Results`, and some want to borrow a `Results`, either
|
||||
/// mutable or immutably. This type allows all of the above. It's similar to `Cow`.
|
||||
pub enum ResultsHandle<'a, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
BorrowedMut(&'a mut Results<'tcx, A>),
|
||||
Owned(Results<'tcx, A>),
|
||||
/// Some `ResultsCursor`s want to own an `Analysis`, and some want to borrow an `Analysis`, either
|
||||
/// mutable or immutably. This type allows all of the above. It's similar to `Cow`, but `Cow`
|
||||
/// doesn't allow mutable borrowing.
|
||||
enum CowMut<'a, T> {
|
||||
BorrowedMut(&'a mut T),
|
||||
Owned(T),
|
||||
}
|
||||
|
||||
impl<'tcx, A> Deref for ResultsHandle<'_, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
type Target = Results<'tcx, A>;
|
||||
impl<T> Deref for CowMut<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Results<'tcx, A> {
|
||||
fn deref(&self) -> &T {
|
||||
match self {
|
||||
ResultsHandle::BorrowedMut(borrowed) => borrowed,
|
||||
ResultsHandle::Owned(owned) => owned,
|
||||
CowMut::BorrowedMut(borrowed) => borrowed,
|
||||
CowMut::Owned(owned) => owned,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, A> DerefMut for ResultsHandle<'_, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Results<'tcx, A> {
|
||||
impl<T> DerefMut for CowMut<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
match self {
|
||||
ResultsHandle::BorrowedMut(borrowed) => borrowed,
|
||||
ResultsHandle::Owned(owned) => owned,
|
||||
CowMut::BorrowedMut(borrowed) => borrowed,
|
||||
CowMut::Owned(owned) => owned,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,7 +53,8 @@ where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
results: ResultsHandle<'mir, 'tcx, A>,
|
||||
analysis: CowMut<'mir, A>,
|
||||
results: Cow<'mir, Results<A::Domain>>,
|
||||
state: A::Domain,
|
||||
|
||||
pos: CursorPosition,
|
||||
@@ -88,11 +82,15 @@ where
|
||||
self.body
|
||||
}
|
||||
|
||||
/// Returns a new cursor that can inspect `results`.
|
||||
pub fn new(body: &'mir mir::Body<'tcx>, results: ResultsHandle<'mir, 'tcx, A>) -> Self {
|
||||
let bottom_value = results.analysis.bottom_value(body);
|
||||
fn new(
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
analysis: CowMut<'mir, A>,
|
||||
results: Cow<'mir, Results<A::Domain>>,
|
||||
) -> Self {
|
||||
let bottom_value = analysis.bottom_value(body);
|
||||
ResultsCursor {
|
||||
body,
|
||||
analysis,
|
||||
results,
|
||||
|
||||
// Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that
|
||||
@@ -107,6 +105,24 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new cursor that takes ownership of and inspects analysis results.
|
||||
pub fn new_owning(
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
analysis: A,
|
||||
results: Results<A::Domain>,
|
||||
) -> Self {
|
||||
Self::new(body, CowMut::Owned(analysis), Cow::Owned(results))
|
||||
}
|
||||
|
||||
/// Returns a new cursor that borrows and inspects analysis results.
|
||||
pub fn new_borrowing(
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
analysis: &'mir mut A,
|
||||
results: &'mir Results<A::Domain>,
|
||||
) -> Self {
|
||||
Self::new(body, CowMut::BorrowedMut(analysis), Cow::Borrowed(results))
|
||||
}
|
||||
|
||||
/// Allows inspection of unreachable basic blocks even with `debug_assertions` enabled.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn allow_unreachable(&mut self) {
|
||||
@@ -116,7 +132,7 @@ where
|
||||
|
||||
/// Returns the `Analysis` used to generate the underlying `Results`.
|
||||
pub fn analysis(&self) -> &A {
|
||||
&self.results.analysis
|
||||
&self.analysis
|
||||
}
|
||||
|
||||
/// Resets the cursor to hold the entry set for the given basic block.
|
||||
@@ -128,7 +144,7 @@ where
|
||||
#[cfg(debug_assertions)]
|
||||
assert!(self.reachable_blocks.contains(block));
|
||||
|
||||
self.state.clone_from(self.results.entry_set_for_block(block));
|
||||
self.state.clone_from(&self.results[block]);
|
||||
self.pos = CursorPosition::block_entry(block);
|
||||
self.state_needs_reset = false;
|
||||
}
|
||||
@@ -220,7 +236,7 @@ where
|
||||
let target_effect_index = effect.at_index(target.statement_index);
|
||||
|
||||
A::Direction::apply_effects_in_range(
|
||||
&mut self.results.analysis,
|
||||
&mut *self.analysis,
|
||||
&mut self.state,
|
||||
target.block,
|
||||
block_data,
|
||||
@@ -236,7 +252,7 @@ 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(&mut A, &mut A::Domain)) {
|
||||
f(&mut self.results.analysis, &mut self.state);
|
||||
f(&mut self.analysis, &mut self.state);
|
||||
self.state_needs_reset = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use rustc_middle::mir::{
|
||||
};
|
||||
|
||||
use super::visitor::ResultsVisitor;
|
||||
use super::{Analysis, Effect, EffectIndex, Results};
|
||||
use super::{Analysis, Effect, EffectIndex};
|
||||
|
||||
pub trait Direction {
|
||||
const IS_FORWARD: bool;
|
||||
@@ -36,13 +36,13 @@ pub trait Direction {
|
||||
A: Analysis<'tcx>;
|
||||
|
||||
/// Called by `ResultsVisitor` to recompute the analysis domain values for
|
||||
/// all locations in a basic block (starting from the entry value stored
|
||||
/// in `Results`) and to visit them with `vis`.
|
||||
/// all locations in a basic block (starting from `entry_state` and to
|
||||
/// visit them with `vis`.
|
||||
fn visit_results_in_block<'mir, 'tcx, A>(
|
||||
state: &mut A::Domain,
|
||||
block: BasicBlock,
|
||||
block_data: &'mir mir::BasicBlockData<'tcx>,
|
||||
results: &mut Results<'tcx, A>,
|
||||
analysis: &mut A,
|
||||
vis: &mut impl ResultsVisitor<'tcx, A>,
|
||||
) where
|
||||
A: Analysis<'tcx>;
|
||||
@@ -211,28 +211,26 @@ impl Direction for Backward {
|
||||
state: &mut A::Domain,
|
||||
block: BasicBlock,
|
||||
block_data: &'mir mir::BasicBlockData<'tcx>,
|
||||
results: &mut Results<'tcx, A>,
|
||||
analysis: &mut A,
|
||||
vis: &mut impl ResultsVisitor<'tcx, A>,
|
||||
) where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
state.clone_from(results.entry_set_for_block(block));
|
||||
|
||||
vis.visit_block_end(state);
|
||||
|
||||
let loc = Location { block, statement_index: block_data.statements.len() };
|
||||
let term = block_data.terminator();
|
||||
results.analysis.apply_early_terminator_effect(state, term, loc);
|
||||
vis.visit_after_early_terminator_effect(results, state, term, loc);
|
||||
results.analysis.apply_primary_terminator_effect(state, term, loc);
|
||||
vis.visit_after_primary_terminator_effect(results, state, term, loc);
|
||||
analysis.apply_early_terminator_effect(state, term, loc);
|
||||
vis.visit_after_early_terminator_effect(analysis, state, term, loc);
|
||||
analysis.apply_primary_terminator_effect(state, term, loc);
|
||||
vis.visit_after_primary_terminator_effect(analysis, state, term, loc);
|
||||
|
||||
for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
|
||||
let loc = Location { block, statement_index };
|
||||
results.analysis.apply_early_statement_effect(state, stmt, loc);
|
||||
vis.visit_after_early_statement_effect(results, state, stmt, loc);
|
||||
results.analysis.apply_primary_statement_effect(state, stmt, loc);
|
||||
vis.visit_after_primary_statement_effect(results, state, stmt, loc);
|
||||
analysis.apply_early_statement_effect(state, stmt, loc);
|
||||
vis.visit_after_early_statement_effect(analysis, state, stmt, loc);
|
||||
analysis.apply_primary_statement_effect(state, stmt, loc);
|
||||
vis.visit_after_primary_statement_effect(analysis, state, stmt, loc);
|
||||
}
|
||||
|
||||
vis.visit_block_start(state);
|
||||
@@ -393,29 +391,27 @@ impl Direction for Forward {
|
||||
state: &mut A::Domain,
|
||||
block: BasicBlock,
|
||||
block_data: &'mir mir::BasicBlockData<'tcx>,
|
||||
results: &mut Results<'tcx, A>,
|
||||
analysis: &mut A,
|
||||
vis: &mut impl ResultsVisitor<'tcx, A>,
|
||||
) where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
state.clone_from(results.entry_set_for_block(block));
|
||||
|
||||
vis.visit_block_start(state);
|
||||
|
||||
for (statement_index, stmt) in block_data.statements.iter().enumerate() {
|
||||
let loc = Location { block, statement_index };
|
||||
results.analysis.apply_early_statement_effect(state, stmt, loc);
|
||||
vis.visit_after_early_statement_effect(results, state, stmt, loc);
|
||||
results.analysis.apply_primary_statement_effect(state, stmt, loc);
|
||||
vis.visit_after_primary_statement_effect(results, state, stmt, loc);
|
||||
analysis.apply_early_statement_effect(state, stmt, loc);
|
||||
vis.visit_after_early_statement_effect(analysis, state, stmt, loc);
|
||||
analysis.apply_primary_statement_effect(state, stmt, loc);
|
||||
vis.visit_after_primary_statement_effect(analysis, state, stmt, loc);
|
||||
}
|
||||
|
||||
let loc = Location { block, statement_index: block_data.statements.len() };
|
||||
let term = block_data.terminator();
|
||||
results.analysis.apply_early_terminator_effect(state, term, loc);
|
||||
vis.visit_after_early_terminator_effect(results, state, term, loc);
|
||||
results.analysis.apply_primary_terminator_effect(state, term, loc);
|
||||
vis.visit_after_primary_terminator_effect(results, state, term, loc);
|
||||
analysis.apply_early_terminator_effect(state, term, loc);
|
||||
vis.visit_after_early_terminator_effect(analysis, state, term, loc);
|
||||
analysis.apply_primary_terminator_effect(state, term, loc);
|
||||
vis.visit_after_primary_terminator_effect(analysis, state, term, loc);
|
||||
|
||||
vis.visit_block_end(state);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ use tracing::debug;
|
||||
use {rustc_ast as ast, rustc_graphviz as dot};
|
||||
|
||||
use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext};
|
||||
use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor};
|
||||
use super::{
|
||||
Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor, visit_results,
|
||||
};
|
||||
use crate::errors::{
|
||||
DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
|
||||
};
|
||||
@@ -32,7 +34,8 @@ use crate::errors::{
|
||||
pub(super) fn write_graphviz_results<'tcx, A>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
results: &mut Results<'tcx, A>,
|
||||
analysis: &mut A,
|
||||
results: &Results<A::Domain>,
|
||||
pass_name: Option<&'static str>,
|
||||
) -> std::io::Result<()>
|
||||
where
|
||||
@@ -77,7 +80,7 @@ where
|
||||
|
||||
let mut buf = Vec::new();
|
||||
|
||||
let graphviz = Formatter::new(body, results, style);
|
||||
let graphviz = Formatter::new(body, analysis, results, style);
|
||||
let mut render_opts =
|
||||
vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())];
|
||||
if tcx.sess.opts.unstable_opts.graphviz_dark_mode {
|
||||
@@ -203,10 +206,11 @@ where
|
||||
{
|
||||
body: &'mir Body<'tcx>,
|
||||
// The `RefCell` is used because `<Formatter as Labeller>::node_label`
|
||||
// takes `&self`, but it needs to modify the results. This is also the
|
||||
// takes `&self`, but it needs to modify the analysis. This is also the
|
||||
// reason for the `Formatter`/`BlockFormatter` split; `BlockFormatter` has
|
||||
// the operations that involve the mutation, i.e. within the `borrow_mut`.
|
||||
results: RefCell<&'mir mut Results<'tcx, A>>,
|
||||
analysis: RefCell<&'mir mut A>,
|
||||
results: &'mir Results<A::Domain>,
|
||||
style: OutputStyle,
|
||||
reachable: DenseBitSet<BasicBlock>,
|
||||
}
|
||||
@@ -217,11 +221,12 @@ where
|
||||
{
|
||||
fn new(
|
||||
body: &'mir Body<'tcx>,
|
||||
results: &'mir mut Results<'tcx, A>,
|
||||
analysis: &'mir mut A,
|
||||
results: &'mir Results<A::Domain>,
|
||||
style: OutputStyle,
|
||||
) -> Self {
|
||||
let reachable = traversal::reachable_as_bitset(body);
|
||||
Formatter { body, results: results.into(), style, reachable }
|
||||
Formatter { body, analysis: analysis.into(), results, style, reachable }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,12 +264,12 @@ where
|
||||
}
|
||||
|
||||
fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
|
||||
let mut results = self.results.borrow_mut();
|
||||
let analysis = &mut **self.analysis.borrow_mut();
|
||||
|
||||
let diffs = StateDiffCollector::run(self.body, *block, *results, self.style);
|
||||
let diffs = StateDiffCollector::run(self.body, *block, analysis, self.results, self.style);
|
||||
|
||||
let mut fmt = BlockFormatter {
|
||||
cursor: results.as_results_cursor(self.body),
|
||||
cursor: ResultsCursor::new_borrowing(self.body, analysis, self.results),
|
||||
style: self.style,
|
||||
bg: Background::Light,
|
||||
};
|
||||
@@ -692,7 +697,8 @@ impl<D> StateDiffCollector<D> {
|
||||
fn run<'tcx, A>(
|
||||
body: &Body<'tcx>,
|
||||
block: BasicBlock,
|
||||
results: &mut Results<'tcx, A>,
|
||||
analysis: &mut A,
|
||||
results: &Results<A::Domain>,
|
||||
style: OutputStyle,
|
||||
) -> Self
|
||||
where
|
||||
@@ -700,12 +706,12 @@ impl<D> StateDiffCollector<D> {
|
||||
D: DebugWithContext<A>,
|
||||
{
|
||||
let mut collector = StateDiffCollector {
|
||||
prev_state: results.analysis.bottom_value(body),
|
||||
prev_state: analysis.bottom_value(body),
|
||||
after: vec![],
|
||||
before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]),
|
||||
};
|
||||
|
||||
results.visit_with(body, std::iter::once(block), &mut collector);
|
||||
visit_results(body, std::iter::once(block), analysis, results, &mut collector);
|
||||
collector
|
||||
}
|
||||
}
|
||||
@@ -729,49 +735,49 @@ where
|
||||
|
||||
fn visit_after_early_statement_effect(
|
||||
&mut self,
|
||||
results: &mut Results<'tcx, A>,
|
||||
analysis: &mut A,
|
||||
state: &A::Domain,
|
||||
_statement: &mir::Statement<'tcx>,
|
||||
_location: Location,
|
||||
) {
|
||||
if let Some(before) = self.before.as_mut() {
|
||||
before.push(diff_pretty(state, &self.prev_state, &results.analysis));
|
||||
before.push(diff_pretty(state, &self.prev_state, analysis));
|
||||
self.prev_state.clone_from(state)
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_after_primary_statement_effect(
|
||||
&mut self,
|
||||
results: &mut Results<'tcx, A>,
|
||||
analysis: &mut A,
|
||||
state: &A::Domain,
|
||||
_statement: &mir::Statement<'tcx>,
|
||||
_location: Location,
|
||||
) {
|
||||
self.after.push(diff_pretty(state, &self.prev_state, &results.analysis));
|
||||
self.after.push(diff_pretty(state, &self.prev_state, analysis));
|
||||
self.prev_state.clone_from(state)
|
||||
}
|
||||
|
||||
fn visit_after_early_terminator_effect(
|
||||
&mut self,
|
||||
results: &mut Results<'tcx, A>,
|
||||
analysis: &mut A,
|
||||
state: &A::Domain,
|
||||
_terminator: &mir::Terminator<'tcx>,
|
||||
_location: Location,
|
||||
) {
|
||||
if let Some(before) = self.before.as_mut() {
|
||||
before.push(diff_pretty(state, &self.prev_state, &results.analysis));
|
||||
before.push(diff_pretty(state, &self.prev_state, analysis));
|
||||
self.prev_state.clone_from(state)
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_after_primary_terminator_effect(
|
||||
&mut self,
|
||||
results: &mut Results<'tcx, A>,
|
||||
analysis: &mut A,
|
||||
state: &A::Domain,
|
||||
_terminator: &mir::Terminator<'tcx>,
|
||||
_location: Location,
|
||||
) {
|
||||
self.after.push(diff_pretty(state, &self.prev_state, &results.analysis));
|
||||
self.after.push(diff_pretty(state, &self.prev_state, analysis));
|
||||
self.prev_state.clone_from(state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +58,9 @@ mod visitor;
|
||||
pub use self::cursor::ResultsCursor;
|
||||
pub use self::direction::{Backward, Direction, Forward};
|
||||
pub use self::lattice::{JoinSemiLattice, MaybeReachable};
|
||||
pub use self::results::{EntryStates, Results};
|
||||
pub use self::visitor::{ResultsVisitor, visit_results};
|
||||
pub(crate) use self::results::AnalysisAndResults;
|
||||
pub use self::results::Results;
|
||||
pub use self::visitor::{ResultsVisitor, visit_reachable_results, visit_results};
|
||||
|
||||
/// Analysis domains are all bitsets of various kinds. This trait holds
|
||||
/// operations needed by all of them.
|
||||
@@ -247,17 +248,15 @@ pub trait Analysis<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
pass_name: Option<&'static str>,
|
||||
) -> Results<'tcx, Self>
|
||||
) -> AnalysisAndResults<'tcx, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
Self::Domain: DebugWithContext<Self>,
|
||||
{
|
||||
let mut entry_states =
|
||||
IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len());
|
||||
self.initialize_start_block(body, &mut entry_states[mir::START_BLOCK]);
|
||||
let mut results = IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len());
|
||||
self.initialize_start_block(body, &mut results[mir::START_BLOCK]);
|
||||
|
||||
if Self::Direction::IS_BACKWARD && entry_states[mir::START_BLOCK] != self.bottom_value(body)
|
||||
{
|
||||
if Self::Direction::IS_BACKWARD && results[mir::START_BLOCK] != self.bottom_value(body) {
|
||||
bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
|
||||
}
|
||||
|
||||
@@ -280,10 +279,9 @@ pub trait Analysis<'tcx> {
|
||||
// every iteration.
|
||||
let mut state = self.bottom_value(body);
|
||||
while let Some(bb) = dirty_queue.pop() {
|
||||
// Set the state to the entry state of the block.
|
||||
// This is equivalent to `state = entry_states[bb].clone()`,
|
||||
// but it saves an allocation, thus improving compile times.
|
||||
state.clone_from(&entry_states[bb]);
|
||||
// Set the state to the entry state of the block. This is equivalent to `state =
|
||||
// results[bb].clone()`, but it saves an allocation, thus improving compile times.
|
||||
state.clone_from(&results[bb]);
|
||||
|
||||
Self::Direction::apply_effects_in_block(
|
||||
&mut self,
|
||||
@@ -292,7 +290,7 @@ pub trait Analysis<'tcx> {
|
||||
bb,
|
||||
&body[bb],
|
||||
|target: BasicBlock, state: &Self::Domain| {
|
||||
let set_changed = entry_states[target].join(state);
|
||||
let set_changed = results[target].join(state);
|
||||
if set_changed {
|
||||
dirty_queue.insert(target);
|
||||
}
|
||||
@@ -300,16 +298,14 @@ pub trait Analysis<'tcx> {
|
||||
);
|
||||
}
|
||||
|
||||
let mut results = Results { analysis: self, entry_states };
|
||||
|
||||
if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
|
||||
let res = write_graphviz_results(tcx, body, &mut results, pass_name);
|
||||
let res = write_graphviz_results(tcx, body, &mut self, &results, pass_name);
|
||||
if let Err(e) = res {
|
||||
error!("Failed to write graphviz dataflow results: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
results
|
||||
AnalysisAndResults { analysis: self, results }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,63 +1,30 @@
|
||||
//! Dataflow analysis results.
|
||||
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::{BasicBlock, Body, traversal};
|
||||
use rustc_middle::mir::{BasicBlock, Body};
|
||||
|
||||
use super::{Analysis, ResultsCursor, ResultsVisitor, visit_results};
|
||||
use crate::framework::cursor::ResultsHandle;
|
||||
use super::{Analysis, ResultsCursor};
|
||||
|
||||
pub type EntryStates<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>;
|
||||
/// The results of a dataflow analysis that has converged to fixpoint. It only holds the domain
|
||||
/// values at the entry of each basic block. Domain values in other parts of the block are
|
||||
/// recomputed on the fly by visitors (i.e. `ResultsCursor`, or `ResultsVisitor` impls).
|
||||
pub type Results<D> = IndexVec<BasicBlock, D>;
|
||||
|
||||
/// A dataflow analysis that has converged to fixpoint. It only holds the domain values at the
|
||||
/// entry of each basic block. Domain values in other parts of the block are recomputed on the fly
|
||||
/// by visitors (i.e. `ResultsCursor`, or `ResultsVisitor` impls).
|
||||
#[derive(Clone)]
|
||||
pub struct Results<'tcx, A>
|
||||
/// Utility type used in a few places where it's convenient to bundle an analysis with its results.
|
||||
pub struct AnalysisAndResults<'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
pub analysis: A,
|
||||
pub entry_states: EntryStates<'tcx, A>,
|
||||
pub results: Results<A::Domain>,
|
||||
}
|
||||
|
||||
impl<'tcx, A> Results<'tcx, A>
|
||||
impl<'tcx, A> AnalysisAndResults<'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
/// Creates a `ResultsCursor` that mutably borrows the `Results`, which is appropriate when the
|
||||
/// `Results` is also used outside the cursor.
|
||||
pub fn as_results_cursor<'mir>(
|
||||
&'mir mut self,
|
||||
body: &'mir Body<'tcx>,
|
||||
) -> ResultsCursor<'mir, 'tcx, A> {
|
||||
ResultsCursor::new(body, ResultsHandle::BorrowedMut(self))
|
||||
}
|
||||
|
||||
/// Creates a `ResultsCursor` that takes ownership of the `Results`.
|
||||
/// Creates a `ResultsCursor` that takes ownership of `self`.
|
||||
pub fn into_results_cursor<'mir>(self, body: &'mir Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
|
||||
ResultsCursor::new(body, ResultsHandle::Owned(self))
|
||||
}
|
||||
|
||||
/// Gets the dataflow state for the given block.
|
||||
pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
|
||||
&self.entry_states[block]
|
||||
}
|
||||
|
||||
pub fn visit_with<'mir>(
|
||||
&mut self,
|
||||
body: &'mir Body<'tcx>,
|
||||
blocks: impl IntoIterator<Item = BasicBlock>,
|
||||
vis: &mut impl ResultsVisitor<'tcx, A>,
|
||||
) {
|
||||
visit_results(body, blocks, self, vis)
|
||||
}
|
||||
|
||||
pub fn visit_reachable_with<'mir>(
|
||||
&mut self,
|
||||
body: &'mir Body<'tcx>,
|
||||
vis: &mut impl ResultsVisitor<'tcx, A>,
|
||||
) {
|
||||
let blocks = traversal::reachable(body);
|
||||
visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
|
||||
ResultsCursor::new_owning(body, self.analysis, self.results)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> {
|
||||
///
|
||||
/// The `102` in the block's entry set is derived from the basic block index and ensures that the
|
||||
/// expected state is unique across all basic blocks. Remember, it is generated by
|
||||
/// `mock_entry_states`, not from actually running `MockAnalysis` to fixpoint.
|
||||
/// `mock_results`, not from actually running `MockAnalysis` to fixpoint.
|
||||
struct MockAnalysis<'tcx, D> {
|
||||
body: &'tcx mir::Body<'tcx>,
|
||||
dir: PhantomData<D>,
|
||||
@@ -96,7 +96,7 @@ impl<D: Direction> MockAnalysis<'_, D> {
|
||||
ret
|
||||
}
|
||||
|
||||
fn mock_entry_states(&self) -> IndexVec<BasicBlock, DenseBitSet<usize>> {
|
||||
fn mock_results(&self) -> IndexVec<BasicBlock, DenseBitSet<usize>> {
|
||||
let empty = self.bottom_value(self.body);
|
||||
let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks);
|
||||
|
||||
@@ -255,7 +255,7 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) {
|
||||
let body = analysis.body;
|
||||
|
||||
let mut cursor =
|
||||
Results { entry_states: analysis.mock_entry_states(), analysis }.into_results_cursor(body);
|
||||
AnalysisAndResults { results: analysis.mock_results(), analysis }.into_results_cursor(body);
|
||||
|
||||
cursor.allow_unreachable();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use rustc_middle::mir::{self, BasicBlock, Location};
|
||||
use rustc_middle::mir::{self, BasicBlock, Location, traversal};
|
||||
|
||||
use super::{Analysis, Direction, Results};
|
||||
|
||||
@@ -7,12 +7,13 @@ use super::{Analysis, Direction, Results};
|
||||
pub fn visit_results<'mir, 'tcx, A>(
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
blocks: impl IntoIterator<Item = BasicBlock>,
|
||||
results: &mut Results<'tcx, A>,
|
||||
analysis: &mut A,
|
||||
results: &Results<A::Domain>,
|
||||
vis: &mut impl ResultsVisitor<'tcx, A>,
|
||||
) where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
let mut state = results.analysis.bottom_value(body);
|
||||
let mut state = analysis.bottom_value(body);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
let reachable_blocks = mir::traversal::reachable_as_bitset(body);
|
||||
@@ -22,10 +23,24 @@ pub fn visit_results<'mir, 'tcx, A>(
|
||||
assert!(reachable_blocks.contains(block));
|
||||
|
||||
let block_data = &body[block];
|
||||
A::Direction::visit_results_in_block(&mut state, block, block_data, results, vis);
|
||||
state.clone_from(&results[block]);
|
||||
A::Direction::visit_results_in_block(&mut state, block, block_data, analysis, vis);
|
||||
}
|
||||
}
|
||||
|
||||
/// Like `visit_results`, but only for reachable blocks.
|
||||
pub fn visit_reachable_results<'mir, 'tcx, A>(
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
analysis: &mut A,
|
||||
results: &Results<A::Domain>,
|
||||
vis: &mut impl ResultsVisitor<'tcx, A>,
|
||||
) where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
let blocks = traversal::reachable(body).map(|(bb, _)| bb);
|
||||
visit_results(body, blocks, analysis, results, vis)
|
||||
}
|
||||
|
||||
/// A visitor over the results of an `Analysis`. Use this when you want to inspect domain values in
|
||||
/// many or all locations; use `ResultsCursor` if you want to inspect domain values only in certain
|
||||
/// locations.
|
||||
@@ -38,7 +53,7 @@ where
|
||||
/// Called after the "early" effect of the given statement is applied to `state`.
|
||||
fn visit_after_early_statement_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, A>,
|
||||
_analysis: &mut A,
|
||||
_state: &A::Domain,
|
||||
_statement: &mir::Statement<'tcx>,
|
||||
_location: Location,
|
||||
@@ -48,7 +63,7 @@ where
|
||||
/// Called after the "primary" effect of the given statement is applied to `state`.
|
||||
fn visit_after_primary_statement_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, A>,
|
||||
_analysis: &mut A,
|
||||
_state: &A::Domain,
|
||||
_statement: &mir::Statement<'tcx>,
|
||||
_location: Location,
|
||||
@@ -58,7 +73,7 @@ where
|
||||
/// Called after the "early" effect of the given terminator is applied to `state`.
|
||||
fn visit_after_early_terminator_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, A>,
|
||||
_analysis: &mut A,
|
||||
_state: &A::Domain,
|
||||
_terminator: &mir::Terminator<'tcx>,
|
||||
_location: Location,
|
||||
@@ -70,7 +85,7 @@ where
|
||||
/// The `call_return_effect` (if one exists) will *not* be applied to `state`.
|
||||
fn visit_after_primary_terminator_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, A>,
|
||||
_analysis: &mut A,
|
||||
_state: &A::Domain,
|
||||
_terminator: &mir::Terminator<'tcx>,
|
||||
_location: Location,
|
||||
|
||||
@@ -18,8 +18,8 @@ pub use self::drop_flag_effects::{
|
||||
move_path_children_matching, on_all_children_bits, on_lookup_result_bits,
|
||||
};
|
||||
pub use self::framework::{
|
||||
Analysis, Backward, Direction, EntryStates, Forward, GenKill, JoinSemiLattice, MaybeReachable,
|
||||
Results, ResultsCursor, ResultsVisitor, fmt, graphviz, lattice, visit_results,
|
||||
Analysis, Backward, Direction, Forward, GenKill, JoinSemiLattice, MaybeReachable, Results,
|
||||
ResultsCursor, ResultsVisitor, fmt, graphviz, lattice, visit_reachable_results, visit_results,
|
||||
};
|
||||
use self::move_paths::MoveData;
|
||||
|
||||
|
||||
@@ -98,7 +98,8 @@ rustc_index::newtype_index! {
|
||||
pub fn save_as_intervals<'tcx, N, A>(
|
||||
elements: &DenseLocationMap,
|
||||
body: &mir::Body<'tcx>,
|
||||
mut results: Results<'tcx, A>,
|
||||
mut analysis: A,
|
||||
results: Results<A::Domain>,
|
||||
) -> SparseIntervalMatrix<N, PointIndex>
|
||||
where
|
||||
N: Idx,
|
||||
@@ -109,7 +110,8 @@ where
|
||||
visit_results(
|
||||
body,
|
||||
body.basic_blocks.reverse_postorder().iter().copied(),
|
||||
&mut results,
|
||||
&mut analysis,
|
||||
&results,
|
||||
&mut visitor,
|
||||
);
|
||||
visitor.values
|
||||
@@ -127,7 +129,7 @@ where
|
||||
{
|
||||
fn visit_after_primary_statement_effect<'mir>(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, A>,
|
||||
_analysis: &mut A,
|
||||
state: &A::Domain,
|
||||
_statement: &'mir mir::Statement<'tcx>,
|
||||
location: Location,
|
||||
@@ -141,7 +143,7 @@ where
|
||||
|
||||
fn visit_after_primary_terminator_effect<'mir>(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, A>,
|
||||
_analysis: &mut A,
|
||||
state: &A::Domain,
|
||||
_terminator: &'mir mir::Terminator<'tcx>,
|
||||
location: Location,
|
||||
|
||||
@@ -39,23 +39,23 @@ pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
|
||||
let move_data = MoveData::gather_moves(body, tcx, |_| true);
|
||||
|
||||
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() {
|
||||
let flow_inits =
|
||||
MaybeInitializedPlaces::new(tcx, body, &move_data).iterate_to_fixpoint(tcx, body, None);
|
||||
|
||||
sanity_check_via_rustc_peek(tcx, flow_inits.into_results_cursor(body));
|
||||
let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
|
||||
.iterate_to_fixpoint(tcx, body, None)
|
||||
.into_results_cursor(body);
|
||||
sanity_check_via_rustc_peek(tcx, flow_inits);
|
||||
}
|
||||
|
||||
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() {
|
||||
let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data)
|
||||
.iterate_to_fixpoint(tcx, body, None);
|
||||
|
||||
sanity_check_via_rustc_peek(tcx, flow_uninits.into_results_cursor(body));
|
||||
.iterate_to_fixpoint(tcx, body, None)
|
||||
.into_results_cursor(body);
|
||||
sanity_check_via_rustc_peek(tcx, flow_uninits);
|
||||
}
|
||||
|
||||
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() {
|
||||
let flow_liveness = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None);
|
||||
|
||||
sanity_check_via_rustc_peek(tcx, flow_liveness.into_results_cursor(body));
|
||||
let flow_liveness =
|
||||
MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body);
|
||||
sanity_check_via_rustc_peek(tcx, flow_liveness);
|
||||
}
|
||||
|
||||
if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() {
|
||||
|
||||
@@ -79,7 +79,9 @@ use rustc_mir_dataflow::impls::{
|
||||
MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive,
|
||||
always_storage_live_locals,
|
||||
};
|
||||
use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor};
|
||||
use rustc_mir_dataflow::{
|
||||
Analysis, Results, ResultsCursor, ResultsVisitor, visit_reachable_results,
|
||||
};
|
||||
use rustc_span::def_id::{DefId, LocalDefId};
|
||||
use rustc_span::source_map::dummy_spanned;
|
||||
use rustc_span::symbol::sym;
|
||||
@@ -680,18 +682,29 @@ fn locals_live_across_suspend_points<'tcx>(
|
||||
.iterate_to_fixpoint(tcx, body, None)
|
||||
.into_results_cursor(body);
|
||||
|
||||
// Calculate the MIR locals which have been previously
|
||||
// borrowed (even if they are still active).
|
||||
let borrowed_locals_results =
|
||||
MaybeBorrowedLocals.iterate_to_fixpoint(tcx, body, Some("coroutine"));
|
||||
|
||||
let mut borrowed_locals_cursor = borrowed_locals_results.clone().into_results_cursor(body);
|
||||
// Calculate the MIR locals that have been previously borrowed (even if they are still active).
|
||||
let borrowed_locals = MaybeBorrowedLocals.iterate_to_fixpoint(tcx, body, Some("coroutine"));
|
||||
let mut borrowed_locals_analysis1 = borrowed_locals.analysis;
|
||||
let mut borrowed_locals_analysis2 = borrowed_locals_analysis1.clone(); // trivial
|
||||
let borrowed_locals_cursor1 = ResultsCursor::new_borrowing(
|
||||
body,
|
||||
&mut borrowed_locals_analysis1,
|
||||
&borrowed_locals.results,
|
||||
);
|
||||
let mut borrowed_locals_cursor2 = ResultsCursor::new_borrowing(
|
||||
body,
|
||||
&mut borrowed_locals_analysis2,
|
||||
&borrowed_locals.results,
|
||||
);
|
||||
|
||||
// Calculate the MIR locals that we need to keep storage around for.
|
||||
let mut requires_storage_results =
|
||||
MaybeRequiresStorage::new(borrowed_locals_results.into_results_cursor(body))
|
||||
.iterate_to_fixpoint(tcx, body, None);
|
||||
let mut requires_storage_cursor = requires_storage_results.as_results_cursor(body);
|
||||
let mut requires_storage =
|
||||
MaybeRequiresStorage::new(borrowed_locals_cursor1).iterate_to_fixpoint(tcx, body, None);
|
||||
let mut requires_storage_cursor = ResultsCursor::new_borrowing(
|
||||
body,
|
||||
&mut requires_storage.analysis,
|
||||
&requires_storage.results,
|
||||
);
|
||||
|
||||
// Calculate the liveness of MIR locals ignoring borrows.
|
||||
let mut liveness =
|
||||
@@ -720,8 +733,8 @@ fn locals_live_across_suspend_points<'tcx>(
|
||||
// If a borrow is converted to a raw reference, we must also assume that it lives
|
||||
// forever. Note that the final liveness is still bounded by the storage liveness
|
||||
// of the local, which happens using the `intersect` operation below.
|
||||
borrowed_locals_cursor.seek_before_primary_effect(loc);
|
||||
live_locals.union(borrowed_locals_cursor.get());
|
||||
borrowed_locals_cursor2.seek_before_primary_effect(loc);
|
||||
live_locals.union(borrowed_locals_cursor2.get());
|
||||
}
|
||||
|
||||
// Store the storage liveness for later use so we can restore the state
|
||||
@@ -763,7 +776,8 @@ fn locals_live_across_suspend_points<'tcx>(
|
||||
body,
|
||||
&saved_locals,
|
||||
always_live_locals.clone(),
|
||||
requires_storage_results,
|
||||
&mut requires_storage.analysis,
|
||||
&requires_storage.results,
|
||||
);
|
||||
|
||||
LivenessInfo {
|
||||
@@ -828,7 +842,8 @@ fn compute_storage_conflicts<'mir, 'tcx>(
|
||||
body: &'mir Body<'tcx>,
|
||||
saved_locals: &'mir CoroutineSavedLocals,
|
||||
always_live_locals: DenseBitSet<Local>,
|
||||
mut requires_storage: Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>,
|
||||
analysis: &mut MaybeRequiresStorage<'mir, 'tcx>,
|
||||
results: &Results<DenseBitSet<Local>>,
|
||||
) -> BitMatrix<CoroutineSavedLocal, CoroutineSavedLocal> {
|
||||
assert_eq!(body.local_decls.len(), saved_locals.domain_size());
|
||||
|
||||
@@ -848,7 +863,7 @@ fn compute_storage_conflicts<'mir, 'tcx>(
|
||||
eligible_storage_live: DenseBitSet::new_empty(body.local_decls.len()),
|
||||
};
|
||||
|
||||
requires_storage.visit_reachable_with(body, &mut visitor);
|
||||
visit_reachable_results(body, analysis, results, &mut visitor);
|
||||
|
||||
let local_conflicts = visitor.local_conflicts;
|
||||
|
||||
@@ -891,7 +906,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, MaybeRequiresStorage<'a, 'tcx>>
|
||||
{
|
||||
fn visit_after_early_statement_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>,
|
||||
_analysis: &mut MaybeRequiresStorage<'a, 'tcx>,
|
||||
state: &DenseBitSet<Local>,
|
||||
_statement: &Statement<'tcx>,
|
||||
loc: Location,
|
||||
@@ -901,7 +916,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, MaybeRequiresStorage<'a, 'tcx>>
|
||||
|
||||
fn visit_after_early_terminator_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>,
|
||||
_analysis: &mut MaybeRequiresStorage<'a, 'tcx>,
|
||||
state: &DenseBitSet<Local>,
|
||||
_terminator: &Terminator<'tcx>,
|
||||
loc: Location,
|
||||
|
||||
@@ -23,7 +23,7 @@ use rustc_mir_dataflow::lattice::{FlatSet, HasBottom};
|
||||
use rustc_mir_dataflow::value_analysis::{
|
||||
Map, PlaceIndex, State, TrackElem, ValueOrPlace, debug_with_context,
|
||||
};
|
||||
use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor};
|
||||
use rustc_mir_dataflow::{Analysis, ResultsVisitor, visit_reachable_results};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use tracing::{debug, debug_span, instrument};
|
||||
|
||||
@@ -61,13 +61,14 @@ impl<'tcx> crate::MirPass<'tcx> for DataflowConstProp {
|
||||
let map = Map::new(tcx, body, place_limit);
|
||||
|
||||
// Perform the actual dataflow analysis.
|
||||
let analysis = ConstAnalysis::new(tcx, body, map);
|
||||
let mut results =
|
||||
debug_span!("analyze").in_scope(|| analysis.iterate_to_fixpoint(tcx, body, None));
|
||||
let mut const_ = debug_span!("analyze")
|
||||
.in_scope(|| ConstAnalysis::new(tcx, body, map).iterate_to_fixpoint(tcx, body, None));
|
||||
|
||||
// Collect results and patch the body afterwards.
|
||||
let mut visitor = Collector::new(tcx, &body.local_decls);
|
||||
debug_span!("collect").in_scope(|| results.visit_reachable_with(body, &mut visitor));
|
||||
debug_span!("collect").in_scope(|| {
|
||||
visit_reachable_results(body, &mut const_.analysis, &const_.results, &mut visitor)
|
||||
});
|
||||
let mut patch = visitor.patch;
|
||||
debug_span!("patch").in_scope(|| patch.visit_body_preserves_cfg(body));
|
||||
}
|
||||
@@ -959,10 +960,10 @@ fn try_write_constant<'tcx>(
|
||||
}
|
||||
|
||||
impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx> {
|
||||
#[instrument(level = "trace", skip(self, results, statement))]
|
||||
#[instrument(level = "trace", skip(self, analysis, statement))]
|
||||
fn visit_after_early_statement_effect(
|
||||
&mut self,
|
||||
results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>,
|
||||
analysis: &mut ConstAnalysis<'_, 'tcx>,
|
||||
state: &State<FlatSet<Scalar>>,
|
||||
statement: &Statement<'tcx>,
|
||||
location: Location,
|
||||
@@ -972,8 +973,8 @@ impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx>
|
||||
OperandCollector {
|
||||
state,
|
||||
visitor: self,
|
||||
ecx: &mut results.analysis.ecx,
|
||||
map: &results.analysis.map,
|
||||
ecx: &mut analysis.ecx,
|
||||
map: &analysis.map,
|
||||
}
|
||||
.visit_rvalue(rvalue, location);
|
||||
}
|
||||
@@ -981,10 +982,10 @@ impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx>
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self, results, statement))]
|
||||
#[instrument(level = "trace", skip(self, analysis, statement))]
|
||||
fn visit_after_primary_statement_effect(
|
||||
&mut self,
|
||||
results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>,
|
||||
analysis: &mut ConstAnalysis<'_, 'tcx>,
|
||||
state: &State<FlatSet<Scalar>>,
|
||||
statement: &Statement<'tcx>,
|
||||
location: Location,
|
||||
@@ -994,12 +995,9 @@ impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx>
|
||||
// Don't overwrite the assignment if it already uses a constant (to keep the span).
|
||||
}
|
||||
StatementKind::Assign(box (place, _)) => {
|
||||
if let Some(value) = self.try_make_constant(
|
||||
&mut results.analysis.ecx,
|
||||
place,
|
||||
state,
|
||||
&results.analysis.map,
|
||||
) {
|
||||
if let Some(value) =
|
||||
self.try_make_constant(&mut analysis.ecx, place, state, &analysis.map)
|
||||
{
|
||||
self.patch.assignments.insert(location, value);
|
||||
}
|
||||
}
|
||||
@@ -1009,18 +1007,13 @@ impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx>
|
||||
|
||||
fn visit_after_early_terminator_effect(
|
||||
&mut self,
|
||||
results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>,
|
||||
analysis: &mut ConstAnalysis<'_, 'tcx>,
|
||||
state: &State<FlatSet<Scalar>>,
|
||||
terminator: &Terminator<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
OperandCollector {
|
||||
state,
|
||||
visitor: self,
|
||||
ecx: &mut results.analysis.ecx,
|
||||
map: &results.analysis.map,
|
||||
}
|
||||
.visit_terminator(terminator, location);
|
||||
OperandCollector { state, visitor: self, ecx: &mut analysis.ecx, map: &analysis.map }
|
||||
.visit_terminator(terminator, location);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
|
||||
|
||||
let live = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, Some("MaybeLiveLocals-DestProp"));
|
||||
let points = DenseLocationMap::new(body);
|
||||
let mut live = save_as_intervals(&points, body, live);
|
||||
let mut live = save_as_intervals(&points, body, live.analysis, live.results);
|
||||
|
||||
// In order to avoid having to collect data for every single pair of locals in the body, we
|
||||
// do not allow doing more than one merge for places that are derived from the same local at
|
||||
|
||||
@@ -235,8 +235,9 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
|
||||
// When we encounter a DROP of some place P we only care
|
||||
// about the drop if `P` may be initialized.
|
||||
let move_data = MoveData::gather_moves(body, tcx, |_| true);
|
||||
let maybe_init = MaybeInitializedPlaces::new(tcx, body, &move_data);
|
||||
let mut maybe_init = maybe_init.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body);
|
||||
let mut maybe_init = MaybeInitializedPlaces::new(tcx, body, &move_data)
|
||||
.iterate_to_fixpoint(tcx, body, None)
|
||||
.into_results_cursor(body);
|
||||
let mut block_drop_value_info =
|
||||
IndexVec::from_elem_n(MovePathIndexAtBlock::Unknown, body.basic_blocks.len());
|
||||
for (&block, candidates) in &bid_per_block {
|
||||
|
||||
@@ -148,8 +148,7 @@ whereas this code uses [`ResultsCursor`]:
|
||||
|
||||
```rust,ignore
|
||||
let mut results = MyAnalysis::new()
|
||||
.into_engine(tcx, body, def_id)
|
||||
.iterate_to_fixpoint()
|
||||
.iterate_to_fixpoint(tcx, body, None);
|
||||
.into_results_cursor(body);
|
||||
|
||||
// Inspect the fixpoint state immediately before each `Drop` terminator.
|
||||
|
||||
Reference in New Issue
Block a user