]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_middle/src/mir/traversal.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / compiler / rustc_middle / src / mir / traversal.rs
CommitLineData
04454e1e
FG
1use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
2use rustc_data_structures::sync::OnceCell;
e74abb32 3use rustc_index::bit_set::BitSet;
923072b8 4use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
54a0048b 5
c30ab7b3 6use super::*;
54a0048b
SL
7
8/// Preorder traversal of a graph.
9///
5099ac24 10/// Preorder traversal is when each node is visited after at least one of its predecessors. If you
5e7ed085 11/// are familiar with some basic graph theory, then this performs a depth first search and returns
5099ac24 12/// nodes in order of discovery time.
54a0048b 13///
a7813a04
XL
14/// ```text
15///
54a0048b
SL
16/// A
17/// / \
18/// / \
19/// B C
20/// \ /
21/// \ /
22/// D
a7813a04 23/// ```
54a0048b
SL
24///
25/// A preorder traversal of this graph is either `A B D C` or `A C D B`
26#[derive(Clone)]
dc9dc135
XL
27pub struct Preorder<'a, 'tcx> {
28 body: &'a Body<'tcx>,
0bf4aa26 29 visited: BitSet<BasicBlock>,
54a0048b 30 worklist: Vec<BasicBlock>,
a1dfa0c6 31 root_is_start_block: bool,
54a0048b
SL
32}
33
34impl<'a, 'tcx> Preorder<'a, 'tcx> {
dc9dc135 35 pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> Preorder<'a, 'tcx> {
54a0048b
SL
36 let worklist = vec![root];
37
38 Preorder {
dc9dc135
XL
39 body,
40 visited: BitSet::new_empty(body.basic_blocks().len()),
041b39d2 41 worklist,
a1dfa0c6 42 root_is_start_block: root == START_BLOCK,
54a0048b
SL
43 }
44 }
45}
46
dc9dc135
XL
47pub fn preorder<'a, 'tcx>(body: &'a Body<'tcx>) -> Preorder<'a, 'tcx> {
48 Preorder::new(body, START_BLOCK)
54a0048b
SL
49}
50
51impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> {
52 type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
53
54 fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
55 while let Some(idx) = self.worklist.pop() {
8faf50e0 56 if !self.visited.insert(idx) {
54a0048b
SL
57 continue;
58 }
59
dc9dc135 60 let data = &self.body[idx];
54a0048b
SL
61
62 if let Some(ref term) = data.terminator {
8faf50e0 63 self.worklist.extend(term.successors());
54a0048b
SL
64 }
65
66 return Some((idx, data));
67 }
68
69 None
70 }
0531ce1d
XL
71
72 fn size_hint(&self) -> (usize, Option<usize>) {
73 // All the blocks, minus the number of blocks we've visited.
dc9dc135 74 let upper = self.body.basic_blocks().len() - self.visited.count();
0531ce1d 75
a1dfa0c6
XL
76 let lower = if self.root_is_start_block {
77 // We will visit all remaining blocks exactly once.
78 upper
79 } else {
80 self.worklist.len()
81 };
82
83 (lower, Some(upper))
0531ce1d 84 }
54a0048b
SL
85}
86
87/// Postorder traversal of a graph.
88///
5099ac24
FG
89/// Postorder traversal is when each node is visited after all of its successors, except when the
90/// successor is only reachable by a back-edge. If you are familiar with some basic graph theory,
91/// then this performs a depth first search and returns nodes in order of completion time.
54a0048b 92///
a7813a04
XL
93///
94/// ```text
95///
54a0048b
SL
96/// A
97/// / \
98/// / \
99/// B C
100/// \ /
101/// \ /
102/// D
a7813a04 103/// ```
54a0048b
SL
104///
105/// A Postorder traversal of this graph is `D B C A` or `D C B A`
dc9dc135
XL
106pub struct Postorder<'a, 'tcx> {
107 body: &'a Body<'tcx>,
0bf4aa26 108 visited: BitSet<BasicBlock>,
a1dfa0c6
XL
109 visit_stack: Vec<(BasicBlock, Successors<'a>)>,
110 root_is_start_block: bool,
54a0048b
SL
111}
112
113impl<'a, 'tcx> Postorder<'a, 'tcx> {
dc9dc135 114 pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> Postorder<'a, 'tcx> {
54a0048b 115 let mut po = Postorder {
dc9dc135
XL
116 body,
117 visited: BitSet::new_empty(body.basic_blocks().len()),
a1dfa0c6
XL
118 visit_stack: Vec::new(),
119 root_is_start_block: root == START_BLOCK,
54a0048b
SL
120 };
121
dc9dc135 122 let data = &po.body[root];
54a0048b
SL
123
124 if let Some(ref term) = data.terminator {
8faf50e0 125 po.visited.insert(root);
83c7162d 126 po.visit_stack.push((root, term.successors()));
54a0048b
SL
127 po.traverse_successor();
128 }
129
130 po
131 }
132
133 fn traverse_successor(&mut self) {
134 // This is quite a complex loop due to 1. the borrow checker not liking it much
135 // and 2. what exactly is going on is not clear
136 //
137 // It does the actual traversal of the graph, while the `next` method on the iterator
138 // just pops off of the stack. `visit_stack` is a stack containing pairs of nodes and
a1dfa0c6 139 // iterators over the successors of those nodes. Each iteration attempts to get the next
54a0048b
SL
140 // node from the top of the stack, then pushes that node and an iterator over the
141 // successors to the top of the stack. This loop only grows `visit_stack`, stopping when
142 // we reach a child that has no children that we haven't already visited.
143 //
144 // For a graph that looks like this:
145 //
146 // A
147 // / \
148 // / \
149 // B C
150 // | |
151 // | |
152 // D |
153 // \ /
154 // \ /
155 // E
156 //
157 // The state of the stack starts out with just the root node (`A` in this case);
158 // [(A, [B, C])]
159 //
a1dfa0c6 160 // When the first call to `traverse_successor` happens, the following happens:
54a0048b
SL
161 //
162 // [(B, [D]), // `B` taken from the successors of `A`, pushed to the
163 // // top of the stack along with the successors of `B`
164 // (A, [C])]
165 //
166 // [(D, [E]), // `D` taken from successors of `B`, pushed to stack
167 // (B, []),
168 // (A, [C])]
169 //
170 // [(E, []), // `E` taken from successors of `D`, pushed to stack
171 // (D, []),
172 // (B, []),
173 // (A, [C])]
174 //
175 // Now that the top of the stack has no successors we can traverse, each item will
b7449926 176 // be popped off during iteration until we get back to `A`. This yields [E, D, B].
54a0048b
SL
177 //
178 // When we yield `B` and call `traverse_successor`, we push `C` to the stack, but
179 // since we've already visited `E`, that child isn't added to the stack. The last
180 // two iterations yield `C` and finally `A` for a final traversal of [E, D, B, C, A]
181 loop {
182 let bb = if let Some(&mut (_, ref mut iter)) = self.visit_stack.last_mut() {
923072b8 183 if let Some(bb) = iter.next() {
54a0048b
SL
184 bb
185 } else {
186 break;
187 }
188 } else {
189 break;
190 };
191
8faf50e0 192 if self.visited.insert(bb) {
dc9dc135 193 if let Some(term) = &self.body[bb].terminator {
83c7162d 194 self.visit_stack.push((bb, term.successors()));
54a0048b
SL
195 }
196 }
197 }
198 }
199}
200
dc9dc135
XL
201pub fn postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> Postorder<'a, 'tcx> {
202 Postorder::new(body, START_BLOCK)
54a0048b
SL
203}
204
205impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
206 type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
207
208 fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
209 let next = self.visit_stack.pop();
210 if next.is_some() {
211 self.traverse_successor();
212 }
213
dc9dc135 214 next.map(|(bb, _)| (bb, &self.body[bb]))
54a0048b 215 }
0531ce1d
XL
216
217 fn size_hint(&self) -> (usize, Option<usize>) {
218 // All the blocks, minus the number of blocks we've visited.
dc9dc135 219 let upper = self.body.basic_blocks().len() - self.visited.count();
0531ce1d 220
a1dfa0c6
XL
221 let lower = if self.root_is_start_block {
222 // We will visit all remaining blocks exactly once.
223 upper
224 } else {
225 self.visit_stack.len()
226 };
227
228 (lower, Some(upper))
0531ce1d 229 }
54a0048b
SL
230}
231
232/// Reverse postorder traversal of a graph
233///
234/// Reverse postorder is the reverse order of a postorder traversal.
235/// This is different to a preorder traversal and represents a natural
3b2f2976 236/// linearization of control-flow.
54a0048b 237///
a7813a04
XL
238/// ```text
239///
54a0048b
SL
240/// A
241/// / \
242/// / \
243/// B C
244/// \ /
245/// \ /
246/// D
a7813a04 247/// ```
54a0048b
SL
248///
249/// A reverse postorder traversal of this graph is either `A B C D` or `A C B D`
0731742a 250/// Note that for a graph containing no loops (i.e., A DAG), this is equivalent to
54a0048b
SL
251/// a topological sort.
252///
253/// Construction of a `ReversePostorder` traversal requires doing a full
254/// postorder traversal of the graph, therefore this traversal should be
255/// constructed as few times as possible. Use the `reset` method to be able
256/// to re-use the traversal
257#[derive(Clone)]
dc9dc135
XL
258pub struct ReversePostorder<'a, 'tcx> {
259 body: &'a Body<'tcx>,
54a0048b 260 blocks: Vec<BasicBlock>,
dfeec247 261 idx: usize,
54a0048b
SL
262}
263
264impl<'a, 'tcx> ReversePostorder<'a, 'tcx> {
dc9dc135 265 pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> ReversePostorder<'a, 'tcx> {
dfeec247 266 let blocks: Vec<_> = Postorder::new(body, root).map(|(bb, _)| bb).collect();
54a0048b
SL
267
268 let len = blocks.len();
269
dfeec247 270 ReversePostorder { body, blocks, idx: len }
54a0048b 271 }
54a0048b
SL
272}
273
54a0048b
SL
274impl<'a, 'tcx> Iterator for ReversePostorder<'a, 'tcx> {
275 type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
276
277 fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
dfeec247
XL
278 if self.idx == 0 {
279 return None;
280 }
54a0048b
SL
281 self.idx -= 1;
282
dc9dc135 283 self.blocks.get(self.idx).map(|&bb| (bb, &self.body[bb]))
54a0048b 284 }
0531ce1d
XL
285
286 fn size_hint(&self) -> (usize, Option<usize>) {
287 (self.idx, Some(self.idx))
288 }
54a0048b 289}
0531ce1d
XL
290
291impl<'a, 'tcx> ExactSizeIterator for ReversePostorder<'a, 'tcx> {}
3dfed10e
XL
292
293/// Returns an iterator over all basic blocks reachable from the `START_BLOCK` in no particular
294/// order.
295///
296/// This is clearer than writing `preorder` in cases where the order doesn't matter.
297pub fn reachable<'a, 'tcx>(
298 body: &'a Body<'tcx>,
299) -> impl 'a + Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> {
300 preorder(body)
301}
302
303/// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`.
a2a8927a 304pub fn reachable_as_bitset<'tcx>(body: &Body<'tcx>) -> BitSet<BasicBlock> {
3dfed10e
XL
305 let mut iter = preorder(body);
306 (&mut iter).for_each(drop);
307 iter.visited
308}
04454e1e
FG
309
310#[derive(Clone)]
311pub struct ReversePostorderIter<'a, 'tcx> {
312 body: &'a Body<'tcx>,
923072b8 313 blocks: &'a [BasicBlock],
04454e1e
FG
314 idx: usize,
315}
316
317impl<'a, 'tcx> Iterator for ReversePostorderIter<'a, 'tcx> {
318 type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
319
320 fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
321 if self.idx == 0 {
322 return None;
323 }
324 self.idx -= 1;
325
326 self.blocks.get(self.idx).map(|&bb| (bb, &self.body[bb]))
327 }
328
329 fn size_hint(&self) -> (usize, Option<usize>) {
330 (self.idx, Some(self.idx))
331 }
332}
333
334impl<'a, 'tcx> ExactSizeIterator for ReversePostorderIter<'a, 'tcx> {}
335
336pub fn reverse_postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> ReversePostorderIter<'a, 'tcx> {
337 let blocks = body.postorder_cache.compute(body);
338
339 let len = blocks.len();
340
341 ReversePostorderIter { body, blocks, idx: len }
342}
343
344#[derive(Clone, Debug)]
345pub(super) struct PostorderCache {
346 cache: OnceCell<Vec<BasicBlock>>,
347}
348
349impl PostorderCache {
350 #[inline]
351 pub(super) fn new() -> Self {
352 PostorderCache { cache: OnceCell::new() }
353 }
354
355 /// Invalidates the postorder cache.
356 #[inline]
357 pub(super) fn invalidate(&mut self) {
358 self.cache = OnceCell::new();
359 }
360
923072b8 361 /// Returns the `&[BasicBlocks]` represents the postorder graph for this MIR.
04454e1e 362 #[inline]
923072b8 363 pub(super) fn compute(&self, body: &Body<'_>) -> &[BasicBlock] {
04454e1e
FG
364 self.cache.get_or_init(|| Postorder::new(body, START_BLOCK).map(|(bb, _)| bb).collect())
365 }
366}
367
923072b8 368impl<S: Encoder> Encodable<S> for PostorderCache {
04454e1e 369 #[inline]
923072b8 370 fn encode(&self, _s: &mut S) {}
04454e1e
FG
371}
372
923072b8 373impl<D: Decoder> Decodable<D> for PostorderCache {
04454e1e
FG
374 #[inline]
375 fn decode(_: &mut D) -> Self {
376 Self::new()
377 }
378}
379
380impl<CTX> HashStable<CTX> for PostorderCache {
381 #[inline]
382 fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
383 // do nothing
384 }
385}
386
387TrivialTypeFoldableAndLiftImpls! {
388 PostorderCache,
389}