1use crate::dominator_tree::DominatorTree;
5use crate::entity::entity_impl;
6use crate::entity::SecondaryMap;
7use crate::entity::{Keys, PrimaryMap};
8use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
9use crate::ir::{Block, Function, Layout};
10use crate::packed_option::PackedOption;
11use crate::timing;
12use alloc::vec::Vec;
13use smallvec::{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 find_loop_headers(
196 &mut self,
197 cfg: &ControlFlowGraph,
198 domtree: &DominatorTree,
199 layout: &Layout,
200 ) {
201 for &block in domtree.cfg_postorder().iter().rev() {
203 for BlockPredecessor {
204 inst: pred_inst, ..
205 } in cfg.pred_iter(block)
206 {
207 if domtree.dominates(block, pred_inst, layout) {
209 let lp = self.loops.push(LoopData::new(block, None));
211 self.block_loop_map[block] = lp.into();
212 break;
213 }
215 }
216 }
217 }
218
219 fn discover_loop_blocks(
223 &mut self,
224 cfg: &ControlFlowGraph,
225 domtree: &DominatorTree,
226 layout: &Layout,
227 ) {
228 let mut stack: Vec<Block> = Vec::new();
229 for lp in self.loops().rev() {
232 for BlockPredecessor {
233 block: pred,
234 inst: pred_inst,
235 } in cfg.pred_iter(self.loops[lp].header)
236 {
237 if domtree.dominates(self.loops[lp].header, pred_inst, layout) {
239 stack.push(pred);
240 }
241 }
242 while let Some(node) = stack.pop() {
243 let continue_dfs: Option<Block>;
244 match self.block_loop_map[node].expand() {
245 None => {
246 self.block_loop_map[node] = PackedOption::from(lp);
248 continue_dfs = Some(node);
249 }
250 Some(node_loop) => {
251 let mut node_loop = node_loop;
253 let mut node_loop_parent_option = self.loops[node_loop].parent;
255 while let Some(node_loop_parent) = node_loop_parent_option.expand() {
256 if node_loop_parent == lp {
257 break;
259 } else {
260 node_loop = node_loop_parent;
262 node_loop_parent_option = self.loops[node_loop].parent;
264 }
265 }
266 match node_loop_parent_option.expand() {
270 Some(_) => continue_dfs = None,
271 None => {
272 if node_loop != lp {
273 self.loops[node_loop].parent = lp.into();
274 continue_dfs = Some(self.loops[node_loop].header)
275 } else {
276 continue_dfs = None
278 }
279 }
280 }
281 }
282 }
283 if let Some(continue_dfs) = continue_dfs {
286 for BlockPredecessor { block: pred, .. } in cfg.pred_iter(continue_dfs) {
287 stack.push(pred)
288 }
289 }
290 }
291 }
292 }
293
294 fn assign_loop_levels(&mut self) {
295 let mut stack: SmallVec<[Loop; 8]> = smallvec![];
296 for lp in self.loops.keys() {
297 if self.loops[lp].level == LoopLevel::invalid() {
298 stack.push(lp);
299 while let Some(&lp) = stack.last() {
300 if let Some(parent) = self.loops[lp].parent.into() {
301 if self.loops[parent].level != LoopLevel::invalid() {
302 self.loops[lp].level = self.loops[parent].level.inc();
303 stack.pop();
304 } else {
305 stack.push(parent);
306 }
307 } else {
308 self.loops[lp].level = LoopLevel::root().inc();
309 stack.pop();
310 }
311 }
312 }
313 }
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use crate::cursor::{Cursor, FuncCursor};
320 use crate::dominator_tree::DominatorTree;
321 use crate::flowgraph::ControlFlowGraph;
322 use crate::ir::{types, Function, InstBuilder};
323 use crate::loop_analysis::{Loop, LoopAnalysis};
324 use alloc::vec::Vec;
325
326 #[test]
327 fn nested_loops_detection() {
328 let mut func = Function::new();
329 let block0 = func.dfg.make_block();
330 let block1 = func.dfg.make_block();
331 let block2 = func.dfg.make_block();
332 let block3 = func.dfg.make_block();
333 let block4 = func.dfg.make_block();
334 let cond = func.dfg.append_block_param(block0, types::I32);
335
336 {
337 let mut cur = FuncCursor::new(&mut func);
338
339 cur.insert_block(block0);
340 cur.ins().jump(block1, &[]);
341
342 cur.insert_block(block1);
343 cur.ins().jump(block2, &[]);
344
345 cur.insert_block(block2);
346 cur.ins().brif(cond, block1, &[], block3, &[]);
347
348 cur.insert_block(block3);
349 cur.ins().brif(cond, block0, &[], block4, &[]);
350
351 cur.insert_block(block4);
352 cur.ins().return_(&[]);
353 }
354
355 let mut loop_analysis = LoopAnalysis::new();
356 let mut cfg = ControlFlowGraph::new();
357 let mut domtree = DominatorTree::new();
358 cfg.compute(&func);
359 domtree.compute(&func, &cfg);
360 loop_analysis.compute(&func, &cfg, &domtree);
361
362 let loops = loop_analysis.loops().collect::<Vec<Loop>>();
363 assert_eq!(loops.len(), 2);
364 assert_eq!(loop_analysis.loop_header(loops[0]), block0);
365 assert_eq!(loop_analysis.loop_header(loops[1]), block1);
366 assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0]));
367 assert_eq!(loop_analysis.loop_parent(loops[0]), None);
368 assert_eq!(loop_analysis.is_in_loop(block0, loops[0]), true);
369 assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false);
370 assert_eq!(loop_analysis.is_in_loop(block1, loops[1]), true);
371 assert_eq!(loop_analysis.is_in_loop(block1, loops[0]), true);
372 assert_eq!(loop_analysis.is_in_loop(block2, loops[1]), true);
373 assert_eq!(loop_analysis.is_in_loop(block2, loops[0]), true);
374 assert_eq!(loop_analysis.is_in_loop(block3, loops[0]), true);
375 assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false);
376 assert_eq!(loop_analysis.loop_level(block0).level(), 1);
377 assert_eq!(loop_analysis.loop_level(block1).level(), 2);
378 assert_eq!(loop_analysis.loop_level(block2).level(), 2);
379 assert_eq!(loop_analysis.loop_level(block3).level(), 1);
380 }
381
382 #[test]
383 fn complex_loop_detection() {
384 let mut func = Function::new();
385 let block0 = func.dfg.make_block();
386 let block1 = func.dfg.make_block();
387 let block2 = func.dfg.make_block();
388 let block3 = func.dfg.make_block();
389 let block4 = func.dfg.make_block();
390 let block5 = func.dfg.make_block();
391 let block6 = func.dfg.make_block();
392 let cond = func.dfg.append_block_param(block0, types::I32);
393
394 {
395 let mut cur = FuncCursor::new(&mut func);
396
397 cur.insert_block(block0);
398 cur.ins().brif(cond, block1, &[], block3, &[]);
399
400 cur.insert_block(block1);
401 cur.ins().jump(block2, &[]);
402
403 cur.insert_block(block2);
404 cur.ins().brif(cond, block1, &[], block5, &[]);
405
406 cur.insert_block(block3);
407 cur.ins().jump(block4, &[]);
408
409 cur.insert_block(block4);
410 cur.ins().brif(cond, block3, &[], block5, &[]);
411
412 cur.insert_block(block5);
413 cur.ins().brif(cond, block0, &[], block6, &[]);
414
415 cur.insert_block(block6);
416 cur.ins().return_(&[]);
417 }
418
419 let mut loop_analysis = LoopAnalysis::new();
420 let cfg = ControlFlowGraph::with_function(&func);
421 let domtree = DominatorTree::with_function(&func, &cfg);
422 loop_analysis.compute(&func, &cfg, &domtree);
423
424 let loops = loop_analysis.loops().collect::<Vec<Loop>>();
425 assert_eq!(loops.len(), 3);
426 assert_eq!(loop_analysis.loop_header(loops[0]), block0);
427 assert_eq!(loop_analysis.loop_header(loops[1]), block3);
428 assert_eq!(loop_analysis.loop_header(loops[2]), block1);
429 assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0]));
430 assert_eq!(loop_analysis.loop_parent(loops[2]), Some(loops[0]));
431 assert_eq!(loop_analysis.loop_parent(loops[0]), None);
432 assert_eq!(loop_analysis.is_in_loop(block0, loops[0]), true);
433 assert_eq!(loop_analysis.is_in_loop(block1, loops[2]), true);
434 assert_eq!(loop_analysis.is_in_loop(block2, loops[2]), true);
435 assert_eq!(loop_analysis.is_in_loop(block3, loops[1]), true);
436 assert_eq!(loop_analysis.is_in_loop(block4, loops[1]), true);
437 assert_eq!(loop_analysis.is_in_loop(block5, loops[0]), true);
438 assert_eq!(loop_analysis.loop_level(block0).level(), 1);
439 assert_eq!(loop_analysis.loop_level(block1).level(), 2);
440 assert_eq!(loop_analysis.loop_level(block2).level(), 2);
441 assert_eq!(loop_analysis.loop_level(block3).level(), 2);
442 assert_eq!(loop_analysis.loop_level(block4).level(), 2);
443 assert_eq!(loop_analysis.loop_level(block5).level(), 1);
444 }
445}