mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-30 00:23:41 +00:00
Rollup merge of #95120 - smoelius:backward-switch-int, r=ecstatic-morse
Implement `apply_switch_int_edge_effects` for backward analyses See #94576 for some discussion. r? `@ecstatic-morse`
This commit is contained in:
commit
a8be562bdf
@ -45,6 +45,7 @@ use std::{iter, mem, option};
|
|||||||
use self::graph_cyclic_cache::GraphIsCyclicCache;
|
use self::graph_cyclic_cache::GraphIsCyclicCache;
|
||||||
use self::predecessors::{PredecessorCache, Predecessors};
|
use self::predecessors::{PredecessorCache, Predecessors};
|
||||||
pub use self::query::*;
|
pub use self::query::*;
|
||||||
|
use self::switch_sources::{SwitchSourceCache, SwitchSources};
|
||||||
|
|
||||||
pub mod coverage;
|
pub mod coverage;
|
||||||
mod generic_graph;
|
mod generic_graph;
|
||||||
@ -58,6 +59,7 @@ mod predecessors;
|
|||||||
pub mod pretty;
|
pub mod pretty;
|
||||||
mod query;
|
mod query;
|
||||||
pub mod spanview;
|
pub mod spanview;
|
||||||
|
mod switch_sources;
|
||||||
pub mod tcx;
|
pub mod tcx;
|
||||||
pub mod terminator;
|
pub mod terminator;
|
||||||
pub use terminator::*;
|
pub use terminator::*;
|
||||||
@ -296,6 +298,7 @@ pub struct Body<'tcx> {
|
|||||||
pub is_polymorphic: bool,
|
pub is_polymorphic: bool,
|
||||||
|
|
||||||
predecessor_cache: PredecessorCache,
|
predecessor_cache: PredecessorCache,
|
||||||
|
switch_source_cache: SwitchSourceCache,
|
||||||
is_cyclic: GraphIsCyclicCache,
|
is_cyclic: GraphIsCyclicCache,
|
||||||
|
|
||||||
pub tainted_by_errors: Option<ErrorGuaranteed>,
|
pub tainted_by_errors: Option<ErrorGuaranteed>,
|
||||||
@ -344,6 +347,7 @@ impl<'tcx> Body<'tcx> {
|
|||||||
required_consts: Vec::new(),
|
required_consts: Vec::new(),
|
||||||
is_polymorphic: false,
|
is_polymorphic: false,
|
||||||
predecessor_cache: PredecessorCache::new(),
|
predecessor_cache: PredecessorCache::new(),
|
||||||
|
switch_source_cache: SwitchSourceCache::new(),
|
||||||
is_cyclic: GraphIsCyclicCache::new(),
|
is_cyclic: GraphIsCyclicCache::new(),
|
||||||
tainted_by_errors,
|
tainted_by_errors,
|
||||||
};
|
};
|
||||||
@ -372,6 +376,7 @@ impl<'tcx> Body<'tcx> {
|
|||||||
var_debug_info: Vec::new(),
|
var_debug_info: Vec::new(),
|
||||||
is_polymorphic: false,
|
is_polymorphic: false,
|
||||||
predecessor_cache: PredecessorCache::new(),
|
predecessor_cache: PredecessorCache::new(),
|
||||||
|
switch_source_cache: SwitchSourceCache::new(),
|
||||||
is_cyclic: GraphIsCyclicCache::new(),
|
is_cyclic: GraphIsCyclicCache::new(),
|
||||||
tainted_by_errors: None,
|
tainted_by_errors: None,
|
||||||
};
|
};
|
||||||
@ -392,6 +397,7 @@ impl<'tcx> Body<'tcx> {
|
|||||||
// FIXME: Use a finer-grained API for this, so only transformations that alter terminators
|
// FIXME: Use a finer-grained API for this, so only transformations that alter terminators
|
||||||
// invalidate the caches.
|
// invalidate the caches.
|
||||||
self.predecessor_cache.invalidate();
|
self.predecessor_cache.invalidate();
|
||||||
|
self.switch_source_cache.invalidate();
|
||||||
self.is_cyclic.invalidate();
|
self.is_cyclic.invalidate();
|
||||||
&mut self.basic_blocks
|
&mut self.basic_blocks
|
||||||
}
|
}
|
||||||
@ -401,6 +407,7 @@ impl<'tcx> Body<'tcx> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
|
) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
|
||||||
self.predecessor_cache.invalidate();
|
self.predecessor_cache.invalidate();
|
||||||
|
self.switch_source_cache.invalidate();
|
||||||
self.is_cyclic.invalidate();
|
self.is_cyclic.invalidate();
|
||||||
(&mut self.basic_blocks, &mut self.local_decls)
|
(&mut self.basic_blocks, &mut self.local_decls)
|
||||||
}
|
}
|
||||||
@ -414,6 +421,7 @@ impl<'tcx> Body<'tcx> {
|
|||||||
&mut Vec<VarDebugInfo<'tcx>>,
|
&mut Vec<VarDebugInfo<'tcx>>,
|
||||||
) {
|
) {
|
||||||
self.predecessor_cache.invalidate();
|
self.predecessor_cache.invalidate();
|
||||||
|
self.switch_source_cache.invalidate();
|
||||||
self.is_cyclic.invalidate();
|
self.is_cyclic.invalidate();
|
||||||
(&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info)
|
(&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info)
|
||||||
}
|
}
|
||||||
@ -541,6 +549,11 @@ impl<'tcx> Body<'tcx> {
|
|||||||
self.predecessor_cache.compute(&self.basic_blocks)
|
self.predecessor_cache.compute(&self.basic_blocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn switch_sources(&self) -> &SwitchSources {
|
||||||
|
self.switch_source_cache.compute(&self.basic_blocks)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn dominators(&self) -> Dominators<BasicBlock> {
|
pub fn dominators(&self) -> Dominators<BasicBlock> {
|
||||||
dominators(self)
|
dominators(self)
|
||||||
|
82
compiler/rustc_middle/src/mir/switch_sources.rs
Normal file
82
compiler/rustc_middle/src/mir/switch_sources.rs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
//! Lazily compute the inverse of each `SwitchInt`'s switch targets. Modeled after
|
||||||
|
//! `Predecessors`/`PredecessorCache`.
|
||||||
|
|
||||||
|
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||||
|
use rustc_data_structures::sync::OnceCell;
|
||||||
|
use rustc_index::vec::IndexVec;
|
||||||
|
use rustc_serialize as serialize;
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
use crate::mir::{BasicBlock, BasicBlockData, Terminator, TerminatorKind};
|
||||||
|
|
||||||
|
pub type SwitchSources = IndexVec<BasicBlock, IndexVec<BasicBlock, SmallVec<[Option<u128>; 1]>>>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(super) struct SwitchSourceCache {
|
||||||
|
cache: OnceCell<SwitchSources>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwitchSourceCache {
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
SwitchSourceCache { cache: OnceCell::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invalidates the switch source cache.
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn invalidate(&mut self) {
|
||||||
|
self.cache = OnceCell::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the switch sources for this MIR.
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn compute(
|
||||||
|
&self,
|
||||||
|
basic_blocks: &IndexVec<BasicBlock, BasicBlockData<'_>>,
|
||||||
|
) -> &SwitchSources {
|
||||||
|
self.cache.get_or_init(|| {
|
||||||
|
let mut switch_sources = IndexVec::from_elem(
|
||||||
|
IndexVec::from_elem(SmallVec::new(), basic_blocks),
|
||||||
|
basic_blocks,
|
||||||
|
);
|
||||||
|
for (bb, data) in basic_blocks.iter_enumerated() {
|
||||||
|
if let Some(Terminator {
|
||||||
|
kind: TerminatorKind::SwitchInt { targets, .. }, ..
|
||||||
|
}) = &data.terminator
|
||||||
|
{
|
||||||
|
for (value, target) in targets.iter() {
|
||||||
|
switch_sources[target][bb].push(Some(value));
|
||||||
|
}
|
||||||
|
switch_sources[targets.otherwise()][bb].push(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch_sources
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: serialize::Encoder> serialize::Encodable<S> for SwitchSourceCache {
|
||||||
|
#[inline]
|
||||||
|
fn encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
|
s.emit_unit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D: serialize::Decoder> serialize::Decodable<D> for SwitchSourceCache {
|
||||||
|
#[inline]
|
||||||
|
fn decode(_: &mut D) -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CTX> HashStable<CTX> for SwitchSourceCache {
|
||||||
|
#[inline]
|
||||||
|
fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TrivialTypeFoldableAndLiftImpls! {
|
||||||
|
SwitchSourceCache,
|
||||||
|
}
|
@ -248,6 +248,7 @@ impl Direction for Backward {
|
|||||||
);
|
);
|
||||||
propagate(pred, &tmp);
|
propagate(pred, &tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
mir::TerminatorKind::InlineAsm {
|
mir::TerminatorKind::InlineAsm {
|
||||||
destination: Some(dest), ref operands, ..
|
destination: Some(dest), ref operands, ..
|
||||||
} if dest == bb => {
|
} if dest == bb => {
|
||||||
@ -266,6 +267,23 @@ impl Direction for Backward {
|
|||||||
propagate(pred, &tmp);
|
propagate(pred, &tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mir::TerminatorKind::SwitchInt { targets: _, ref discr, switch_ty: _ } => {
|
||||||
|
let mut applier = BackwardSwitchIntEdgeEffectsApplier {
|
||||||
|
pred,
|
||||||
|
exit_state,
|
||||||
|
values: &body.switch_sources()[bb][pred],
|
||||||
|
bb,
|
||||||
|
propagate: &mut propagate,
|
||||||
|
effects_applied: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);
|
||||||
|
|
||||||
|
if !applier.effects_applied {
|
||||||
|
propagate(pred, exit_state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ignore dead unwinds.
|
// Ignore dead unwinds.
|
||||||
mir::TerminatorKind::Call { cleanup: Some(unwind), .. }
|
mir::TerminatorKind::Call { cleanup: Some(unwind), .. }
|
||||||
| mir::TerminatorKind::Assert { cleanup: Some(unwind), .. }
|
| mir::TerminatorKind::Assert { cleanup: Some(unwind), .. }
|
||||||
@ -286,6 +304,37 @@ impl Direction for Backward {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct BackwardSwitchIntEdgeEffectsApplier<'a, D, F> {
|
||||||
|
pred: BasicBlock,
|
||||||
|
exit_state: &'a mut D,
|
||||||
|
values: &'a [Option<u128>],
|
||||||
|
bb: BasicBlock,
|
||||||
|
propagate: &'a mut F,
|
||||||
|
|
||||||
|
effects_applied: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D, F> super::SwitchIntEdgeEffects<D> for BackwardSwitchIntEdgeEffectsApplier<'_, D, F>
|
||||||
|
where
|
||||||
|
D: Clone,
|
||||||
|
F: FnMut(BasicBlock, &D),
|
||||||
|
{
|
||||||
|
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
|
||||||
|
assert!(!self.effects_applied);
|
||||||
|
|
||||||
|
let targets = self.values.iter().map(|&value| SwitchIntTarget { value, target: self.bb });
|
||||||
|
|
||||||
|
let mut tmp = None;
|
||||||
|
for target in targets {
|
||||||
|
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
|
||||||
|
apply_edge_effect(tmp, target);
|
||||||
|
(self.propagate)(self.pred, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.effects_applied = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
|
/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
|
||||||
pub struct Forward;
|
pub struct Forward;
|
||||||
|
|
||||||
@ -528,7 +577,7 @@ impl Direction for Forward {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SwitchInt { ref targets, ref discr, switch_ty: _ } => {
|
SwitchInt { ref targets, ref discr, switch_ty: _ } => {
|
||||||
let mut applier = SwitchIntEdgeEffectApplier {
|
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
|
||||||
exit_state,
|
exit_state,
|
||||||
targets,
|
targets,
|
||||||
propagate,
|
propagate,
|
||||||
@ -537,8 +586,11 @@ impl Direction for Forward {
|
|||||||
|
|
||||||
analysis.apply_switch_int_edge_effects(bb, discr, &mut applier);
|
analysis.apply_switch_int_edge_effects(bb, discr, &mut applier);
|
||||||
|
|
||||||
let SwitchIntEdgeEffectApplier {
|
let ForwardSwitchIntEdgeEffectsApplier {
|
||||||
exit_state, mut propagate, effects_applied, ..
|
exit_state,
|
||||||
|
mut propagate,
|
||||||
|
effects_applied,
|
||||||
|
..
|
||||||
} = applier;
|
} = applier;
|
||||||
|
|
||||||
if !effects_applied {
|
if !effects_applied {
|
||||||
@ -551,7 +603,7 @@ impl Direction for Forward {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SwitchIntEdgeEffectApplier<'a, D, F> {
|
struct ForwardSwitchIntEdgeEffectsApplier<'a, D, F> {
|
||||||
exit_state: &'a mut D,
|
exit_state: &'a mut D,
|
||||||
targets: &'a SwitchTargets,
|
targets: &'a SwitchTargets,
|
||||||
propagate: F,
|
propagate: F,
|
||||||
@ -559,7 +611,7 @@ struct SwitchIntEdgeEffectApplier<'a, D, F> {
|
|||||||
effects_applied: bool,
|
effects_applied: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D, F> super::SwitchIntEdgeEffects<D> for SwitchIntEdgeEffectApplier<'_, D, F>
|
impl<D, F> super::SwitchIntEdgeEffects<D> for ForwardSwitchIntEdgeEffectsApplier<'_, D, F>
|
||||||
where
|
where
|
||||||
D: Clone,
|
D: Clone,
|
||||||
F: FnMut(BasicBlock, &D),
|
F: FnMut(BasicBlock, &D),
|
||||||
|
@ -234,8 +234,6 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
|
|||||||
/// about a given `SwitchInt` terminator for each one of its edges—and more efficient—the
|
/// about a given `SwitchInt` terminator for each one of its edges—and more efficient—the
|
||||||
/// engine doesn't need to clone the exit state for a block unless
|
/// engine doesn't need to clone the exit state for a block unless
|
||||||
/// `SwitchIntEdgeEffects::apply` is actually called.
|
/// `SwitchIntEdgeEffects::apply` is actually called.
|
||||||
///
|
|
||||||
/// FIXME: This class of effects is not supported for backward dataflow analyses.
|
|
||||||
fn apply_switch_int_edge_effects(
|
fn apply_switch_int_edge_effects(
|
||||||
&self,
|
&self,
|
||||||
_block: BasicBlock,
|
_block: BasicBlock,
|
||||||
|
@ -28,7 +28,7 @@ pub use self::drop_flag_effects::{
|
|||||||
pub use self::framework::{
|
pub use self::framework::{
|
||||||
fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, CallReturnPlaces,
|
fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, CallReturnPlaces,
|
||||||
Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor,
|
Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor,
|
||||||
ResultsRefCursor, ResultsVisitable, ResultsVisitor,
|
ResultsRefCursor, ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::move_paths::MoveData;
|
use self::move_paths::MoveData;
|
||||||
|
Loading…
Reference in New Issue
Block a user