mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 00:34:06 +00:00
Remove Engine::new_gen_kill
.
This is an alternative to `Engine::new_generic` for gen/kill analyses. It's supposed to be an optimization, but it has negligible effect. The commit merges `Engine::new_generic` into `Engine::new`. This allows the removal of various other things: `GenKillSet`, `gen_kill_statement_effects_in_block`, `is_cfg_cyclic`.
This commit is contained in:
parent
874b03ec28
commit
e0b83c34c3
@ -26,7 +26,6 @@ type SwitchSources = FxHashMap<(BasicBlock, BasicBlock), SmallVec<[Option<u128>;
|
|||||||
struct Cache {
|
struct Cache {
|
||||||
predecessors: OnceLock<Predecessors>,
|
predecessors: OnceLock<Predecessors>,
|
||||||
switch_sources: OnceLock<SwitchSources>,
|
switch_sources: OnceLock<SwitchSources>,
|
||||||
is_cyclic: OnceLock<bool>,
|
|
||||||
reverse_postorder: OnceLock<Vec<BasicBlock>>,
|
reverse_postorder: OnceLock<Vec<BasicBlock>>,
|
||||||
dominators: OnceLock<Dominators<BasicBlock>>,
|
dominators: OnceLock<Dominators<BasicBlock>>,
|
||||||
}
|
}
|
||||||
@ -37,12 +36,6 @@ impl<'tcx> BasicBlocks<'tcx> {
|
|||||||
BasicBlocks { basic_blocks, cache: Cache::default() }
|
BasicBlocks { basic_blocks, cache: Cache::default() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if control-flow graph contains a cycle reachable from the `START_BLOCK`.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_cfg_cyclic(&self) -> bool {
|
|
||||||
*self.cache.is_cyclic.get_or_init(|| graph::is_cyclic(self))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dominators(&self) -> &Dominators<BasicBlock> {
|
pub fn dominators(&self) -> &Dominators<BasicBlock> {
|
||||||
self.cache.dominators.get_or_init(|| dominators(self))
|
self.cache.dominators.get_or_init(|| dominators(self))
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use rustc_middle::mir::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::visitor::{ResultsVisitable, ResultsVisitor};
|
use super::visitor::{ResultsVisitable, ResultsVisitor};
|
||||||
use super::{Analysis, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget};
|
use super::{Analysis, Effect, EffectIndex, SwitchIntTarget};
|
||||||
|
|
||||||
pub trait Direction {
|
pub trait Direction {
|
||||||
const IS_FORWARD: bool;
|
const IS_FORWARD: bool;
|
||||||
@ -29,19 +29,10 @@ pub trait Direction {
|
|||||||
state: &mut A::Domain,
|
state: &mut A::Domain,
|
||||||
block: BasicBlock,
|
block: BasicBlock,
|
||||||
block_data: &'mir mir::BasicBlockData<'tcx>,
|
block_data: &'mir mir::BasicBlockData<'tcx>,
|
||||||
statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
|
|
||||||
) -> TerminatorEdges<'mir, 'tcx>
|
) -> TerminatorEdges<'mir, 'tcx>
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>;
|
A: Analysis<'tcx>;
|
||||||
|
|
||||||
fn gen_kill_statement_effects_in_block<'tcx, A>(
|
|
||||||
analysis: &mut A,
|
|
||||||
trans: &mut GenKillSet<A::Idx>,
|
|
||||||
block: BasicBlock,
|
|
||||||
block_data: &mir::BasicBlockData<'tcx>,
|
|
||||||
) where
|
|
||||||
A: GenKillAnalysis<'tcx>;
|
|
||||||
|
|
||||||
fn visit_results_in_block<'mir, 'tcx, D, R>(
|
fn visit_results_in_block<'mir, 'tcx, D, R>(
|
||||||
state: &mut D,
|
state: &mut D,
|
||||||
block: BasicBlock,
|
block: BasicBlock,
|
||||||
@ -73,7 +64,6 @@ impl Direction for Backward {
|
|||||||
state: &mut A::Domain,
|
state: &mut A::Domain,
|
||||||
block: BasicBlock,
|
block: BasicBlock,
|
||||||
block_data: &'mir mir::BasicBlockData<'tcx>,
|
block_data: &'mir mir::BasicBlockData<'tcx>,
|
||||||
statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
|
|
||||||
) -> TerminatorEdges<'mir, 'tcx>
|
) -> TerminatorEdges<'mir, 'tcx>
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
@ -82,31 +72,12 @@ impl Direction for Backward {
|
|||||||
let location = Location { block, statement_index: block_data.statements.len() };
|
let location = Location { block, statement_index: block_data.statements.len() };
|
||||||
analysis.apply_before_terminator_effect(state, terminator, location);
|
analysis.apply_before_terminator_effect(state, terminator, location);
|
||||||
let edges = analysis.apply_terminator_effect(state, terminator, location);
|
let edges = analysis.apply_terminator_effect(state, terminator, location);
|
||||||
if let Some(statement_effect) = statement_effect {
|
|
||||||
statement_effect(block, state)
|
|
||||||
} else {
|
|
||||||
for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
|
|
||||||
let location = Location { block, statement_index };
|
|
||||||
analysis.apply_before_statement_effect(state, statement, location);
|
|
||||||
analysis.apply_statement_effect(state, statement, location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
edges
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gen_kill_statement_effects_in_block<'tcx, A>(
|
|
||||||
analysis: &mut A,
|
|
||||||
trans: &mut GenKillSet<A::Idx>,
|
|
||||||
block: BasicBlock,
|
|
||||||
block_data: &mir::BasicBlockData<'tcx>,
|
|
||||||
) where
|
|
||||||
A: GenKillAnalysis<'tcx>,
|
|
||||||
{
|
|
||||||
for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
|
for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
|
||||||
let location = Location { block, statement_index };
|
let location = Location { block, statement_index };
|
||||||
analysis.before_statement_effect(trans, statement, location);
|
analysis.apply_before_statement_effect(state, statement, location);
|
||||||
analysis.statement_effect(trans, statement, location);
|
analysis.apply_statement_effect(state, statement, location);
|
||||||
}
|
}
|
||||||
|
edges
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_effects_in_range<'tcx, A>(
|
fn apply_effects_in_range<'tcx, A>(
|
||||||
@ -330,42 +301,21 @@ impl Direction for Forward {
|
|||||||
state: &mut A::Domain,
|
state: &mut A::Domain,
|
||||||
block: BasicBlock,
|
block: BasicBlock,
|
||||||
block_data: &'mir mir::BasicBlockData<'tcx>,
|
block_data: &'mir mir::BasicBlockData<'tcx>,
|
||||||
statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
|
|
||||||
) -> TerminatorEdges<'mir, 'tcx>
|
) -> TerminatorEdges<'mir, 'tcx>
|
||||||
where
|
where
|
||||||
A: Analysis<'tcx>,
|
A: Analysis<'tcx>,
|
||||||
{
|
{
|
||||||
if let Some(statement_effect) = statement_effect {
|
for (statement_index, statement) in block_data.statements.iter().enumerate() {
|
||||||
statement_effect(block, state)
|
let location = Location { block, statement_index };
|
||||||
} else {
|
analysis.apply_before_statement_effect(state, statement, location);
|
||||||
for (statement_index, statement) in block_data.statements.iter().enumerate() {
|
analysis.apply_statement_effect(state, statement, location);
|
||||||
let location = Location { block, statement_index };
|
|
||||||
analysis.apply_before_statement_effect(state, statement, location);
|
|
||||||
analysis.apply_statement_effect(state, statement, location);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let terminator = block_data.terminator();
|
let terminator = block_data.terminator();
|
||||||
let location = Location { block, statement_index: block_data.statements.len() };
|
let location = Location { block, statement_index: block_data.statements.len() };
|
||||||
analysis.apply_before_terminator_effect(state, terminator, location);
|
analysis.apply_before_terminator_effect(state, terminator, location);
|
||||||
analysis.apply_terminator_effect(state, terminator, location)
|
analysis.apply_terminator_effect(state, terminator, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_kill_statement_effects_in_block<'tcx, A>(
|
|
||||||
analysis: &mut A,
|
|
||||||
trans: &mut GenKillSet<A::Idx>,
|
|
||||||
block: BasicBlock,
|
|
||||||
block_data: &mir::BasicBlockData<'tcx>,
|
|
||||||
) where
|
|
||||||
A: GenKillAnalysis<'tcx>,
|
|
||||||
{
|
|
||||||
for (statement_index, statement) in block_data.statements.iter().enumerate() {
|
|
||||||
let location = Location { block, statement_index };
|
|
||||||
analysis.before_statement_effect(trans, statement, location);
|
|
||||||
analysis.statement_effect(trans, statement, location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_effects_in_range<'tcx, A>(
|
fn apply_effects_in_range<'tcx, A>(
|
||||||
analysis: &mut A,
|
analysis: &mut A,
|
||||||
state: &mut A::Domain,
|
state: &mut A::Domain,
|
||||||
|
@ -5,7 +5,7 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use rustc_data_structures::work_queue::WorkQueue;
|
use rustc_data_structures::work_queue::WorkQueue;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_index::{Idx, IndexVec};
|
use rustc_index::IndexVec;
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_middle::mir::{self, BasicBlock, create_dump_file, dump_enabled, traversal};
|
use rustc_middle::mir::{self, BasicBlock, create_dump_file, dump_enabled, traversal};
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
@ -16,13 +16,12 @@ use {rustc_ast as ast, rustc_graphviz as dot};
|
|||||||
|
|
||||||
use super::fmt::DebugWithContext;
|
use super::fmt::DebugWithContext;
|
||||||
use super::{
|
use super::{
|
||||||
Analysis, AnalysisDomain, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice,
|
Analysis, AnalysisDomain, Direction, JoinSemiLattice, ResultsCursor, ResultsVisitor, graphviz,
|
||||||
ResultsCursor, ResultsVisitor, graphviz, visit_results,
|
visit_results,
|
||||||
};
|
};
|
||||||
use crate::errors::{
|
use crate::errors::{
|
||||||
DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
|
DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
|
||||||
};
|
};
|
||||||
use crate::framework::BitSetExt;
|
|
||||||
|
|
||||||
type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as AnalysisDomain<'tcx>>::Domain>;
|
type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as AnalysisDomain<'tcx>>::Domain>;
|
||||||
|
|
||||||
@ -82,53 +81,6 @@ where
|
|||||||
entry_sets: IndexVec<BasicBlock, A::Domain>,
|
entry_sets: IndexVec<BasicBlock, A::Domain>,
|
||||||
pass_name: Option<&'static str>,
|
pass_name: Option<&'static str>,
|
||||||
analysis: A,
|
analysis: A,
|
||||||
|
|
||||||
/// Cached, cumulative transfer functions for each block.
|
|
||||||
//
|
|
||||||
// FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for
|
|
||||||
// gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade
|
|
||||||
// performance in practice. I've tried a few ways to avoid this, but they have downsides. See
|
|
||||||
// the message for the commit that added this FIXME for more information.
|
|
||||||
apply_statement_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'mir, 'tcx, A, D, T> Engine<'mir, 'tcx, A>
|
|
||||||
where
|
|
||||||
A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
|
|
||||||
D: Clone + JoinSemiLattice + GenKill<T> + BitSetExt<T>,
|
|
||||||
T: Idx,
|
|
||||||
{
|
|
||||||
/// Creates a new `Engine` to solve a gen-kill dataflow problem.
|
|
||||||
pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>, mut analysis: A) -> Self {
|
|
||||||
// If there are no back-edges in the control-flow graph, we only ever need to apply the
|
|
||||||
// transfer function for each block exactly once (assuming that we process blocks in RPO).
|
|
||||||
//
|
|
||||||
// In this case, there's no need to compute the block transfer functions ahead of time.
|
|
||||||
if !body.basic_blocks.is_cfg_cyclic() {
|
|
||||||
return Self::new(tcx, body, analysis, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, compute and store the cumulative transfer function for each block.
|
|
||||||
|
|
||||||
let identity = GenKillSet::identity(analysis.domain_size(body));
|
|
||||||
let mut trans_for_block = IndexVec::from_elem(identity, &body.basic_blocks);
|
|
||||||
|
|
||||||
for (block, block_data) in body.basic_blocks.iter_enumerated() {
|
|
||||||
let trans = &mut trans_for_block[block];
|
|
||||||
A::Direction::gen_kill_statement_effects_in_block(
|
|
||||||
&mut analysis,
|
|
||||||
trans,
|
|
||||||
block,
|
|
||||||
block_data,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
|
|
||||||
trans_for_block[bb].apply(state);
|
|
||||||
});
|
|
||||||
|
|
||||||
Self::new(tcx, body, analysis, Some(apply_trans as Box<_>))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'mir, 'tcx, A, D> Engine<'mir, 'tcx, A>
|
impl<'mir, 'tcx, A, D> Engine<'mir, 'tcx, A>
|
||||||
@ -138,19 +90,7 @@ where
|
|||||||
{
|
{
|
||||||
/// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
|
/// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
|
||||||
/// function.
|
/// function.
|
||||||
///
|
pub(crate) fn new(tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>, analysis: A) -> Self {
|
||||||
/// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
|
|
||||||
/// better performance.
|
|
||||||
pub fn new_generic(tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>, analysis: A) -> Self {
|
|
||||||
Self::new(tcx, body, analysis, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
body: &'mir mir::Body<'tcx>,
|
|
||||||
analysis: A,
|
|
||||||
apply_statement_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
|
|
||||||
) -> Self {
|
|
||||||
let mut entry_sets =
|
let mut entry_sets =
|
||||||
IndexVec::from_fn_n(|_| analysis.bottom_value(body), body.basic_blocks.len());
|
IndexVec::from_fn_n(|_| analysis.bottom_value(body), body.basic_blocks.len());
|
||||||
analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
|
analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
|
||||||
@ -160,7 +100,7 @@ where
|
|||||||
bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
|
bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
|
||||||
}
|
}
|
||||||
|
|
||||||
Engine { analysis, tcx, body, pass_name: None, entry_sets, apply_statement_trans_for_block }
|
Engine { analysis, tcx, body, pass_name: None, entry_sets }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
|
/// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
|
||||||
@ -177,14 +117,7 @@ where
|
|||||||
where
|
where
|
||||||
A::Domain: DebugWithContext<A>,
|
A::Domain: DebugWithContext<A>,
|
||||||
{
|
{
|
||||||
let Engine {
|
let Engine { mut analysis, body, mut entry_sets, tcx, pass_name } = self;
|
||||||
mut analysis,
|
|
||||||
body,
|
|
||||||
mut entry_sets,
|
|
||||||
tcx,
|
|
||||||
apply_statement_trans_for_block,
|
|
||||||
pass_name,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_none(body.basic_blocks.len());
|
let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_none(body.basic_blocks.len());
|
||||||
|
|
||||||
@ -213,13 +146,8 @@ where
|
|||||||
state.clone_from(&entry_sets[bb]);
|
state.clone_from(&entry_sets[bb]);
|
||||||
|
|
||||||
// Apply the block transfer function, using the cached one if it exists.
|
// Apply the block transfer function, using the cached one if it exists.
|
||||||
let edges = A::Direction::apply_effects_in_block(
|
let edges =
|
||||||
&mut analysis,
|
A::Direction::apply_effects_in_block(&mut analysis, &mut state, bb, bb_data);
|
||||||
&mut state,
|
|
||||||
bb,
|
|
||||||
bb_data,
|
|
||||||
apply_statement_trans_for_block.as_deref(),
|
|
||||||
);
|
|
||||||
|
|
||||||
A::Direction::join_state_into_successors_of(
|
A::Direction::join_state_into_successors_of(
|
||||||
&mut analysis,
|
&mut analysis,
|
||||||
|
@ -242,7 +242,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
|
|||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
Engine::new_generic(tcx, body, self)
|
Engine::new(tcx, body, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,19 +376,6 @@ where
|
|||||||
) {
|
) {
|
||||||
self.switch_int_edge_effects(block, discr, edge_effects);
|
self.switch_int_edge_effects(block, discr, edge_effects);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extension methods */
|
|
||||||
#[inline]
|
|
||||||
fn into_engine<'mir>(
|
|
||||||
self,
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
body: &'mir mir::Body<'tcx>,
|
|
||||||
) -> Engine<'mir, 'tcx, Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
Engine::new_gen_kill(tcx, body, self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The legal operations for a transfer function in a gen/kill problem.
|
/// The legal operations for a transfer function in a gen/kill problem.
|
||||||
@ -422,44 +409,6 @@ pub trait GenKill<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stores a transfer function for a gen/kill problem.
|
|
||||||
///
|
|
||||||
/// Calling `gen_`/`kill` on a `GenKillSet` will "build up" a transfer function so that it can be
|
|
||||||
/// applied multiple times efficiently. When there are multiple calls to `gen_` and/or `kill` for
|
|
||||||
/// the same element, the most recent one takes precedence.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct GenKillSet<T> {
|
|
||||||
gen_: HybridBitSet<T>,
|
|
||||||
kill: HybridBitSet<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Idx> GenKillSet<T> {
|
|
||||||
/// Creates a new transfer function that will leave the dataflow state unchanged.
|
|
||||||
pub fn identity(universe: usize) -> Self {
|
|
||||||
GenKillSet {
|
|
||||||
gen_: HybridBitSet::new_empty(universe),
|
|
||||||
kill: HybridBitSet::new_empty(universe),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn apply(&self, state: &mut impl BitSetExt<T>) {
|
|
||||||
state.union(&self.gen_);
|
|
||||||
state.subtract(&self.kill);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Idx> GenKill<T> for GenKillSet<T> {
|
|
||||||
fn gen_(&mut self, elem: T) {
|
|
||||||
self.gen_.insert(elem);
|
|
||||||
self.kill.remove(elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kill(&mut self, elem: T) {
|
|
||||||
self.kill.insert(elem);
|
|
||||||
self.gen_.remove(elem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Idx> GenKill<T> for BitSet<T> {
|
impl<T: Idx> GenKill<T> for BitSet<T> {
|
||||||
fn gen_(&mut self, elem: T) {
|
fn gen_(&mut self, elem: T) {
|
||||||
self.insert(elem);
|
self.insert(elem);
|
||||||
|
Loading…
Reference in New Issue
Block a user