1use crate::dominator_tree::DominatorTree;
5use crate::entity::entity_impl;
6use crate::entity::SecondaryMap;
7use crate::entity::{Keys, PrimaryMap};
8use crate::flowgraph::{BasicBlock, ControlFlowGraph};
9use crate::ir::{Ebb, Function, Layout};
10use crate::packed_option::PackedOption;
11use crate::timing;
12use alloc::vec::Vec;
13
14#[derive(Copy, Clone, PartialEq, Eq, Hash)]
16pub struct Loop(u32);
17entity_impl!(Loop, "loop");
18
19pub struct LoopAnalysis {
24 loops: PrimaryMap<Loop, LoopData>,
25 ebb_loop_map: SecondaryMap<Ebb, PackedOption<Loop>>,
26 valid: bool,
27}
28
29struct LoopData {
30 header: Ebb,
31 parent: PackedOption<Loop>,
32}
33
34impl LoopData {
35 pub fn new(header: Ebb, parent: Option<Loop>) -> Self {
37 Self {
38 header,
39 parent: parent.into(),
40 }
41 }
42}
43
44impl LoopAnalysis {
46 pub fn new() -> Self {
49 Self {
50 valid: false,
51 loops: PrimaryMap::new(),
52 ebb_loop_map: SecondaryMap::new(),
53 }
54 }
55
56 pub fn loops(&self) -> Keys<Loop> {
58 self.loops.keys()
59 }
60
61 pub fn loop_header(&self, lp: Loop) -> Ebb {
66 self.loops[lp].header
67 }
68
69 pub fn loop_parent(&self, lp: Loop) -> Option<Loop> {
71 self.loops[lp].parent.expand()
72 }
73
74 pub fn is_in_loop(&self, ebb: Ebb, lp: Loop) -> bool {
78 let ebb_loop = self.ebb_loop_map[ebb];
79 match ebb_loop.expand() {
80 None => false,
81 Some(ebb_loop) => self.is_child_loop(ebb_loop, lp),
82 }
83 }
84
85 pub fn is_child_loop(&self, child: Loop, parent: Loop) -> bool {
90 let mut finger = Some(child);
91 while let Some(finger_loop) = finger {
92 if finger_loop == parent {
93 return true;
94 }
95 finger = self.loop_parent(finger_loop);
96 }
97 false
98 }
99}
100
101impl LoopAnalysis {
102 pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph, domtree: &DominatorTree) {
104 let _tt = timing::loop_analysis();
105 self.loops.clear();
106 self.ebb_loop_map.clear();
107 self.ebb_loop_map.resize(func.dfg.num_ebbs());
108 self.find_loop_headers(cfg, domtree, &func.layout);
109 self.discover_loop_blocks(cfg, domtree, &func.layout);
110 self.valid = true;
111 }
112
113 pub fn is_valid(&self) -> bool {
119 self.valid
120 }
121
122 pub fn clear(&mut self) {
126 self.loops.clear();
127 self.ebb_loop_map.clear();
128 self.valid = false;
129 }
130
131 fn find_loop_headers(
134 &mut self,
135 cfg: &ControlFlowGraph,
136 domtree: &DominatorTree,
137 layout: &Layout,
138 ) {
139 for &ebb in domtree.cfg_postorder().iter().rev() {
141 for BasicBlock {
142 inst: pred_inst, ..
143 } in cfg.pred_iter(ebb)
144 {
145 if domtree.dominates(ebb, pred_inst, layout) {
147 let lp = self.loops.push(LoopData::new(ebb, None));
149 self.ebb_loop_map[ebb] = lp.into();
150 break;
151 }
153 }
154 }
155 }
156
157 fn discover_loop_blocks(
161 &mut self,
162 cfg: &ControlFlowGraph,
163 domtree: &DominatorTree,
164 layout: &Layout,
165 ) {
166 let mut stack: Vec<Ebb> = Vec::new();
167 for lp in self.loops().rev() {
170 for BasicBlock {
171 ebb: pred,
172 inst: pred_inst,
173 } in cfg.pred_iter(self.loops[lp].header)
174 {
175 if domtree.dominates(self.loops[lp].header, pred_inst, layout) {
177 stack.push(pred);
178 }
179 }
180 while let Some(node) = stack.pop() {
181 let continue_dfs: Option<Ebb>;
182 match self.ebb_loop_map[node].expand() {
183 None => {
184 self.ebb_loop_map[node] = PackedOption::from(lp);
186 continue_dfs = Some(node);
187 }
188 Some(node_loop) => {
189 let mut node_loop = node_loop;
191 let mut node_loop_parent_option = self.loops[node_loop].parent;
193 while let Some(node_loop_parent) = node_loop_parent_option.expand() {
194 if node_loop_parent == lp {
195 break;
197 } else {
198 node_loop = node_loop_parent;
200 node_loop_parent_option = self.loops[node_loop].parent;
202 }
203 }
204 match node_loop_parent_option.expand() {
208 Some(_) => continue_dfs = None,
209 None => {
210 if node_loop != lp {
211 self.loops[node_loop].parent = lp.into();
212 continue_dfs = Some(self.loops[node_loop].header)
213 } else {
214 continue_dfs = None
216 }
217 }
218 }
219 }
220 }
221 if let Some(continue_dfs) = continue_dfs {
224 for BasicBlock { ebb: pred, .. } in cfg.pred_iter(continue_dfs) {
225 stack.push(pred)
226 }
227 }
228 }
229 }
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use crate::cursor::{Cursor, FuncCursor};
236 use crate::dominator_tree::DominatorTree;
237 use crate::flowgraph::ControlFlowGraph;
238 use crate::ir::{types, Function, InstBuilder};
239 use crate::loop_analysis::{Loop, LoopAnalysis};
240 use alloc::vec::Vec;
241
242 #[test]
243 fn nested_loops_detection() {
244 let mut func = Function::new();
245 let ebb0 = func.dfg.make_ebb();
246 let ebb1 = func.dfg.make_ebb();
247 let ebb2 = func.dfg.make_ebb();
248 let ebb3 = func.dfg.make_ebb();
249 let cond = func.dfg.append_ebb_param(ebb0, types::I32);
250
251 {
252 let mut cur = FuncCursor::new(&mut func);
253
254 cur.insert_ebb(ebb0);
255 cur.ins().jump(ebb1, &[]);
256
257 cur.insert_ebb(ebb1);
258 cur.ins().jump(ebb2, &[]);
259
260 cur.insert_ebb(ebb2);
261 cur.ins().brnz(cond, ebb1, &[]);
262 cur.ins().jump(ebb3, &[]);
263
264 cur.insert_ebb(ebb3);
265 cur.ins().brnz(cond, ebb0, &[]);
266 }
267
268 let mut loop_analysis = LoopAnalysis::new();
269 let mut cfg = ControlFlowGraph::new();
270 let mut domtree = DominatorTree::new();
271 cfg.compute(&func);
272 domtree.compute(&func, &cfg);
273 loop_analysis.compute(&func, &cfg, &domtree);
274
275 let loops = loop_analysis.loops().collect::<Vec<Loop>>();
276 assert_eq!(loops.len(), 2);
277 assert_eq!(loop_analysis.loop_header(loops[0]), ebb0);
278 assert_eq!(loop_analysis.loop_header(loops[1]), ebb1);
279 assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0]));
280 assert_eq!(loop_analysis.loop_parent(loops[0]), None);
281 assert_eq!(loop_analysis.is_in_loop(ebb0, loops[0]), true);
282 assert_eq!(loop_analysis.is_in_loop(ebb0, loops[1]), false);
283 assert_eq!(loop_analysis.is_in_loop(ebb1, loops[1]), true);
284 assert_eq!(loop_analysis.is_in_loop(ebb1, loops[0]), true);
285 assert_eq!(loop_analysis.is_in_loop(ebb2, loops[1]), true);
286 assert_eq!(loop_analysis.is_in_loop(ebb2, loops[0]), true);
287 assert_eq!(loop_analysis.is_in_loop(ebb3, loops[0]), true);
288 assert_eq!(loop_analysis.is_in_loop(ebb0, loops[1]), false);
289 }
290
291 #[test]
292 fn complex_loop_detection() {
293 let mut func = Function::new();
294 let ebb0 = func.dfg.make_ebb();
295 let ebb1 = func.dfg.make_ebb();
296 let ebb2 = func.dfg.make_ebb();
297 let ebb3 = func.dfg.make_ebb();
298 let ebb4 = func.dfg.make_ebb();
299 let ebb5 = func.dfg.make_ebb();
300 let cond = func.dfg.append_ebb_param(ebb0, types::I32);
301
302 {
303 let mut cur = FuncCursor::new(&mut func);
304
305 cur.insert_ebb(ebb0);
306 cur.ins().brnz(cond, ebb1, &[]);
307 cur.ins().jump(ebb3, &[]);
308
309 cur.insert_ebb(ebb1);
310 cur.ins().jump(ebb2, &[]);
311
312 cur.insert_ebb(ebb2);
313 cur.ins().brnz(cond, ebb1, &[]);
314 cur.ins().jump(ebb5, &[]);
315
316 cur.insert_ebb(ebb3);
317 cur.ins().jump(ebb4, &[]);
318
319 cur.insert_ebb(ebb4);
320 cur.ins().brnz(cond, ebb3, &[]);
321 cur.ins().jump(ebb5, &[]);
322
323 cur.insert_ebb(ebb5);
324 cur.ins().brnz(cond, ebb0, &[]);
325 }
326
327 let mut loop_analysis = LoopAnalysis::new();
328 let mut cfg = ControlFlowGraph::new();
329 let mut domtree = DominatorTree::new();
330 cfg.compute(&func);
331 domtree.compute(&func, &cfg);
332 loop_analysis.compute(&func, &cfg, &domtree);
333
334 let loops = loop_analysis.loops().collect::<Vec<Loop>>();
335 assert_eq!(loops.len(), 3);
336 assert_eq!(loop_analysis.loop_header(loops[0]), ebb0);
337 assert_eq!(loop_analysis.loop_header(loops[1]), ebb1);
338 assert_eq!(loop_analysis.loop_header(loops[2]), ebb3);
339 assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0]));
340 assert_eq!(loop_analysis.loop_parent(loops[2]), Some(loops[0]));
341 assert_eq!(loop_analysis.loop_parent(loops[0]), None);
342 assert_eq!(loop_analysis.is_in_loop(ebb0, loops[0]), true);
343 assert_eq!(loop_analysis.is_in_loop(ebb1, loops[1]), true);
344 assert_eq!(loop_analysis.is_in_loop(ebb2, loops[1]), true);
345 assert_eq!(loop_analysis.is_in_loop(ebb3, loops[2]), true);
346 assert_eq!(loop_analysis.is_in_loop(ebb4, loops[2]), true);
347 assert_eq!(loop_analysis.is_in_loop(ebb5, loops[0]), true);
348 }
349}