Skip to content

Commit 75d731e

Browse files
committed
Auto merge of #116254 - WaffleLapkin:nicen-traversal, r=cjgillot
Assorted improvements for `rustc_middle::mir::traversal` r? `@cjgillot` I'm not _entirely_ sure about all changes, although I do like all of them. If you'd like I can drop some commits. Best reviewed on a commit-by-commit basis, I think, since they are fairly isolated.
2 parents 5282e5e + 814fbd8 commit 75d731e

File tree

3 files changed

+64
-85
lines changed

3 files changed

+64
-85
lines changed

Diff for: compiler/rustc_middle/src/mir/basic_blocks.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,14 @@ impl<'tcx> BasicBlocks<'tcx> {
6363
}
6464

6565
/// Returns basic blocks in a reverse postorder.
66+
///
67+
/// See [`traversal::reverse_postorder`]'s docs to learn what is preorder traversal.
68+
///
69+
/// [`traversal::reverse_postorder`]: crate::mir::traversal::reverse_postorder
6670
#[inline]
6771
pub fn reverse_postorder(&self) -> &[BasicBlock] {
6872
self.cache.reverse_postorder.get_or_init(|| {
69-
let mut rpo: Vec<_> =
70-
Postorder::new(&self.basic_blocks, START_BLOCK).map(|(bb, _)| bb).collect();
73+
let mut rpo: Vec<_> = Postorder::new(&self.basic_blocks, START_BLOCK).collect();
7174
rpo.reverse();
7275
rpo
7376
})

Diff for: compiler/rustc_middle/src/mir/traversal.rs

+40-70
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ impl<'a, 'tcx> Preorder<'a, 'tcx> {
4141
}
4242
}
4343

44+
/// Preorder traversal of a graph.
45+
///
46+
/// This function creates an iterator over the `Body`'s basic blocks, that
47+
/// returns basic blocks in a preorder.
48+
///
49+
/// See [`Preorder`]'s docs to learn what is preorder traversal.
4450
pub fn preorder<'a, 'tcx>(body: &'a Body<'tcx>) -> Preorder<'a, 'tcx> {
4551
Preorder::new(body, START_BLOCK)
4652
}
@@ -178,7 +184,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
178184
// When we yield `C` and call `traverse_successor`, we push `B` to the stack, but
179185
// since we've already visited `E`, that child isn't added to the stack. The last
180186
// two iterations yield `B` and finally `A` for a final traversal of [E, D, C, B, A]
181-
while let Some(&mut (_, ref mut iter)) = self.visit_stack.last_mut() && let Some(bb) = iter.next_back() {
187+
while let Some(bb) = self.visit_stack.last_mut().and_then(|(_, iter)| iter.next_back()) {
182188
if self.visited.insert(bb) {
183189
if let Some(term) = &self.basic_blocks[bb].terminator {
184190
self.visit_stack.push((bb, term.successors()));
@@ -188,16 +194,14 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
188194
}
189195
}
190196

191-
impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
192-
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
197+
impl<'tcx> Iterator for Postorder<'_, 'tcx> {
198+
type Item = BasicBlock;
193199

194-
fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
195-
let next = self.visit_stack.pop();
196-
if next.is_some() {
197-
self.traverse_successor();
198-
}
200+
fn next(&mut self) -> Option<BasicBlock> {
201+
let (bb, _) = self.visit_stack.pop()?;
202+
self.traverse_successor();
199203

200-
next.map(|(bb, _)| (bb, &self.basic_blocks[bb]))
204+
Some(bb)
201205
}
202206

203207
fn size_hint(&self) -> (usize, Option<usize>) {
@@ -215,18 +219,43 @@ impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
215219
}
216220
}
217221

218-
/// Creates an iterator over the `Body`'s basic blocks, that:
222+
/// Postorder traversal of a graph.
223+
///
224+
/// This function creates an iterator over the `Body`'s basic blocks, that:
219225
/// - returns basic blocks in a postorder,
220226
/// - traverses the `BasicBlocks` CFG cache's reverse postorder backwards, and does not cache the
221227
/// postorder itself.
228+
///
229+
/// See [`Postorder`]'s docs to learn what is postorder traversal.
222230
pub fn postorder<'a, 'tcx>(
223231
body: &'a Body<'tcx>,
224232
) -> impl Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> + ExactSizeIterator + DoubleEndedIterator
225233
{
226234
reverse_postorder(body).rev()
227235
}
228236

229-
/// Reverse postorder traversal of a graph
237+
/// Returns an iterator over all basic blocks reachable from the `START_BLOCK` in no particular
238+
/// order.
239+
///
240+
/// This is clearer than writing `preorder` in cases where the order doesn't matter.
241+
pub fn reachable<'a, 'tcx>(
242+
body: &'a Body<'tcx>,
243+
) -> impl 'a + Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> {
244+
preorder(body)
245+
}
246+
247+
/// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`.
248+
pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet<BasicBlock> {
249+
let mut iter = preorder(body);
250+
iter.by_ref().for_each(drop);
251+
iter.visited
252+
}
253+
254+
/// Reverse postorder traversal of a graph.
255+
///
256+
/// This function creates an iterator over the `Body`'s basic blocks, that:
257+
/// - returns basic blocks in a reverse postorder,
258+
/// - makes use of the `BasicBlocks` CFG cache's reverse postorder.
230259
///
231260
/// Reverse postorder is the reverse order of a postorder traversal.
232261
/// This is different to a preorder traversal and represents a natural
@@ -246,65 +275,6 @@ pub fn postorder<'a, 'tcx>(
246275
/// A reverse postorder traversal of this graph is either `A B C D` or `A C B D`
247276
/// Note that for a graph containing no loops (i.e., A DAG), this is equivalent to
248277
/// a topological sort.
249-
///
250-
/// Construction of a `ReversePostorder` traversal requires doing a full
251-
/// postorder traversal of the graph, therefore this traversal should be
252-
/// constructed as few times as possible. Use the `reset` method to be able
253-
/// to re-use the traversal
254-
#[derive(Clone)]
255-
pub struct ReversePostorder<'a, 'tcx> {
256-
body: &'a Body<'tcx>,
257-
blocks: Vec<BasicBlock>,
258-
idx: usize,
259-
}
260-
261-
impl<'a, 'tcx> ReversePostorder<'a, 'tcx> {
262-
pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> ReversePostorder<'a, 'tcx> {
263-
let blocks: Vec<_> = Postorder::new(&body.basic_blocks, root).map(|(bb, _)| bb).collect();
264-
let len = blocks.len();
265-
ReversePostorder { body, blocks, idx: len }
266-
}
267-
}
268-
269-
impl<'a, 'tcx> Iterator for ReversePostorder<'a, 'tcx> {
270-
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
271-
272-
fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
273-
if self.idx == 0 {
274-
return None;
275-
}
276-
self.idx -= 1;
277-
278-
self.blocks.get(self.idx).map(|&bb| (bb, &self.body[bb]))
279-
}
280-
281-
fn size_hint(&self) -> (usize, Option<usize>) {
282-
(self.idx, Some(self.idx))
283-
}
284-
}
285-
286-
impl<'a, 'tcx> ExactSizeIterator for ReversePostorder<'a, 'tcx> {}
287-
288-
/// Returns an iterator over all basic blocks reachable from the `START_BLOCK` in no particular
289-
/// order.
290-
///
291-
/// This is clearer than writing `preorder` in cases where the order doesn't matter.
292-
pub fn reachable<'a, 'tcx>(
293-
body: &'a Body<'tcx>,
294-
) -> impl 'a + Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> {
295-
preorder(body)
296-
}
297-
298-
/// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`.
299-
pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet<BasicBlock> {
300-
let mut iter = preorder(body);
301-
(&mut iter).for_each(drop);
302-
iter.visited
303-
}
304-
305-
/// Creates an iterator over the `Body`'s basic blocks, that:
306-
/// - returns basic blocks in a reverse postorder,
307-
/// - makes use of the `BasicBlocks` CFG cache's reverse postorder.
308278
pub fn reverse_postorder<'a, 'tcx>(
309279
body: &'a Body<'tcx>,
310280
) -> impl Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> + ExactSizeIterator + DoubleEndedIterator

Diff for: src/tools/clippy/clippy_utils/src/mir/mod.rs

+19-13
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,26 @@ pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) -
3030
locals.len()
3131
];
3232

33-
traversal::ReversePostorder::new(mir, location.block).try_fold(init, |usage, (tbb, tdata)| {
34-
// Give up on loops
35-
if tdata.terminator().successors().any(|s| s == location.block) {
36-
return None;
37-
}
33+
traversal::Postorder::new(&mir.basic_blocks, location.block)
34+
.collect::<Vec<_>>()
35+
.into_iter()
36+
.rev()
37+
.try_fold(init, |usage, tbb| {
38+
let tdata = &mir.basic_blocks[tbb];
39+
40+
// Give up on loops
41+
if tdata.terminator().successors().any(|s| s == location.block) {
42+
return None;
43+
}
3844

39-
let mut v = V {
40-
locals,
41-
location,
42-
results: usage,
43-
};
44-
v.visit_basic_block_data(tbb, tdata);
45-
Some(v.results)
46-
})
45+
let mut v = V {
46+
locals,
47+
location,
48+
results: usage,
49+
};
50+
v.visit_basic_block_data(tbb, tdata);
51+
Some(v.results)
52+
})
4753
}
4854

4955
struct V<'a> {

0 commit comments

Comments
 (0)