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