about summary refs log tree commit diff
path: root/compiler/rustc_middle/src
diff options
context:
space:
mode:
authorBen Kimock <kimockb@gmail.com>2024-03-31 12:31:48 -0400
committerBen Kimock <kimockb@gmail.com>2024-04-07 14:36:42 -0400
commit339f4be04627f192ba29c33ab2251a582241a112 (patch)
treef79a6cfd139d3b2d07c2c17405efcf6565b6c0e1 /compiler/rustc_middle/src
parente78913baef70895c966f0456ad16086a6a9aa37b (diff)
downloadrust-339f4be04627f192ba29c33ab2251a582241a112.tar.gz
rust-339f4be04627f192ba29c33ab2251a582241a112.zip
Only collect mono items from reachable blocks
Diffstat (limited to 'compiler/rustc_middle/src')
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs52
-rw-r--r--compiler/rustc_middle/src/mir/traversal.rs96
2 files changed, 95 insertions, 53 deletions
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 7ecac0c0e78..601bfc770f4 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -30,7 +30,6 @@ pub use rustc_ast::Mutability;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::graph::dominators::Dominators;
-use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_index::bit_set::BitSet;
 use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_serialize::{Decodable, Encodable};
@@ -687,57 +686,6 @@ impl<'tcx> Body<'tcx> {
         self.injection_phase.is_some()
     }
 
-    /// Finds which basic blocks are actually reachable for a specific
-    /// monomorphization of this body.
-    ///
-    /// This is allowed to have false positives; just because this says a block
-    /// is reachable doesn't mean that's necessarily true. It's thus always
-    /// legal for this to return a filled set.
-    ///
-    /// Regardless, the [`BitSet::domain_size`] of the returned set will always
-    /// exactly match the number of blocks in the body so that `contains`
-    /// checks can be done without worrying about panicking.
-    ///
-    /// This is mostly useful because it lets us skip lowering the `false` side
-    /// of `if <T as Trait>::CONST`, as well as `intrinsics::debug_assertions`.
-    pub fn reachable_blocks_in_mono(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        instance: Instance<'tcx>,
-    ) -> BitSet<BasicBlock> {
-        let mut set = BitSet::new_empty(self.basic_blocks.len());
-        self.reachable_blocks_in_mono_from(tcx, instance, &mut set, START_BLOCK);
-        set
-    }
-
-    fn reachable_blocks_in_mono_from(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        instance: Instance<'tcx>,
-        set: &mut BitSet<BasicBlock>,
-        bb: BasicBlock,
-    ) {
-        if !set.insert(bb) {
-            return;
-        }
-
-        let data = &self.basic_blocks[bb];
-
-        if let Some((bits, targets)) = Self::try_const_mono_switchint(tcx, instance, data) {
-            let target = targets.target_for_value(bits);
-            ensure_sufficient_stack(|| {
-                self.reachable_blocks_in_mono_from(tcx, instance, set, target)
-            });
-            return;
-        }
-
-        for target in data.terminator().successors() {
-            ensure_sufficient_stack(|| {
-                self.reachable_blocks_in_mono_from(tcx, instance, set, target)
-            });
-        }
-    }
-
     /// If this basic block ends with a [`TerminatorKind::SwitchInt`] for which we can evaluate the
     /// dimscriminant in monomorphization, we return the discriminant bits and the
     /// [`SwitchTargets`], just so the caller doesn't also have to match on the terminator.
diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs
index 0a938bcd315..245e9096bad 100644
--- a/compiler/rustc_middle/src/mir/traversal.rs
+++ b/compiler/rustc_middle/src/mir/traversal.rs
@@ -245,7 +245,7 @@ pub fn reachable<'a, 'tcx>(
 /// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`.
 pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet<BasicBlock> {
     let mut iter = preorder(body);
-    iter.by_ref().for_each(drop);
+    while let Some(_) = iter.next() {}
     iter.visited
 }
 
@@ -279,3 +279,97 @@ pub fn reverse_postorder<'a, 'tcx>(
 {
     body.basic_blocks.reverse_postorder().iter().map(|&bb| (bb, &body.basic_blocks[bb]))
 }
+
+/// Traversal of a [`Body`] that tries to avoid unreachable blocks in a monomorphized [`Instance`].
+///
+/// This is allowed to have false positives; blocks may be visited even if they are not actually
+/// reachable.
+///
+/// Such a traversal is mostly useful because it lets us skip lowering the `false` side
+/// of `if <T as Trait>::CONST`, as well as [`NullOp::UbChecks`].
+///
+/// [`NullOp::UbChecks`]: rustc_middle::mir::NullOp::UbChecks
+pub fn mono_reachable<'a, 'tcx>(
+    body: &'a Body<'tcx>,
+    tcx: TyCtxt<'tcx>,
+    instance: Instance<'tcx>,
+) -> MonoReachable<'a, 'tcx> {
+    MonoReachable::new(body, tcx, instance)
+}
+
+/// [`MonoReachable`] internally accumulates a [`BitSet`] of visited blocks. This is just a
+/// convenience function to run that traversal then extract its set of reached blocks.
+pub fn mono_reachable_as_bitset<'a, 'tcx>(
+    body: &'a Body<'tcx>,
+    tcx: TyCtxt<'tcx>,
+    instance: Instance<'tcx>,
+) -> BitSet<BasicBlock> {
+    let mut iter = mono_reachable(body, tcx, instance);
+    while let Some(_) = iter.next() {}
+    iter.visited
+}
+
+pub struct MonoReachable<'a, 'tcx> {
+    body: &'a Body<'tcx>,
+    tcx: TyCtxt<'tcx>,
+    instance: Instance<'tcx>,
+    visited: BitSet<BasicBlock>,
+    // Other traversers track their worklist in a Vec. But we don't care about order, so we can
+    // store ours in a BitSet and thus save allocations because BitSet has a small size
+    // optimization.
+    worklist: BitSet<BasicBlock>,
+}
+
+impl<'a, 'tcx> MonoReachable<'a, 'tcx> {
+    pub fn new(
+        body: &'a Body<'tcx>,
+        tcx: TyCtxt<'tcx>,
+        instance: Instance<'tcx>,
+    ) -> MonoReachable<'a, 'tcx> {
+        let mut worklist = BitSet::new_empty(body.basic_blocks.len());
+        worklist.insert(START_BLOCK);
+        MonoReachable {
+            body,
+            tcx,
+            instance,
+            visited: BitSet::new_empty(body.basic_blocks.len()),
+            worklist,
+        }
+    }
+
+    fn add_work(&mut self, blocks: impl IntoIterator<Item = BasicBlock>) {
+        for block in blocks.into_iter() {
+            if !self.visited.contains(block) {
+                self.worklist.insert(block);
+            }
+        }
+    }
+}
+
+impl<'a, 'tcx> Iterator for MonoReachable<'a, 'tcx> {
+    type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
+
+    fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
+        while let Some(idx) = self.worklist.iter().next() {
+            self.worklist.remove(idx);
+            if !self.visited.insert(idx) {
+                continue;
+            }
+
+            let data = &self.body[idx];
+
+            if let Some((bits, targets)) =
+                Body::try_const_mono_switchint(self.tcx, self.instance, data)
+            {
+                let target = targets.target_for_value(bits);
+                self.add_work([target]);
+            } else {
+                self.add_work(data.terminator().successors());
+            }
+
+            return Some((idx, data));
+        }
+
+        None
+    }
+}