1cacfaf8bSNick Fitzgerald //! Traversals over the IR.
2cacfaf8bSNick Fitzgerald 
3cacfaf8bSNick Fitzgerald use crate::ir;
4cacfaf8bSNick Fitzgerald use alloc::vec::Vec;
5cacfaf8bSNick Fitzgerald use core::fmt::Debug;
6cacfaf8bSNick Fitzgerald use core::hash::Hash;
7cacfaf8bSNick Fitzgerald use cranelift_entity::EntitySet;
8cacfaf8bSNick Fitzgerald 
9cacfaf8bSNick Fitzgerald /// A low-level DFS traversal event: either entering or exiting the traversal of
10cacfaf8bSNick Fitzgerald /// a block.
11cacfaf8bSNick Fitzgerald #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
12cacfaf8bSNick Fitzgerald pub enum Event {
13cacfaf8bSNick Fitzgerald     /// Entering traversal of a block.
14cacfaf8bSNick Fitzgerald     ///
15cacfaf8bSNick Fitzgerald     /// Processing a block upon this event corresponds to a pre-order,
16cacfaf8bSNick Fitzgerald     /// depth-first traversal.
17cacfaf8bSNick Fitzgerald     Enter,
18cacfaf8bSNick Fitzgerald 
19cacfaf8bSNick Fitzgerald     /// Exiting traversal of a block.
20cacfaf8bSNick Fitzgerald     ///
21cacfaf8bSNick Fitzgerald     /// Processing a block upon this event corresponds to a post-order,
22cacfaf8bSNick Fitzgerald     /// depth-first traversal.
23cacfaf8bSNick Fitzgerald     Exit,
24cacfaf8bSNick Fitzgerald }
25cacfaf8bSNick Fitzgerald 
26cacfaf8bSNick Fitzgerald /// A depth-first traversal.
27cacfaf8bSNick Fitzgerald ///
28cacfaf8bSNick Fitzgerald /// This is a fairly low-level traversal type, and is generally intended to be
29cacfaf8bSNick Fitzgerald /// used as a building block for making specific pre-order or post-order
30cacfaf8bSNick Fitzgerald /// traversals for whatever problem is at hand.
31cacfaf8bSNick Fitzgerald ///
32cacfaf8bSNick Fitzgerald /// This type may be reused multiple times across different passes or functions
33cacfaf8bSNick Fitzgerald /// and will internally reuse any heap allocations its already made.
34cacfaf8bSNick Fitzgerald ///
35cacfaf8bSNick Fitzgerald /// Traversal is not recursive.
36cacfaf8bSNick Fitzgerald #[derive(Debug, Default, Clone)]
37cacfaf8bSNick Fitzgerald pub struct Dfs {
38cacfaf8bSNick Fitzgerald     stack: Vec<(Event, ir::Block)>,
39cacfaf8bSNick Fitzgerald     seen: EntitySet<ir::Block>,
40cacfaf8bSNick Fitzgerald }
41cacfaf8bSNick Fitzgerald 
42cacfaf8bSNick Fitzgerald impl Dfs {
43cacfaf8bSNick Fitzgerald     /// Construct a new depth-first traversal.
new() -> Self44cacfaf8bSNick Fitzgerald     pub fn new() -> Self {
45cacfaf8bSNick Fitzgerald         Self::default()
46cacfaf8bSNick Fitzgerald     }
47cacfaf8bSNick Fitzgerald 
48cacfaf8bSNick Fitzgerald     /// Perform a depth-first traversal over the given function.
49cacfaf8bSNick Fitzgerald     ///
50cacfaf8bSNick Fitzgerald     /// Yields pairs of `(Event, ir::Block)`.
51cacfaf8bSNick Fitzgerald     ///
52cacfaf8bSNick Fitzgerald     /// This iterator can be used to perform either pre- or post-order
53cacfaf8bSNick Fitzgerald     /// traversals, or a combination of the two.
iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsIter<'a>54cacfaf8bSNick Fitzgerald     pub fn iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsIter<'a> {
5551948efeSNick Fitzgerald         self.clear();
56cacfaf8bSNick Fitzgerald         if let Some(e) = func.layout.entry_block() {
57cacfaf8bSNick Fitzgerald             self.stack.push((Event::Enter, e));
58cacfaf8bSNick Fitzgerald         }
59cacfaf8bSNick Fitzgerald         DfsIter { dfs: self, func }
60cacfaf8bSNick Fitzgerald     }
61cacfaf8bSNick Fitzgerald 
62cacfaf8bSNick Fitzgerald     /// Perform a pre-order traversal over the given function.
63cacfaf8bSNick Fitzgerald     ///
64cacfaf8bSNick Fitzgerald     /// Yields `ir::Block` items.
pre_order_iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsPreOrderIter<'a>65cacfaf8bSNick Fitzgerald     pub fn pre_order_iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsPreOrderIter<'a> {
66cacfaf8bSNick Fitzgerald         DfsPreOrderIter(self.iter(func))
67cacfaf8bSNick Fitzgerald     }
68cacfaf8bSNick Fitzgerald 
69cacfaf8bSNick Fitzgerald     /// Perform a post-order traversal over the given function.
70cacfaf8bSNick Fitzgerald     ///
71cacfaf8bSNick Fitzgerald     /// Yields `ir::Block` items.
post_order_iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsPostOrderIter<'a>72cacfaf8bSNick Fitzgerald     pub fn post_order_iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsPostOrderIter<'a> {
73cacfaf8bSNick Fitzgerald         DfsPostOrderIter(self.iter(func))
74cacfaf8bSNick Fitzgerald     }
7551948efeSNick Fitzgerald 
7651948efeSNick Fitzgerald     /// Clear this DFS, but keep its allocations for future reuse.
clear(&mut self)7751948efeSNick Fitzgerald     pub fn clear(&mut self) {
7851948efeSNick Fitzgerald         let Dfs { stack, seen } = self;
7951948efeSNick Fitzgerald         stack.clear();
8051948efeSNick Fitzgerald         seen.clear();
8151948efeSNick Fitzgerald     }
82cacfaf8bSNick Fitzgerald }
83cacfaf8bSNick Fitzgerald 
84cacfaf8bSNick Fitzgerald /// An iterator that yields pairs of `(Event, ir::Block)` items as it performs a
85cacfaf8bSNick Fitzgerald /// depth-first traversal over its associated function.
86cacfaf8bSNick Fitzgerald pub struct DfsIter<'a> {
87cacfaf8bSNick Fitzgerald     dfs: &'a mut Dfs,
88cacfaf8bSNick Fitzgerald     func: &'a ir::Function,
89cacfaf8bSNick Fitzgerald }
90cacfaf8bSNick Fitzgerald 
91cacfaf8bSNick Fitzgerald impl Iterator for DfsIter<'_> {
92cacfaf8bSNick Fitzgerald     type Item = (Event, ir::Block);
93cacfaf8bSNick Fitzgerald 
next(&mut self) -> Option<(Event, ir::Block)>94cacfaf8bSNick Fitzgerald     fn next(&mut self) -> Option<(Event, ir::Block)> {
95*c848860cSNick Fitzgerald         loop {
96cacfaf8bSNick Fitzgerald             let (event, block) = self.dfs.stack.pop()?;
97cacfaf8bSNick Fitzgerald 
98*c848860cSNick Fitzgerald             if event == Event::Enter {
99*c848860cSNick Fitzgerald                 let first_time_seeing = self.dfs.seen.insert(block);
100*c848860cSNick Fitzgerald                 if !first_time_seeing {
101*c848860cSNick Fitzgerald                     continue;
102*c848860cSNick Fitzgerald                 }
103*c848860cSNick Fitzgerald 
104cacfaf8bSNick Fitzgerald                 self.dfs.stack.push((Event::Exit, block));
105cacfaf8bSNick Fitzgerald                 self.dfs.stack.extend(
106f763f0e7SNick Fitzgerald                     self.func
107f763f0e7SNick Fitzgerald                         .block_successors(block)
108cacfaf8bSNick Fitzgerald                         // Heuristic: chase the children in reverse. This puts
109cacfaf8bSNick Fitzgerald                         // the first successor block first in the postorder, all
110cacfaf8bSNick Fitzgerald                         // other things being equal, which tends to prioritize
111cacfaf8bSNick Fitzgerald                         // loop backedges over out-edges, putting the edge-block
112cacfaf8bSNick Fitzgerald                         // closer to the loop body and minimizing live-ranges in
113cacfaf8bSNick Fitzgerald                         // linear instruction space. This heuristic doesn't have
114cacfaf8bSNick Fitzgerald                         // any effect on the computation of dominators, and is
115cacfaf8bSNick Fitzgerald                         // purely for other consumers of the postorder we cache
116cacfaf8bSNick Fitzgerald                         // here.
117cacfaf8bSNick Fitzgerald                         .rev()
118cacfaf8bSNick Fitzgerald                         // This is purely an optimization to avoid additional
119cacfaf8bSNick Fitzgerald                         // iterations of the loop, and is not required; it's
120cacfaf8bSNick Fitzgerald                         // merely inlining the check from the outer conditional
121cacfaf8bSNick Fitzgerald                         // of this case to avoid the extra loop iteration. This
122cacfaf8bSNick Fitzgerald                         // also avoids potential excess stack growth.
123cacfaf8bSNick Fitzgerald                         .filter(|block| !self.dfs.seen.contains(*block))
124cacfaf8bSNick Fitzgerald                         .map(|block| (Event::Enter, block)),
125cacfaf8bSNick Fitzgerald                 );
126cacfaf8bSNick Fitzgerald             }
127cacfaf8bSNick Fitzgerald 
128*c848860cSNick Fitzgerald             return Some((event, block));
129*c848860cSNick Fitzgerald         }
130cacfaf8bSNick Fitzgerald     }
131cacfaf8bSNick Fitzgerald }
132cacfaf8bSNick Fitzgerald 
133cacfaf8bSNick Fitzgerald /// An iterator that yields `ir::Block` items during a depth-first, pre-order
134cacfaf8bSNick Fitzgerald /// traversal over its associated function.
135cacfaf8bSNick Fitzgerald pub struct DfsPreOrderIter<'a>(DfsIter<'a>);
136cacfaf8bSNick Fitzgerald 
137cacfaf8bSNick Fitzgerald impl Iterator for DfsPreOrderIter<'_> {
138cacfaf8bSNick Fitzgerald     type Item = ir::Block;
139cacfaf8bSNick Fitzgerald 
next(&mut self) -> Option<Self::Item>140cacfaf8bSNick Fitzgerald     fn next(&mut self) -> Option<Self::Item> {
141cacfaf8bSNick Fitzgerald         loop {
142cacfaf8bSNick Fitzgerald             match self.0.next()? {
143cacfaf8bSNick Fitzgerald                 (Event::Enter, b) => return Some(b),
144cacfaf8bSNick Fitzgerald                 (Event::Exit, _) => continue,
145cacfaf8bSNick Fitzgerald             }
146cacfaf8bSNick Fitzgerald         }
147cacfaf8bSNick Fitzgerald     }
148cacfaf8bSNick Fitzgerald }
149cacfaf8bSNick Fitzgerald 
150cacfaf8bSNick Fitzgerald /// An iterator that yields `ir::Block` items during a depth-first, post-order
151cacfaf8bSNick Fitzgerald /// traversal over its associated function.
152cacfaf8bSNick Fitzgerald pub struct DfsPostOrderIter<'a>(DfsIter<'a>);
153cacfaf8bSNick Fitzgerald 
154cacfaf8bSNick Fitzgerald impl Iterator for DfsPostOrderIter<'_> {
155cacfaf8bSNick Fitzgerald     type Item = ir::Block;
156cacfaf8bSNick Fitzgerald 
next(&mut self) -> Option<Self::Item>157cacfaf8bSNick Fitzgerald     fn next(&mut self) -> Option<Self::Item> {
158cacfaf8bSNick Fitzgerald         loop {
159cacfaf8bSNick Fitzgerald             match self.0.next()? {
160cacfaf8bSNick Fitzgerald                 (Event::Exit, b) => return Some(b),
161cacfaf8bSNick Fitzgerald                 (Event::Enter, _) => continue,
162cacfaf8bSNick Fitzgerald             }
163cacfaf8bSNick Fitzgerald         }
164cacfaf8bSNick Fitzgerald     }
165cacfaf8bSNick Fitzgerald }
166cacfaf8bSNick Fitzgerald 
167cacfaf8bSNick Fitzgerald #[cfg(test)]
168cacfaf8bSNick Fitzgerald mod tests {
169cacfaf8bSNick Fitzgerald     use super::*;
170cacfaf8bSNick Fitzgerald     use crate::cursor::{Cursor, FuncCursor};
17190ac295eSAlex Crichton     use crate::ir::{Function, InstBuilder, TrapCode, types::I32};
172cacfaf8bSNick Fitzgerald 
173cacfaf8bSNick Fitzgerald     #[test]
test_dfs_traversal()174cacfaf8bSNick Fitzgerald     fn test_dfs_traversal() {
175cacfaf8bSNick Fitzgerald         let _ = env_logger::try_init();
176cacfaf8bSNick Fitzgerald 
177cacfaf8bSNick Fitzgerald         let mut func = Function::new();
178cacfaf8bSNick Fitzgerald 
179cacfaf8bSNick Fitzgerald         let block0 = func.dfg.make_block();
180cacfaf8bSNick Fitzgerald         let v0 = func.dfg.append_block_param(block0, I32);
181cacfaf8bSNick Fitzgerald         let block1 = func.dfg.make_block();
182cacfaf8bSNick Fitzgerald         let block2 = func.dfg.make_block();
183cacfaf8bSNick Fitzgerald         let block3 = func.dfg.make_block();
184cacfaf8bSNick Fitzgerald 
185cacfaf8bSNick Fitzgerald         let mut cur = FuncCursor::new(&mut func);
186cacfaf8bSNick Fitzgerald 
187cacfaf8bSNick Fitzgerald         // block0(v0):
188cacfaf8bSNick Fitzgerald         //   br_if v0, block2, trap_block
189cacfaf8bSNick Fitzgerald         cur.insert_block(block0);
190cacfaf8bSNick Fitzgerald         cur.ins().brif(v0, block2, &[], block3, &[]);
191cacfaf8bSNick Fitzgerald 
192cacfaf8bSNick Fitzgerald         // block3:
193cacfaf8bSNick Fitzgerald         //   trap user0
194cacfaf8bSNick Fitzgerald         cur.insert_block(block3);
1959fc41baeSAlex Crichton         cur.ins().trap(TrapCode::unwrap_user(1));
196cacfaf8bSNick Fitzgerald 
197cacfaf8bSNick Fitzgerald         // block1:
198cacfaf8bSNick Fitzgerald         //   v1 = iconst.i32 1
199cacfaf8bSNick Fitzgerald         //   v2 = iadd v0, v1
200cacfaf8bSNick Fitzgerald         //   jump block0(v2)
201cacfaf8bSNick Fitzgerald         cur.insert_block(block1);
202cacfaf8bSNick Fitzgerald         let v1 = cur.ins().iconst(I32, 1);
203cacfaf8bSNick Fitzgerald         let v2 = cur.ins().iadd(v0, v1);
20494ec88eaSChris Fallin         cur.ins().jump(block0, &[v2.into()]);
205cacfaf8bSNick Fitzgerald 
206cacfaf8bSNick Fitzgerald         // block2:
207cacfaf8bSNick Fitzgerald         //   return v0
208cacfaf8bSNick Fitzgerald         cur.insert_block(block2);
209cacfaf8bSNick Fitzgerald         cur.ins().return_(&[v0]);
210cacfaf8bSNick Fitzgerald 
211cacfaf8bSNick Fitzgerald         let mut dfs = Dfs::new();
212cacfaf8bSNick Fitzgerald 
213cacfaf8bSNick Fitzgerald         assert_eq!(
214cacfaf8bSNick Fitzgerald             dfs.iter(&func).collect::<Vec<_>>(),
215cacfaf8bSNick Fitzgerald             vec![
216cacfaf8bSNick Fitzgerald                 (Event::Enter, block0),
217cacfaf8bSNick Fitzgerald                 (Event::Enter, block2),
218cacfaf8bSNick Fitzgerald                 (Event::Exit, block2),
219cacfaf8bSNick Fitzgerald                 (Event::Enter, block3),
220cacfaf8bSNick Fitzgerald                 (Event::Exit, block3),
221cacfaf8bSNick Fitzgerald                 (Event::Exit, block0)
222cacfaf8bSNick Fitzgerald             ],
223cacfaf8bSNick Fitzgerald         );
224cacfaf8bSNick Fitzgerald     }
225*c848860cSNick Fitzgerald 
226*c848860cSNick Fitzgerald     #[test]
multiple_successors_to_the_same_block()227*c848860cSNick Fitzgerald     fn multiple_successors_to_the_same_block() {
228*c848860cSNick Fitzgerald         let _ = env_logger::try_init();
229*c848860cSNick Fitzgerald 
230*c848860cSNick Fitzgerald         let mut func = Function::new();
231*c848860cSNick Fitzgerald 
232*c848860cSNick Fitzgerald         let block0 = func.dfg.make_block();
233*c848860cSNick Fitzgerald         let block1 = func.dfg.make_block();
234*c848860cSNick Fitzgerald 
235*c848860cSNick Fitzgerald         let mut cur = FuncCursor::new(&mut func);
236*c848860cSNick Fitzgerald 
237*c848860cSNick Fitzgerald         // block0(v0):
238*c848860cSNick Fitzgerald         //   v1 = iconst.i32 36
239*c848860cSNick Fitzgerald         //   v2 = iconst.i32 42
240*c848860cSNick Fitzgerald         //   br_if v0, block1(v1), block1(v2)
241*c848860cSNick Fitzgerald         cur.insert_block(block0);
242*c848860cSNick Fitzgerald         let v0 = cur.func.dfg.append_block_param(block0, I32);
243*c848860cSNick Fitzgerald         let v1 = cur.ins().iconst(ir::types::I32, 36);
244*c848860cSNick Fitzgerald         let v2 = cur.ins().iconst(ir::types::I32, 42);
245*c848860cSNick Fitzgerald         cur.ins()
246*c848860cSNick Fitzgerald             .brif(v0, block1, &[v1.into()], block1, &[v2.into()]);
247*c848860cSNick Fitzgerald 
248*c848860cSNick Fitzgerald         // block1(v3: i32):
249*c848860cSNick Fitzgerald         //   return v3
250*c848860cSNick Fitzgerald         cur.insert_block(block1);
251*c848860cSNick Fitzgerald         let v3 = cur.func.dfg.append_block_param(block1, I32);
252*c848860cSNick Fitzgerald         cur.ins().return_(&[v3]);
253*c848860cSNick Fitzgerald 
254*c848860cSNick Fitzgerald         let mut dfs = Dfs::new();
255*c848860cSNick Fitzgerald 
256*c848860cSNick Fitzgerald         // We should only enter `block1` once.
257*c848860cSNick Fitzgerald         assert_eq!(
258*c848860cSNick Fitzgerald             dfs.iter(&func).collect::<Vec<_>>(),
259*c848860cSNick Fitzgerald             vec![
260*c848860cSNick Fitzgerald                 (Event::Enter, block0),
261*c848860cSNick Fitzgerald                 (Event::Enter, block1),
262*c848860cSNick Fitzgerald                 (Event::Exit, block1),
263*c848860cSNick Fitzgerald                 (Event::Exit, block0),
264*c848860cSNick Fitzgerald             ],
265*c848860cSNick Fitzgerald         );
266*c848860cSNick Fitzgerald 
267*c848860cSNick Fitzgerald         // We should only iterate over `block1` once in a pre-order traversal.
268*c848860cSNick Fitzgerald         assert_eq!(
269*c848860cSNick Fitzgerald             dfs.pre_order_iter(&func).collect::<Vec<_>>(),
270*c848860cSNick Fitzgerald             vec![block0, block1],
271*c848860cSNick Fitzgerald         );
272*c848860cSNick Fitzgerald 
273*c848860cSNick Fitzgerald         // We should only iterate over `block1` once in a post-order traversal.
274*c848860cSNick Fitzgerald         assert_eq!(
275*c848860cSNick Fitzgerald             dfs.post_order_iter(&func).collect::<Vec<_>>(),
276*c848860cSNick Fitzgerald             vec![block1, block0],
277*c848860cSNick Fitzgerald         );
278*c848860cSNick Fitzgerald     }
279cacfaf8bSNick Fitzgerald }
280