1use crate::lua51::cfg::{ControlFlowGraph, EdgeKind};
2use crate::lua51::opcodes::OpCode;
3
4#[derive(Debug)]
9pub struct DominatorTree {
10 idom: Vec<usize>,
12 children: Vec<Vec<usize>>,
14 frontier: Vec<Vec<usize>>,
16 num_blocks: usize,
18}
19
20impl DominatorTree {
21 pub fn build(cfg: &ControlFlowGraph) -> Self {
23 let n = cfg.num_blocks();
24 if n == 0 {
25 return DominatorTree {
26 idom: Vec::new(),
27 children: Vec::new(),
28 frontier: Vec::new(),
29 num_blocks: 0,
30 };
31 }
32
33 let rpo = compute_rpo(cfg, n);
35 let rpo_order = &rpo.order; let rpo_num = &rpo.number; const UNDEF: usize = usize::MAX;
40 let mut idom = vec![UNDEF; n];
41 idom[0] = 0; let intersect = |idom: &[usize], rpo_num: &[usize], mut b1: usize, mut b2: usize| -> usize {
44 while b1 != b2 {
45 while rpo_num[b1] > rpo_num[b2] {
46 b1 = idom[b1];
47 }
48 while rpo_num[b2] > rpo_num[b1] {
49 b2 = idom[b2];
50 }
51 }
52 b1
53 };
54
55 let mut changed = true;
56 while changed {
57 changed = false;
58 for &b in &rpo_order[1..] {
60 let preds = &cfg.blocks[b].predecessors;
61 let mut new_idom = UNDEF;
63 for &p in preds {
64 if idom[p] != UNDEF {
65 new_idom = p;
66 break;
67 }
68 }
69 if new_idom == UNDEF {
70 continue; }
72 for &p in preds {
74 if p != new_idom && idom[p] != UNDEF {
75 new_idom = intersect(&idom, rpo_num, p, new_idom);
76 }
77 }
78 if idom[b] != new_idom {
79 idom[b] = new_idom;
80 changed = true;
81 }
82 }
83 }
84
85 let mut children = vec![Vec::new(); n];
87 for b in 0..n {
88 if idom[b] != b && idom[b] != UNDEF {
89 children[idom[b]].push(b);
90 }
91 }
92
93 let frontier = compute_dominance_frontiers(&idom, cfg, n);
95
96 DominatorTree {
97 idom,
98 children,
99 frontier,
100 num_blocks: n,
101 }
102 }
103
104 pub fn idom(&self, b: usize) -> usize {
106 self.idom[b]
107 }
108
109 pub fn children(&self, b: usize) -> &[usize] {
111 &self.children[b]
112 }
113
114 pub fn frontier(&self, b: usize) -> &[usize] {
116 &self.frontier[b]
117 }
118
119 pub fn dominates(&self, a: usize, b: usize) -> bool {
121 const UNDEF: usize = usize::MAX;
122 let mut cur = b;
123 loop {
124 if cur == a {
125 return true;
126 }
127 let idom = self.idom[cur];
128 if idom == UNDEF || idom == cur {
129 return false; }
131 cur = idom;
132 }
133 }
134
135 pub fn num_blocks(&self) -> usize {
137 self.num_blocks
138 }
139}
140
141impl std::fmt::Display for DominatorTree {
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 writeln!(f, "Dominator Tree:")?;
144 for b in 0..self.num_blocks {
145 writeln!(
146 f,
147 " B{}: idom=B{}, children={:?}, frontier={:?}",
148 b, self.idom[b], self.children[b], self.frontier[b]
149 )?;
150 }
151 Ok(())
152 }
153}
154
155#[derive(Debug, Clone)]
159pub struct NaturalLoop {
160 pub header: usize,
162 pub latch: usize,
164 pub body: Vec<usize>,
166 pub kind: LoopKind,
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172pub enum LoopKind {
173 NumericFor,
175 GenericFor,
177 WhileRepeat,
179}
180
181pub fn find_loops(cfg: &ControlFlowGraph, dom: &DominatorTree) -> Vec<NaturalLoop> {
187 let mut loops = Vec::new();
188
189 for edge in &cfg.edges {
190 let target_block = &cfg.blocks[edge.to];
191 let target_ends_with_forloop = matches!(
192 cfg.instructions[target_block.end].op,
193 OpCode::ForLoop | OpCode::TForLoop
194 );
195
196 let is_explicit_loop_back = matches!(edge.kind, EdgeKind::ForLoopBack | EdgeKind::TForLoopBack);
200 let is_natural_back_edge = dom.dominates(edge.to, edge.from)
201 && !(target_ends_with_forloop && !is_explicit_loop_back);
202
203 if is_explicit_loop_back || is_natural_back_edge {
204 let header = edge.to;
205 let latch = edge.from;
206
207 let mut body = collect_loop_body(cfg, header, latch);
210 if edge.kind == EdgeKind::ForLoopBack {
211 body.retain(|&block_id| {
212 let block = &cfg.blocks[block_id];
213 cfg.instructions[block.end].op != OpCode::ForPrep
214 });
215 }
216
217 let kind = classify_loop(cfg, edge.kind, header);
219
220 loops.push(NaturalLoop {
221 header,
222 latch,
223 body,
224 kind,
225 });
226 }
227 }
228
229 loops.sort_by_key(|l| (l.header, l.latch));
231 loops
232}
233
234fn collect_loop_body(cfg: &ControlFlowGraph, header: usize, latch: usize) -> Vec<usize> {
236 let mut body = vec![false; cfg.num_blocks()];
237 body[header] = true;
238
239 if header == latch {
240 return vec![header];
241 }
242
243 body[latch] = true;
244 let mut stack = vec![latch];
245
246 while let Some(b) = stack.pop() {
248 for &pred in &cfg.blocks[b].predecessors {
249 if !body[pred] {
250 body[pred] = true;
251 stack.push(pred);
252 }
253 }
254 }
255
256 body.iter()
257 .enumerate()
258 .filter_map(|(i, &in_loop)| if in_loop { Some(i) } else { None })
259 .collect()
260}
261
262fn classify_loop(cfg: &ControlFlowGraph, edge_kind: EdgeKind, header: usize) -> LoopKind {
264 match edge_kind {
265 EdgeKind::ForLoopBack => LoopKind::NumericFor,
266 EdgeKind::TForLoopBack => LoopKind::GenericFor,
267 _ => {
268 let block = &cfg.blocks[header];
270 use crate::lua51::opcodes::OpCode;
271 for pc in block.start..=block.end {
272 match cfg.instructions[pc].op {
273 OpCode::ForLoop => return LoopKind::NumericFor,
274 OpCode::TForLoop => return LoopKind::GenericFor,
275 _ => {}
276 }
277 }
278 LoopKind::WhileRepeat
279 }
280 }
281}
282
283struct Rpo {
286 order: Vec<usize>,
288 number: Vec<usize>,
290}
291
292fn compute_rpo(cfg: &ControlFlowGraph, n: usize) -> Rpo {
293 let mut visited = vec![false; n];
294 let mut postorder = Vec::with_capacity(n);
295
296 fn dfs(
297 b: usize,
298 cfg: &ControlFlowGraph,
299 visited: &mut Vec<bool>,
300 postorder: &mut Vec<usize>,
301 ) {
302 visited[b] = true;
303 for &succ in &cfg.blocks[b].successors {
304 if !visited[succ] {
305 dfs(succ, cfg, visited, postorder);
306 }
307 }
308 postorder.push(b);
309 }
310
311 dfs(0, cfg, &mut visited, &mut postorder);
312
313 postorder.reverse();
315 let mut number = vec![0usize; n];
316 for (i, &b) in postorder.iter().enumerate() {
317 number[b] = i;
318 }
319
320 Rpo {
321 order: postorder,
322 number,
323 }
324}
325
326fn compute_dominance_frontiers(idom: &[usize], cfg: &ControlFlowGraph, n: usize) -> Vec<Vec<usize>> {
329 let mut frontiers = vec![Vec::new(); n];
330
331 for b in 0..n {
332 let preds = &cfg.blocks[b].predecessors;
333 if preds.len() >= 2 {
334 for &p in preds {
335 let mut runner = p;
336 while runner != idom[b] && runner != usize::MAX {
337 if !frontiers[runner].contains(&b) {
338 frontiers[runner].push(b);
339 }
340 if runner == idom[runner] {
341 break;
342 }
343 runner = idom[runner];
344 }
345 }
346 }
347 }
348
349 frontiers
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355 use crate::lua51::cfg::ControlFlowGraph;
356 use crate::lua51::instruction::{encode_abc, encode_abx, encode_asbx};
357 use crate::lua51::opcodes::OpCode;
358
359 #[test]
360 fn dominator_linear() {
361 let code = vec![
363 encode_abx(OpCode::LoadK, 0, 0),
364 encode_abc(OpCode::Return, 0, 1, 0),
365 ];
366 let cfg = ControlFlowGraph::build(&code);
367 let dom = DominatorTree::build(&cfg);
368 assert_eq!(dom.idom(0), 0);
369 assert!(dom.dominates(0, 0));
370 }
371
372 #[test]
373 fn dominator_diamond() {
374 let code = vec![
388 encode_abc(OpCode::Eq, 0, 0, 1),
389 encode_asbx(OpCode::Jmp, 0, 2),
390 encode_abx(OpCode::LoadK, 2, 0),
391 encode_asbx(OpCode::Jmp, 0, 0),
392 encode_abc(OpCode::Return, 0, 1, 0),
393 ];
394 let cfg = ControlFlowGraph::build(&code);
395 let dom = DominatorTree::build(&cfg);
396
397 for b in 0..cfg.num_blocks() {
399 assert!(dom.dominates(0, b), "block 0 should dominate block {}", b);
400 }
401 }
402
403 #[test]
404 fn detect_numeric_for_loop() {
405 let code = vec![
413 encode_abx(OpCode::LoadK, 0, 0),
414 encode_abx(OpCode::LoadK, 1, 1),
415 encode_abx(OpCode::LoadK, 2, 2),
416 encode_asbx(OpCode::ForPrep, 0, 1),
417 encode_abx(OpCode::LoadK, 4, 0),
418 encode_asbx(OpCode::ForLoop, 0, -2),
419 encode_abc(OpCode::Return, 0, 1, 0),
420 ];
421 let cfg = ControlFlowGraph::build(&code);
422 let dom = DominatorTree::build(&cfg);
423 let loops = find_loops(&cfg, &dom);
424
425 assert_eq!(loops.len(), 1);
426 assert_eq!(loops[0].kind, LoopKind::NumericFor);
427 assert!(loops[0].body.len() >= 1);
428 }
429
430 #[test]
431 fn detect_while_loop() {
432 let code = vec![
439 encode_abc(OpCode::Eq, 0, 0, 1),
440 encode_asbx(OpCode::Jmp, 0, 2),
441 encode_abx(OpCode::LoadK, 2, 0),
442 encode_asbx(OpCode::Jmp, 0, -4),
443 encode_abc(OpCode::Return, 0, 1, 0),
444 ];
445 let cfg = ControlFlowGraph::build(&code);
446 let dom = DominatorTree::build(&cfg);
447 let loops = find_loops(&cfg, &dom);
448
449 assert_eq!(loops.len(), 1);
450 assert_eq!(loops[0].kind, LoopKind::WhileRepeat);
451 }
452
453 #[test]
454 fn empty_cfg_dominator() {
455 let cfg = ControlFlowGraph::build(&[]);
456 let dom = DominatorTree::build(&cfg);
457 assert_eq!(dom.num_blocks(), 0);
458 let loops = find_loops(&cfg, &dom);
459 assert!(loops.is_empty());
460 }
461}