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 if let Some(inst) = self.func.layout.last_inst(block) { 100 self.dfs.stack.extend( 101 self.func.dfg.insts[inst] 102 .branch_destination(&self.func.dfg.jump_tables) 103 .iter() 104 // Heuristic: chase the children in reverse. This puts 105 // the first successor block first in the postorder, all 106 // other things being equal, which tends to prioritize 107 // loop backedges over out-edges, putting the edge-block 108 // closer to the loop body and minimizing live-ranges in 109 // linear instruction space. This heuristic doesn't have 110 // any effect on the computation of dominators, and is 111 // purely for other consumers of the postorder we cache 112 // here. 113 .rev() 114 .map(|block| block.block(&self.func.dfg.value_lists)) 115 // This is purely an optimization to avoid additional 116 // iterations of the loop, and is not required; it's 117 // merely inlining the check from the outer conditional 118 // of this case to avoid the extra loop iteration. This 119 // also avoids potential excess stack growth. 120 .filter(|block| !self.dfs.seen.contains(*block)) 121 .map(|block| (Event::Enter, block)), 122 ); 123 } 124 } 125 126 Some((event, block)) 127 } 128 } 129 130 /// An iterator that yields `ir::Block` items during a depth-first, pre-order 131 /// traversal over its associated function. 132 pub struct DfsPreOrderIter<'a>(DfsIter<'a>); 133 134 impl Iterator for DfsPreOrderIter<'_> { 135 type Item = ir::Block; 136 137 fn next(&mut self) -> Option<Self::Item> { 138 loop { 139 match self.0.next()? { 140 (Event::Enter, b) => return Some(b), 141 (Event::Exit, _) => continue, 142 } 143 } 144 } 145 } 146 147 /// An iterator that yields `ir::Block` items during a depth-first, post-order 148 /// traversal over its associated function. 149 pub struct DfsPostOrderIter<'a>(DfsIter<'a>); 150 151 impl Iterator for DfsPostOrderIter<'_> { 152 type Item = ir::Block; 153 154 fn next(&mut self) -> Option<Self::Item> { 155 loop { 156 match self.0.next()? { 157 (Event::Exit, b) => return Some(b), 158 (Event::Enter, _) => continue, 159 } 160 } 161 } 162 } 163 164 #[cfg(test)] 165 mod tests { 166 use super::*; 167 use crate::cursor::{Cursor, FuncCursor}; 168 use crate::ir::{types::I32, Function, InstBuilder, TrapCode}; 169 170 #[test] 171 fn test_dfs_traversal() { 172 let _ = env_logger::try_init(); 173 174 let mut func = Function::new(); 175 176 let block0 = func.dfg.make_block(); 177 let v0 = func.dfg.append_block_param(block0, I32); 178 let block1 = func.dfg.make_block(); 179 let block2 = func.dfg.make_block(); 180 let block3 = func.dfg.make_block(); 181 182 let mut cur = FuncCursor::new(&mut func); 183 184 // block0(v0): 185 // br_if v0, block2, trap_block 186 cur.insert_block(block0); 187 cur.ins().brif(v0, block2, &[], block3, &[]); 188 189 // block3: 190 // trap user0 191 cur.insert_block(block3); 192 cur.ins().trap(TrapCode::User(0)); 193 194 // block1: 195 // v1 = iconst.i32 1 196 // v2 = iadd v0, v1 197 // jump block0(v2) 198 cur.insert_block(block1); 199 let v1 = cur.ins().iconst(I32, 1); 200 let v2 = cur.ins().iadd(v0, v1); 201 cur.ins().jump(block0, &[v2]); 202 203 // block2: 204 // return v0 205 cur.insert_block(block2); 206 cur.ins().return_(&[v0]); 207 208 let mut dfs = Dfs::new(); 209 210 assert_eq!( 211 dfs.iter(&func).collect::<Vec<_>>(), 212 vec![ 213 (Event::Enter, block0), 214 (Event::Enter, block2), 215 (Event::Exit, block2), 216 (Event::Enter, block3), 217 (Event::Exit, block3), 218 (Event::Exit, block0) 219 ], 220 ); 221 } 222 } 223