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; 132be12a51SChris 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>, 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 { 40*f980defeSChris 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 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 } 2932be12a51SChris Fallin 2942be12a51SChris Fallin fn assign_loop_levels(&mut self) { 2952be12a51SChris Fallin let mut stack: SmallVec<[Loop; 8]> = smallvec![]; 2962be12a51SChris Fallin for lp in self.loops.keys() { 2972be12a51SChris Fallin if self.loops[lp].level == LoopLevel::invalid() { 2982be12a51SChris Fallin stack.push(lp); 2992be12a51SChris Fallin while let Some(&lp) = stack.last() { 3002be12a51SChris Fallin if let Some(parent) = self.loops[lp].parent.into() { 3012be12a51SChris Fallin if self.loops[parent].level != LoopLevel::invalid() { 3022be12a51SChris Fallin self.loops[lp].level = self.loops[parent].level.inc(); 3032be12a51SChris Fallin stack.pop(); 3042be12a51SChris Fallin } else { 3052be12a51SChris Fallin stack.push(parent); 3062be12a51SChris Fallin } 3072be12a51SChris Fallin } else { 3082be12a51SChris Fallin self.loops[lp].level = LoopLevel::root().inc(); 3092be12a51SChris Fallin stack.pop(); 3102be12a51SChris Fallin } 3112be12a51SChris Fallin } 3122be12a51SChris Fallin } 3132be12a51SChris Fallin } 3142be12a51SChris 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); 3732be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block0).level(), 1); 3742be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block1).level(), 2); 3752be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block2).level(), 2); 3762be12a51SChris 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); 4362be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block0).level(), 1); 4372be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block1).level(), 2); 4382be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block2).level(), 2); 4392be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block3).level(), 2); 4402be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block4).level(), 2); 4412be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block5).level(), 1); 442747ad3c4Slazypassion } 443747ad3c4Slazypassion } 444