1use std::collections::BTreeSet;
7
8use cranelift_entity::{packed_option::PackedOption, SecondaryMap};
9
10use sonatina_ir::Block;
11
12use super::cfg::ControlFlowGraph;
13
14#[derive(Default, Debug)]
15pub struct DomTree {
16 doms: SecondaryMap<Block, PackedOption<Block>>,
17 rpo: Vec<Block>,
18}
19
20impl DomTree {
21 pub fn new() -> Self {
22 Self::default()
23 }
24
25 pub fn clear(&mut self) {
26 self.doms.clear();
27 self.rpo.clear();
28 }
29
30 pub fn idom_of(&self, block: Block) -> Option<Block> {
33 if self.rpo[0] == block {
34 return None;
35 }
36 self.doms[block].expand()
37 }
38
39 pub fn strictly_dominates(&self, block1: Block, block2: Block) -> bool {
41 let mut current_block = block2;
42 while let Some(block) = self.idom_of(current_block) {
43 if block == block1 {
44 return true;
45 }
46 current_block = block;
47 }
48
49 false
50 }
51
52 pub fn dominates(&self, block1: Block, block2: Block) -> bool {
54 if block1 == block2 {
55 return true;
56 }
57
58 self.strictly_dominates(block1, block2)
59 }
60
61 pub fn compute(&mut self, cfg: &ControlFlowGraph) {
62 self.clear();
63
64 self.rpo = cfg.post_order().collect();
65 self.rpo.reverse();
66
67 let block_num = self.rpo.len();
68
69 if self.doms.capacity() < block_num {
70 self.doms = SecondaryMap::with_capacity(block_num);
71 } else {
72 self.doms.clear();
73 }
74
75 let mut rpo_nums = SecondaryMap::with_capacity(block_num);
76 for (i, &block) in self.rpo.iter().enumerate() {
77 rpo_nums[block] = (block_num - i) as u32;
78 }
79
80 match self.rpo.first() {
81 Some(&entry) => self.doms[entry] = entry.into(),
82 None => return,
83 }
84
85 let mut changed = true;
86 while changed {
87 changed = false;
88 for &block in self.rpo.iter().skip(1) {
89 let processed_pred =
90 match cfg.preds_of(block).find(|&&pred| self.doms[pred].is_some()) {
91 Some(pred) => *pred,
92 _ => continue,
93 };
94 let mut new_dom = processed_pred;
95
96 for &pred in cfg.preds_of(block) {
97 if pred != processed_pred && self.doms[pred].is_some() {
98 new_dom = self.intersect(new_dom, pred, &rpo_nums);
99 }
100 }
101 if Some(new_dom) != self.doms[block].expand() {
102 changed = true;
103 self.doms[block] = new_dom.into();
104 }
105 }
106 }
107 }
108
109 pub fn compute_df(&self, cfg: &ControlFlowGraph) -> DFSet {
111 let mut df = DFSet::default();
112
113 for &block in &self.rpo {
114 if cfg.pred_num_of(block) < 2 {
115 continue;
116 }
117 for pred in cfg.preds_of(block) {
118 let mut runner = *pred;
119 while PackedOption::from(runner) != self.doms[block] && self.is_reachable(runner) {
120 df.0[runner].insert(block);
121 runner = self.doms[runner].unwrap();
122 }
123 }
124 }
125
126 df
127 }
128
129 pub fn is_reachable(&self, block: Block) -> bool {
131 self.idom_of(block).is_some()
132 }
133
134 pub fn rpo(&self) -> &[Block] {
136 &self.rpo
137 }
138
139 fn intersect(
140 &self,
141 mut b1: Block,
142 mut b2: Block,
143 rpo_nums: &SecondaryMap<Block, u32>,
144 ) -> Block {
145 while b1 != b2 {
146 while rpo_nums[b1] < rpo_nums[b2] {
147 b1 = self.doms[b1].unwrap();
148 }
149 while rpo_nums[b2] < rpo_nums[b1] {
150 b2 = self.doms[b2].unwrap();
151 }
152 }
153
154 b1
155 }
156}
157
158#[derive(Default, Debug)]
160pub struct DFSet(SecondaryMap<Block, BTreeSet<Block>>);
161
162impl DFSet {
163 pub fn frontiers(&self, block: Block) -> impl Iterator<Item = &Block> {
164 self.0[block].iter()
165 }
166
167 pub fn in_frontier_of(&self, block: Block, of: Block) -> bool {
168 self.0[of].contains(&block)
169 }
170
171 pub fn frontier_num_of(&self, of: Block) -> usize {
172 self.0[of].len()
173 }
174
175 pub fn clear(&mut self) {
176 self.0.clear()
177 }
178}
179
180#[derive(Default)]
181pub struct DominatorTreeTraversable {
182 children: SecondaryMap<Block, Vec<Block>>,
183}
184
185impl DominatorTreeTraversable {
186 pub fn compute(&mut self, domtree: &DomTree) {
187 for &block in domtree.rpo() {
188 if let Some(idom) = domtree.idom_of(block) {
189 self.children[idom].push(block)
190 }
191 }
192 }
193
194 pub fn children_of(&self, block: Block) -> &[Block] {
195 &self.children[block]
196 }
197
198 pub fn clear(&mut self) {
199 self.children.clear();
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 #![allow(clippy::many_single_char_names)]
206
207 use super::*;
208
209 use sonatina_ir::{builder::test_util::*, Function, Type};
210
211 fn calc_dom(func: &Function) -> (DomTree, DFSet) {
212 let mut cfg = ControlFlowGraph::default();
213 cfg.compute(func);
214 let mut dom_tree = DomTree::default();
215 dom_tree.compute(&cfg);
216 let df = dom_tree.compute_df(&cfg);
217 (dom_tree, df)
218 }
219
220 fn test_df(df: &DFSet, of: Block, frontiers: &[Block]) -> bool {
221 if df.frontier_num_of(of) != frontiers.len() {
222 return false;
223 }
224
225 for &block in frontiers {
226 if !df.in_frontier_of(block, of) {
227 return false;
228 }
229 }
230 true
231 }
232
233 #[test]
234 fn dom_tree_if_else() {
235 let mut test_module_builder = TestModuleBuilder::new();
236 let mut builder = test_module_builder.func_builder(&[], Type::Void);
237
238 let entry_block = builder.append_block();
239 let then_block = builder.append_block();
240 let else_block = builder.append_block();
241 let merge_block = builder.append_block();
242
243 builder.switch_to_block(entry_block);
244 let v0 = builder.make_imm_value(true);
245 builder.br(v0, else_block, then_block);
246
247 builder.switch_to_block(then_block);
248 builder.jump(merge_block);
249
250 builder.switch_to_block(else_block);
251 builder.jump(merge_block);
252
253 builder.switch_to_block(merge_block);
254 builder.ret(None);
255
256 builder.seal_all();
257 let func_ref = builder.finish();
258
259 let module = test_module_builder.build();
260 let func = &module.funcs[func_ref];
261
262 let (dom_tree, df) = calc_dom(func);
263 assert_eq!(dom_tree.idom_of(entry_block), None);
264 assert_eq!(dom_tree.idom_of(then_block), Some(entry_block));
265 assert_eq!(dom_tree.idom_of(else_block), Some(entry_block));
266 assert_eq!(dom_tree.idom_of(merge_block), Some(entry_block));
267
268 assert!(test_df(&df, entry_block, &[]));
269 assert!(test_df(&df, then_block, &[merge_block]));
270 assert!(test_df(&df, else_block, &[merge_block]));
271 assert!(test_df(&df, merge_block, &[]));
272 }
273
274 #[test]
275 fn unreachable_edge() {
276 let mut test_module_builder = TestModuleBuilder::new();
277 let mut builder = test_module_builder.func_builder(&[], Type::Void);
278
279 let a = builder.append_block();
280 let b = builder.append_block();
281 let c = builder.append_block();
282 let d = builder.append_block();
283 let e = builder.append_block();
284
285 builder.switch_to_block(a);
286 let v0 = builder.make_imm_value(true);
287 builder.br(v0, b, c);
288
289 builder.switch_to_block(b);
290 builder.jump(e);
291
292 builder.switch_to_block(c);
293 builder.jump(e);
294
295 builder.switch_to_block(d);
296 builder.jump(e);
297
298 builder.switch_to_block(e);
299 builder.ret(None);
300
301 builder.seal_all();
302 let func_ref = builder.finish();
303
304 let module = test_module_builder.build();
305 let func = &module.funcs[func_ref];
306
307 let (dom_tree, df) = calc_dom(func);
308 assert_eq!(dom_tree.idom_of(a), None);
309 assert_eq!(dom_tree.idom_of(b), Some(a));
310 assert_eq!(dom_tree.idom_of(c), Some(a));
311 assert_eq!(dom_tree.idom_of(d), None);
312 assert!(!dom_tree.is_reachable(d));
313 assert_eq!(dom_tree.idom_of(e), Some(a));
314
315 assert!(test_df(&df, a, &[]));
316 assert!(test_df(&df, b, &[e]));
317 assert!(test_df(&df, c, &[e]));
318 assert!(test_df(&df, d, &[]));
319 assert!(test_df(&df, e, &[]));
320 }
321
322 #[test]
323 fn dom_tree_complex() {
324 let mut test_module_builder = TestModuleBuilder::new();
325 let mut builder = test_module_builder.func_builder(&[], Type::Void);
326
327 let a = builder.append_block();
328 let b = builder.append_block();
329 let c = builder.append_block();
330 let d = builder.append_block();
331 let e = builder.append_block();
332 let f = builder.append_block();
333 let g = builder.append_block();
334 let h = builder.append_block();
335 let i = builder.append_block();
336 let j = builder.append_block();
337 let k = builder.append_block();
338 let l = builder.append_block();
339 let m = builder.append_block();
340
341 builder.switch_to_block(a);
342 let v0 = builder.make_imm_value(true);
343 builder.br(v0, c, b);
344
345 builder.switch_to_block(b);
346 builder.br(v0, g, d);
347
348 builder.switch_to_block(c);
349 builder.br(v0, h, e);
350
351 builder.switch_to_block(d);
352 builder.br(v0, g, f);
353
354 builder.switch_to_block(e);
355 builder.br(v0, h, c);
356
357 builder.switch_to_block(f);
358 builder.br(v0, k, i);
359
360 builder.switch_to_block(g);
361 builder.jump(j);
362
363 builder.switch_to_block(h);
364 builder.jump(m);
365
366 builder.switch_to_block(i);
367 builder.jump(l);
368
369 builder.switch_to_block(j);
370 builder.jump(i);
371
372 builder.switch_to_block(k);
373 builder.jump(l);
374
375 builder.switch_to_block(l);
376 builder.br(v0, m, b);
377
378 builder.switch_to_block(m);
379 builder.ret(None);
380
381 builder.seal_all();
382 let func_ref = builder.finish();
383
384 let module = test_module_builder.build();
385 let func = &module.funcs[func_ref];
386
387 let (dom_tree, df) = calc_dom(func);
388 assert_eq!(dom_tree.idom_of(a), None);
389 assert_eq!(dom_tree.idom_of(b), Some(a));
390 assert_eq!(dom_tree.idom_of(c), Some(a));
391 assert_eq!(dom_tree.idom_of(d), Some(b));
392 assert_eq!(dom_tree.idom_of(e), Some(c));
393 assert_eq!(dom_tree.idom_of(f), Some(d));
394 assert_eq!(dom_tree.idom_of(g), Some(b));
395 assert_eq!(dom_tree.idom_of(h), Some(c));
396 assert_eq!(dom_tree.idom_of(i), Some(b));
397 assert_eq!(dom_tree.idom_of(j), Some(g));
398 assert_eq!(dom_tree.idom_of(k), Some(f));
399
400 assert!(test_df(&df, a, &[]));
401 assert!(test_df(&df, b, &[b, m]));
402 assert!(test_df(&df, c, &[c, m]));
403 assert!(test_df(&df, d, &[g, i, l]));
404 assert!(test_df(&df, e, &[c, h]));
405 assert!(test_df(&df, f, &[i, l]));
406 assert!(test_df(&df, g, &[i]));
407 assert!(test_df(&df, h, &[m]));
408 assert!(test_df(&df, i, &[l]));
409 assert!(test_df(&df, j, &[i]));
410 assert!(test_df(&df, k, &[l]));
411 assert!(test_df(&df, l, &[b, m]));
412 assert!(test_df(&df, m, &[]));
413 }
414
415 #[test]
416 fn dom_tree_br_table() {
417 let mut test_module_builder = TestModuleBuilder::new();
418 let mut builder = test_module_builder.func_builder(&[], Type::Void);
419
420 let a = builder.append_block();
421 let b = builder.append_block();
422 let c = builder.append_block();
423 let d = builder.append_block();
424 let e = builder.append_block();
425 let f = builder.append_block();
426
427 builder.switch_to_block(a);
428 let v0 = builder.make_imm_value(0i32);
429 let v1 = builder.make_imm_value(1i32);
430 let v2 = builder.make_imm_value(2i32);
431 builder.br_table(v0, Some(b), &[(v1, c), (v2, d)]);
432
433 builder.switch_to_block(b);
434 let v3 = builder.make_imm_value(true);
435 builder.br(v3, a, e);
436
437 builder.switch_to_block(c);
438 builder.jump(f);
439
440 builder.switch_to_block(d);
441 builder.jump(f);
442
443 builder.switch_to_block(e);
444 builder.ret(None);
445
446 builder.switch_to_block(f);
447 builder.ret(None);
448
449 builder.seal_all();
450 let func_ref = builder.finish();
451
452 let module = test_module_builder.build();
453 let func = &module.funcs[func_ref];
454
455 let (dom_tree, df) = calc_dom(func);
456 assert_eq!(dom_tree.idom_of(a), None);
457 assert_eq!(dom_tree.idom_of(b), Some(a));
458 assert_eq!(dom_tree.idom_of(c), Some(a));
459 assert_eq!(dom_tree.idom_of(d), Some(a));
460 assert_eq!(dom_tree.idom_of(e), Some(b));
461 assert_eq!(dom_tree.idom_of(f), Some(a));
462
463 assert!(test_df(&df, a, &[]));
464 assert!(test_df(&df, b, &[]));
465 assert!(test_df(&df, c, &[f]));
466 assert!(test_df(&df, d, &[f]));
467 assert!(test_df(&df, e, &[]));
468 assert!(test_df(&df, f, &[]));
469 }
470}