Skip to content

Separate dataflow analysis and results #140234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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, &regioncx);
let (mut flow_analysis, flow_entry_states) =
get_flow_results(tcx, body, &move_data, &borrow_set, &regioncx);
visit_results(
body,
traversal::reverse_postorder(body).map(|(bb, _)| bb),
&mut flow_results,
&mut flow_analysis,
&flow_entry_states,
&mut mbcx,
);

Expand Down Expand Up @@ -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(
Expand All @@ -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> {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -894,7 +896,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,
Expand Down
78 changes: 47 additions & 31 deletions compiler/rustc_mir_dataflow/src/framework/cursor.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand All @@ -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,
}
}
}
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -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.
Expand All @@ -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;
}
Expand Down Expand Up @@ -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,
Expand All @@ -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;
}
}
Expand Down
48 changes: 22 additions & 26 deletions compiler/rustc_mir_dataflow/src/framework/direction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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>;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down
Loading
Loading