about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <99973273+Dylan-DPC@users.noreply.github.com>2022-03-27 22:51:39 +0200
committerGitHub <noreply@github.com>2022-03-27 22:51:39 +0200
commita8be562bdfeb775768ec5ca7a6c4e07809a1f829 (patch)
tree3d28aa03bbb096ea0022def9a95f0bcdba566aa0
parent726cd737d625a1420e3eed5f00879f6f136f1c83 (diff)
parent241ec5b3b35900cf5a9becf7da3e3ea49448d8fb (diff)
downloadrust-a8be562bdfeb775768ec5ca7a6c4e07809a1f829.tar.gz
rust-a8be562bdfeb775768ec5ca7a6c4e07809a1f829.zip
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`
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs13
-rw-r--r--compiler/rustc_middle/src/mir/switch_sources.rs82
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/direction.rs62
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/mod.rs2
-rw-r--r--compiler/rustc_mir_dataflow/src/lib.rs2
5 files changed, 153 insertions, 8 deletions
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index ea71cff1616..eec6eed311b 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -45,6 +45,7 @@ use std::{iter, mem, option};
 use self::graph_cyclic_cache::GraphIsCyclicCache;
 use self::predecessors::{PredecessorCache, Predecessors};
 pub use self::query::*;
+use self::switch_sources::{SwitchSourceCache, SwitchSources};
 
 pub mod coverage;
 mod generic_graph;
@@ -58,6 +59,7 @@ mod predecessors;
 pub mod pretty;
 mod query;
 pub mod spanview;
+mod switch_sources;
 pub mod tcx;
 pub mod terminator;
 pub use terminator::*;
@@ -296,6 +298,7 @@ pub struct Body<'tcx> {
     pub is_polymorphic: bool,
 
     predecessor_cache: PredecessorCache,
+    switch_source_cache: SwitchSourceCache,
     is_cyclic: GraphIsCyclicCache,
 
     pub tainted_by_errors: Option<ErrorGuaranteed>,
@@ -344,6 +347,7 @@ impl<'tcx> Body<'tcx> {
             required_consts: Vec::new(),
             is_polymorphic: false,
             predecessor_cache: PredecessorCache::new(),
+            switch_source_cache: SwitchSourceCache::new(),
             is_cyclic: GraphIsCyclicCache::new(),
             tainted_by_errors,
         };
@@ -372,6 +376,7 @@ impl<'tcx> Body<'tcx> {
             var_debug_info: Vec::new(),
             is_polymorphic: false,
             predecessor_cache: PredecessorCache::new(),
+            switch_source_cache: SwitchSourceCache::new(),
             is_cyclic: GraphIsCyclicCache::new(),
             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
         // invalidate the caches.
         self.predecessor_cache.invalidate();
+        self.switch_source_cache.invalidate();
         self.is_cyclic.invalidate();
         &mut self.basic_blocks
     }
@@ -401,6 +407,7 @@ impl<'tcx> Body<'tcx> {
         &mut self,
     ) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
         self.predecessor_cache.invalidate();
+        self.switch_source_cache.invalidate();
         self.is_cyclic.invalidate();
         (&mut self.basic_blocks, &mut self.local_decls)
     }
@@ -414,6 +421,7 @@ impl<'tcx> Body<'tcx> {
         &mut Vec<VarDebugInfo<'tcx>>,
     ) {
         self.predecessor_cache.invalidate();
+        self.switch_source_cache.invalidate();
         self.is_cyclic.invalidate();
         (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info)
     }
@@ -542,6 +550,11 @@ impl<'tcx> Body<'tcx> {
     }
 
     #[inline]
+    pub fn switch_sources(&self) -> &SwitchSources {
+        self.switch_source_cache.compute(&self.basic_blocks)
+    }
+
+    #[inline]
     pub fn dominators(&self) -> Dominators<BasicBlock> {
         dominators(self)
     }
diff --git a/compiler/rustc_middle/src/mir/switch_sources.rs b/compiler/rustc_middle/src/mir/switch_sources.rs
new file mode 100644
index 00000000000..7f62b4d0dba
--- /dev/null
+++ b/compiler/rustc_middle/src/mir/switch_sources.rs
@@ -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,
+}
diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs
index 102e7439772..93118dfeb77 100644
--- a/compiler/rustc_mir_dataflow/src/framework/direction.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs
@@ -248,6 +248,7 @@ impl Direction for Backward {
                     );
                     propagate(pred, &tmp);
                 }
+
                 mir::TerminatorKind::InlineAsm {
                     destination: Some(dest), ref operands, ..
                 } if dest == bb => {
@@ -266,6 +267,23 @@ impl Direction for Backward {
                     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.
                 mir::TerminatorKind::Call { 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).
 pub struct Forward;
 
@@ -528,7 +577,7 @@ impl Direction for Forward {
             }
 
             SwitchInt { ref targets, ref discr, switch_ty: _ } => {
-                let mut applier = SwitchIntEdgeEffectApplier {
+                let mut applier = ForwardSwitchIntEdgeEffectsApplier {
                     exit_state,
                     targets,
                     propagate,
@@ -537,8 +586,11 @@ impl Direction for Forward {
 
                 analysis.apply_switch_int_edge_effects(bb, discr, &mut applier);
 
-                let SwitchIntEdgeEffectApplier {
-                    exit_state, mut propagate, effects_applied, ..
+                let ForwardSwitchIntEdgeEffectsApplier {
+                    exit_state,
+                    mut propagate,
+                    effects_applied,
+                    ..
                 } = applier;
 
                 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,
     targets: &'a SwitchTargets,
     propagate: F,
@@ -559,7 +611,7 @@ struct SwitchIntEdgeEffectApplier<'a, D, F> {
     effects_applied: bool,
 }
 
-impl<D, F> super::SwitchIntEdgeEffects<D> for SwitchIntEdgeEffectApplier<'_, D, F>
+impl<D, F> super::SwitchIntEdgeEffects<D> for ForwardSwitchIntEdgeEffectsApplier<'_, D, F>
 where
     D: Clone,
     F: FnMut(BasicBlock, &D),
diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs
index c51dd06de25..67c16e6c084 100644
--- a/compiler/rustc_mir_dataflow/src/framework/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs
@@ -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
     /// engine doesn't need to clone the exit state for a block unless
     /// `SwitchIntEdgeEffects::apply` is actually called.
-    ///
-    /// FIXME: This class of effects is not supported for backward dataflow analyses.
     fn apply_switch_int_edge_effects(
         &self,
         _block: BasicBlock,
diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs
index 6c2d1b85646..c221b358670 100644
--- a/compiler/rustc_mir_dataflow/src/lib.rs
+++ b/compiler/rustc_mir_dataflow/src/lib.rs
@@ -28,7 +28,7 @@ pub use self::drop_flag_effects::{
 pub use self::framework::{
     fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, CallReturnPlaces,
     Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor,
-    ResultsRefCursor, ResultsVisitable, ResultsVisitor,
+    ResultsRefCursor, ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects,
 };
 
 use self::move_paths::MoveData;