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::SecondaryMap; 6*90ac295eSAlex Crichton use crate::entity::entity_impl; 7747ad3c4Slazypassion use crate::entity::{Keys, PrimaryMap}; 83e0b7e50SKirpal Grewal use crate::flowgraph::ControlFlowGraph; 9832666c4SRyan Hunt use crate::ir::{Block, Function, Layout}; 10747ad3c4Slazypassion use crate::packed_option::PackedOption; 11747ad3c4Slazypassion use crate::timing; 1210e226f9Sbjorn3 use alloc::vec::Vec; 133e0b7e50SKirpal Grewal use 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>, 332be12a51SChris Fallin level: LoopLevel, 342be12a51SChris Fallin } 352be12a51SChris Fallin 362be12a51SChris Fallin /// A level in a loop nest. 372be12a51SChris Fallin #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 382be12a51SChris Fallin pub struct LoopLevel(u8); 392be12a51SChris Fallin impl LoopLevel { 40f980defeSChris Fallin const INVALID: u8 = u8::MAX; 412be12a51SChris Fallin 422be12a51SChris Fallin /// Get the root level (no loop). 432be12a51SChris Fallin pub fn root() -> Self { 442be12a51SChris Fallin Self(0) 452be12a51SChris Fallin } 462be12a51SChris Fallin /// Get the loop level. 472be12a51SChris Fallin pub fn level(self) -> usize { 482be12a51SChris Fallin self.0 as usize 492be12a51SChris Fallin } 502be12a51SChris Fallin /// Invalid loop level. 512be12a51SChris Fallin pub fn invalid() -> Self { 522be12a51SChris Fallin Self(Self::INVALID) 532be12a51SChris Fallin } 542be12a51SChris Fallin /// One loop level deeper. 552be12a51SChris Fallin pub fn inc(self) -> Self { 562be12a51SChris Fallin if self.0 == (Self::INVALID - 1) { 572be12a51SChris Fallin self 582be12a51SChris Fallin } else { 592be12a51SChris Fallin Self(self.0 + 1) 602be12a51SChris Fallin } 612be12a51SChris Fallin } 622be12a51SChris Fallin /// A clamped loop level from a larger-width (usize) depth. 632be12a51SChris Fallin pub fn clamped(level: usize) -> Self { 642be12a51SChris Fallin Self( 652be12a51SChris Fallin u8::try_from(std::cmp::min(level, (Self::INVALID as usize) - 1)) 662be12a51SChris Fallin .expect("Clamped value must always convert"), 672be12a51SChris Fallin ) 682be12a51SChris Fallin } 692be12a51SChris Fallin } 702be12a51SChris Fallin 712be12a51SChris Fallin impl std::default::Default for LoopLevel { 722be12a51SChris Fallin fn default() -> Self { 732be12a51SChris Fallin LoopLevel::invalid() 742be12a51SChris 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(), 832be12a51SChris 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 1182be12a51SChris Fallin /// Return the innermost loop for a given block. 1192be12a51SChris Fallin pub fn innermost_loop(&self, block: Block) -> Option<Loop> { 1202be12a51SChris Fallin self.block_loop_map[block].expand() 1212be12a51SChris Fallin } 1222be12a51SChris Fallin 1232be12a51SChris Fallin /// Determine if a Block is a loop header. If so, return the loop. 1242be12a51SChris Fallin pub fn is_loop_header(&self, block: Block) -> Option<Loop> { 1252be12a51SChris Fallin self.innermost_loop(block) 1262be12a51SChris Fallin .filter(|&lp| self.loop_header(lp) == block) 1272be12a51SChris Fallin } 1282be12a51SChris 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 } 1542be12a51SChris Fallin 1552be12a51SChris Fallin /// Returns the loop-nest level of a given block. 1562be12a51SChris Fallin pub fn loop_level(&self, block: Block) -> LoopLevel { 1572be12a51SChris Fallin self.innermost_loop(block) 1582be12a51SChris Fallin .map_or(LoopLevel(0), |lp| self.loops[lp].level) 1592be12a51SChris 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); 1712be12a51SChris 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 1933e0b7e50SKirpal Grewal // Determines if a block dominates any predecessor 1943e0b7e50SKirpal Grewal // and thus is a loop header. 1953e0b7e50SKirpal Grewal fn is_block_loop_header( 1963e0b7e50SKirpal Grewal block: Block, 1973e0b7e50SKirpal Grewal cfg: &ControlFlowGraph, 1983e0b7e50SKirpal Grewal domtree: &DominatorTree, 1993e0b7e50SKirpal Grewal layout: &Layout, 2003e0b7e50SKirpal Grewal ) -> bool { 2013e0b7e50SKirpal Grewal // A block is a loop header if it dominates any of its predecessors. 2023e0b7e50SKirpal Grewal cfg.pred_iter(block) 2033e0b7e50SKirpal Grewal .any(|pred| domtree.dominates(block, pred.inst, layout)) 2043e0b7e50SKirpal Grewal } 2053e0b7e50SKirpal Grewal 206832666c4SRyan Hunt // Traverses the CFG in reverse postorder and create a loop object for every block having a 207747ad3c4Slazypassion // back edge. 208747ad3c4Slazypassion fn find_loop_headers( 209747ad3c4Slazypassion &mut self, 210747ad3c4Slazypassion cfg: &ControlFlowGraph, 211747ad3c4Slazypassion domtree: &DominatorTree, 212747ad3c4Slazypassion layout: &Layout, 213747ad3c4Slazypassion ) { 2143e0b7e50SKirpal Grewal for &block in domtree 2153e0b7e50SKirpal Grewal .cfg_rpo() 2163e0b7e50SKirpal Grewal .filter(|&&block| Self::is_block_loop_header(block, cfg, domtree, layout)) 217747ad3c4Slazypassion { 218832666c4SRyan Hunt // This block is a loop header, so we create its associated loop 219832666c4SRyan Hunt let lp = self.loops.push(LoopData::new(block, None)); 220832666c4SRyan Hunt self.block_loop_map[block] = lp.into(); 221747ad3c4Slazypassion } 222747ad3c4Slazypassion } 223747ad3c4Slazypassion 224747ad3c4Slazypassion // Intended to be called after `find_loop_headers`. For each detected loop header, 225832666c4SRyan Hunt // discovers all the block belonging to the loop and its inner loops. After a call to this 226747ad3c4Slazypassion // function, the loop tree is fully constructed. 227747ad3c4Slazypassion fn discover_loop_blocks( 228747ad3c4Slazypassion &mut self, 229747ad3c4Slazypassion cfg: &ControlFlowGraph, 230747ad3c4Slazypassion domtree: &DominatorTree, 231747ad3c4Slazypassion layout: &Layout, 232747ad3c4Slazypassion ) { 233832666c4SRyan Hunt let mut stack: Vec<Block> = Vec::new(); 234747ad3c4Slazypassion // We handle each loop header in reverse order, corresponding to a pseudo postorder 235747ad3c4Slazypassion // traversal of the graph. 236747ad3c4Slazypassion for lp in self.loops().rev() { 2373e0b7e50SKirpal Grewal // Push all predecessors of this header that it dominates onto the stack. 2383e0b7e50SKirpal Grewal stack.extend( 2393e0b7e50SKirpal Grewal cfg.pred_iter(self.loops[lp].header) 2403e0b7e50SKirpal Grewal .filter(|pred| { 241747ad3c4Slazypassion // We follow the back edges 2423e0b7e50SKirpal Grewal domtree.dominates(self.loops[lp].header, pred.inst, layout) 2433e0b7e50SKirpal Grewal }) 2443e0b7e50SKirpal Grewal .map(|pred| pred.block), 2453e0b7e50SKirpal Grewal ); 246747ad3c4Slazypassion while let Some(node) = stack.pop() { 247832666c4SRyan Hunt let continue_dfs: Option<Block>; 248832666c4SRyan Hunt match self.block_loop_map[node].expand() { 249747ad3c4Slazypassion None => { 250747ad3c4Slazypassion // The node hasn't been visited yet, we tag it as part of the loop 251832666c4SRyan Hunt self.block_loop_map[node] = PackedOption::from(lp); 252747ad3c4Slazypassion continue_dfs = Some(node); 253747ad3c4Slazypassion } 254747ad3c4Slazypassion Some(node_loop) => { 255747ad3c4Slazypassion // We copy the node_loop into a mutable reference passed along the while 256747ad3c4Slazypassion let mut node_loop = node_loop; 257747ad3c4Slazypassion // The node is part of a loop, which can be lp or an inner loop 258747ad3c4Slazypassion let mut node_loop_parent_option = self.loops[node_loop].parent; 259747ad3c4Slazypassion while let Some(node_loop_parent) = node_loop_parent_option.expand() { 260747ad3c4Slazypassion if node_loop_parent == lp { 261747ad3c4Slazypassion // We have encountered lp so we stop (already visited) 262747ad3c4Slazypassion break; 263747ad3c4Slazypassion } else { 264747ad3c4Slazypassion // 265747ad3c4Slazypassion node_loop = node_loop_parent; 266747ad3c4Slazypassion // We lookup the parent loop 267747ad3c4Slazypassion node_loop_parent_option = self.loops[node_loop].parent; 268747ad3c4Slazypassion } 269747ad3c4Slazypassion } 270747ad3c4Slazypassion // Now node_loop_parent is either: 271747ad3c4Slazypassion // - None and node_loop is an new inner loop of lp 272747ad3c4Slazypassion // - Some(...) and the initial node_loop was a known inner loop of lp 273747ad3c4Slazypassion match node_loop_parent_option.expand() { 274747ad3c4Slazypassion Some(_) => continue_dfs = None, 275747ad3c4Slazypassion None => { 276747ad3c4Slazypassion if node_loop != lp { 277747ad3c4Slazypassion self.loops[node_loop].parent = lp.into(); 278747ad3c4Slazypassion continue_dfs = Some(self.loops[node_loop].header) 279747ad3c4Slazypassion } else { 280747ad3c4Slazypassion // If lp is a one-block loop then we make sure we stop 281747ad3c4Slazypassion continue_dfs = None 282747ad3c4Slazypassion } 283747ad3c4Slazypassion } 284747ad3c4Slazypassion } 285747ad3c4Slazypassion } 286747ad3c4Slazypassion } 287747ad3c4Slazypassion // Now we have handled the popped node and need to continue the DFS by adding the 288747ad3c4Slazypassion // predecessors of that node 289747ad3c4Slazypassion if let Some(continue_dfs) = continue_dfs { 2903e0b7e50SKirpal Grewal stack.extend(cfg.pred_iter(continue_dfs).map(|pred| pred.block)); 291747ad3c4Slazypassion } 292747ad3c4Slazypassion } 293747ad3c4Slazypassion } 294747ad3c4Slazypassion } 2952be12a51SChris Fallin 2962be12a51SChris Fallin fn assign_loop_levels(&mut self) { 2973e0b7e50SKirpal Grewal let mut stack: SmallVec<[Loop; 8]> = SmallVec::new(); 2982be12a51SChris Fallin for lp in self.loops.keys() { 2992be12a51SChris Fallin if self.loops[lp].level == LoopLevel::invalid() { 3002be12a51SChris Fallin stack.push(lp); 3012be12a51SChris Fallin while let Some(&lp) = stack.last() { 3022be12a51SChris Fallin if let Some(parent) = self.loops[lp].parent.into() { 3032be12a51SChris Fallin if self.loops[parent].level != LoopLevel::invalid() { 3042be12a51SChris Fallin self.loops[lp].level = self.loops[parent].level.inc(); 3052be12a51SChris Fallin stack.pop(); 3062be12a51SChris Fallin } else { 3072be12a51SChris Fallin stack.push(parent); 3082be12a51SChris Fallin } 3092be12a51SChris Fallin } else { 3102be12a51SChris Fallin self.loops[lp].level = LoopLevel::root().inc(); 3112be12a51SChris Fallin stack.pop(); 3122be12a51SChris Fallin } 3132be12a51SChris Fallin } 3142be12a51SChris Fallin } 3152be12a51SChris Fallin } 3162be12a51SChris Fallin } 317747ad3c4Slazypassion } 318747ad3c4Slazypassion 319747ad3c4Slazypassion #[cfg(test)] 320747ad3c4Slazypassion mod tests { 321747ad3c4Slazypassion use crate::cursor::{Cursor, FuncCursor}; 322747ad3c4Slazypassion use crate::dominator_tree::DominatorTree; 323747ad3c4Slazypassion use crate::flowgraph::ControlFlowGraph; 324*90ac295eSAlex Crichton use crate::ir::{Function, InstBuilder, types}; 325747ad3c4Slazypassion use crate::loop_analysis::{Loop, LoopAnalysis}; 32610e226f9Sbjorn3 use alloc::vec::Vec; 327747ad3c4Slazypassion 328747ad3c4Slazypassion #[test] 329747ad3c4Slazypassion fn nested_loops_detection() { 330747ad3c4Slazypassion let mut func = Function::new(); 331832666c4SRyan Hunt let block0 = func.dfg.make_block(); 332832666c4SRyan Hunt let block1 = func.dfg.make_block(); 333832666c4SRyan Hunt let block2 = func.dfg.make_block(); 334832666c4SRyan Hunt let block3 = func.dfg.make_block(); 335a5698cedSTrevor Elliott let block4 = func.dfg.make_block(); 336832666c4SRyan Hunt let cond = func.dfg.append_block_param(block0, types::I32); 337747ad3c4Slazypassion 338747ad3c4Slazypassion { 339747ad3c4Slazypassion let mut cur = FuncCursor::new(&mut func); 340747ad3c4Slazypassion 341832666c4SRyan Hunt cur.insert_block(block0); 342832666c4SRyan Hunt cur.ins().jump(block1, &[]); 343747ad3c4Slazypassion 344832666c4SRyan Hunt cur.insert_block(block1); 345832666c4SRyan Hunt cur.ins().jump(block2, &[]); 346747ad3c4Slazypassion 347832666c4SRyan Hunt cur.insert_block(block2); 348a5698cedSTrevor Elliott cur.ins().brif(cond, block1, &[], block3, &[]); 349747ad3c4Slazypassion 350832666c4SRyan Hunt cur.insert_block(block3); 351a5698cedSTrevor Elliott cur.ins().brif(cond, block0, &[], block4, &[]); 352a5698cedSTrevor Elliott 353a5698cedSTrevor Elliott cur.insert_block(block4); 354a5698cedSTrevor Elliott cur.ins().return_(&[]); 355747ad3c4Slazypassion } 356747ad3c4Slazypassion 357747ad3c4Slazypassion let mut loop_analysis = LoopAnalysis::new(); 358747ad3c4Slazypassion let mut cfg = ControlFlowGraph::new(); 359747ad3c4Slazypassion let mut domtree = DominatorTree::new(); 360747ad3c4Slazypassion cfg.compute(&func); 361747ad3c4Slazypassion domtree.compute(&func, &cfg); 362747ad3c4Slazypassion loop_analysis.compute(&func, &cfg, &domtree); 363747ad3c4Slazypassion 364747ad3c4Slazypassion let loops = loop_analysis.loops().collect::<Vec<Loop>>(); 365747ad3c4Slazypassion assert_eq!(loops.len(), 2); 366832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[0]), block0); 367832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[1]), block1); 368747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0])); 369747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[0]), None); 370832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[0]), true); 371832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false); 372832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block1, loops[1]), true); 373832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block1, loops[0]), true); 374832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block2, loops[1]), true); 375832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block2, loops[0]), true); 376832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block3, loops[0]), true); 377832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false); 3782be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block0).level(), 1); 3792be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block1).level(), 2); 3802be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block2).level(), 2); 3812be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block3).level(), 1); 382747ad3c4Slazypassion } 383747ad3c4Slazypassion 384747ad3c4Slazypassion #[test] 385747ad3c4Slazypassion fn complex_loop_detection() { 386747ad3c4Slazypassion let mut func = Function::new(); 387832666c4SRyan Hunt let block0 = func.dfg.make_block(); 388832666c4SRyan Hunt let block1 = func.dfg.make_block(); 389832666c4SRyan Hunt let block2 = func.dfg.make_block(); 390832666c4SRyan Hunt let block3 = func.dfg.make_block(); 391832666c4SRyan Hunt let block4 = func.dfg.make_block(); 392832666c4SRyan Hunt let block5 = func.dfg.make_block(); 393a5698cedSTrevor Elliott let block6 = func.dfg.make_block(); 394832666c4SRyan Hunt let cond = func.dfg.append_block_param(block0, types::I32); 395747ad3c4Slazypassion 396747ad3c4Slazypassion { 397747ad3c4Slazypassion let mut cur = FuncCursor::new(&mut func); 398747ad3c4Slazypassion 399832666c4SRyan Hunt cur.insert_block(block0); 400a5698cedSTrevor Elliott cur.ins().brif(cond, block1, &[], block3, &[]); 401747ad3c4Slazypassion 402832666c4SRyan Hunt cur.insert_block(block1); 403832666c4SRyan Hunt cur.ins().jump(block2, &[]); 404747ad3c4Slazypassion 405832666c4SRyan Hunt cur.insert_block(block2); 406a5698cedSTrevor Elliott cur.ins().brif(cond, block1, &[], block5, &[]); 407747ad3c4Slazypassion 408832666c4SRyan Hunt cur.insert_block(block3); 409832666c4SRyan Hunt cur.ins().jump(block4, &[]); 410747ad3c4Slazypassion 411832666c4SRyan Hunt cur.insert_block(block4); 412a5698cedSTrevor Elliott cur.ins().brif(cond, block3, &[], block5, &[]); 413747ad3c4Slazypassion 414832666c4SRyan Hunt cur.insert_block(block5); 415a5698cedSTrevor Elliott cur.ins().brif(cond, block0, &[], block6, &[]); 416a5698cedSTrevor Elliott 417a5698cedSTrevor Elliott cur.insert_block(block6); 418a5698cedSTrevor Elliott cur.ins().return_(&[]); 419747ad3c4Slazypassion } 420747ad3c4Slazypassion 421747ad3c4Slazypassion let mut loop_analysis = LoopAnalysis::new(); 4228abfe928STrevor Elliott let cfg = ControlFlowGraph::with_function(&func); 4238abfe928STrevor Elliott let domtree = DominatorTree::with_function(&func, &cfg); 424747ad3c4Slazypassion loop_analysis.compute(&func, &cfg, &domtree); 425747ad3c4Slazypassion 426747ad3c4Slazypassion let loops = loop_analysis.loops().collect::<Vec<Loop>>(); 427747ad3c4Slazypassion assert_eq!(loops.len(), 3); 428832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[0]), block0); 4298abfe928STrevor Elliott assert_eq!(loop_analysis.loop_header(loops[1]), block3); 4308abfe928STrevor Elliott assert_eq!(loop_analysis.loop_header(loops[2]), block1); 431747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0])); 432747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[2]), Some(loops[0])); 433747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[0]), None); 434832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[0]), true); 4358abfe928STrevor Elliott assert_eq!(loop_analysis.is_in_loop(block1, loops[2]), true); 4368abfe928STrevor Elliott assert_eq!(loop_analysis.is_in_loop(block2, loops[2]), true); 4378abfe928STrevor Elliott assert_eq!(loop_analysis.is_in_loop(block3, loops[1]), true); 4388abfe928STrevor Elliott assert_eq!(loop_analysis.is_in_loop(block4, loops[1]), true); 439832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block5, loops[0]), true); 4402be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block0).level(), 1); 4412be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block1).level(), 2); 4422be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block2).level(), 2); 4432be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block3).level(), 2); 4442be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block4).level(), 2); 4452be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block5).level(), 1); 446747ad3c4Slazypassion } 447747ad3c4Slazypassion } 448