Skip to content

Commit a8be562

Browse files
authored
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`
2 parents 726cd73 + 241ec5b commit a8be562

File tree

5 files changed

+153
-8
lines changed

5 files changed

+153
-8
lines changed

compiler/rustc_middle/src/mir/mod.rs

+13
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use std::{iter, mem, option};
4545
use self::graph_cyclic_cache::GraphIsCyclicCache;
4646
use self::predecessors::{PredecessorCache, Predecessors};
4747
pub use self::query::*;
48+
use self::switch_sources::{SwitchSourceCache, SwitchSources};
4849

4950
pub mod coverage;
5051
mod generic_graph;
@@ -58,6 +59,7 @@ mod predecessors;
5859
pub mod pretty;
5960
mod query;
6061
pub mod spanview;
62+
mod switch_sources;
6163
pub mod tcx;
6264
pub mod terminator;
6365
pub use terminator::*;
@@ -296,6 +298,7 @@ pub struct Body<'tcx> {
296298
pub is_polymorphic: bool,
297299

298300
predecessor_cache: PredecessorCache,
301+
switch_source_cache: SwitchSourceCache,
299302
is_cyclic: GraphIsCyclicCache,
300303

301304
pub tainted_by_errors: Option<ErrorGuaranteed>,
@@ -344,6 +347,7 @@ impl<'tcx> Body<'tcx> {
344347
required_consts: Vec::new(),
345348
is_polymorphic: false,
346349
predecessor_cache: PredecessorCache::new(),
350+
switch_source_cache: SwitchSourceCache::new(),
347351
is_cyclic: GraphIsCyclicCache::new(),
348352
tainted_by_errors,
349353
};
@@ -372,6 +376,7 @@ impl<'tcx> Body<'tcx> {
372376
var_debug_info: Vec::new(),
373377
is_polymorphic: false,
374378
predecessor_cache: PredecessorCache::new(),
379+
switch_source_cache: SwitchSourceCache::new(),
375380
is_cyclic: GraphIsCyclicCache::new(),
376381
tainted_by_errors: None,
377382
};
@@ -392,6 +397,7 @@ impl<'tcx> Body<'tcx> {
392397
// FIXME: Use a finer-grained API for this, so only transformations that alter terminators
393398
// invalidate the caches.
394399
self.predecessor_cache.invalidate();
400+
self.switch_source_cache.invalidate();
395401
self.is_cyclic.invalidate();
396402
&mut self.basic_blocks
397403
}
@@ -401,6 +407,7 @@ impl<'tcx> Body<'tcx> {
401407
&mut self,
402408
) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
403409
self.predecessor_cache.invalidate();
410+
self.switch_source_cache.invalidate();
404411
self.is_cyclic.invalidate();
405412
(&mut self.basic_blocks, &mut self.local_decls)
406413
}
@@ -414,6 +421,7 @@ impl<'tcx> Body<'tcx> {
414421
&mut Vec<VarDebugInfo<'tcx>>,
415422
) {
416423
self.predecessor_cache.invalidate();
424+
self.switch_source_cache.invalidate();
417425
self.is_cyclic.invalidate();
418426
(&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info)
419427
}
@@ -541,6 +549,11 @@ impl<'tcx> Body<'tcx> {
541549
self.predecessor_cache.compute(&self.basic_blocks)
542550
}
543551

552+
#[inline]
553+
pub fn switch_sources(&self) -> &SwitchSources {
554+
self.switch_source_cache.compute(&self.basic_blocks)
555+
}
556+
544557
#[inline]
545558
pub fn dominators(&self) -> Dominators<BasicBlock> {
546559
dominators(self)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//! Lazily compute the inverse of each `SwitchInt`'s switch targets. Modeled after
2+
//! `Predecessors`/`PredecessorCache`.
3+
4+
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
5+
use rustc_data_structures::sync::OnceCell;
6+
use rustc_index::vec::IndexVec;
7+
use rustc_serialize as serialize;
8+
use smallvec::SmallVec;
9+
10+
use crate::mir::{BasicBlock, BasicBlockData, Terminator, TerminatorKind};
11+
12+
pub type SwitchSources = IndexVec<BasicBlock, IndexVec<BasicBlock, SmallVec<[Option<u128>; 1]>>>;
13+
14+
#[derive(Clone, Debug)]
15+
pub(super) struct SwitchSourceCache {
16+
cache: OnceCell<SwitchSources>,
17+
}
18+
19+
impl SwitchSourceCache {
20+
#[inline]
21+
pub(super) fn new() -> Self {
22+
SwitchSourceCache { cache: OnceCell::new() }
23+
}
24+
25+
/// Invalidates the switch source cache.
26+
#[inline]
27+
pub(super) fn invalidate(&mut self) {
28+
self.cache = OnceCell::new();
29+
}
30+
31+
/// Returns the switch sources for this MIR.
32+
#[inline]
33+
pub(super) fn compute(
34+
&self,
35+
basic_blocks: &IndexVec<BasicBlock, BasicBlockData<'_>>,
36+
) -> &SwitchSources {
37+
self.cache.get_or_init(|| {
38+
let mut switch_sources = IndexVec::from_elem(
39+
IndexVec::from_elem(SmallVec::new(), basic_blocks),
40+
basic_blocks,
41+
);
42+
for (bb, data) in basic_blocks.iter_enumerated() {
43+
if let Some(Terminator {
44+
kind: TerminatorKind::SwitchInt { targets, .. }, ..
45+
}) = &data.terminator
46+
{
47+
for (value, target) in targets.iter() {
48+
switch_sources[target][bb].push(Some(value));
49+
}
50+
switch_sources[targets.otherwise()][bb].push(None);
51+
}
52+
}
53+
54+
switch_sources
55+
})
56+
}
57+
}
58+
59+
impl<S: serialize::Encoder> serialize::Encodable<S> for SwitchSourceCache {
60+
#[inline]
61+
fn encode(&self, s: &mut S) -> Result<(), S::Error> {
62+
s.emit_unit()
63+
}
64+
}
65+
66+
impl<D: serialize::Decoder> serialize::Decodable<D> for SwitchSourceCache {
67+
#[inline]
68+
fn decode(_: &mut D) -> Self {
69+
Self::new()
70+
}
71+
}
72+
73+
impl<CTX> HashStable<CTX> for SwitchSourceCache {
74+
#[inline]
75+
fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
76+
// do nothing
77+
}
78+
}
79+
80+
TrivialTypeFoldableAndLiftImpls! {
81+
SwitchSourceCache,
82+
}

compiler/rustc_mir_dataflow/src/framework/direction.rs

+57-5
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ impl Direction for Backward {
248248
);
249249
propagate(pred, &tmp);
250250
}
251+
251252
mir::TerminatorKind::InlineAsm {
252253
destination: Some(dest), ref operands, ..
253254
} if dest == bb => {
@@ -266,6 +267,23 @@ impl Direction for Backward {
266267
propagate(pred, &tmp);
267268
}
268269

270+
mir::TerminatorKind::SwitchInt { targets: _, ref discr, switch_ty: _ } => {
271+
let mut applier = BackwardSwitchIntEdgeEffectsApplier {
272+
pred,
273+
exit_state,
274+
values: &body.switch_sources()[bb][pred],
275+
bb,
276+
propagate: &mut propagate,
277+
effects_applied: false,
278+
};
279+
280+
analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);
281+
282+
if !applier.effects_applied {
283+
propagate(pred, exit_state)
284+
}
285+
}
286+
269287
// Ignore dead unwinds.
270288
mir::TerminatorKind::Call { cleanup: Some(unwind), .. }
271289
| mir::TerminatorKind::Assert { cleanup: Some(unwind), .. }
@@ -286,6 +304,37 @@ impl Direction for Backward {
286304
}
287305
}
288306

307+
struct BackwardSwitchIntEdgeEffectsApplier<'a, D, F> {
308+
pred: BasicBlock,
309+
exit_state: &'a mut D,
310+
values: &'a [Option<u128>],
311+
bb: BasicBlock,
312+
propagate: &'a mut F,
313+
314+
effects_applied: bool,
315+
}
316+
317+
impl<D, F> super::SwitchIntEdgeEffects<D> for BackwardSwitchIntEdgeEffectsApplier<'_, D, F>
318+
where
319+
D: Clone,
320+
F: FnMut(BasicBlock, &D),
321+
{
322+
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
323+
assert!(!self.effects_applied);
324+
325+
let targets = self.values.iter().map(|&value| SwitchIntTarget { value, target: self.bb });
326+
327+
let mut tmp = None;
328+
for target in targets {
329+
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
330+
apply_edge_effect(tmp, target);
331+
(self.propagate)(self.pred, tmp);
332+
}
333+
334+
self.effects_applied = true;
335+
}
336+
}
337+
289338
/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
290339
pub struct Forward;
291340

@@ -528,7 +577,7 @@ impl Direction for Forward {
528577
}
529578

530579
SwitchInt { ref targets, ref discr, switch_ty: _ } => {
531-
let mut applier = SwitchIntEdgeEffectApplier {
580+
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
532581
exit_state,
533582
targets,
534583
propagate,
@@ -537,8 +586,11 @@ impl Direction for Forward {
537586

538587
analysis.apply_switch_int_edge_effects(bb, discr, &mut applier);
539588

540-
let SwitchIntEdgeEffectApplier {
541-
exit_state, mut propagate, effects_applied, ..
589+
let ForwardSwitchIntEdgeEffectsApplier {
590+
exit_state,
591+
mut propagate,
592+
effects_applied,
593+
..
542594
} = applier;
543595

544596
if !effects_applied {
@@ -551,15 +603,15 @@ impl Direction for Forward {
551603
}
552604
}
553605

554-
struct SwitchIntEdgeEffectApplier<'a, D, F> {
606+
struct ForwardSwitchIntEdgeEffectsApplier<'a, D, F> {
555607
exit_state: &'a mut D,
556608
targets: &'a SwitchTargets,
557609
propagate: F,
558610

559611
effects_applied: bool,
560612
}
561613

562-
impl<D, F> super::SwitchIntEdgeEffects<D> for SwitchIntEdgeEffectApplier<'_, D, F>
614+
impl<D, F> super::SwitchIntEdgeEffects<D> for ForwardSwitchIntEdgeEffectsApplier<'_, D, F>
563615
where
564616
D: Clone,
565617
F: FnMut(BasicBlock, &D),

compiler/rustc_mir_dataflow/src/framework/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,6 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
234234
/// about a given `SwitchInt` terminator for each one of its edges—and more efficient—the
235235
/// engine doesn't need to clone the exit state for a block unless
236236
/// `SwitchIntEdgeEffects::apply` is actually called.
237-
///
238-
/// FIXME: This class of effects is not supported for backward dataflow analyses.
239237
fn apply_switch_int_edge_effects(
240238
&self,
241239
_block: BasicBlock,

compiler/rustc_mir_dataflow/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub use self::drop_flag_effects::{
2828
pub use self::framework::{
2929
fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, CallReturnPlaces,
3030
Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor,
31-
ResultsRefCursor, ResultsVisitable, ResultsVisitor,
31+
ResultsRefCursor, ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects,
3232
};
3333

3434
use self::move_paths::MoveData;

0 commit comments

Comments
 (0)