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; 690ac295eSAlex Crichton use crate::entity::entity_impl; 7747ad3c4Slazypassion use crate::entity::{Keys, PrimaryMap}; 83e0b7e50SKirpal Grewal use crate::flowgraph::ControlFlowGraph; 93a14fa39SPaul Nodet use crate::ir::{Block, Function}; 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). root() -> Self432be12a51SChris Fallin pub fn root() -> Self { 442be12a51SChris Fallin Self(0) 452be12a51SChris Fallin } 462be12a51SChris Fallin /// Get the loop level. level(self) -> usize472be12a51SChris Fallin pub fn level(self) -> usize { 482be12a51SChris Fallin self.0 as usize 492be12a51SChris Fallin } 502be12a51SChris Fallin /// Invalid loop level. invalid() -> Self512be12a51SChris Fallin pub fn invalid() -> Self { 522be12a51SChris Fallin Self(Self::INVALID) 532be12a51SChris Fallin } 542be12a51SChris Fallin /// One loop level deeper. inc(self) -> Self552be12a51SChris 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. clamped(level: usize) -> Self632be12a51SChris Fallin pub fn clamped(level: usize) -> Self { 642be12a51SChris Fallin Self( 65*0889323aSSSD u8::try_from(core::cmp::min(level, (Self::INVALID as usize) - 1)) 662be12a51SChris Fallin .expect("Clamped value must always convert"), 672be12a51SChris Fallin ) 682be12a51SChris Fallin } 692be12a51SChris Fallin } 702be12a51SChris Fallin 71*0889323aSSSD impl core::default::Default for LoopLevel { default() -> Self722be12a51SChris 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. new(header: Block, parent: Option<Loop>) -> Self79832666c4SRyan 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. new() -> Self92747ad3c4Slazypassion 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. loops(&self) -> Keys<Loop>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. loop_header(&self, lp: Loop) -> Block109832666c4SRyan 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. loop_parent(&self, lp: Loop) -> Option<Loop>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. innermost_loop(&self, block: Block) -> Option<Loop>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. is_loop_header(&self, block: Block) -> Option<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`. is_in_loop(&self, block: Block, lp: Loop) -> bool132832666c4SRyan 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`). is_child_loop(&self, child: Loop, parent: Loop) -> bool144747ad3c4Slazypassion 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. loop_level(&self, block: Block) -> LoopLevel1562be12a51SChris 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. compute(&mut self, func: &Function, cfg: &ControlFlowGraph, domtree: &DominatorTree)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()); 1693a14fa39SPaul Nodet self.find_loop_headers(cfg, domtree); 1703a14fa39SPaul Nodet self.discover_loop_blocks(cfg, domtree); 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. is_valid(&self) -> bool180747ad3c4Slazypassion 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. clear(&mut self)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. is_block_loop_header(block: Block, cfg: &ControlFlowGraph, domtree: &DominatorTree) -> bool1953a14fa39SPaul Nodet fn is_block_loop_header(block: Block, cfg: &ControlFlowGraph, domtree: &DominatorTree) -> bool { 1963e0b7e50SKirpal Grewal // A block is a loop header if it dominates any of its predecessors. 1973e0b7e50SKirpal Grewal cfg.pred_iter(block) 1983a14fa39SPaul Nodet .any(|pred| domtree.block_dominates(block, pred.block)) 1993e0b7e50SKirpal Grewal } 2003e0b7e50SKirpal Grewal 201832666c4SRyan Hunt // Traverses the CFG in reverse postorder and create a loop object for every block having a 202747ad3c4Slazypassion // back edge. find_loop_headers(&mut self, cfg: &ControlFlowGraph, domtree: &DominatorTree)2033a14fa39SPaul Nodet fn find_loop_headers(&mut self, cfg: &ControlFlowGraph, domtree: &DominatorTree) { 2043e0b7e50SKirpal Grewal for &block in domtree 2053e0b7e50SKirpal Grewal .cfg_rpo() 2063a14fa39SPaul Nodet .filter(|&&block| Self::is_block_loop_header(block, cfg, domtree)) 207747ad3c4Slazypassion { 208832666c4SRyan Hunt // This block is a loop header, so we create its associated loop 209832666c4SRyan Hunt let lp = self.loops.push(LoopData::new(block, None)); 210832666c4SRyan Hunt self.block_loop_map[block] = lp.into(); 211747ad3c4Slazypassion } 212747ad3c4Slazypassion } 213747ad3c4Slazypassion 214747ad3c4Slazypassion // Intended to be called after `find_loop_headers`. For each detected loop header, 215832666c4SRyan Hunt // discovers all the block belonging to the loop and its inner loops. After a call to this 216747ad3c4Slazypassion // function, the loop tree is fully constructed. discover_loop_blocks(&mut self, cfg: &ControlFlowGraph, domtree: &DominatorTree)2173a14fa39SPaul Nodet fn discover_loop_blocks(&mut self, cfg: &ControlFlowGraph, domtree: &DominatorTree) { 218832666c4SRyan Hunt let mut stack: Vec<Block> = Vec::new(); 219747ad3c4Slazypassion // We handle each loop header in reverse order, corresponding to a pseudo postorder 220747ad3c4Slazypassion // traversal of the graph. 221747ad3c4Slazypassion for lp in self.loops().rev() { 2223e0b7e50SKirpal Grewal // Push all predecessors of this header that it dominates onto the stack. 2233e0b7e50SKirpal Grewal stack.extend( 2243e0b7e50SKirpal Grewal cfg.pred_iter(self.loops[lp].header) 2253e0b7e50SKirpal Grewal .filter(|pred| { 226747ad3c4Slazypassion // We follow the back edges 2273a14fa39SPaul Nodet domtree.block_dominates(self.loops[lp].header, pred.block) 2283e0b7e50SKirpal Grewal }) 2293e0b7e50SKirpal Grewal .map(|pred| pred.block), 2303e0b7e50SKirpal Grewal ); 231747ad3c4Slazypassion while let Some(node) = stack.pop() { 232832666c4SRyan Hunt let continue_dfs: Option<Block>; 233832666c4SRyan Hunt match self.block_loop_map[node].expand() { 234747ad3c4Slazypassion None => { 235747ad3c4Slazypassion // The node hasn't been visited yet, we tag it as part of the loop 236832666c4SRyan Hunt self.block_loop_map[node] = PackedOption::from(lp); 237747ad3c4Slazypassion continue_dfs = Some(node); 238747ad3c4Slazypassion } 239747ad3c4Slazypassion Some(node_loop) => { 240747ad3c4Slazypassion // We copy the node_loop into a mutable reference passed along the while 241747ad3c4Slazypassion let mut node_loop = node_loop; 242747ad3c4Slazypassion // The node is part of a loop, which can be lp or an inner loop 243747ad3c4Slazypassion let mut node_loop_parent_option = self.loops[node_loop].parent; 244747ad3c4Slazypassion while let Some(node_loop_parent) = node_loop_parent_option.expand() { 245747ad3c4Slazypassion if node_loop_parent == lp { 246747ad3c4Slazypassion // We have encountered lp so we stop (already visited) 247747ad3c4Slazypassion break; 248747ad3c4Slazypassion } else { 249747ad3c4Slazypassion // 250747ad3c4Slazypassion node_loop = node_loop_parent; 251747ad3c4Slazypassion // We lookup the parent loop 252747ad3c4Slazypassion node_loop_parent_option = self.loops[node_loop].parent; 253747ad3c4Slazypassion } 254747ad3c4Slazypassion } 255747ad3c4Slazypassion // Now node_loop_parent is either: 256747ad3c4Slazypassion // - None and node_loop is an new inner loop of lp 257747ad3c4Slazypassion // - Some(...) and the initial node_loop was a known inner loop of lp 258747ad3c4Slazypassion match node_loop_parent_option.expand() { 259747ad3c4Slazypassion Some(_) => continue_dfs = None, 260747ad3c4Slazypassion None => { 261747ad3c4Slazypassion if node_loop != lp { 262747ad3c4Slazypassion self.loops[node_loop].parent = lp.into(); 263747ad3c4Slazypassion continue_dfs = Some(self.loops[node_loop].header) 264747ad3c4Slazypassion } else { 265747ad3c4Slazypassion // If lp is a one-block loop then we make sure we stop 266747ad3c4Slazypassion continue_dfs = None 267747ad3c4Slazypassion } 268747ad3c4Slazypassion } 269747ad3c4Slazypassion } 270747ad3c4Slazypassion } 271747ad3c4Slazypassion } 272747ad3c4Slazypassion // Now we have handled the popped node and need to continue the DFS by adding the 273747ad3c4Slazypassion // predecessors of that node 274747ad3c4Slazypassion if let Some(continue_dfs) = continue_dfs { 2753e0b7e50SKirpal Grewal stack.extend(cfg.pred_iter(continue_dfs).map(|pred| pred.block)); 276747ad3c4Slazypassion } 277747ad3c4Slazypassion } 278747ad3c4Slazypassion } 279747ad3c4Slazypassion } 2802be12a51SChris Fallin assign_loop_levels(&mut self)2812be12a51SChris Fallin fn assign_loop_levels(&mut self) { 2823e0b7e50SKirpal Grewal let mut stack: SmallVec<[Loop; 8]> = SmallVec::new(); 2832be12a51SChris Fallin for lp in self.loops.keys() { 2842be12a51SChris Fallin if self.loops[lp].level == LoopLevel::invalid() { 2852be12a51SChris Fallin stack.push(lp); 2862be12a51SChris Fallin while let Some(&lp) = stack.last() { 2872be12a51SChris Fallin if let Some(parent) = self.loops[lp].parent.into() { 2882be12a51SChris Fallin if self.loops[parent].level != LoopLevel::invalid() { 2892be12a51SChris Fallin self.loops[lp].level = self.loops[parent].level.inc(); 2902be12a51SChris Fallin stack.pop(); 2912be12a51SChris Fallin } else { 2922be12a51SChris Fallin stack.push(parent); 2932be12a51SChris Fallin } 2942be12a51SChris Fallin } else { 2952be12a51SChris Fallin self.loops[lp].level = LoopLevel::root().inc(); 2962be12a51SChris Fallin stack.pop(); 2972be12a51SChris Fallin } 2982be12a51SChris Fallin } 2992be12a51SChris Fallin } 3002be12a51SChris Fallin } 3012be12a51SChris Fallin } 302747ad3c4Slazypassion } 303747ad3c4Slazypassion 304747ad3c4Slazypassion #[cfg(test)] 305747ad3c4Slazypassion mod tests { 306747ad3c4Slazypassion use crate::cursor::{Cursor, FuncCursor}; 307747ad3c4Slazypassion use crate::dominator_tree::DominatorTree; 308747ad3c4Slazypassion use crate::flowgraph::ControlFlowGraph; 30990ac295eSAlex Crichton use crate::ir::{Function, InstBuilder, types}; 310747ad3c4Slazypassion use crate::loop_analysis::{Loop, LoopAnalysis}; 31110e226f9Sbjorn3 use alloc::vec::Vec; 312747ad3c4Slazypassion 313747ad3c4Slazypassion #[test] nested_loops_detection()314747ad3c4Slazypassion fn nested_loops_detection() { 315747ad3c4Slazypassion let mut func = Function::new(); 316832666c4SRyan Hunt let block0 = func.dfg.make_block(); 317832666c4SRyan Hunt let block1 = func.dfg.make_block(); 318832666c4SRyan Hunt let block2 = func.dfg.make_block(); 319832666c4SRyan Hunt let block3 = func.dfg.make_block(); 320a5698cedSTrevor Elliott let block4 = func.dfg.make_block(); 321832666c4SRyan Hunt let cond = func.dfg.append_block_param(block0, types::I32); 322747ad3c4Slazypassion 323747ad3c4Slazypassion { 324747ad3c4Slazypassion let mut cur = FuncCursor::new(&mut func); 325747ad3c4Slazypassion 326832666c4SRyan Hunt cur.insert_block(block0); 327832666c4SRyan Hunt cur.ins().jump(block1, &[]); 328747ad3c4Slazypassion 329832666c4SRyan Hunt cur.insert_block(block1); 330832666c4SRyan Hunt cur.ins().jump(block2, &[]); 331747ad3c4Slazypassion 332832666c4SRyan Hunt cur.insert_block(block2); 333a5698cedSTrevor Elliott cur.ins().brif(cond, block1, &[], block3, &[]); 334747ad3c4Slazypassion 335832666c4SRyan Hunt cur.insert_block(block3); 336a5698cedSTrevor Elliott cur.ins().brif(cond, block0, &[], block4, &[]); 337a5698cedSTrevor Elliott 338a5698cedSTrevor Elliott cur.insert_block(block4); 339a5698cedSTrevor Elliott cur.ins().return_(&[]); 340747ad3c4Slazypassion } 341747ad3c4Slazypassion 342747ad3c4Slazypassion let mut loop_analysis = LoopAnalysis::new(); 343747ad3c4Slazypassion let mut cfg = ControlFlowGraph::new(); 344747ad3c4Slazypassion let mut domtree = DominatorTree::new(); 345747ad3c4Slazypassion cfg.compute(&func); 346747ad3c4Slazypassion domtree.compute(&func, &cfg); 347747ad3c4Slazypassion loop_analysis.compute(&func, &cfg, &domtree); 348747ad3c4Slazypassion 349747ad3c4Slazypassion let loops = loop_analysis.loops().collect::<Vec<Loop>>(); 350747ad3c4Slazypassion assert_eq!(loops.len(), 2); 351832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[0]), block0); 352832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[1]), block1); 353747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0])); 354747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[0]), None); 355832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[0]), true); 356832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false); 357832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block1, loops[1]), true); 358832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block1, loops[0]), true); 359832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block2, loops[1]), true); 360832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block2, loops[0]), true); 361832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block3, loops[0]), true); 362832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false); 3632be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block0).level(), 1); 3642be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block1).level(), 2); 3652be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block2).level(), 2); 3662be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block3).level(), 1); 367747ad3c4Slazypassion } 368747ad3c4Slazypassion 369747ad3c4Slazypassion #[test] complex_loop_detection()370747ad3c4Slazypassion fn complex_loop_detection() { 371747ad3c4Slazypassion let mut func = Function::new(); 372832666c4SRyan Hunt let block0 = func.dfg.make_block(); 373832666c4SRyan Hunt let block1 = func.dfg.make_block(); 374832666c4SRyan Hunt let block2 = func.dfg.make_block(); 375832666c4SRyan Hunt let block3 = func.dfg.make_block(); 376832666c4SRyan Hunt let block4 = func.dfg.make_block(); 377832666c4SRyan Hunt let block5 = func.dfg.make_block(); 378a5698cedSTrevor Elliott let block6 = func.dfg.make_block(); 379832666c4SRyan Hunt let cond = func.dfg.append_block_param(block0, types::I32); 380747ad3c4Slazypassion 381747ad3c4Slazypassion { 382747ad3c4Slazypassion let mut cur = FuncCursor::new(&mut func); 383747ad3c4Slazypassion 384832666c4SRyan Hunt cur.insert_block(block0); 385a5698cedSTrevor Elliott cur.ins().brif(cond, block1, &[], block3, &[]); 386747ad3c4Slazypassion 387832666c4SRyan Hunt cur.insert_block(block1); 388832666c4SRyan Hunt cur.ins().jump(block2, &[]); 389747ad3c4Slazypassion 390832666c4SRyan Hunt cur.insert_block(block2); 391a5698cedSTrevor Elliott cur.ins().brif(cond, block1, &[], block5, &[]); 392747ad3c4Slazypassion 393832666c4SRyan Hunt cur.insert_block(block3); 394832666c4SRyan Hunt cur.ins().jump(block4, &[]); 395747ad3c4Slazypassion 396832666c4SRyan Hunt cur.insert_block(block4); 397a5698cedSTrevor Elliott cur.ins().brif(cond, block3, &[], block5, &[]); 398747ad3c4Slazypassion 399832666c4SRyan Hunt cur.insert_block(block5); 400a5698cedSTrevor Elliott cur.ins().brif(cond, block0, &[], block6, &[]); 401a5698cedSTrevor Elliott 402a5698cedSTrevor Elliott cur.insert_block(block6); 403a5698cedSTrevor Elliott cur.ins().return_(&[]); 404747ad3c4Slazypassion } 405747ad3c4Slazypassion 406747ad3c4Slazypassion let mut loop_analysis = LoopAnalysis::new(); 4078abfe928STrevor Elliott let cfg = ControlFlowGraph::with_function(&func); 4088abfe928STrevor Elliott let domtree = DominatorTree::with_function(&func, &cfg); 409747ad3c4Slazypassion loop_analysis.compute(&func, &cfg, &domtree); 410747ad3c4Slazypassion 411747ad3c4Slazypassion let loops = loop_analysis.loops().collect::<Vec<Loop>>(); 412747ad3c4Slazypassion assert_eq!(loops.len(), 3); 413832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[0]), block0); 4148abfe928STrevor Elliott assert_eq!(loop_analysis.loop_header(loops[1]), block3); 4158abfe928STrevor Elliott assert_eq!(loop_analysis.loop_header(loops[2]), block1); 416747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0])); 417747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[2]), Some(loops[0])); 418747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[0]), None); 419832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[0]), true); 4208abfe928STrevor Elliott assert_eq!(loop_analysis.is_in_loop(block1, loops[2]), true); 4218abfe928STrevor Elliott assert_eq!(loop_analysis.is_in_loop(block2, loops[2]), true); 4228abfe928STrevor Elliott assert_eq!(loop_analysis.is_in_loop(block3, loops[1]), true); 4238abfe928STrevor Elliott assert_eq!(loop_analysis.is_in_loop(block4, loops[1]), true); 424832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block5, loops[0]), true); 4252be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block0).level(), 1); 4262be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block1).level(), 2); 4272be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block2).level(), 2); 4282be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block3).level(), 2); 4292be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block4).level(), 2); 4302be12a51SChris Fallin assert_eq!(loop_analysis.loop_level(block5).level(), 1); 431747ad3c4Slazypassion } 432747ad3c4Slazypassion } 433