Skip to content

Commit

Permalink
Unrolled build for rust-lang#132338
Browse files Browse the repository at this point in the history
Rollup merge of rust-lang#132338 - nnethercote:rm-Engine, r=nnethercote

Remove `Engine`

It's just unnecessary plumbing. Removing it results in less code, and simpler code.

r? ``@cjgillot``
  • Loading branch information
rust-timer authored Oct 30, 2024
2 parents 16422db + d78e7bb commit 217c058
Show file tree
Hide file tree
Showing 15 changed files with 139 additions and 205 deletions.
31 changes: 16 additions & 15 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,7 @@ fn do_mir_borrowck<'tcx>(
.map(|(idx, body)| (idx, MoveData::gather_moves(body, tcx, |_| true)));

let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
.into_engine(tcx, body)
.pass_name("borrowck")
.iterate_to_fixpoint()
.iterate_to_fixpoint(tcx, body, Some("borrowck"))
.into_results_cursor(body);

let locals_are_invalidated_at_exit = tcx.hir().body_owner_kind(def).is_fn_or_closure();
Expand Down Expand Up @@ -243,18 +241,21 @@ fn do_mir_borrowck<'tcx>(
// usage significantly on some benchmarks.
drop(flow_inits);

let flow_borrows = Borrows::new(tcx, body, &regioncx, &borrow_set)
.into_engine(tcx, body)
.pass_name("borrowck")
.iterate_to_fixpoint();
let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data)
.into_engine(tcx, body)
.pass_name("borrowck")
.iterate_to_fixpoint();
let flow_ever_inits = EverInitializedPlaces::new(body, &move_data)
.into_engine(tcx, body)
.pass_name("borrowck")
.iterate_to_fixpoint();
let flow_borrows = Borrows::new(tcx, body, &regioncx, &borrow_set).iterate_to_fixpoint(
tcx,
body,
Some("borrowck"),
);
let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data).iterate_to_fixpoint(
tcx,
body,
Some("borrowck"),
);
let flow_ever_inits = EverInitializedPlaces::new(body, &move_data).iterate_to_fixpoint(
tcx,
body,
Some("borrowck"),
);

let movable_coroutine =
// The first argument is the coroutine type passed by value
Expand Down
12 changes: 4 additions & 8 deletions compiler/rustc_const_eval/src/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
let ConstCx { tcx, body, .. } = *ccx;

FlowSensitiveAnalysis::new(NeedsDrop, ccx)
.into_engine(tcx, body)
.iterate_to_fixpoint()
.iterate_to_fixpoint(tcx, body, None)
.into_results_cursor(body)
});

Expand Down Expand Up @@ -94,8 +93,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
let ConstCx { tcx, body, .. } = *ccx;

FlowSensitiveAnalysis::new(NeedsNonConstDrop, ccx)
.into_engine(tcx, body)
.iterate_to_fixpoint()
.iterate_to_fixpoint(tcx, body, None)
.into_results_cursor(body)
});

Expand Down Expand Up @@ -124,8 +122,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
let ConstCx { tcx, body, .. } = *ccx;

FlowSensitiveAnalysis::new(HasMutInterior, ccx)
.into_engine(tcx, body)
.iterate_to_fixpoint()
.iterate_to_fixpoint(tcx, body, None)
.into_results_cursor(body)
});

Expand Down Expand Up @@ -240,8 +237,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
let always_live_locals = &always_storage_live_locals(&ccx.body);
let mut maybe_storage_live =
MaybeStorageLive::new(Cow::Borrowed(always_live_locals))
.into_engine(ccx.tcx, &ccx.body)
.iterate_to_fixpoint()
.iterate_to_fixpoint(ccx.tcx, &ccx.body, None)
.into_results_cursor(&ccx.body);

// And then check all `Return` in the MIR, and if a local is "maybe live" at a
Expand Down
109 changes: 90 additions & 19 deletions compiler/rustc_mir_dataflow/src/framework/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,17 @@
//!
//! The `impls` module contains several examples of dataflow analyses.
//!
//! Create an `Engine` for your analysis using the `into_engine` method on the `Analysis` trait,
//! then call `iterate_to_fixpoint`. From there, you can use a `ResultsCursor` to inspect the
//! fixpoint solution to your dataflow problem, or implement the `ResultsVisitor` interface and use
//! `visit_results`. The following example uses the `ResultsCursor` approach.
//! Then call `iterate_to_fixpoint` on your type that impls `Analysis` to get a `Results`. From
//! there, you can use a `ResultsCursor` to inspect the fixpoint solution to your dataflow problem,
//! or implement the `ResultsVisitor` interface and use `visit_results`. The following example uses
//! the `ResultsCursor` approach.
//!
//! ```ignore (cross-crate-imports)
//! use rustc_const_eval::dataflow::Analysis; // Makes `into_engine` available.
//! use rustc_const_eval::dataflow::Analysis; // Makes `iterate_to_fixpoint` available.
//!
//! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>) {
//! let analysis = MyAnalysis::new()
//! .into_engine(tcx, body)
//! .iterate_to_fixpoint()
//! .iterate_to_fixpoint(tcx, body, None)
//! .into_results_cursor(body);
//!
//! // Print the dataflow state *after* each statement in the start block.
Expand All @@ -34,23 +33,29 @@
use std::cmp::Ordering;

use rustc_index::Idx;
use rustc_data_structures::work_queue::WorkQueue;
use rustc_index::bit_set::{BitSet, ChunkedBitSet, HybridBitSet};
use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges};
use rustc_index::{Idx, IndexVec};
use rustc_middle::bug;
use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges, traversal};
use rustc_middle::ty::TyCtxt;
use tracing::error;

use self::results::write_graphviz_results;
use super::fmt::DebugWithContext;

mod cursor;
mod direction;
mod engine;
pub mod fmt;
pub mod graphviz;
pub mod lattice;
mod results;
mod visitor;

pub use self::cursor::ResultsCursor;
pub use self::direction::{Backward, Direction, Forward};
pub use self::engine::{Engine, Results};
pub use self::lattice::{JoinSemiLattice, MaybeReachable};
pub use self::results::Results;
pub use self::visitor::{ResultsVisitable, ResultsVisitor, visit_results};

/// Analysis domains are all bitsets of various kinds. This trait holds
Expand Down Expand Up @@ -223,26 +228,92 @@ pub trait Analysis<'tcx> {

/* Extension methods */

/// Creates an `Engine` to find the fixpoint for this dataflow problem.
/// Finds the fixpoint for this dataflow problem.
///
/// You shouldn't need to override this. Its purpose is to enable method chaining like so:
///
/// ```ignore (cross-crate-imports)
/// let results = MyAnalysis::new(tcx, body)
/// .into_engine(tcx, body, def_id)
/// .iterate_to_fixpoint()
/// .iterate_to_fixpoint(tcx, body, None)
/// .into_results_cursor(body);
/// ```
#[inline]
fn into_engine<'mir>(
self,
/// You can optionally add a `pass_name` to the graphviz output for this particular run of a
/// dataflow analysis. Some analyses are run multiple times in the compilation pipeline.
/// Without a `pass_name` to differentiates them, only the results for the latest run will be
/// saved.
fn iterate_to_fixpoint<'mir>(
mut self,
tcx: TyCtxt<'tcx>,
body: &'mir mir::Body<'tcx>,
) -> Engine<'mir, 'tcx, Self>
pass_name: Option<&'static str>,
) -> Results<'tcx, Self>
where
Self: Sized,
Self::Domain: DebugWithContext<Self>,
{
Engine::new(tcx, body, self)
let mut entry_sets =
IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len());
self.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);

if Self::Direction::IS_BACKWARD && entry_sets[mir::START_BLOCK] != self.bottom_value(body) {
bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
}

let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_none(body.basic_blocks.len());

if Self::Direction::IS_FORWARD {
for (bb, _) in traversal::reverse_postorder(body) {
dirty_queue.insert(bb);
}
} else {
// Reverse post-order on the reverse CFG may generate a better iteration order for
// backward dataflow analyses, but probably not enough to matter.
for (bb, _) in traversal::postorder(body) {
dirty_queue.insert(bb);
}
}

// `state` is not actually used between iterations;
// this is just an optimization to avoid reallocating
// every iteration.
let mut state = self.bottom_value(body);
while let Some(bb) = dirty_queue.pop() {
let bb_data = &body[bb];

// Set the state to the entry state of the block.
// This is equivalent to `state = entry_sets[bb].clone()`,
// but it saves an allocation, thus improving compile times.
state.clone_from(&entry_sets[bb]);

// Apply the block transfer function, using the cached one if it exists.
let edges = Self::Direction::apply_effects_in_block(&mut self, &mut state, bb, bb_data);

Self::Direction::join_state_into_successors_of(
&mut self,
body,
&mut state,
bb,
edges,
|target: BasicBlock, state: &Self::Domain| {
let set_changed = entry_sets[target].join(state);
if set_changed {
dirty_queue.insert(target);
}
},
);
}

let results = Results { analysis: self, entry_sets };

if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
let (res, results) = write_graphviz_results(tcx, body, results, pass_name);
if let Err(e) = res {
error!("Failed to write graphviz dataflow results: {}", e);
}
results
} else {
results
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
//! A solver for dataflow problems.
//! Dataflow analysis results.
use std::ffi::OsString;
use std::path::PathBuf;

use rustc_data_structures::work_queue::WorkQueue;
use rustc_hir::def_id::DefId;
use rustc_index::IndexVec;
use rustc_middle::bug;
use rustc_middle::mir::{self, BasicBlock, create_dump_file, dump_enabled, traversal};
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_span::symbol::{Symbol, sym};
use tracing::{debug, error};
use tracing::debug;
use {rustc_ast as ast, rustc_graphviz as dot};

use super::fmt::DebugWithContext;
use super::{
Analysis, Direction, JoinSemiLattice, ResultsCursor, ResultsVisitor, graphviz, visit_results,
};
use super::{Analysis, ResultsCursor, ResultsVisitor, graphviz, visit_results};
use crate::errors::{
DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
};
Expand Down Expand Up @@ -65,124 +61,17 @@ where
body: &'mir mir::Body<'tcx>,
vis: &mut impl ResultsVisitor<'mir, 'tcx, Self, Domain = A::Domain>,
) {
let blocks = mir::traversal::reachable(body);
let blocks = traversal::reachable(body);
visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
}
}

/// A solver for dataflow problems.
pub struct Engine<'mir, 'tcx, A>
where
A: Analysis<'tcx>,
{
tcx: TyCtxt<'tcx>,
body: &'mir mir::Body<'tcx>,
entry_sets: IndexVec<BasicBlock, A::Domain>,
pass_name: Option<&'static str>,
analysis: A,
}

impl<'mir, 'tcx, A, D> Engine<'mir, 'tcx, A>
where
A: Analysis<'tcx, Domain = D>,
D: Clone + JoinSemiLattice,
{
/// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
/// function.
pub(crate) fn new(tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>, analysis: A) -> Self {
let mut entry_sets =
IndexVec::from_fn_n(|_| analysis.bottom_value(body), body.basic_blocks.len());
analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);

if A::Direction::IS_BACKWARD && entry_sets[mir::START_BLOCK] != analysis.bottom_value(body)
{
bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
}

Engine { analysis, tcx, body, pass_name: None, entry_sets }
}

/// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
///
/// Some analyses are run multiple times in the compilation pipeline. Give them a `pass_name`
/// to differentiate them. Otherwise, only the results for the latest run will be saved.
pub fn pass_name(mut self, name: &'static str) -> Self {
self.pass_name = Some(name);
self
}

/// Computes the fixpoint for this dataflow problem and returns it.
pub fn iterate_to_fixpoint(self) -> Results<'tcx, A>
where
A::Domain: DebugWithContext<A>,
{
let Engine { mut analysis, body, mut entry_sets, tcx, pass_name } = self;

let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_none(body.basic_blocks.len());

if A::Direction::IS_FORWARD {
for (bb, _) in traversal::reverse_postorder(body) {
dirty_queue.insert(bb);
}
} else {
// Reverse post-order on the reverse CFG may generate a better iteration order for
// backward dataflow analyses, but probably not enough to matter.
for (bb, _) in traversal::postorder(body) {
dirty_queue.insert(bb);
}
}

// `state` is not actually used between iterations;
// this is just an optimization to avoid reallocating
// every iteration.
let mut state = analysis.bottom_value(body);
while let Some(bb) = dirty_queue.pop() {
let bb_data = &body[bb];

// Set the state to the entry state of the block.
// This is equivalent to `state = entry_sets[bb].clone()`,
// but it saves an allocation, thus improving compile times.
state.clone_from(&entry_sets[bb]);

// Apply the block transfer function, using the cached one if it exists.
let edges =
A::Direction::apply_effects_in_block(&mut analysis, &mut state, bb, bb_data);

A::Direction::join_state_into_successors_of(
&mut analysis,
body,
&mut state,
bb,
edges,
|target: BasicBlock, state: &A::Domain| {
let set_changed = entry_sets[target].join(state);
if set_changed {
dirty_queue.insert(target);
}
},
);
}

let results = Results { analysis, entry_sets };

if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
let (res, results) = write_graphviz_results(tcx, body, results, pass_name);
if let Err(e) = res {
error!("Failed to write graphviz dataflow results: {}", e);
}
results
} else {
results
}
}
}

// Graphviz

/// 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`. The `Result` in and the `Results` out are
/// the same.
fn write_graphviz_results<'tcx, A>(
pub(super) fn write_graphviz_results<'tcx, A>(
tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
results: Results<'tcx, A>,
Expand Down
Loading

0 comments on commit 217c058

Please sign in to comment.