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}