1832666c4SRyan Hunt //! A loop analysis represented as mappings of loops to their header Block 2747ad3c4Slazypassion //! and parent in the loop tree. 3747ad3c4Slazypassion 4747ad3c4Slazypassion use crate::dominator_tree::DominatorTree; 5747ad3c4Slazypassion use crate::entity::entity_impl; 6747ad3c4Slazypassion use crate::entity::SecondaryMap; 7747ad3c4Slazypassion use crate::entity::{Keys, PrimaryMap}; 8832666c4SRyan Hunt use crate::flowgraph::{BlockPredecessor, ControlFlowGraph}; 9832666c4SRyan Hunt use crate::ir::{Block, Function, Layout}; 10747ad3c4Slazypassion use crate::packed_option::PackedOption; 11747ad3c4Slazypassion use crate::timing; 1210e226f9Sbjorn3 use alloc::vec::Vec; 13*2be12a51SChris Fallin use smallvec::{smallvec, SmallVec}; 14747ad3c4Slazypassion 15747ad3c4Slazypassion /// A opaque reference to a code loop. 16747ad3c4Slazypassion #[derive(Copy, Clone, PartialEq, Eq, Hash)] 17747ad3c4Slazypassion pub struct Loop(u32); 18747ad3c4Slazypassion entity_impl!(Loop, "loop"); 19747ad3c4Slazypassion 20747ad3c4Slazypassion /// Loop tree information for a single function. 21747ad3c4Slazypassion /// 22832666c4SRyan Hunt /// Loops are referenced by the Loop object, and for each loop you can access its header block, 23832666c4SRyan Hunt /// its eventual parent in the loop tree and all the block belonging to the loop. 24747ad3c4Slazypassion pub struct LoopAnalysis { 25747ad3c4Slazypassion loops: PrimaryMap<Loop, LoopData>, 26832666c4SRyan Hunt block_loop_map: SecondaryMap<Block, PackedOption<Loop>>, 27747ad3c4Slazypassion valid: bool, 28747ad3c4Slazypassion } 29747ad3c4Slazypassion 30747ad3c4Slazypassion struct LoopData { 31832666c4SRyan Hunt header: Block, 32747ad3c4Slazypassion parent: PackedOption<Loop>, 33*2be12a51SChris Fallin level: LoopLevel, 34*2be12a51SChris Fallin } 35*2be12a51SChris Fallin 36*2be12a51SChris Fallin /// A level in a loop nest. 37*2be12a51SChris Fallin #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 38*2be12a51SChris Fallin pub struct LoopLevel(u8); 39*2be12a51SChris Fallin impl LoopLevel { 40*2be12a51SChris Fallin const INVALID: u8 = 0xff; 41*2be12a51SChris Fallin 42*2be12a51SChris Fallin /// Get the root level (no loop). 43*2be12a51SChris Fallin pub fn root() -> Self { 44*2be12a51SChris Fallin Self(0) 45*2be12a51SChris Fallin } 46*2be12a51SChris Fallin /// Get the loop level. 47*2be12a51SChris Fallin pub fn level(self) -> usize { 48*2be12a51SChris Fallin self.0 as usize 49*2be12a51SChris Fallin } 50*2be12a51SChris Fallin /// Invalid loop level. 51*2be12a51SChris Fallin pub fn invalid() -> Self { 52*2be12a51SChris Fallin Self(Self::INVALID) 53*2be12a51SChris Fallin } 54*2be12a51SChris Fallin /// One loop level deeper. 55*2be12a51SChris Fallin pub fn inc(self) -> Self { 56*2be12a51SChris Fallin if self.0 == (Self::INVALID - 1) { 57*2be12a51SChris Fallin self 58*2be12a51SChris Fallin } else { 59*2be12a51SChris Fallin Self(self.0 + 1) 60*2be12a51SChris Fallin } 61*2be12a51SChris Fallin } 62*2be12a51SChris Fallin /// A clamped loop level from a larger-width (usize) depth. 63*2be12a51SChris Fallin pub fn clamped(level: usize) -> Self { 64*2be12a51SChris Fallin Self( 65*2be12a51SChris Fallin u8::try_from(std::cmp::min(level, (Self::INVALID as usize) - 1)) 66*2be12a51SChris Fallin .expect("Clamped value must always convert"), 67*2be12a51SChris Fallin ) 68*2be12a51SChris Fallin } 69*2be12a51SChris Fallin } 70*2be12a51SChris Fallin 71*2be12a51SChris Fallin impl std::default::Default for LoopLevel { 72*2be12a51SChris Fallin fn default() -> Self { 73*2be12a51SChris Fallin LoopLevel::invalid() 74*2be12a51SChris Fallin } 75747ad3c4Slazypassion } 76747ad3c4Slazypassion 77747ad3c4Slazypassion impl LoopData { 78747ad3c4Slazypassion /// Creates a `LoopData` object with the loop header and its eventual parent in the loop tree. 79832666c4SRyan Hunt pub fn new(header: Block, parent: Option<Loop>) -> Self { 80747ad3c4Slazypassion Self { 81747ad3c4Slazypassion header, 82747ad3c4Slazypassion parent: parent.into(), 83*2be12a51SChris Fallin level: LoopLevel::invalid(), 84747ad3c4Slazypassion } 85747ad3c4Slazypassion } 86747ad3c4Slazypassion } 87747ad3c4Slazypassion 88747ad3c4Slazypassion /// Methods for querying the loop analysis. 89747ad3c4Slazypassion impl LoopAnalysis { 90747ad3c4Slazypassion /// Allocate a new blank loop analysis struct. Use `compute` to compute the loop analysis for 91747ad3c4Slazypassion /// a function. 92747ad3c4Slazypassion pub fn new() -> Self { 93747ad3c4Slazypassion Self { 94747ad3c4Slazypassion valid: false, 95747ad3c4Slazypassion loops: PrimaryMap::new(), 96832666c4SRyan Hunt block_loop_map: SecondaryMap::new(), 97747ad3c4Slazypassion } 98747ad3c4Slazypassion } 99747ad3c4Slazypassion 100747ad3c4Slazypassion /// Returns all the loops contained in a function. 101747ad3c4Slazypassion pub fn loops(&self) -> Keys<Loop> { 102747ad3c4Slazypassion self.loops.keys() 103747ad3c4Slazypassion } 104747ad3c4Slazypassion 105832666c4SRyan Hunt /// Returns the header block of a particular loop. 106747ad3c4Slazypassion /// 107747ad3c4Slazypassion /// The characteristic property of a loop header block is that it dominates some of its 108747ad3c4Slazypassion /// predecessors. 109832666c4SRyan Hunt pub fn loop_header(&self, lp: Loop) -> Block { 110747ad3c4Slazypassion self.loops[lp].header 111747ad3c4Slazypassion } 112747ad3c4Slazypassion 113747ad3c4Slazypassion /// Return the eventual parent of a loop in the loop tree. 114747ad3c4Slazypassion pub fn loop_parent(&self, lp: Loop) -> Option<Loop> { 115747ad3c4Slazypassion self.loops[lp].parent.expand() 116747ad3c4Slazypassion } 117747ad3c4Slazypassion 118*2be12a51SChris Fallin /// Return the innermost loop for a given block. 119*2be12a51SChris Fallin pub fn innermost_loop(&self, block: Block) -> Option<Loop> { 120*2be12a51SChris Fallin self.block_loop_map[block].expand() 121*2be12a51SChris Fallin } 122*2be12a51SChris Fallin 123*2be12a51SChris Fallin /// Determine if a Block is a loop header. If so, return the loop. 124*2be12a51SChris Fallin pub fn is_loop_header(&self, block: Block) -> Option<Loop> { 125*2be12a51SChris Fallin self.innermost_loop(block) 126*2be12a51SChris Fallin .filter(|&lp| self.loop_header(lp) == block) 127*2be12a51SChris Fallin } 128*2be12a51SChris Fallin 12907f335dcSRyan Hunt /// Determine if a Block belongs to a loop by running a finger along the loop tree. 130747ad3c4Slazypassion /// 131832666c4SRyan Hunt /// Returns `true` if `block` is in loop `lp`. 132832666c4SRyan Hunt pub fn is_in_loop(&self, block: Block, lp: Loop) -> bool { 133832666c4SRyan Hunt let block_loop = self.block_loop_map[block]; 134832666c4SRyan Hunt match block_loop.expand() { 135747ad3c4Slazypassion None => false, 136832666c4SRyan Hunt Some(block_loop) => self.is_child_loop(block_loop, lp), 137747ad3c4Slazypassion } 138747ad3c4Slazypassion } 139747ad3c4Slazypassion 140747ad3c4Slazypassion /// Determines if a loop is contained in another loop. 141747ad3c4Slazypassion /// 142747ad3c4Slazypassion /// `is_child_loop(child,parent)` returns `true` if and only if `child` is a child loop of 143747ad3c4Slazypassion /// `parent` (or `child == parent`). 144747ad3c4Slazypassion pub fn is_child_loop(&self, child: Loop, parent: Loop) -> bool { 145747ad3c4Slazypassion let mut finger = Some(child); 146747ad3c4Slazypassion while let Some(finger_loop) = finger { 147747ad3c4Slazypassion if finger_loop == parent { 148747ad3c4Slazypassion return true; 149747ad3c4Slazypassion } 150747ad3c4Slazypassion finger = self.loop_parent(finger_loop); 151747ad3c4Slazypassion } 152747ad3c4Slazypassion false 153747ad3c4Slazypassion } 154*2be12a51SChris Fallin 155*2be12a51SChris Fallin /// Returns the loop-nest level of a given block. 156*2be12a51SChris Fallin pub fn loop_level(&self, block: Block) -> LoopLevel { 157*2be12a51SChris Fallin self.innermost_loop(block) 158*2be12a51SChris Fallin .map_or(LoopLevel(0), |lp| self.loops[lp].level) 159*2be12a51SChris Fallin } 160747ad3c4Slazypassion } 161747ad3c4Slazypassion 162747ad3c4Slazypassion impl LoopAnalysis { 163747ad3c4Slazypassion /// Detects the loops in a function. Needs the control flow graph and the dominator tree. 164747ad3c4Slazypassion pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph, domtree: &DominatorTree) { 165747ad3c4Slazypassion let _tt = timing::loop_analysis(); 166747ad3c4Slazypassion self.loops.clear(); 167832666c4SRyan Hunt self.block_loop_map.clear(); 168832666c4SRyan Hunt self.block_loop_map.resize(func.dfg.num_blocks()); 169747ad3c4Slazypassion self.find_loop_headers(cfg, domtree, &func.layout); 170747ad3c4Slazypassion self.discover_loop_blocks(cfg, domtree, &func.layout); 171*2be12a51SChris Fallin self.assign_loop_levels(); 172747ad3c4Slazypassion self.valid = true; 173747ad3c4Slazypassion } 174747ad3c4Slazypassion 175747ad3c4Slazypassion /// Check if the loop analysis is in a valid state. 176747ad3c4Slazypassion /// 177747ad3c4Slazypassion /// Note that this doesn't perform any kind of validity checks. It simply checks if the 178747ad3c4Slazypassion /// `compute()` method has been called since the last `clear()`. It does not check that the 179747ad3c4Slazypassion /// loop analysis is consistent with the CFG. 180747ad3c4Slazypassion pub fn is_valid(&self) -> bool { 181747ad3c4Slazypassion self.valid 182747ad3c4Slazypassion } 183747ad3c4Slazypassion 184747ad3c4Slazypassion /// Clear all the data structures contained in the loop analysis. This will leave the 185747ad3c4Slazypassion /// analysis in a similar state to a context returned by `new()` except that allocated 186747ad3c4Slazypassion /// memory be retained. 187747ad3c4Slazypassion pub fn clear(&mut self) { 188747ad3c4Slazypassion self.loops.clear(); 189832666c4SRyan Hunt self.block_loop_map.clear(); 190747ad3c4Slazypassion self.valid = false; 191747ad3c4Slazypassion } 192747ad3c4Slazypassion 193832666c4SRyan Hunt // Traverses the CFG in reverse postorder and create a loop object for every block having a 194747ad3c4Slazypassion // back edge. 195747ad3c4Slazypassion fn find_loop_headers( 196747ad3c4Slazypassion &mut self, 197747ad3c4Slazypassion cfg: &ControlFlowGraph, 198747ad3c4Slazypassion domtree: &DominatorTree, 199747ad3c4Slazypassion layout: &Layout, 200747ad3c4Slazypassion ) { 201747ad3c4Slazypassion // We traverse the CFG in reverse postorder 202832666c4SRyan Hunt for &block in domtree.cfg_postorder().iter().rev() { 203832666c4SRyan Hunt for BlockPredecessor { 204747ad3c4Slazypassion inst: pred_inst, .. 205832666c4SRyan Hunt } in cfg.pred_iter(block) 206747ad3c4Slazypassion { 207832666c4SRyan Hunt // If the block dominates one of its predecessors it is a back edge 208832666c4SRyan Hunt if domtree.dominates(block, pred_inst, layout) { 209832666c4SRyan Hunt // This block is a loop header, so we create its associated loop 210832666c4SRyan Hunt let lp = self.loops.push(LoopData::new(block, None)); 211832666c4SRyan Hunt self.block_loop_map[block] = lp.into(); 212747ad3c4Slazypassion break; 213747ad3c4Slazypassion // We break because we only need one back edge to identify a loop header. 214747ad3c4Slazypassion } 215747ad3c4Slazypassion } 216747ad3c4Slazypassion } 217747ad3c4Slazypassion } 218747ad3c4Slazypassion 219747ad3c4Slazypassion // Intended to be called after `find_loop_headers`. For each detected loop header, 220832666c4SRyan Hunt // discovers all the block belonging to the loop and its inner loops. After a call to this 221747ad3c4Slazypassion // function, the loop tree is fully constructed. 222747ad3c4Slazypassion fn discover_loop_blocks( 223747ad3c4Slazypassion &mut self, 224747ad3c4Slazypassion cfg: &ControlFlowGraph, 225747ad3c4Slazypassion domtree: &DominatorTree, 226747ad3c4Slazypassion layout: &Layout, 227747ad3c4Slazypassion ) { 228832666c4SRyan Hunt let mut stack: Vec<Block> = Vec::new(); 229747ad3c4Slazypassion // We handle each loop header in reverse order, corresponding to a pseudo postorder 230747ad3c4Slazypassion // traversal of the graph. 231747ad3c4Slazypassion for lp in self.loops().rev() { 232832666c4SRyan Hunt for BlockPredecessor { 233832666c4SRyan Hunt block: pred, 234747ad3c4Slazypassion inst: pred_inst, 235747ad3c4Slazypassion } in cfg.pred_iter(self.loops[lp].header) 236747ad3c4Slazypassion { 237747ad3c4Slazypassion // We follow the back edges 238747ad3c4Slazypassion if domtree.dominates(self.loops[lp].header, pred_inst, layout) { 239747ad3c4Slazypassion stack.push(pred); 240747ad3c4Slazypassion } 241747ad3c4Slazypassion } 242747ad3c4Slazypassion while let Some(node) = stack.pop() { 243832666c4SRyan Hunt let continue_dfs: Option<Block>; 244832666c4SRyan Hunt match self.block_loop_map[node].expand() { 245747ad3c4Slazypassion None => { 246747ad3c4Slazypassion // The node hasn't been visited yet, we tag it as part of the loop 247832666c4SRyan Hunt self.block_loop_map[node] = PackedOption::from(lp); 248747ad3c4Slazypassion continue_dfs = Some(node); 249747ad3c4Slazypassion } 250747ad3c4Slazypassion Some(node_loop) => { 251747ad3c4Slazypassion // We copy the node_loop into a mutable reference passed along the while 252747ad3c4Slazypassion let mut node_loop = node_loop; 253747ad3c4Slazypassion // The node is part of a loop, which can be lp or an inner loop 254747ad3c4Slazypassion let mut node_loop_parent_option = self.loops[node_loop].parent; 255747ad3c4Slazypassion while let Some(node_loop_parent) = node_loop_parent_option.expand() { 256747ad3c4Slazypassion if node_loop_parent == lp { 257747ad3c4Slazypassion // We have encountered lp so we stop (already visited) 258747ad3c4Slazypassion break; 259747ad3c4Slazypassion } else { 260747ad3c4Slazypassion // 261747ad3c4Slazypassion node_loop = node_loop_parent; 262747ad3c4Slazypassion // We lookup the parent loop 263747ad3c4Slazypassion node_loop_parent_option = self.loops[node_loop].parent; 264747ad3c4Slazypassion } 265747ad3c4Slazypassion } 266747ad3c4Slazypassion // Now node_loop_parent is either: 267747ad3c4Slazypassion // - None and node_loop is an new inner loop of lp 268747ad3c4Slazypassion // - Some(...) and the initial node_loop was a known inner loop of lp 269747ad3c4Slazypassion match node_loop_parent_option.expand() { 270747ad3c4Slazypassion Some(_) => continue_dfs = None, 271747ad3c4Slazypassion None => { 272747ad3c4Slazypassion if node_loop != lp { 273747ad3c4Slazypassion self.loops[node_loop].parent = lp.into(); 274747ad3c4Slazypassion continue_dfs = Some(self.loops[node_loop].header) 275747ad3c4Slazypassion } else { 276747ad3c4Slazypassion // If lp is a one-block loop then we make sure we stop 277747ad3c4Slazypassion continue_dfs = None 278747ad3c4Slazypassion } 279747ad3c4Slazypassion } 280747ad3c4Slazypassion } 281747ad3c4Slazypassion } 282747ad3c4Slazypassion } 283747ad3c4Slazypassion // Now we have handled the popped node and need to continue the DFS by adding the 284747ad3c4Slazypassion // predecessors of that node 285747ad3c4Slazypassion if let Some(continue_dfs) = continue_dfs { 286832666c4SRyan Hunt for BlockPredecessor { block: pred, .. } in cfg.pred_iter(continue_dfs) { 287747ad3c4Slazypassion stack.push(pred) 288747ad3c4Slazypassion } 289747ad3c4Slazypassion } 290747ad3c4Slazypassion } 291747ad3c4Slazypassion } 292747ad3c4Slazypassion } 293*2be12a51SChris Fallin 294*2be12a51SChris Fallin fn assign_loop_levels(&mut self) { 295*2be12a51SChris Fallin let mut stack: SmallVec<[Loop; 8]> = smallvec![]; 296*2be12a51SChris Fallin for lp in self.loops.keys() { 297*2be12a51SChris Fallin if self.loops[lp].level == LoopLevel::invalid() { 298*2be12a51SChris Fallin stack.push(lp); 299*2be12a51SChris Fallin while let Some(&lp) = stack.last() { 300*2be12a51SChris Fallin if let Some(parent) = self.loops[lp].parent.into() { 301*2be12a51SChris Fallin if self.loops[parent].level != LoopLevel::invalid() { 302*2be12a51SChris Fallin self.loops[lp].level = self.loops[parent].level.inc(); 303*2be12a51SChris Fallin stack.pop(); 304*2be12a51SChris Fallin } else { 305*2be12a51SChris Fallin stack.push(parent); 306*2be12a51SChris Fallin } 307*2be12a51SChris Fallin } else { 308*2be12a51SChris Fallin self.loops[lp].level = LoopLevel::root().inc(); 309*2be12a51SChris Fallin stack.pop(); 310*2be12a51SChris Fallin } 311*2be12a51SChris Fallin } 312*2be12a51SChris Fallin } 313*2be12a51SChris Fallin } 314*2be12a51SChris Fallin } 315747ad3c4Slazypassion } 316747ad3c4Slazypassion 317747ad3c4Slazypassion #[cfg(test)] 318747ad3c4Slazypassion mod tests { 319747ad3c4Slazypassion use crate::cursor::{Cursor, FuncCursor}; 320747ad3c4Slazypassion use crate::dominator_tree::DominatorTree; 321747ad3c4Slazypassion use crate::flowgraph::ControlFlowGraph; 322747ad3c4Slazypassion use crate::ir::{types, Function, InstBuilder}; 323747ad3c4Slazypassion use crate::loop_analysis::{Loop, LoopAnalysis}; 32410e226f9Sbjorn3 use alloc::vec::Vec; 325747ad3c4Slazypassion 326747ad3c4Slazypassion #[test] 327747ad3c4Slazypassion fn nested_loops_detection() { 328747ad3c4Slazypassion let mut func = Function::new(); 329832666c4SRyan Hunt let block0 = func.dfg.make_block(); 330832666c4SRyan Hunt let block1 = func.dfg.make_block(); 331832666c4SRyan Hunt let block2 = func.dfg.make_block(); 332832666c4SRyan Hunt let block3 = func.dfg.make_block(); 333832666c4SRyan Hunt let cond = func.dfg.append_block_param(block0, types::I32); 334747ad3c4Slazypassion 335747ad3c4Slazypassion { 336747ad3c4Slazypassion let mut cur = FuncCursor::new(&mut func); 337747ad3c4Slazypassion 338832666c4SRyan Hunt cur.insert_block(block0); 339832666c4SRyan Hunt cur.ins().jump(block1, &[]); 340747ad3c4Slazypassion 341832666c4SRyan Hunt cur.insert_block(block1); 342832666c4SRyan Hunt cur.ins().jump(block2, &[]); 343747ad3c4Slazypassion 344832666c4SRyan Hunt cur.insert_block(block2); 345832666c4SRyan Hunt cur.ins().brnz(cond, block1, &[]); 346832666c4SRyan Hunt cur.ins().jump(block3, &[]); 347747ad3c4Slazypassion 348832666c4SRyan Hunt cur.insert_block(block3); 349832666c4SRyan Hunt cur.ins().brnz(cond, block0, &[]); 350747ad3c4Slazypassion } 351747ad3c4Slazypassion 352747ad3c4Slazypassion let mut loop_analysis = LoopAnalysis::new(); 353747ad3c4Slazypassion let mut cfg = ControlFlowGraph::new(); 354747ad3c4Slazypassion let mut domtree = DominatorTree::new(); 355747ad3c4Slazypassion cfg.compute(&func); 356747ad3c4Slazypassion domtree.compute(&func, &cfg); 357747ad3c4Slazypassion loop_analysis.compute(&func, &cfg, &domtree); 358747ad3c4Slazypassion 359747ad3c4Slazypassion let loops = loop_analysis.loops().collect::<Vec<Loop>>(); 360747ad3c4Slazypassion assert_eq!(loops.len(), 2); 361832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[0]), block0); 362832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[1]), block1); 363747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0])); 364747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[0]), None); 365832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[0]), true); 366832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false); 367832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block1, loops[1]), true); 368832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block1, loops[0]), true); 369832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block2, loops[1]), true); 370832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block2, loops[0]), true); 371832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block3, loops[0]), true); 372832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false); 373*2be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block0).level(), 1); 374*2be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block1).level(), 2); 375*2be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block2).level(), 2); 376*2be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block3).level(), 1); 377747ad3c4Slazypassion } 378747ad3c4Slazypassion 379747ad3c4Slazypassion #[test] 380747ad3c4Slazypassion fn complex_loop_detection() { 381747ad3c4Slazypassion let mut func = Function::new(); 382832666c4SRyan Hunt let block0 = func.dfg.make_block(); 383832666c4SRyan Hunt let block1 = func.dfg.make_block(); 384832666c4SRyan Hunt let block2 = func.dfg.make_block(); 385832666c4SRyan Hunt let block3 = func.dfg.make_block(); 386832666c4SRyan Hunt let block4 = func.dfg.make_block(); 387832666c4SRyan Hunt let block5 = func.dfg.make_block(); 388832666c4SRyan Hunt let cond = func.dfg.append_block_param(block0, types::I32); 389747ad3c4Slazypassion 390747ad3c4Slazypassion { 391747ad3c4Slazypassion let mut cur = FuncCursor::new(&mut func); 392747ad3c4Slazypassion 393832666c4SRyan Hunt cur.insert_block(block0); 394832666c4SRyan Hunt cur.ins().brnz(cond, block1, &[]); 395832666c4SRyan Hunt cur.ins().jump(block3, &[]); 396747ad3c4Slazypassion 397832666c4SRyan Hunt cur.insert_block(block1); 398832666c4SRyan Hunt cur.ins().jump(block2, &[]); 399747ad3c4Slazypassion 400832666c4SRyan Hunt cur.insert_block(block2); 401832666c4SRyan Hunt cur.ins().brnz(cond, block1, &[]); 402832666c4SRyan Hunt cur.ins().jump(block5, &[]); 403747ad3c4Slazypassion 404832666c4SRyan Hunt cur.insert_block(block3); 405832666c4SRyan Hunt cur.ins().jump(block4, &[]); 406747ad3c4Slazypassion 407832666c4SRyan Hunt cur.insert_block(block4); 408832666c4SRyan Hunt cur.ins().brnz(cond, block3, &[]); 409832666c4SRyan Hunt cur.ins().jump(block5, &[]); 410747ad3c4Slazypassion 411832666c4SRyan Hunt cur.insert_block(block5); 412832666c4SRyan Hunt cur.ins().brnz(cond, block0, &[]); 413747ad3c4Slazypassion } 414747ad3c4Slazypassion 415747ad3c4Slazypassion let mut loop_analysis = LoopAnalysis::new(); 416747ad3c4Slazypassion let mut cfg = ControlFlowGraph::new(); 417747ad3c4Slazypassion let mut domtree = DominatorTree::new(); 418747ad3c4Slazypassion cfg.compute(&func); 419747ad3c4Slazypassion domtree.compute(&func, &cfg); 420747ad3c4Slazypassion loop_analysis.compute(&func, &cfg, &domtree); 421747ad3c4Slazypassion 422747ad3c4Slazypassion let loops = loop_analysis.loops().collect::<Vec<Loop>>(); 423747ad3c4Slazypassion assert_eq!(loops.len(), 3); 424832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[0]), block0); 425832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[1]), block1); 426832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[2]), block3); 427747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0])); 428747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[2]), Some(loops[0])); 429747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[0]), None); 430832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[0]), true); 431832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block1, loops[1]), true); 432832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block2, loops[1]), true); 433832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block3, loops[2]), true); 434832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block4, loops[2]), true); 435832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block5, loops[0]), true); 436*2be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block0).level(), 1); 437*2be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block1).level(), 2); 438*2be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block2).level(), 2); 439*2be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block3).level(), 2); 440*2be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block4).level(), 2); 441*2be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block5).level(), 1); 442747ad3c4Slazypassion } 443747ad3c4Slazypassion } 444