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