Skip to content

Commit 60a28e6

Browse files
committed
Add some standard traversal iterators for MIR
Adds Preorder, Postorder and Reverse Postorder traversal iterators. Also makes trans/mir use Reverse Postorder traversal for blocks.
1 parent 8f5c3f1 commit 60a28e6

File tree

4 files changed

+284
-2
lines changed

4 files changed

+284
-2
lines changed

src/librustc_data_structures/bitvec.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
/// A very simple BitVector type.
12+
#[derive(Clone)]
1213
pub struct BitVector {
1314
data: Vec<u64>,
1415
}

src/librustc_mir/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ mod hair;
4040
pub mod mir_map;
4141
pub mod pretty;
4242
pub mod transform;
43+
pub mod traversal;

src/librustc_mir/traversal.rs

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::vec;
12+
13+
use rustc_data_structures::bitvec::BitVector;
14+
15+
use rustc::mir::repr::*;
16+
17+
/// Preorder traversal of a graph.
18+
///
19+
/// Preorder traversal is when each node is visited before an of it's
20+
/// successors
21+
///
22+
/// A
23+
/// / \
24+
/// / \
25+
/// B C
26+
/// \ /
27+
/// \ /
28+
/// D
29+
///
30+
/// A preorder traversal of this graph is either `A B D C` or `A C D B`
31+
#[derive(Clone)]
32+
pub struct Preorder<'a, 'tcx: 'a> {
33+
mir: &'a Mir<'tcx>,
34+
visited: BitVector,
35+
worklist: Vec<BasicBlock>,
36+
}
37+
38+
impl<'a, 'tcx> Preorder<'a, 'tcx> {
39+
pub fn new(mir: &'a Mir<'tcx>, root: BasicBlock) -> Preorder<'a, 'tcx> {
40+
let worklist = vec![root];
41+
42+
Preorder {
43+
mir: mir,
44+
visited: BitVector::new(mir.basic_blocks.len()),
45+
worklist: worklist
46+
}
47+
}
48+
}
49+
50+
pub fn preorder<'a, 'tcx>(mir: &'a Mir<'tcx>) -> Preorder<'a, 'tcx> {
51+
Preorder::new(mir, START_BLOCK)
52+
}
53+
54+
impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> {
55+
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
56+
57+
fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
58+
while let Some(idx) = self.worklist.pop() {
59+
if !self.visited.insert(idx.index()) {
60+
continue;
61+
}
62+
63+
let data = self.mir.basic_block_data(idx);
64+
65+
if let Some(ref term) = data.terminator {
66+
for &succ in term.successors().iter() {
67+
self.worklist.push(succ);
68+
}
69+
}
70+
71+
return Some((idx, data));
72+
}
73+
74+
None
75+
}
76+
}
77+
78+
/// Postorder traversal of a graph.
79+
///
80+
/// Postorder traversal is when each node is visited after all of it's
81+
/// successors, except when the successor is only reachable by a back-edge
82+
///
83+
/// A
84+
/// / \
85+
/// / \
86+
/// B C
87+
/// \ /
88+
/// \ /
89+
/// D
90+
///
91+
/// A Postorder traversal of this graph is `D B C A` or `D C B A`
92+
pub struct Postorder<'a, 'tcx: 'a> {
93+
mir: &'a Mir<'tcx>,
94+
visited: BitVector,
95+
visit_stack: Vec<(BasicBlock, vec::IntoIter<BasicBlock>)>
96+
}
97+
98+
impl<'a, 'tcx> Postorder<'a, 'tcx> {
99+
pub fn new(mir: &'a Mir<'tcx>, root: BasicBlock) -> Postorder<'a, 'tcx> {
100+
let mut po = Postorder {
101+
mir: mir,
102+
visited: BitVector::new(mir.basic_blocks.len()),
103+
visit_stack: Vec::new()
104+
};
105+
106+
107+
let data = po.mir.basic_block_data(root);
108+
109+
if let Some(ref term) = data.terminator {
110+
po.visited.insert(root.index());
111+
112+
let succs = term.successors().into_owned().into_iter();
113+
114+
po.visit_stack.push((root, succs));
115+
po.traverse_successor();
116+
}
117+
118+
po
119+
}
120+
121+
fn traverse_successor(&mut self) {
122+
// This is quite a complex loop due to 1. the borrow checker not liking it much
123+
// and 2. what exactly is going on is not clear
124+
//
125+
// It does the actual traversal of the graph, while the `next` method on the iterator
126+
// just pops off of the stack. `visit_stack` is a stack containing pairs of nodes and
127+
// iterators over the sucessors of those nodes. Each iteration attempts to get the next
128+
// node from the top of the stack, then pushes that node and an iterator over the
129+
// successors to the top of the stack. This loop only grows `visit_stack`, stopping when
130+
// we reach a child that has no children that we haven't already visited.
131+
//
132+
// For a graph that looks like this:
133+
//
134+
// A
135+
// / \
136+
// / \
137+
// B C
138+
// | |
139+
// | |
140+
// D |
141+
// \ /
142+
// \ /
143+
// E
144+
//
145+
// The state of the stack starts out with just the root node (`A` in this case);
146+
// [(A, [B, C])]
147+
//
148+
// When the first call to `traverse_sucessor` happens, the following happens:
149+
//
150+
// [(B, [D]), // `B` taken from the successors of `A`, pushed to the
151+
// // top of the stack along with the successors of `B`
152+
// (A, [C])]
153+
//
154+
// [(D, [E]), // `D` taken from successors of `B`, pushed to stack
155+
// (B, []),
156+
// (A, [C])]
157+
//
158+
// [(E, []), // `E` taken from successors of `D`, pushed to stack
159+
// (D, []),
160+
// (B, []),
161+
// (A, [C])]
162+
//
163+
// Now that the top of the stack has no successors we can traverse, each item will
164+
// be popped off during iteration until we get back to `A`. This yeilds [E, D, B].
165+
//
166+
// When we yeild `B` and call `traverse_successor`, We push `C` to the stack, but
167+
// since we've already visited `E`, that child isn't added to the stack. The last
168+
// two iterations yield `C` and finally `A` for a final traversal of [E, D, B, C, A]
169+
loop {
170+
let bb = if let Some(&mut (_, ref mut iter)) = self.visit_stack.last_mut() {
171+
if let Some(bb) = iter.next() {
172+
bb
173+
} else {
174+
break;
175+
}
176+
} else {
177+
break;
178+
};
179+
180+
if self.visited.insert(bb.index()) {
181+
let data = self.mir.basic_block_data(bb);
182+
183+
if let Some(ref term) = data.terminator {
184+
let succs = term.successors().into_owned().into_iter();
185+
self.visit_stack.push((bb, succs));
186+
}
187+
}
188+
}
189+
}
190+
}
191+
192+
pub fn postorder<'a, 'tcx>(mir: &'a Mir<'tcx>) -> Postorder<'a, 'tcx> {
193+
Postorder::new(mir, START_BLOCK)
194+
}
195+
196+
impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
197+
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
198+
199+
fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
200+
let next = self.visit_stack.pop();
201+
if next.is_some() {
202+
self.traverse_successor();
203+
}
204+
205+
next.map(|(bb, _)| {
206+
let data = self.mir.basic_block_data(bb);
207+
(bb, data)
208+
})
209+
}
210+
}
211+
212+
/// Reverse postorder traversal of a graph
213+
///
214+
/// Reverse postorder is the reverse order of a postorder traversal.
215+
/// This is different to a preorder traversal and represents a natural
216+
/// linearisation of control-flow.
217+
///
218+
/// A
219+
/// / \
220+
/// / \
221+
/// B C
222+
/// \ /
223+
/// \ /
224+
/// D
225+
///
226+
/// A reverse postorder traversal of this graph is either `A B C D` or `A C B D`
227+
/// Note that for a graph containing no loops (i.e. A DAG), this is equivalent to
228+
/// a topological sort.
229+
///
230+
/// Construction of a `ReversePostorder` traversal requires doing a full
231+
/// postorder traversal of the graph, therefore this traversal should be
232+
/// constructed as few times as possible. Use the `reset` method to be able
233+
/// to re-use the traversal
234+
#[derive(Clone)]
235+
pub struct ReversePostorder<'a, 'tcx: 'a> {
236+
mir: &'a Mir<'tcx>,
237+
blocks: Vec<BasicBlock>,
238+
idx: usize
239+
}
240+
241+
impl<'a, 'tcx> ReversePostorder<'a, 'tcx> {
242+
pub fn new(mir: &'a Mir<'tcx>, root: BasicBlock) -> ReversePostorder<'a, 'tcx> {
243+
let blocks : Vec<_> = Postorder::new(mir, root).map(|(bb, _)| bb).collect();
244+
245+
let len = blocks.len();
246+
247+
ReversePostorder {
248+
mir: mir,
249+
blocks: blocks,
250+
idx: len
251+
}
252+
}
253+
254+
pub fn reset(&mut self) {
255+
self.idx = self.blocks.len();
256+
}
257+
}
258+
259+
260+
pub fn reverse_postorder<'a, 'tcx>(mir: &'a Mir<'tcx>) -> ReversePostorder<'a, 'tcx> {
261+
ReversePostorder::new(mir, START_BLOCK)
262+
}
263+
264+
impl<'a, 'tcx> Iterator for ReversePostorder<'a, 'tcx> {
265+
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
266+
267+
fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
268+
if self.idx == 0 { return None; }
269+
self.idx -= 1;
270+
271+
self.blocks.get(self.idx).map(|&bb| {
272+
let data = self.mir.basic_block_data(bb);
273+
(bb, data)
274+
})
275+
}
276+
}

src/librustc_trans/mir/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ use std::ops::Deref;
2020
use std::rc::Rc;
2121

2222
use self::lvalue::{LvalueRef, get_dataptr, get_meta};
23+
use rustc_mir::traversal;
24+
25+
use self::lvalue::LvalueRef;
2326
use self::operand::OperandRef;
2427

2528
#[derive(Clone)]
@@ -152,8 +155,9 @@ pub fn trans_mir<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
152155
args: args,
153156
};
154157

155-
// Translate the body of each block
156-
for &bb in &mir_blocks {
158+
let rpo = traversal::reverse_postorder(mir);
159+
// Translate the body of each block using reverse postorder
160+
for (bb, _) in rpo {
157161
mircx.trans_block(bb);
158162
}
159163

0 commit comments

Comments
 (0)