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