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