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; 13747ad3c4Slazypassion 14747ad3c4Slazypassion /// A opaque reference to a code loop. 15747ad3c4Slazypassion #[derive(Copy, Clone, PartialEq, Eq, Hash)] 16747ad3c4Slazypassion pub struct Loop(u32); 17747ad3c4Slazypassion entity_impl!(Loop, "loop"); 18747ad3c4Slazypassion 19747ad3c4Slazypassion /// Loop tree information for a single function. 20747ad3c4Slazypassion /// 21832666c4SRyan Hunt /// Loops are referenced by the Loop object, and for each loop you can access its header block, 22832666c4SRyan Hunt /// its eventual parent in the loop tree and all the block belonging to the loop. 23747ad3c4Slazypassion pub struct LoopAnalysis { 24747ad3c4Slazypassion loops: PrimaryMap<Loop, LoopData>, 25832666c4SRyan Hunt block_loop_map: SecondaryMap<Block, PackedOption<Loop>>, 26747ad3c4Slazypassion valid: bool, 27747ad3c4Slazypassion } 28747ad3c4Slazypassion 29747ad3c4Slazypassion struct LoopData { 30832666c4SRyan Hunt header: Block, 31747ad3c4Slazypassion parent: PackedOption<Loop>, 32747ad3c4Slazypassion } 33747ad3c4Slazypassion 34747ad3c4Slazypassion impl LoopData { 35747ad3c4Slazypassion /// Creates a `LoopData` object with the loop header and its eventual parent in the loop tree. 36832666c4SRyan Hunt pub fn new(header: Block, parent: Option<Loop>) -> Self { 37747ad3c4Slazypassion Self { 38747ad3c4Slazypassion header, 39747ad3c4Slazypassion parent: parent.into(), 40747ad3c4Slazypassion } 41747ad3c4Slazypassion } 42747ad3c4Slazypassion } 43747ad3c4Slazypassion 44747ad3c4Slazypassion /// Methods for querying the loop analysis. 45747ad3c4Slazypassion impl LoopAnalysis { 46747ad3c4Slazypassion /// Allocate a new blank loop analysis struct. Use `compute` to compute the loop analysis for 47747ad3c4Slazypassion /// a function. 48747ad3c4Slazypassion pub fn new() -> Self { 49747ad3c4Slazypassion Self { 50747ad3c4Slazypassion valid: false, 51747ad3c4Slazypassion loops: PrimaryMap::new(), 52832666c4SRyan Hunt block_loop_map: SecondaryMap::new(), 53747ad3c4Slazypassion } 54747ad3c4Slazypassion } 55747ad3c4Slazypassion 56747ad3c4Slazypassion /// Returns all the loops contained in a function. 57747ad3c4Slazypassion pub fn loops(&self) -> Keys<Loop> { 58747ad3c4Slazypassion self.loops.keys() 59747ad3c4Slazypassion } 60747ad3c4Slazypassion 61832666c4SRyan Hunt /// Returns the header block of a particular loop. 62747ad3c4Slazypassion /// 63747ad3c4Slazypassion /// The characteristic property of a loop header block is that it dominates some of its 64747ad3c4Slazypassion /// predecessors. 65832666c4SRyan Hunt pub fn loop_header(&self, lp: Loop) -> Block { 66747ad3c4Slazypassion self.loops[lp].header 67747ad3c4Slazypassion } 68747ad3c4Slazypassion 69747ad3c4Slazypassion /// Return the eventual parent of a loop in the loop tree. 70747ad3c4Slazypassion pub fn loop_parent(&self, lp: Loop) -> Option<Loop> { 71747ad3c4Slazypassion self.loops[lp].parent.expand() 72747ad3c4Slazypassion } 73747ad3c4Slazypassion 74*07f335dcSRyan Hunt /// Determine if a Block belongs to a loop by running a finger along the loop tree. 75747ad3c4Slazypassion /// 76832666c4SRyan Hunt /// Returns `true` if `block` is in loop `lp`. 77832666c4SRyan Hunt pub fn is_in_loop(&self, block: Block, lp: Loop) -> bool { 78832666c4SRyan Hunt let block_loop = self.block_loop_map[block]; 79832666c4SRyan Hunt match block_loop.expand() { 80747ad3c4Slazypassion None => false, 81832666c4SRyan Hunt Some(block_loop) => self.is_child_loop(block_loop, lp), 82747ad3c4Slazypassion } 83747ad3c4Slazypassion } 84747ad3c4Slazypassion 85747ad3c4Slazypassion /// Determines if a loop is contained in another loop. 86747ad3c4Slazypassion /// 87747ad3c4Slazypassion /// `is_child_loop(child,parent)` returns `true` if and only if `child` is a child loop of 88747ad3c4Slazypassion /// `parent` (or `child == parent`). 89747ad3c4Slazypassion pub fn is_child_loop(&self, child: Loop, parent: Loop) -> bool { 90747ad3c4Slazypassion let mut finger = Some(child); 91747ad3c4Slazypassion while let Some(finger_loop) = finger { 92747ad3c4Slazypassion if finger_loop == parent { 93747ad3c4Slazypassion return true; 94747ad3c4Slazypassion } 95747ad3c4Slazypassion finger = self.loop_parent(finger_loop); 96747ad3c4Slazypassion } 97747ad3c4Slazypassion false 98747ad3c4Slazypassion } 99747ad3c4Slazypassion } 100747ad3c4Slazypassion 101747ad3c4Slazypassion impl LoopAnalysis { 102747ad3c4Slazypassion /// Detects the loops in a function. Needs the control flow graph and the dominator tree. 103747ad3c4Slazypassion pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph, domtree: &DominatorTree) { 104747ad3c4Slazypassion let _tt = timing::loop_analysis(); 105747ad3c4Slazypassion self.loops.clear(); 106832666c4SRyan Hunt self.block_loop_map.clear(); 107832666c4SRyan Hunt self.block_loop_map.resize(func.dfg.num_blocks()); 108747ad3c4Slazypassion self.find_loop_headers(cfg, domtree, &func.layout); 109747ad3c4Slazypassion self.discover_loop_blocks(cfg, domtree, &func.layout); 110747ad3c4Slazypassion self.valid = true; 111747ad3c4Slazypassion } 112747ad3c4Slazypassion 113747ad3c4Slazypassion /// Check if the loop analysis is in a valid state. 114747ad3c4Slazypassion /// 115747ad3c4Slazypassion /// Note that this doesn't perform any kind of validity checks. It simply checks if the 116747ad3c4Slazypassion /// `compute()` method has been called since the last `clear()`. It does not check that the 117747ad3c4Slazypassion /// loop analysis is consistent with the CFG. 118747ad3c4Slazypassion pub fn is_valid(&self) -> bool { 119747ad3c4Slazypassion self.valid 120747ad3c4Slazypassion } 121747ad3c4Slazypassion 122747ad3c4Slazypassion /// Clear all the data structures contained in the loop analysis. This will leave the 123747ad3c4Slazypassion /// analysis in a similar state to a context returned by `new()` except that allocated 124747ad3c4Slazypassion /// memory be retained. 125747ad3c4Slazypassion pub fn clear(&mut self) { 126747ad3c4Slazypassion self.loops.clear(); 127832666c4SRyan Hunt self.block_loop_map.clear(); 128747ad3c4Slazypassion self.valid = false; 129747ad3c4Slazypassion } 130747ad3c4Slazypassion 131832666c4SRyan Hunt // Traverses the CFG in reverse postorder and create a loop object for every block having a 132747ad3c4Slazypassion // back edge. 133747ad3c4Slazypassion fn find_loop_headers( 134747ad3c4Slazypassion &mut self, 135747ad3c4Slazypassion cfg: &ControlFlowGraph, 136747ad3c4Slazypassion domtree: &DominatorTree, 137747ad3c4Slazypassion layout: &Layout, 138747ad3c4Slazypassion ) { 139747ad3c4Slazypassion // We traverse the CFG in reverse postorder 140832666c4SRyan Hunt for &block in domtree.cfg_postorder().iter().rev() { 141832666c4SRyan Hunt for BlockPredecessor { 142747ad3c4Slazypassion inst: pred_inst, .. 143832666c4SRyan Hunt } in cfg.pred_iter(block) 144747ad3c4Slazypassion { 145832666c4SRyan Hunt // If the block dominates one of its predecessors it is a back edge 146832666c4SRyan Hunt if domtree.dominates(block, pred_inst, layout) { 147832666c4SRyan Hunt // This block is a loop header, so we create its associated loop 148832666c4SRyan Hunt let lp = self.loops.push(LoopData::new(block, None)); 149832666c4SRyan Hunt self.block_loop_map[block] = lp.into(); 150747ad3c4Slazypassion break; 151747ad3c4Slazypassion // We break because we only need one back edge to identify a loop header. 152747ad3c4Slazypassion } 153747ad3c4Slazypassion } 154747ad3c4Slazypassion } 155747ad3c4Slazypassion } 156747ad3c4Slazypassion 157747ad3c4Slazypassion // Intended to be called after `find_loop_headers`. For each detected loop header, 158832666c4SRyan Hunt // discovers all the block belonging to the loop and its inner loops. After a call to this 159747ad3c4Slazypassion // function, the loop tree is fully constructed. 160747ad3c4Slazypassion fn discover_loop_blocks( 161747ad3c4Slazypassion &mut self, 162747ad3c4Slazypassion cfg: &ControlFlowGraph, 163747ad3c4Slazypassion domtree: &DominatorTree, 164747ad3c4Slazypassion layout: &Layout, 165747ad3c4Slazypassion ) { 166832666c4SRyan Hunt let mut stack: Vec<Block> = Vec::new(); 167747ad3c4Slazypassion // We handle each loop header in reverse order, corresponding to a pseudo postorder 168747ad3c4Slazypassion // traversal of the graph. 169747ad3c4Slazypassion for lp in self.loops().rev() { 170832666c4SRyan Hunt for BlockPredecessor { 171832666c4SRyan Hunt block: pred, 172747ad3c4Slazypassion inst: pred_inst, 173747ad3c4Slazypassion } in cfg.pred_iter(self.loops[lp].header) 174747ad3c4Slazypassion { 175747ad3c4Slazypassion // We follow the back edges 176747ad3c4Slazypassion if domtree.dominates(self.loops[lp].header, pred_inst, layout) { 177747ad3c4Slazypassion stack.push(pred); 178747ad3c4Slazypassion } 179747ad3c4Slazypassion } 180747ad3c4Slazypassion while let Some(node) = stack.pop() { 181832666c4SRyan Hunt let continue_dfs: Option<Block>; 182832666c4SRyan Hunt match self.block_loop_map[node].expand() { 183747ad3c4Slazypassion None => { 184747ad3c4Slazypassion // The node hasn't been visited yet, we tag it as part of the loop 185832666c4SRyan Hunt self.block_loop_map[node] = PackedOption::from(lp); 186747ad3c4Slazypassion continue_dfs = Some(node); 187747ad3c4Slazypassion } 188747ad3c4Slazypassion Some(node_loop) => { 189747ad3c4Slazypassion // We copy the node_loop into a mutable reference passed along the while 190747ad3c4Slazypassion let mut node_loop = node_loop; 191747ad3c4Slazypassion // The node is part of a loop, which can be lp or an inner loop 192747ad3c4Slazypassion let mut node_loop_parent_option = self.loops[node_loop].parent; 193747ad3c4Slazypassion while let Some(node_loop_parent) = node_loop_parent_option.expand() { 194747ad3c4Slazypassion if node_loop_parent == lp { 195747ad3c4Slazypassion // We have encountered lp so we stop (already visited) 196747ad3c4Slazypassion break; 197747ad3c4Slazypassion } else { 198747ad3c4Slazypassion // 199747ad3c4Slazypassion node_loop = node_loop_parent; 200747ad3c4Slazypassion // We lookup the parent loop 201747ad3c4Slazypassion node_loop_parent_option = self.loops[node_loop].parent; 202747ad3c4Slazypassion } 203747ad3c4Slazypassion } 204747ad3c4Slazypassion // Now node_loop_parent is either: 205747ad3c4Slazypassion // - None and node_loop is an new inner loop of lp 206747ad3c4Slazypassion // - Some(...) and the initial node_loop was a known inner loop of lp 207747ad3c4Slazypassion match node_loop_parent_option.expand() { 208747ad3c4Slazypassion Some(_) => continue_dfs = None, 209747ad3c4Slazypassion None => { 210747ad3c4Slazypassion if node_loop != lp { 211747ad3c4Slazypassion self.loops[node_loop].parent = lp.into(); 212747ad3c4Slazypassion continue_dfs = Some(self.loops[node_loop].header) 213747ad3c4Slazypassion } else { 214747ad3c4Slazypassion // If lp is a one-block loop then we make sure we stop 215747ad3c4Slazypassion continue_dfs = None 216747ad3c4Slazypassion } 217747ad3c4Slazypassion } 218747ad3c4Slazypassion } 219747ad3c4Slazypassion } 220747ad3c4Slazypassion } 221747ad3c4Slazypassion // Now we have handled the popped node and need to continue the DFS by adding the 222747ad3c4Slazypassion // predecessors of that node 223747ad3c4Slazypassion if let Some(continue_dfs) = continue_dfs { 224832666c4SRyan Hunt for BlockPredecessor { block: pred, .. } in cfg.pred_iter(continue_dfs) { 225747ad3c4Slazypassion stack.push(pred) 226747ad3c4Slazypassion } 227747ad3c4Slazypassion } 228747ad3c4Slazypassion } 229747ad3c4Slazypassion } 230747ad3c4Slazypassion } 231747ad3c4Slazypassion } 232747ad3c4Slazypassion 233747ad3c4Slazypassion #[cfg(test)] 234747ad3c4Slazypassion mod tests { 235747ad3c4Slazypassion use crate::cursor::{Cursor, FuncCursor}; 236747ad3c4Slazypassion use crate::dominator_tree::DominatorTree; 237747ad3c4Slazypassion use crate::flowgraph::ControlFlowGraph; 238747ad3c4Slazypassion use crate::ir::{types, Function, InstBuilder}; 239747ad3c4Slazypassion use crate::loop_analysis::{Loop, LoopAnalysis}; 24010e226f9Sbjorn3 use alloc::vec::Vec; 241747ad3c4Slazypassion 242747ad3c4Slazypassion #[test] 243747ad3c4Slazypassion fn nested_loops_detection() { 244747ad3c4Slazypassion let mut func = Function::new(); 245832666c4SRyan Hunt let block0 = func.dfg.make_block(); 246832666c4SRyan Hunt let block1 = func.dfg.make_block(); 247832666c4SRyan Hunt let block2 = func.dfg.make_block(); 248832666c4SRyan Hunt let block3 = func.dfg.make_block(); 249832666c4SRyan Hunt let cond = func.dfg.append_block_param(block0, types::I32); 250747ad3c4Slazypassion 251747ad3c4Slazypassion { 252747ad3c4Slazypassion let mut cur = FuncCursor::new(&mut func); 253747ad3c4Slazypassion 254832666c4SRyan Hunt cur.insert_block(block0); 255832666c4SRyan Hunt cur.ins().jump(block1, &[]); 256747ad3c4Slazypassion 257832666c4SRyan Hunt cur.insert_block(block1); 258832666c4SRyan Hunt cur.ins().jump(block2, &[]); 259747ad3c4Slazypassion 260832666c4SRyan Hunt cur.insert_block(block2); 261832666c4SRyan Hunt cur.ins().brnz(cond, block1, &[]); 262832666c4SRyan Hunt cur.ins().jump(block3, &[]); 263747ad3c4Slazypassion 264832666c4SRyan Hunt cur.insert_block(block3); 265832666c4SRyan Hunt cur.ins().brnz(cond, block0, &[]); 266747ad3c4Slazypassion } 267747ad3c4Slazypassion 268747ad3c4Slazypassion let mut loop_analysis = LoopAnalysis::new(); 269747ad3c4Slazypassion let mut cfg = ControlFlowGraph::new(); 270747ad3c4Slazypassion let mut domtree = DominatorTree::new(); 271747ad3c4Slazypassion cfg.compute(&func); 272747ad3c4Slazypassion domtree.compute(&func, &cfg); 273747ad3c4Slazypassion loop_analysis.compute(&func, &cfg, &domtree); 274747ad3c4Slazypassion 275747ad3c4Slazypassion let loops = loop_analysis.loops().collect::<Vec<Loop>>(); 276747ad3c4Slazypassion assert_eq!(loops.len(), 2); 277832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[0]), block0); 278832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[1]), block1); 279747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0])); 280747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[0]), None); 281832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[0]), true); 282832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false); 283832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block1, loops[1]), true); 284832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block1, loops[0]), true); 285832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block2, loops[1]), true); 286832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block2, loops[0]), true); 287832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block3, loops[0]), true); 288832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false); 289747ad3c4Slazypassion } 290747ad3c4Slazypassion 291747ad3c4Slazypassion #[test] 292747ad3c4Slazypassion fn complex_loop_detection() { 293747ad3c4Slazypassion let mut func = Function::new(); 294832666c4SRyan Hunt let block0 = func.dfg.make_block(); 295832666c4SRyan Hunt let block1 = func.dfg.make_block(); 296832666c4SRyan Hunt let block2 = func.dfg.make_block(); 297832666c4SRyan Hunt let block3 = func.dfg.make_block(); 298832666c4SRyan Hunt let block4 = func.dfg.make_block(); 299832666c4SRyan Hunt let block5 = func.dfg.make_block(); 300832666c4SRyan Hunt let cond = func.dfg.append_block_param(block0, types::I32); 301747ad3c4Slazypassion 302747ad3c4Slazypassion { 303747ad3c4Slazypassion let mut cur = FuncCursor::new(&mut func); 304747ad3c4Slazypassion 305832666c4SRyan Hunt cur.insert_block(block0); 306832666c4SRyan Hunt cur.ins().brnz(cond, block1, &[]); 307832666c4SRyan Hunt cur.ins().jump(block3, &[]); 308747ad3c4Slazypassion 309832666c4SRyan Hunt cur.insert_block(block1); 310832666c4SRyan Hunt cur.ins().jump(block2, &[]); 311747ad3c4Slazypassion 312832666c4SRyan Hunt cur.insert_block(block2); 313832666c4SRyan Hunt cur.ins().brnz(cond, block1, &[]); 314832666c4SRyan Hunt cur.ins().jump(block5, &[]); 315747ad3c4Slazypassion 316832666c4SRyan Hunt cur.insert_block(block3); 317832666c4SRyan Hunt cur.ins().jump(block4, &[]); 318747ad3c4Slazypassion 319832666c4SRyan Hunt cur.insert_block(block4); 320832666c4SRyan Hunt cur.ins().brnz(cond, block3, &[]); 321832666c4SRyan Hunt cur.ins().jump(block5, &[]); 322747ad3c4Slazypassion 323832666c4SRyan Hunt cur.insert_block(block5); 324832666c4SRyan Hunt cur.ins().brnz(cond, block0, &[]); 325747ad3c4Slazypassion } 326747ad3c4Slazypassion 327747ad3c4Slazypassion let mut loop_analysis = LoopAnalysis::new(); 328747ad3c4Slazypassion let mut cfg = ControlFlowGraph::new(); 329747ad3c4Slazypassion let mut domtree = DominatorTree::new(); 330747ad3c4Slazypassion cfg.compute(&func); 331747ad3c4Slazypassion domtree.compute(&func, &cfg); 332747ad3c4Slazypassion loop_analysis.compute(&func, &cfg, &domtree); 333747ad3c4Slazypassion 334747ad3c4Slazypassion let loops = loop_analysis.loops().collect::<Vec<Loop>>(); 335747ad3c4Slazypassion assert_eq!(loops.len(), 3); 336832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[0]), block0); 337832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[1]), block1); 338832666c4SRyan Hunt assert_eq!(loop_analysis.loop_header(loops[2]), block3); 339747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0])); 340747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[2]), Some(loops[0])); 341747ad3c4Slazypassion assert_eq!(loop_analysis.loop_parent(loops[0]), None); 342832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block0, loops[0]), true); 343832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block1, loops[1]), true); 344832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block2, loops[1]), true); 345832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block3, loops[2]), true); 346832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block4, loops[2]), true); 347832666c4SRyan Hunt assert_eq!(loop_analysis.is_in_loop(block5, loops[0]), true); 348747ad3c4Slazypassion } 349747ad3c4Slazypassion } 350