1use crate::dominator_tree::DominatorTree;
5use crate::entity::entity_impl;
6use crate::entity::SecondaryMap;
7use crate::entity::{Keys, PrimaryMap};
8use crate::flowgraph::ControlFlowGraph;
9use crate::ir::{Block, Function, Layout};
10use crate::packed_option::PackedOption;
11use crate::timing;
12use alloc::vec::Vec;
13use smallvec::SmallVec;
14
15#[derive(Copy, Clone, PartialEq, Eq, Hash)]
17pub struct Loop(u32);
18entity_impl!(Loop, "loop");
19
20pub struct LoopAnalysis {
25 loops: PrimaryMap<Loop, LoopData>,
26 block_loop_map: SecondaryMap<Block, PackedOption<Loop>>,
27 valid: bool,
28}
29
30struct LoopData {
31 header: Block,
32 parent: PackedOption<Loop>,
33 level: LoopLevel,
34}
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
38pub struct LoopLevel(u8);
39impl LoopLevel {
40 const INVALID: u8 = u8::MAX;
41
42 pub fn root() -> Self {
44 Self(0)
45 }
46 pub fn level(self) -> usize {
48 self.0 as usize
49 }
50 pub fn invalid() -> Self {
52 Self(Self::INVALID)
53 }
54 pub fn inc(self) -> Self {
56 if self.0 == (Self::INVALID - 1) {
57 self
58 } else {
59 Self(self.0 + 1)
60 }
61 }
62 pub fn clamped(level: usize) -> Self {
64 Self(
65 u8::try_from(std::cmp::min(level, (Self::INVALID as usize) - 1))
66 .expect("Clamped value must always convert"),
67 )
68 }
69}
70
71impl std::default::Default for LoopLevel {
72 fn default() -> Self {
73 LoopLevel::invalid()
74 }
75}
76
77impl LoopData {
78 pub fn new(header: Block, parent: Option<Loop>) -> Self {
80 Self {
81 header,
82 parent: parent.into(),
83 level: LoopLevel::invalid(),
84 }
85 }
86}
87
88impl LoopAnalysis {
90 pub fn new() -> Self {
93 Self {
94 valid: false,
95 loops: PrimaryMap::new(),
96 block_loop_map: SecondaryMap::new(),
97 }
98 }
99
100 pub fn loops(&self) -> Keys<Loop> {
102 self.loops.keys()
103 }
104
105 pub fn loop_header(&self, lp: Loop) -> Block {
110 self.loops[lp].header
111 }
112
113 pub fn loop_parent(&self, lp: Loop) -> Option<Loop> {
115 self.loops[lp].parent.expand()
116 }
117
118 pub fn innermost_loop(&self, block: Block) -> Option<Loop> {
120 self.block_loop_map[block].expand()
121 }
122
123 pub fn is_loop_header(&self, block: Block) -> Option<Loop> {
125 self.innermost_loop(block)
126 .filter(|&lp| self.loop_header(lp) == block)
127 }
128
129 pub fn is_in_loop(&self, block: Block, lp: Loop) -> bool {
133 let block_loop = self.block_loop_map[block];
134 match block_loop.expand() {
135 None => false,
136 Some(block_loop) => self.is_child_loop(block_loop, lp),
137 }
138 }
139
140 pub fn is_child_loop(&self, child: Loop, parent: Loop) -> bool {
145 let mut finger = Some(child);
146 while let Some(finger_loop) = finger {
147 if finger_loop == parent {
148 return true;
149 }
150 finger = self.loop_parent(finger_loop);
151 }
152 false
153 }
154
155 pub fn loop_level(&self, block: Block) -> LoopLevel {
157 self.innermost_loop(block)
158 .map_or(LoopLevel(0), |lp| self.loops[lp].level)
159 }
160}
161
162impl LoopAnalysis {
163 pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph, domtree: &DominatorTree) {
165 let _tt = timing::loop_analysis();
166 self.loops.clear();
167 self.block_loop_map.clear();
168 self.block_loop_map.resize(func.dfg.num_blocks());
169 self.find_loop_headers(cfg, domtree, &func.layout);
170 self.discover_loop_blocks(cfg, domtree, &func.layout);
171 self.assign_loop_levels();
172 self.valid = true;
173 }
174
175 pub fn is_valid(&self) -> bool {
181 self.valid
182 }
183
184 pub fn clear(&mut self) {
188 self.loops.clear();
189 self.block_loop_map.clear();
190 self.valid = false;
191 }
192
193 fn is_block_loop_header(
196 block: Block,
197 cfg: &ControlFlowGraph,
198 domtree: &DominatorTree,
199 layout: &Layout,
200 ) -> bool {
201 cfg.pred_iter(block)
203 .any(|pred| domtree.dominates(block, pred.inst, layout))
204 }
205
206 fn find_loop_headers(
209 &mut self,
210 cfg: &ControlFlowGraph,
211 domtree: &DominatorTree,
212 layout: &Layout,
213 ) {
214 for &block in domtree
215 .cfg_rpo()
216 .filter(|&&block| Self::is_block_loop_header(block, cfg, domtree, layout))
217 {
218 let lp = self.loops.push(LoopData::new(block, None));
220 self.block_loop_map[block] = lp.into();
221 }
222 }
223
224 fn discover_loop_blocks(
228 &mut self,
229 cfg: &ControlFlowGraph,
230 domtree: &DominatorTree,
231 layout: &Layout,
232 ) {
233 let mut stack: Vec<Block> = Vec::new();
234 for lp in self.loops().rev() {
237 stack.extend(
239 cfg.pred_iter(self.loops[lp].header)
240 .filter(|pred| {
241 domtree.dominates(self.loops[lp].header, pred.inst, layout)
243 })
244 .map(|pred| pred.block),
245 );
246 while let Some(node) = stack.pop() {
247 let continue_dfs: Option<Block>;
248 match self.block_loop_map[node].expand() {
249 None => {
250 self.block_loop_map[node] = PackedOption::from(lp);
252 continue_dfs = Some(node);
253 }
254 Some(node_loop) => {
255 let mut node_loop = node_loop;
257 let mut node_loop_parent_option = self.loops[node_loop].parent;
259 while let Some(node_loop_parent) = node_loop_parent_option.expand() {
260 if node_loop_parent == lp {
261 break;
263 } else {
264 node_loop = node_loop_parent;
266 node_loop_parent_option = self.loops[node_loop].parent;
268 }
269 }
270 match node_loop_parent_option.expand() {
274 Some(_) => continue_dfs = None,
275 None => {
276 if node_loop != lp {
277 self.loops[node_loop].parent = lp.into();
278 continue_dfs = Some(self.loops[node_loop].header)
279 } else {
280 continue_dfs = None
282 }
283 }
284 }
285 }
286 }
287 if let Some(continue_dfs) = continue_dfs {
290 stack.extend(cfg.pred_iter(continue_dfs).map(|pred| pred.block));
291 }
292 }
293 }
294 }
295
296 fn assign_loop_levels(&mut self) {
297 let mut stack: SmallVec<[Loop; 8]> = SmallVec::new();
298 for lp in self.loops.keys() {
299 if self.loops[lp].level == LoopLevel::invalid() {
300 stack.push(lp);
301 while let Some(&lp) = stack.last() {
302 if let Some(parent) = self.loops[lp].parent.into() {
303 if self.loops[parent].level != LoopLevel::invalid() {
304 self.loops[lp].level = self.loops[parent].level.inc();
305 stack.pop();
306 } else {
307 stack.push(parent);
308 }
309 } else {
310 self.loops[lp].level = LoopLevel::root().inc();
311 stack.pop();
312 }
313 }
314 }
315 }
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use crate::cursor::{Cursor, FuncCursor};
322 use crate::dominator_tree::DominatorTree;
323 use crate::flowgraph::ControlFlowGraph;
324 use crate::ir::{types, Function, InstBuilder};
325 use crate::loop_analysis::{Loop, LoopAnalysis};
326 use alloc::vec::Vec;
327
328 #[test]
329 fn nested_loops_detection() {
330 let mut func = Function::new();
331 let block0 = func.dfg.make_block();
332 let block1 = func.dfg.make_block();
333 let block2 = func.dfg.make_block();
334 let block3 = func.dfg.make_block();
335 let block4 = func.dfg.make_block();
336 let cond = func.dfg.append_block_param(block0, types::I32);
337
338 {
339 let mut cur = FuncCursor::new(&mut func);
340
341 cur.insert_block(block0);
342 cur.ins().jump(block1, &[]);
343
344 cur.insert_block(block1);
345 cur.ins().jump(block2, &[]);
346
347 cur.insert_block(block2);
348 cur.ins().brif(cond, block1, &[], block3, &[]);
349
350 cur.insert_block(block3);
351 cur.ins().brif(cond, block0, &[], block4, &[]);
352
353 cur.insert_block(block4);
354 cur.ins().return_(&[]);
355 }
356
357 let mut loop_analysis = LoopAnalysis::new();
358 let mut cfg = ControlFlowGraph::new();
359 let mut domtree = DominatorTree::new();
360 cfg.compute(&func);
361 domtree.compute(&func, &cfg);
362 loop_analysis.compute(&func, &cfg, &domtree);
363
364 let loops = loop_analysis.loops().collect::<Vec<Loop>>();
365 assert_eq!(loops.len(), 2);
366 assert_eq!(loop_analysis.loop_header(loops[0]), block0);
367 assert_eq!(loop_analysis.loop_header(loops[1]), block1);
368 assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0]));
369 assert_eq!(loop_analysis.loop_parent(loops[0]), None);
370 assert_eq!(loop_analysis.is_in_loop(block0, loops[0]), true);
371 assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false);
372 assert_eq!(loop_analysis.is_in_loop(block1, loops[1]), true);
373 assert_eq!(loop_analysis.is_in_loop(block1, loops[0]), true);
374 assert_eq!(loop_analysis.is_in_loop(block2, loops[1]), true);
375 assert_eq!(loop_analysis.is_in_loop(block2, loops[0]), true);
376 assert_eq!(loop_analysis.is_in_loop(block3, loops[0]), true);
377 assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false);
378 assert_eq!(loop_analysis.loop_level(block0).level(), 1);
379 assert_eq!(loop_analysis.loop_level(block1).level(), 2);
380 assert_eq!(loop_analysis.loop_level(block2).level(), 2);
381 assert_eq!(loop_analysis.loop_level(block3).level(), 1);
382 }
383
384 #[test]
385 fn complex_loop_detection() {
386 let mut func = Function::new();
387 let block0 = func.dfg.make_block();
388 let block1 = func.dfg.make_block();
389 let block2 = func.dfg.make_block();
390 let block3 = func.dfg.make_block();
391 let block4 = func.dfg.make_block();
392 let block5 = func.dfg.make_block();
393 let block6 = func.dfg.make_block();
394 let cond = func.dfg.append_block_param(block0, types::I32);
395
396 {
397 let mut cur = FuncCursor::new(&mut func);
398
399 cur.insert_block(block0);
400 cur.ins().brif(cond, block1, &[], block3, &[]);
401
402 cur.insert_block(block1);
403 cur.ins().jump(block2, &[]);
404
405 cur.insert_block(block2);
406 cur.ins().brif(cond, block1, &[], block5, &[]);
407
408 cur.insert_block(block3);
409 cur.ins().jump(block4, &[]);
410
411 cur.insert_block(block4);
412 cur.ins().brif(cond, block3, &[], block5, &[]);
413
414 cur.insert_block(block5);
415 cur.ins().brif(cond, block0, &[], block6, &[]);
416
417 cur.insert_block(block6);
418 cur.ins().return_(&[]);
419 }
420
421 let mut loop_analysis = LoopAnalysis::new();
422 let cfg = ControlFlowGraph::with_function(&func);
423 let domtree = DominatorTree::with_function(&func, &cfg);
424 loop_analysis.compute(&func, &cfg, &domtree);
425
426 let loops = loop_analysis.loops().collect::<Vec<Loop>>();
427 assert_eq!(loops.len(), 3);
428 assert_eq!(loop_analysis.loop_header(loops[0]), block0);
429 assert_eq!(loop_analysis.loop_header(loops[1]), block3);
430 assert_eq!(loop_analysis.loop_header(loops[2]), block1);
431 assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0]));
432 assert_eq!(loop_analysis.loop_parent(loops[2]), Some(loops[0]));
433 assert_eq!(loop_analysis.loop_parent(loops[0]), None);
434 assert_eq!(loop_analysis.is_in_loop(block0, loops[0]), true);
435 assert_eq!(loop_analysis.is_in_loop(block1, loops[2]), true);
436 assert_eq!(loop_analysis.is_in_loop(block2, loops[2]), true);
437 assert_eq!(loop_analysis.is_in_loop(block3, loops[1]), true);
438 assert_eq!(loop_analysis.is_in_loop(block4, loops[1]), true);
439 assert_eq!(loop_analysis.is_in_loop(block5, loops[0]), true);
440 assert_eq!(loop_analysis.loop_level(block0).level(), 1);
441 assert_eq!(loop_analysis.loop_level(block1).level(), 2);
442 assert_eq!(loop_analysis.loop_level(block2).level(), 2);
443 assert_eq!(loop_analysis.loop_level(block3).level(), 2);
444 assert_eq!(loop_analysis.loop_level(block4).level(), 2);
445 assert_eq!(loop_analysis.loop_level(block5).level(), 1);
446 }
447}