rustc_middle/mir/
traversal.rs

1use super::*;
2
3/// Preorder traversal of a graph.
4///
5/// Preorder traversal is when each node is visited after at least one of its predecessors. If you
6/// are familiar with some basic graph theory, then this performs a depth first search and returns
7/// nodes in order of discovery time.
8///
9/// ```text
10///
11///         A
12///        / \
13///       /   \
14///      B     C
15///       \   /
16///        \ /
17///         D
18/// ```
19///
20/// A preorder traversal of this graph is either `A B D C` or `A C D B`
21#[derive(Clone)]
22pub struct Preorder<'a, 'tcx> {
23    body: &'a Body<'tcx>,
24    visited: DenseBitSet<BasicBlock>,
25    worklist: Vec<BasicBlock>,
26}
27
28impl<'a, 'tcx> Preorder<'a, 'tcx> {
29    pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> Preorder<'a, 'tcx> {
30        let worklist = vec![root];
31
32        Preorder { body, visited: DenseBitSet::new_empty(body.basic_blocks.len()), worklist }
33    }
34}
35
36/// Preorder traversal of a graph.
37///
38/// This function creates an iterator over the `Body`'s basic blocks, that
39/// returns basic blocks in a preorder.
40///
41/// See [`Preorder`]'s docs to learn what is preorder traversal.
42pub fn preorder<'a, 'tcx>(body: &'a Body<'tcx>) -> Preorder<'a, 'tcx> {
43    Preorder::new(body, START_BLOCK)
44}
45
46impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> {
47    type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
48
49    fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
50        while let Some(idx) = self.worklist.pop() {
51            if !self.visited.insert(idx) {
52                continue;
53            }
54
55            let data = &self.body[idx];
56
57            if let Some(ref term) = data.terminator {
58                self.worklist.extend(term.successors());
59            }
60
61            return Some((idx, data));
62        }
63
64        None
65    }
66
67    fn size_hint(&self) -> (usize, Option<usize>) {
68        // The worklist might be only things already visited.
69        let lower = 0;
70
71        // This is extremely loose, but it's not worth a popcnt loop to do better.
72        let upper = self.body.basic_blocks.len();
73
74        (lower, Some(upper))
75    }
76}
77
78/// Postorder traversal of a graph.
79///
80/// Postorder traversal is when each node is visited after all of its successors, except when the
81/// successor is only reachable by a back-edge. If you are familiar with some basic graph theory,
82/// then this performs a depth first search and returns nodes in order of completion time.
83///
84///
85/// ```text
86///
87///         A
88///        / \
89///       /   \
90///      B     C
91///       \   /
92///        \ /
93///         D
94/// ```
95///
96/// A Postorder traversal of this graph is `D B C A` or `D C B A`
97pub struct Postorder<'a, 'tcx> {
98    basic_blocks: &'a IndexSlice<BasicBlock, BasicBlockData<'tcx>>,
99    visited: DenseBitSet<BasicBlock>,
100    visit_stack: Vec<(BasicBlock, Successors<'a>)>,
101    /// A non-empty `extra` allows for a precise calculation of the successors.
102    extra: Option<(TyCtxt<'tcx>, Instance<'tcx>)>,
103}
104
105impl<'a, 'tcx> Postorder<'a, 'tcx> {
106    pub fn new(
107        basic_blocks: &'a IndexSlice<BasicBlock, BasicBlockData<'tcx>>,
108        root: BasicBlock,
109        extra: Option<(TyCtxt<'tcx>, Instance<'tcx>)>,
110    ) -> Postorder<'a, 'tcx> {
111        let mut po = Postorder {
112            basic_blocks,
113            visited: DenseBitSet::new_empty(basic_blocks.len()),
114            visit_stack: Vec::new(),
115            extra,
116        };
117
118        po.visit(root);
119        po.traverse_successor();
120
121        po
122    }
123
124    fn visit(&mut self, bb: BasicBlock) {
125        if !self.visited.insert(bb) {
126            return;
127        }
128        let data = &self.basic_blocks[bb];
129        let successors = if let Some(extra) = self.extra {
130            data.mono_successors(extra.0, extra.1)
131        } else {
132            data.terminator().successors()
133        };
134        self.visit_stack.push((bb, successors));
135    }
136
137    fn traverse_successor(&mut self) {
138        // This is quite a complex loop due to 1. the borrow checker not liking it much
139        // and 2. what exactly is going on is not clear
140        //
141        // It does the actual traversal of the graph, while the `next` method on the iterator
142        // just pops off of the stack. `visit_stack` is a stack containing pairs of nodes and
143        // iterators over the successors of those nodes. Each iteration attempts to get the next
144        // node from the top of the stack, then pushes that node and an iterator over the
145        // successors to the top of the stack. This loop only grows `visit_stack`, stopping when
146        // we reach a child that has no children that we haven't already visited.
147        //
148        // For a graph that looks like this:
149        //
150        //         A
151        //        / \
152        //       /   \
153        //      B     C
154        //      |     |
155        //      |     |
156        //      |     D
157        //       \   /
158        //        \ /
159        //         E
160        //
161        // The state of the stack starts out with just the root node (`A` in this case);
162        //     [(A, [B, C])]
163        //
164        // When the first call to `traverse_successor` happens, the following happens:
165        //
166        //     [(C, [D]),  // `C` taken from the successors of `A`, pushed to the
167        //                 // top of the stack along with the successors of `C`
168        //      (A, [B])]
169        //
170        //     [(D, [E]),  // `D` taken from successors of `C`, pushed to stack
171        //      (C, []),
172        //      (A, [B])]
173        //
174        //     [(E, []),   // `E` taken from successors of `D`, pushed to stack
175        //      (D, []),
176        //      (C, []),
177        //      (A, [B])]
178        //
179        // Now that the top of the stack has no successors we can traverse, each item will
180        // be popped off during iteration until we get back to `A`. This yields [E, D, C].
181        //
182        // When we yield `C` and call `traverse_successor`, we push `B` to the stack, but
183        // since we've already visited `E`, that child isn't added to the stack. The last
184        // two iterations yield `B` and finally `A` for a final traversal of [E, D, C, B, A]
185        while let Some(bb) = self.visit_stack.last_mut().and_then(|(_, iter)| iter.next_back()) {
186            self.visit(bb);
187        }
188    }
189}
190
191impl<'tcx> Iterator for Postorder<'_, 'tcx> {
192    type Item = BasicBlock;
193
194    fn next(&mut self) -> Option<BasicBlock> {
195        let (bb, _) = self.visit_stack.pop()?;
196        self.traverse_successor();
197
198        Some(bb)
199    }
200
201    fn size_hint(&self) -> (usize, Option<usize>) {
202        // These bounds are not at all tight, but that's fine.
203        // It's not worth a popcnt loop in `DenseBitSet` to improve the upper,
204        // and in mono-reachable we can't be precise anyway.
205        // Leaning on amortized growth is fine.
206
207        let lower = self.visit_stack.len();
208        let upper = self.basic_blocks.len();
209        (lower, Some(upper))
210    }
211}
212
213/// Postorder traversal of a graph.
214///
215/// This function creates an iterator over the `Body`'s basic blocks, that:
216/// - returns basic blocks in a postorder,
217/// - traverses the `BasicBlocks` CFG cache's reverse postorder backwards, and does not cache the
218///   postorder itself.
219///
220/// See [`Postorder`]'s docs to learn what is postorder traversal.
221pub fn postorder<'a, 'tcx>(
222    body: &'a Body<'tcx>,
223) -> impl Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> + ExactSizeIterator + DoubleEndedIterator
224{
225    reverse_postorder(body).rev()
226}
227
228pub fn mono_reachable_reverse_postorder<'a, 'tcx>(
229    body: &'a Body<'tcx>,
230    tcx: TyCtxt<'tcx>,
231    instance: Instance<'tcx>,
232) -> Vec<BasicBlock> {
233    let mut iter = Postorder::new(&body.basic_blocks, START_BLOCK, Some((tcx, instance)));
234    let mut items = Vec::with_capacity(body.basic_blocks.len());
235    while let Some(block) = iter.next() {
236        items.push(block);
237    }
238    items.reverse();
239    items
240}
241
242/// Returns an iterator over all basic blocks reachable from the `START_BLOCK` in no particular
243/// order.
244///
245/// This is clearer than writing `preorder` in cases where the order doesn't matter.
246pub fn reachable<'a, 'tcx>(
247    body: &'a Body<'tcx>,
248) -> impl 'a + Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> {
249    preorder(body)
250}
251
252/// Returns a `DenseBitSet` containing all basic blocks reachable from the `START_BLOCK`.
253pub fn reachable_as_bitset(body: &Body<'_>) -> DenseBitSet<BasicBlock> {
254    let mut iter = preorder(body);
255    while let Some(_) = iter.next() {}
256    iter.visited
257}
258
259/// Reverse postorder traversal of a graph.
260///
261/// This function creates an iterator over the `Body`'s basic blocks, that:
262/// - returns basic blocks in a reverse postorder,
263/// - makes use of the `BasicBlocks` CFG cache's reverse postorder.
264///
265/// Reverse postorder is the reverse order of a postorder traversal.
266/// This is different to a preorder traversal and represents a natural
267/// linearization of control-flow.
268///
269/// ```text
270///
271///         A
272///        / \
273///       /   \
274///      B     C
275///       \   /
276///        \ /
277///         D
278/// ```
279///
280/// A reverse postorder traversal of this graph is either `A B C D` or `A C B D`
281/// Note that for a graph containing no loops (i.e., A DAG), this is equivalent to
282/// a topological sort.
283pub fn reverse_postorder<'a, 'tcx>(
284    body: &'a Body<'tcx>,
285) -> impl Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> + ExactSizeIterator + DoubleEndedIterator
286{
287    body.basic_blocks.reverse_postorder().iter().map(|&bb| (bb, &body.basic_blocks[bb]))
288}
289
290/// Traversal of a [`Body`] that tries to avoid unreachable blocks in a monomorphized [`Instance`].
291///
292/// This is allowed to have false positives; blocks may be visited even if they are not actually
293/// reachable.
294///
295/// Such a traversal is mostly useful because it lets us skip lowering the `false` side
296/// of `if <T as Trait>::CONST`, as well as [`NullOp::UbChecks`].
297///
298/// [`NullOp::UbChecks`]: rustc_middle::mir::NullOp::UbChecks
299pub fn mono_reachable<'a, 'tcx>(
300    body: &'a Body<'tcx>,
301    tcx: TyCtxt<'tcx>,
302    instance: Instance<'tcx>,
303) -> MonoReachable<'a, 'tcx> {
304    MonoReachable::new(body, tcx, instance)
305}
306
307/// [`MonoReachable`] internally accumulates a [`DenseBitSet`] of visited blocks. This is just a
308/// convenience function to run that traversal then extract its set of reached blocks.
309pub fn mono_reachable_as_bitset<'a, 'tcx>(
310    body: &'a Body<'tcx>,
311    tcx: TyCtxt<'tcx>,
312    instance: Instance<'tcx>,
313) -> DenseBitSet<BasicBlock> {
314    let mut iter = mono_reachable(body, tcx, instance);
315    while let Some(_) = iter.next() {}
316    iter.visited
317}
318
319pub struct MonoReachable<'a, 'tcx> {
320    body: &'a Body<'tcx>,
321    tcx: TyCtxt<'tcx>,
322    instance: Instance<'tcx>,
323    visited: DenseBitSet<BasicBlock>,
324    // Other traversers track their worklist in a Vec. But we don't care about order, so we can
325    // store ours in a DenseBitSet and thus save allocations because DenseBitSet has a small size
326    // optimization.
327    worklist: DenseBitSet<BasicBlock>,
328}
329
330impl<'a, 'tcx> MonoReachable<'a, 'tcx> {
331    pub fn new(
332        body: &'a Body<'tcx>,
333        tcx: TyCtxt<'tcx>,
334        instance: Instance<'tcx>,
335    ) -> MonoReachable<'a, 'tcx> {
336        let mut worklist = DenseBitSet::new_empty(body.basic_blocks.len());
337        worklist.insert(START_BLOCK);
338        MonoReachable {
339            body,
340            tcx,
341            instance,
342            visited: DenseBitSet::new_empty(body.basic_blocks.len()),
343            worklist,
344        }
345    }
346
347    fn add_work(&mut self, blocks: impl IntoIterator<Item = BasicBlock>) {
348        for block in blocks.into_iter() {
349            if !self.visited.contains(block) {
350                self.worklist.insert(block);
351            }
352        }
353    }
354}
355
356impl<'a, 'tcx> Iterator for MonoReachable<'a, 'tcx> {
357    type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
358
359    fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
360        while let Some(idx) = self.worklist.iter().next() {
361            self.worklist.remove(idx);
362            if !self.visited.insert(idx) {
363                continue;
364            }
365
366            let data = &self.body[idx];
367
368            let targets = data.mono_successors(self.tcx, self.instance);
369            self.add_work(targets);
370
371            return Some((idx, data));
372        }
373
374        None
375    }
376}