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