Auto merge of #118230 - nnethercote:streamline-dataflow-cursors, r=cjgillot
Streamline MIR dataflow cursors `rustc_mir_dataflow` has two kinds of results (`Results` and `ResultsCloned`) and three kinds of results cursor (`ResultsCursor`, `ResultsClonedCursor`, `ResultsRefCursor`). I found this quite confusing. This PR removes `ResultsCloned`, `ResultsClonedCursor`, and `ResultsRefCursor`, leaving just `Results` and `ResultsCursor`. This makes the relevant code shorter and easier to read, and there is no performance penalty. r? `@cjgillot`
This commit is contained in:
commit
317d14a56c
@ -1,60 +1,14 @@
|
|||||||
//! Random access inspection of the results of a dataflow analysis.
|
//! Random access inspection of the results of a dataflow analysis.
|
||||||
|
|
||||||
use crate::{framework::BitSetExt, CloneAnalysis};
|
use crate::framework::BitSetExt;
|
||||||
|
|
||||||
use std::borrow::{Borrow, BorrowMut};
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
use rustc_index::bit_set::BitSet;
|
use rustc_index::bit_set::BitSet;
|
||||||
use rustc_middle::mir::{self, BasicBlock, Location};
|
use rustc_middle::mir::{self, BasicBlock, Location};
|
||||||
|
|
||||||
use super::{Analysis, Direction, Effect, EffectIndex, EntrySets, Results, ResultsCloned};
|
use super::{Analysis, Direction, Effect, EffectIndex, Results};
|
||||||
|
|
||||||
// `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<'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.
|
/// Allows random access inspection of the results of a dataflow analysis.
|
||||||
///
|
///
|
||||||
@ -62,15 +16,12 @@ pub type ResultsClonedCursor<'res, 'mir, 'tcx, A> =
|
|||||||
/// the same order as the `DIRECTION` of the analysis. In the worst case—when statements are
|
/// the same order as the `DIRECTION` of the analysis. In the worst case—when statements are
|
||||||
/// visited in *reverse* order—performance will be quadratic in the number of statements in the
|
/// visited in *reverse* order—performance will be quadratic in the number of statements in the
|
||||||
/// block. The order in which basic blocks are inspected has no impact on performance.
|
/// block. The order in which basic blocks are inspected has no impact on performance.
|
||||||
///
|
pub struct ResultsCursor<'mir, 'tcx, A>
|
||||||
/// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The
|
|
||||||
/// type of ownership is determined by `R` (see `ResultsRefCursor` above).
|
|
||||||
pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>>
|
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
{
|
{
|
||||||
body: &'mir mir::Body<'tcx>,
|
body: &'mir mir::Body<'tcx>,
|
||||||
results: R,
|
results: Results<'tcx, A>,
|
||||||
state: A::Domain,
|
state: A::Domain,
|
||||||
|
|
||||||
pos: CursorPosition,
|
pos: CursorPosition,
|
||||||
@ -84,7 +35,7 @@ where
|
|||||||
reachable_blocks: BitSet<BasicBlock>,
|
reachable_blocks: BitSet<BasicBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
|
impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A>
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
{
|
{
|
||||||
@ -99,30 +50,13 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Unwraps this cursor, returning the underlying `Results`.
|
/// Unwraps this cursor, returning the underlying `Results`.
|
||||||
pub fn into_results(self) -> R {
|
pub fn into_results(self) -> Results<'tcx, A> {
|
||||||
self.results
|
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`.
|
/// Returns a new cursor that can inspect `results`.
|
||||||
pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
|
pub fn new(body: &'mir mir::Body<'tcx>, results: Results<'tcx, A>) -> Self {
|
||||||
let bottom_value = results.borrow().analysis.bottom_value(body);
|
let bottom_value = results.analysis.bottom_value(body);
|
||||||
ResultsCursor {
|
ResultsCursor {
|
||||||
body,
|
body,
|
||||||
results,
|
results,
|
||||||
@ -147,23 +81,23 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the underlying `Results`.
|
/// Returns the underlying `Results`.
|
||||||
pub fn results(&self) -> &Results<'tcx, A, R::EntrySets> {
|
pub fn results(&self) -> &Results<'tcx, A> {
|
||||||
self.results.borrow()
|
&self.results
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the underlying `Results`.
|
/// Returns the underlying `Results`.
|
||||||
pub fn mut_results(&mut self) -> &mut Results<'tcx, A, R::EntrySets> {
|
pub fn mut_results(&mut self) -> &mut Results<'tcx, A> {
|
||||||
self.results.borrow_mut()
|
&mut self.results
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `Analysis` used to generate the underlying `Results`.
|
/// Returns the `Analysis` used to generate the underlying `Results`.
|
||||||
pub fn analysis(&self) -> &A {
|
pub fn analysis(&self) -> &A {
|
||||||
&self.results.borrow().analysis
|
&self.results.analysis
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `Analysis` used to generate the underlying `Results`.
|
/// Returns the `Analysis` used to generate the underlying `Results`.
|
||||||
pub fn mut_analysis(&mut self) -> &mut A {
|
pub fn mut_analysis(&mut self) -> &mut A {
|
||||||
&mut self.results.borrow_mut().analysis
|
&mut self.results.analysis
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets the cursor to hold the entry set for the given basic block.
|
/// Resets the cursor to hold the entry set for the given basic block.
|
||||||
@ -175,7 +109,7 @@ where
|
|||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
assert!(self.reachable_blocks.contains(block));
|
assert!(self.reachable_blocks.contains(block));
|
||||||
|
|
||||||
self.state.clone_from(self.results.borrow().entry_set_for_block(block));
|
self.state.clone_from(self.results.entry_set_for_block(block));
|
||||||
self.pos = CursorPosition::block_entry(block);
|
self.pos = CursorPosition::block_entry(block);
|
||||||
self.state_needs_reset = false;
|
self.state_needs_reset = false;
|
||||||
}
|
}
|
||||||
@ -264,11 +198,10 @@ where
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let analysis = &mut self.results.borrow_mut().analysis;
|
|
||||||
let target_effect_index = effect.at_index(target.statement_index);
|
let target_effect_index = effect.at_index(target.statement_index);
|
||||||
|
|
||||||
A::Direction::apply_effects_in_range(
|
A::Direction::apply_effects_in_range(
|
||||||
analysis,
|
&mut self.results.analysis,
|
||||||
&mut self.state,
|
&mut self.state,
|
||||||
target.block,
|
target.block,
|
||||||
block_data,
|
block_data,
|
||||||
@ -284,12 +217,12 @@ where
|
|||||||
/// This can be used, e.g., to apply the call return effect directly to the cursor without
|
/// 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.
|
/// creating an extra copy of the dataflow state.
|
||||||
pub fn apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain)) {
|
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);
|
f(&mut self.results.analysis, &mut self.state);
|
||||||
self.state_needs_reset = true;
|
self.state_needs_reset = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
|
impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A>
|
||||||
where
|
where
|
||||||
A: crate::GenKillAnalysis<'tcx>,
|
A: crate::GenKillAnalysis<'tcx>,
|
||||||
A::Domain: BitSetExt<A::Idx>,
|
A::Domain: BitSetExt<A::Idx>,
|
||||||
|
@ -5,9 +5,7 @@ use crate::errors::{
|
|||||||
};
|
};
|
||||||
use crate::framework::BitSetExt;
|
use crate::framework::BitSetExt;
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
@ -24,42 +22,37 @@ use rustc_span::symbol::{sym, Symbol};
|
|||||||
use super::fmt::DebugWithContext;
|
use super::fmt::DebugWithContext;
|
||||||
use super::graphviz;
|
use super::graphviz;
|
||||||
use super::{
|
use super::{
|
||||||
visit_results, Analysis, AnalysisDomain, CloneAnalysis, Direction, GenKill, GenKillAnalysis,
|
visit_results, Analysis, AnalysisDomain, Direction, GenKill, GenKillAnalysis, GenKillSet,
|
||||||
GenKillSet, JoinSemiLattice, ResultsClonedCursor, ResultsCursor, ResultsRefCursor,
|
JoinSemiLattice, ResultsCursor, ResultsVisitor,
|
||||||
ResultsVisitor,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as AnalysisDomain<'tcx>>::Domain>;
|
pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as AnalysisDomain<'tcx>>::Domain>;
|
||||||
|
|
||||||
/// A dataflow analysis that has converged to fixpoint.
|
/// A dataflow analysis that has converged to fixpoint.
|
||||||
pub struct Results<'tcx, A, E = EntrySets<'tcx, A>>
|
#[derive(Clone)]
|
||||||
|
pub struct Results<'tcx, A>
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
{
|
{
|
||||||
pub analysis: A,
|
pub analysis: A,
|
||||||
pub(super) entry_sets: E,
|
pub(super) entry_sets: EntrySets<'tcx, A>,
|
||||||
pub(super) _marker: PhantomData<&'tcx ()>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Results` type with a cloned `Analysis` and borrowed entry sets.
|
impl<'tcx, A> Results<'tcx, A>
|
||||||
pub type ResultsCloned<'res, 'tcx, A> = Results<'tcx, A, &'res EntrySets<'tcx, A>>;
|
|
||||||
|
|
||||||
impl<'tcx, A, E> Results<'tcx, A, E>
|
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
E: Borrow<EntrySets<'tcx, A>>,
|
|
||||||
{
|
{
|
||||||
/// Creates a `ResultsCursor` that can inspect these `Results`.
|
/// Creates a `ResultsCursor` that can inspect these `Results`.
|
||||||
pub fn into_results_cursor<'mir>(
|
pub fn into_results_cursor<'mir>(
|
||||||
self,
|
self,
|
||||||
body: &'mir mir::Body<'tcx>,
|
body: &'mir mir::Body<'tcx>,
|
||||||
) -> ResultsCursor<'mir, 'tcx, A, Self> {
|
) -> ResultsCursor<'mir, 'tcx, A> {
|
||||||
ResultsCursor::new(body, self)
|
ResultsCursor::new(body, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the dataflow state for the given block.
|
/// Gets the dataflow state for the given block.
|
||||||
pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
|
pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
|
||||||
&self.entry_sets.borrow()[block]
|
&self.entry_sets[block]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn visit_with<'mir>(
|
pub fn visit_with<'mir>(
|
||||||
@ -80,52 +73,6 @@ where
|
|||||||
visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
|
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.
|
/// A solver for dataflow problems.
|
||||||
pub struct Engine<'mir, 'tcx, A>
|
pub struct Engine<'mir, 'tcx, A>
|
||||||
@ -291,29 +238,31 @@ where
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut results = Results { analysis, entry_sets, _marker: PhantomData };
|
let results = Results { analysis, entry_sets };
|
||||||
|
|
||||||
if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
|
if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
|
||||||
let res = write_graphviz_results(tcx, body, &mut results, pass_name);
|
let (res, results) = write_graphviz_results(tcx, body, results, pass_name);
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
error!("Failed to write graphviz dataflow results: {}", e);
|
error!("Failed to write graphviz dataflow results: {}", e);
|
||||||
}
|
}
|
||||||
|
results
|
||||||
|
} else {
|
||||||
|
results
|
||||||
}
|
}
|
||||||
|
|
||||||
results
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Graphviz
|
// Graphviz
|
||||||
|
|
||||||
/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
|
/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
|
||||||
/// `rustc_mir` attributes and `-Z dump-mir-dataflow`.
|
/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are
|
||||||
|
/// the same.
|
||||||
fn write_graphviz_results<'tcx, A>(
|
fn write_graphviz_results<'tcx, A>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
body: &mir::Body<'tcx>,
|
body: &mir::Body<'tcx>,
|
||||||
results: &mut Results<'tcx, A>,
|
results: Results<'tcx, A>,
|
||||||
pass_name: Option<&'static str>,
|
pass_name: Option<&'static str>,
|
||||||
) -> std::io::Result<()>
|
) -> (std::io::Result<()>, Results<'tcx, A>)
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
A::Domain: DebugWithContext<A>,
|
A::Domain: DebugWithContext<A>,
|
||||||
@ -324,23 +273,30 @@ where
|
|||||||
let def_id = body.source.def_id();
|
let def_id = body.source.def_id();
|
||||||
let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else {
|
let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else {
|
||||||
// Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
|
// Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
|
||||||
return Ok(());
|
return (Ok(()), results);
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut file = match attrs.output_path(A::NAME) {
|
let file = try {
|
||||||
Some(path) => {
|
match attrs.output_path(A::NAME) {
|
||||||
debug!("printing dataflow results for {:?} to {}", def_id, path.display());
|
Some(path) => {
|
||||||
if let Some(parent) = path.parent() {
|
debug!("printing dataflow results for {:?} to {}", def_id, path.display());
|
||||||
fs::create_dir_all(parent)?;
|
if let Some(parent) = path.parent() {
|
||||||
|
fs::create_dir_all(parent)?;
|
||||||
|
}
|
||||||
|
let f = fs::File::create(&path)?;
|
||||||
|
io::BufWriter::new(f)
|
||||||
}
|
}
|
||||||
io::BufWriter::new(fs::File::create(&path)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
None if dump_enabled(tcx, A::NAME, def_id) => {
|
None if dump_enabled(tcx, A::NAME, def_id) => {
|
||||||
create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
|
create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => return Ok(()),
|
_ => return (Ok(()), results),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut file = match file {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(e) => return (Err(e), results),
|
||||||
};
|
};
|
||||||
|
|
||||||
let style = match attrs.formatter {
|
let style = match attrs.formatter {
|
||||||
@ -356,11 +312,14 @@ where
|
|||||||
if tcx.sess.opts.unstable_opts.graphviz_dark_mode {
|
if tcx.sess.opts.unstable_opts.graphviz_dark_mode {
|
||||||
render_opts.push(dot::RenderOption::DarkTheme);
|
render_opts.push(dot::RenderOption::DarkTheme);
|
||||||
}
|
}
|
||||||
with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts)?);
|
let r = with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts));
|
||||||
|
|
||||||
file.write_all(&buf)?;
|
let lhs = try {
|
||||||
|
r?;
|
||||||
|
file.write_all(&buf)?;
|
||||||
|
};
|
||||||
|
|
||||||
Ok(())
|
(lhs, graphviz.into_results())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -12,7 +12,7 @@ use rustc_middle::mir::graphviz_safe_def_name;
|
|||||||
use rustc_middle::mir::{self, BasicBlock, Body, Location};
|
use rustc_middle::mir::{self, BasicBlock, Body, Location};
|
||||||
|
|
||||||
use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext};
|
use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext};
|
||||||
use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsRefCursor, ResultsVisitor};
|
use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub(crate) enum OutputStyle {
|
pub(crate) enum OutputStyle {
|
||||||
@ -29,27 +29,31 @@ impl OutputStyle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Formatter<'res, 'mir, 'tcx, A>
|
pub(crate) struct Formatter<'mir, 'tcx, A>
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
{
|
{
|
||||||
body: &'mir Body<'tcx>,
|
body: &'mir Body<'tcx>,
|
||||||
results: RefCell<&'res mut Results<'tcx, A>>,
|
results: RefCell<Option<Results<'tcx, A>>>,
|
||||||
style: OutputStyle,
|
style: OutputStyle,
|
||||||
reachable: BitSet<BasicBlock>,
|
reachable: BitSet<BasicBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'res, 'mir, 'tcx, A> Formatter<'res, 'mir, 'tcx, A>
|
impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A>
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
{
|
{
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
body: &'mir Body<'tcx>,
|
body: &'mir Body<'tcx>,
|
||||||
results: &'res mut Results<'tcx, A>,
|
results: Results<'tcx, A>,
|
||||||
style: OutputStyle,
|
style: OutputStyle,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let reachable = mir::traversal::reachable_as_bitset(body);
|
let reachable = mir::traversal::reachable_as_bitset(body);
|
||||||
Formatter { body, results: results.into(), style, reachable }
|
Formatter { body, results: Some(results).into(), style, reachable }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn into_results(self) -> Results<'tcx, A> {
|
||||||
|
self.results.into_inner().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +73,7 @@ fn dataflow_successors(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, '_, 'tcx, A>
|
impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, 'tcx, A>
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
A::Domain: DebugWithContext<A>,
|
A::Domain: DebugWithContext<A>,
|
||||||
@ -88,14 +92,19 @@ where
|
|||||||
|
|
||||||
fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
|
fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
|
||||||
let mut label = Vec::new();
|
let mut label = Vec::new();
|
||||||
let mut results = self.results.borrow_mut();
|
self.results.replace_with(|results| {
|
||||||
let mut fmt = BlockFormatter {
|
// `Formatter::result` is a `RefCell<Option<_>>` so we can replace
|
||||||
results: results.as_results_cursor(self.body),
|
// the value with `None`, move it into the results cursor, move it
|
||||||
style: self.style,
|
// back out, and return it to the refcell wrapped in `Some`.
|
||||||
bg: Background::Light,
|
let mut fmt = BlockFormatter {
|
||||||
};
|
results: results.take().unwrap().into_results_cursor(self.body),
|
||||||
|
style: self.style,
|
||||||
|
bg: Background::Light,
|
||||||
|
};
|
||||||
|
|
||||||
fmt.write_node_label(&mut label, *block).unwrap();
|
fmt.write_node_label(&mut label, *block).unwrap();
|
||||||
|
Some(fmt.results.into_results())
|
||||||
|
});
|
||||||
dot::LabelText::html(String::from_utf8(label).unwrap())
|
dot::LabelText::html(String::from_utf8(label).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +118,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'mir, 'tcx, A> dot::GraphWalk<'mir> for Formatter<'_, 'mir, 'tcx, A>
|
impl<'mir, 'tcx, A> dot::GraphWalk<'mir> for Formatter<'mir, 'tcx, A>
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
{
|
{
|
||||||
@ -143,16 +152,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BlockFormatter<'res, 'mir, 'tcx, A>
|
struct BlockFormatter<'mir, 'tcx, A>
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
{
|
{
|
||||||
results: ResultsRefCursor<'res, 'mir, 'tcx, A>,
|
results: ResultsCursor<'mir, 'tcx, A>,
|
||||||
bg: Background,
|
bg: Background,
|
||||||
style: OutputStyle,
|
style: OutputStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'res, 'mir, 'tcx, A> BlockFormatter<'res, 'mir, 'tcx, A>
|
impl<'mir, 'tcx, A> BlockFormatter<'mir, 'tcx, A>
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
A::Domain: DebugWithContext<A>,
|
A::Domain: DebugWithContext<A>,
|
||||||
|
@ -45,9 +45,9 @@ pub mod graphviz;
|
|||||||
pub mod lattice;
|
pub mod lattice;
|
||||||
mod visitor;
|
mod visitor;
|
||||||
|
|
||||||
pub use self::cursor::{ResultsClonedCursor, ResultsCursor, ResultsRefCursor};
|
pub use self::cursor::ResultsCursor;
|
||||||
pub use self::direction::{Backward, Direction, Forward};
|
pub use self::direction::{Backward, Direction, Forward};
|
||||||
pub use self::engine::{Engine, EntrySets, Results, ResultsCloned};
|
pub use self::engine::{Engine, Results};
|
||||||
pub use self::lattice::{JoinSemiLattice, MaybeReachable};
|
pub use self::lattice::{JoinSemiLattice, MaybeReachable};
|
||||||
pub use self::visitor::{visit_results, ResultsVisitable, ResultsVisitor};
|
pub use self::visitor::{visit_results, ResultsVisitable, ResultsVisitor};
|
||||||
|
|
||||||
@ -246,21 +246,6 @@ 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.
|
/// A gen/kill dataflow problem.
|
||||||
///
|
///
|
||||||
/// Each method in this trait has a corresponding one in `Analysis`. However, these methods only
|
/// Each method in this trait has a corresponding one in `Analysis`. However, these methods only
|
||||||
|
@ -267,8 +267,7 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) {
|
|||||||
let body = analysis.body;
|
let body = analysis.body;
|
||||||
|
|
||||||
let mut cursor =
|
let mut cursor =
|
||||||
Results { entry_sets: analysis.mock_entry_sets(), analysis, _marker: PhantomData }
|
Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body);
|
||||||
.into_results_cursor(body);
|
|
||||||
|
|
||||||
cursor.allow_unreachable();
|
cursor.allow_unreachable();
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use std::borrow::Borrow;
|
|
||||||
|
|
||||||
use rustc_middle::mir::{self, BasicBlock, Location};
|
use rustc_middle::mir::{self, BasicBlock, Location};
|
||||||
|
|
||||||
use super::{Analysis, Direction, EntrySets, Results};
|
use super::{Analysis, Direction, Results};
|
||||||
|
|
||||||
/// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the
|
/// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the
|
||||||
/// dataflow state at that location.
|
/// dataflow state at that location.
|
||||||
@ -143,10 +141,9 @@ pub trait ResultsVisitable<'tcx> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx, A, E> ResultsVisitable<'tcx> for Results<'tcx, A, E>
|
impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A>
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
E: Borrow<EntrySets<'tcx, A>>,
|
|
||||||
{
|
{
|
||||||
type FlowState = A::Domain;
|
type FlowState = A::Domain;
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ use rustc_middle::mir::*;
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use super::MaybeBorrowedLocals;
|
use super::MaybeBorrowedLocals;
|
||||||
use crate::{GenKill, ResultsClonedCursor};
|
use crate::{GenKill, ResultsCursor};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MaybeStorageLive<'a> {
|
pub struct MaybeStorageLive<'a> {
|
||||||
@ -18,12 +18,6 @@ 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> {
|
impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> {
|
||||||
type Domain = BitSet<Local>;
|
type Domain = BitSet<Local>;
|
||||||
|
|
||||||
@ -158,28 +152,21 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type BorrowedLocalsResults<'res, 'mir, 'tcx> =
|
type BorrowedLocalsResults<'mir, 'tcx> = ResultsCursor<'mir, 'tcx, MaybeBorrowedLocals>;
|
||||||
ResultsClonedCursor<'res, 'mir, 'tcx, MaybeBorrowedLocals>;
|
|
||||||
|
|
||||||
/// Dataflow analysis that determines whether each local requires storage at a
|
/// Dataflow analysis that determines whether each local requires storage at a
|
||||||
/// given location; i.e. whether its storage can go away without being observed.
|
/// given location; i.e. whether its storage can go away without being observed.
|
||||||
pub struct MaybeRequiresStorage<'res, 'mir, 'tcx> {
|
pub struct MaybeRequiresStorage<'mir, 'tcx> {
|
||||||
borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>,
|
borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'res, 'mir, 'tcx> MaybeRequiresStorage<'res, 'mir, 'tcx> {
|
impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
|
||||||
pub fn new(borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>) -> Self {
|
pub fn new(borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>) -> Self {
|
||||||
MaybeRequiresStorage { borrowed_locals }
|
MaybeRequiresStorage { borrowed_locals }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::CloneAnalysis for MaybeRequiresStorage<'_, '_, '_> {
|
impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
|
||||||
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>;
|
type Domain = BitSet<Local>;
|
||||||
|
|
||||||
const NAME: &'static str = "requires_storage";
|
const NAME: &'static str = "requires_storage";
|
||||||
@ -198,7 +185,7 @@ impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
|
impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
|
||||||
type Idx = Local;
|
type Idx = Local;
|
||||||
|
|
||||||
fn domain_size(&self, body: &Body<'tcx>) -> usize {
|
fn domain_size(&self, body: &Body<'tcx>) -> usize {
|
||||||
@ -355,7 +342,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> MaybeRequiresStorage<'_, '_, 'tcx> {
|
impl<'tcx> MaybeRequiresStorage<'_, 'tcx> {
|
||||||
/// Kill locals that are fully moved and have not been borrowed.
|
/// Kill locals that are fully moved and have not been borrowed.
|
||||||
fn check_for_move(&mut self, trans: &mut impl GenKill<Local>, loc: Location) {
|
fn check_for_move(&mut self, trans: &mut impl GenKill<Local>, loc: Location) {
|
||||||
let body = self.borrowed_locals.body();
|
let body = self.borrowed_locals.body();
|
||||||
@ -364,12 +351,12 @@ impl<'tcx> MaybeRequiresStorage<'_, '_, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MoveVisitor<'a, 'res, 'mir, 'tcx, T> {
|
struct MoveVisitor<'a, 'mir, 'tcx, T> {
|
||||||
borrowed_locals: &'a mut BorrowedLocalsResults<'res, 'mir, 'tcx>,
|
borrowed_locals: &'a mut BorrowedLocalsResults<'mir, 'tcx>,
|
||||||
trans: &'a mut T,
|
trans: &'a mut T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx, T> Visitor<'tcx> for MoveVisitor<'_, '_, '_, 'tcx, T>
|
impl<'tcx, T> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx, T>
|
||||||
where
|
where
|
||||||
T: GenKill<Local>,
|
T: GenKill<Local>,
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![feature(min_specialization)]
|
#![feature(min_specialization)]
|
||||||
#![feature(stmt_expr_attributes)]
|
#![feature(stmt_expr_attributes)]
|
||||||
|
#![feature(try_blocks)]
|
||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
#![deny(rustc::untranslatable_diagnostic)]
|
#![deny(rustc::untranslatable_diagnostic)]
|
||||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||||
@ -23,7 +24,7 @@ pub use self::framework::{
|
|||||||
fmt, lattice, visit_results, Analysis, AnalysisDomain, Direction, GenKill, GenKillAnalysis,
|
fmt, lattice, visit_results, Analysis, AnalysisDomain, Direction, GenKill, GenKillAnalysis,
|
||||||
JoinSemiLattice, MaybeReachable, Results, ResultsCursor, ResultsVisitable, ResultsVisitor,
|
JoinSemiLattice, MaybeReachable, Results, ResultsCursor, ResultsVisitable, ResultsVisitor,
|
||||||
};
|
};
|
||||||
use self::framework::{Backward, CloneAnalysis, ResultsClonedCursor, SwitchIntEdgeEffects};
|
use self::framework::{Backward, SwitchIntEdgeEffects};
|
||||||
use self::move_paths::MoveData;
|
use self::move_paths::MoveData;
|
||||||
|
|
||||||
pub mod debuginfo;
|
pub mod debuginfo;
|
||||||
|
@ -679,15 +679,15 @@ fn locals_live_across_suspend_points<'tcx>(
|
|||||||
let borrowed_locals_results =
|
let borrowed_locals_results =
|
||||||
MaybeBorrowedLocals.into_engine(tcx, body).pass_name("coroutine").iterate_to_fixpoint();
|
MaybeBorrowedLocals.into_engine(tcx, body).pass_name("coroutine").iterate_to_fixpoint();
|
||||||
|
|
||||||
let mut borrowed_locals_cursor = borrowed_locals_results.cloned_results_cursor(body);
|
let mut borrowed_locals_cursor = borrowed_locals_results.clone().into_results_cursor(body);
|
||||||
|
|
||||||
// Calculate the MIR locals that we actually need to keep storage around
|
// Calculate the MIR locals that we actually need to keep storage around
|
||||||
// for.
|
// for.
|
||||||
let mut requires_storage_results =
|
let mut requires_storage_cursor =
|
||||||
MaybeRequiresStorage::new(borrowed_locals_results.cloned_results_cursor(body))
|
MaybeRequiresStorage::new(borrowed_locals_results.into_results_cursor(body))
|
||||||
.into_engine(tcx, body)
|
.into_engine(tcx, body)
|
||||||
.iterate_to_fixpoint();
|
.iterate_to_fixpoint()
|
||||||
let mut requires_storage_cursor = requires_storage_results.as_results_cursor(body);
|
.into_results_cursor(body);
|
||||||
|
|
||||||
// Calculate the liveness of MIR locals ignoring borrows.
|
// Calculate the liveness of MIR locals ignoring borrows.
|
||||||
let mut liveness = MaybeLiveLocals
|
let mut liveness = MaybeLiveLocals
|
||||||
@ -763,7 +763,7 @@ fn locals_live_across_suspend_points<'tcx>(
|
|||||||
body,
|
body,
|
||||||
&saved_locals,
|
&saved_locals,
|
||||||
always_live_locals.clone(),
|
always_live_locals.clone(),
|
||||||
requires_storage_results,
|
requires_storage_cursor.into_results(),
|
||||||
);
|
);
|
||||||
|
|
||||||
LivenessInfo {
|
LivenessInfo {
|
||||||
@ -828,7 +828,7 @@ fn compute_storage_conflicts<'mir, 'tcx>(
|
|||||||
body: &'mir Body<'tcx>,
|
body: &'mir Body<'tcx>,
|
||||||
saved_locals: &CoroutineSavedLocals,
|
saved_locals: &CoroutineSavedLocals,
|
||||||
always_live_locals: BitSet<Local>,
|
always_live_locals: BitSet<Local>,
|
||||||
mut requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'_, 'mir, 'tcx>>,
|
mut requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>,
|
||||||
) -> BitMatrix<CoroutineSavedLocal, CoroutineSavedLocal> {
|
) -> BitMatrix<CoroutineSavedLocal, CoroutineSavedLocal> {
|
||||||
assert_eq!(body.local_decls.len(), saved_locals.domain_size());
|
assert_eq!(body.local_decls.len(), saved_locals.domain_size());
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user