1use crate::entity::SecondaryMap;
4use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
5use crate::ir::{Block, Function, Inst, Layout, ProgramPoint};
6use crate::packed_option::PackedOption;
7use crate::timing;
8use crate::traversals::Dfs;
9use alloc::vec::Vec;
10use core::cmp;
11use core::cmp::Ordering;
12use core::mem;
13
14const STRIDE: u32 = 4;
17
18#[derive(Clone, Default)]
20struct DomNode {
21 rpo_number: u32,
26
27 idom: PackedOption<Inst>,
33}
34
35pub struct DominatorTree {
37 nodes: SecondaryMap<Block, DomNode>,
38
39 postorder: Vec<Block>,
41
42 dfs: Dfs,
44
45 valid: bool,
46}
47
48impl DominatorTree {
50 pub fn is_reachable(&self, block: Block) -> bool {
52 self.nodes[block].rpo_number != 0
53 }
54
55 pub fn cfg_postorder(&self) -> &[Block] {
60 debug_assert!(self.is_valid());
61 &self.postorder
62 }
63
64 pub fn idom(&self, block: Block) -> Option<Inst> {
79 self.nodes[block].idom.into()
80 }
81
82 pub fn rpo_cmp_block(&self, a: Block, b: Block) -> Ordering {
84 self.nodes[a].rpo_number.cmp(&self.nodes[b].rpo_number)
85 }
86
87 pub fn rpo_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering
94 where
95 A: Into<ProgramPoint>,
96 B: Into<ProgramPoint>,
97 {
98 let a = a.into();
99 let b = b.into();
100 self.rpo_cmp_block(layout.pp_block(a), layout.pp_block(b))
101 .then_with(|| layout.pp_cmp(a, b))
102 }
103
104 pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool
114 where
115 A: Into<ProgramPoint>,
116 B: Into<ProgramPoint>,
117 {
118 let a = a.into();
119 let b = b.into();
120 match a {
121 ProgramPoint::Block(block_a) => {
122 a == b || self.last_dominator(block_a, b, layout).is_some()
123 }
124 ProgramPoint::Inst(inst_a) => {
125 let block_a = layout
126 .inst_block(inst_a)
127 .expect("Instruction not in layout.");
128 match self.last_dominator(block_a, b, layout) {
129 Some(last) => layout.pp_cmp(inst_a, last) != Ordering::Greater,
130 None => false,
131 }
132 }
133 }
134 }
135
136 pub fn last_dominator<B>(&self, a: Block, b: B, layout: &Layout) -> Option<Inst>
139 where
140 B: Into<ProgramPoint>,
141 {
142 let (mut block_b, mut inst_b) = match b.into() {
143 ProgramPoint::Block(block) => (block, None),
144 ProgramPoint::Inst(inst) => (
145 layout.inst_block(inst).expect("Instruction not in layout."),
146 Some(inst),
147 ),
148 };
149 let rpo_a = self.nodes[a].rpo_number;
150
151 while rpo_a < self.nodes[block_b].rpo_number {
154 let idom = match self.idom(block_b) {
155 Some(idom) => idom,
156 None => return None, };
158 block_b = layout.inst_block(idom).expect("Dominator got removed.");
159 inst_b = Some(idom);
160 }
161 if a == block_b {
162 inst_b
163 } else {
164 None
165 }
166 }
167
168 pub fn common_dominator(
172 &self,
173 mut a: BlockPredecessor,
174 mut b: BlockPredecessor,
175 layout: &Layout,
176 ) -> BlockPredecessor {
177 loop {
178 match self.rpo_cmp_block(a.block, b.block) {
179 Ordering::Less => {
180 let idom = self.nodes[b.block].idom.expect("Unreachable basic block?");
182 b = BlockPredecessor::new(
183 layout.inst_block(idom).expect("Dangling idom instruction"),
184 idom,
185 );
186 }
187 Ordering::Greater => {
188 let idom = self.nodes[a.block].idom.expect("Unreachable basic block?");
190 a = BlockPredecessor::new(
191 layout.inst_block(idom).expect("Dangling idom instruction"),
192 idom,
193 );
194 }
195 Ordering::Equal => break,
196 }
197 }
198
199 debug_assert_eq!(
200 a.block, b.block,
201 "Unreachable block passed to common_dominator?"
202 );
203
204 if layout.pp_cmp(a.inst, b.inst) == Ordering::Less {
206 a
207 } else {
208 b
209 }
210 }
211}
212
213impl DominatorTree {
214 pub fn new() -> Self {
217 Self {
218 nodes: SecondaryMap::new(),
219 postorder: Vec::new(),
220 dfs: Dfs::new(),
221 valid: false,
222 }
223 }
224
225 pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self {
227 let block_capacity = func.layout.block_capacity();
228 let mut domtree = Self {
229 nodes: SecondaryMap::with_capacity(block_capacity),
230 postorder: Vec::with_capacity(block_capacity),
231 dfs: Dfs::new(),
232 valid: false,
233 };
234 domtree.compute(func, cfg);
235 domtree
236 }
237
238 pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) {
240 let _tt = timing::domtree();
241 debug_assert!(cfg.is_valid());
242 self.compute_postorder(func);
243 self.compute_domtree(func, cfg);
244 self.valid = true;
245 }
246
247 pub fn clear(&mut self) {
250 self.nodes.clear();
251 self.postorder.clear();
252 self.valid = false;
253 }
254
255 pub fn is_valid(&self) -> bool {
261 self.valid
262 }
263
264 fn compute_postorder(&mut self, func: &Function) {
268 self.clear();
269 self.nodes.resize(func.dfg.num_blocks());
270 self.postorder.extend(self.dfs.post_order_iter(func));
271 }
272
273 fn compute_domtree(&mut self, func: &Function, cfg: &ControlFlowGraph) {
276 let (entry_block, postorder) = match self.postorder.as_slice().split_last() {
285 Some((&eb, rest)) => (eb, rest),
286 None => return,
287 };
288 debug_assert_eq!(Some(entry_block), func.layout.entry_block());
289
290 self.nodes[entry_block].rpo_number = 2 * STRIDE;
292 for (rpo_idx, &block) in postorder.iter().rev().enumerate() {
293 self.nodes[block] = DomNode {
303 idom: self.compute_idom(block, cfg, &func.layout).into(),
304 rpo_number: (rpo_idx as u32 + 3) * STRIDE,
305 }
306 }
307
308 let mut changed = true;
313 while changed {
314 changed = false;
315 for &block in postorder.iter().rev() {
316 let idom = self.compute_idom(block, cfg, &func.layout).into();
317 if self.nodes[block].idom != idom {
318 self.nodes[block].idom = idom;
319 changed = true;
320 }
321 }
322 }
323 }
324
325 fn compute_idom(&self, block: Block, cfg: &ControlFlowGraph, layout: &Layout) -> Inst {
328 let mut reachable_preds = cfg
332 .pred_iter(block)
333 .filter(|&BlockPredecessor { block: pred, .. }| self.nodes[pred].rpo_number > 1);
334
335 let mut idom = reachable_preds
337 .next()
338 .expect("block node must have one reachable predecessor");
339
340 for pred in reachable_preds {
341 idom = self.common_dominator(idom, pred, layout);
342 }
343
344 idom.inst
345 }
346}
347
348pub struct DominatorTreePreorder {
359 nodes: SecondaryMap<Block, ExtraNode>,
360
361 stack: Vec<Block>,
363}
364
365#[derive(Default, Clone)]
366struct ExtraNode {
367 child: PackedOption<Block>,
369
370 sibling: PackedOption<Block>,
372
373 pre_number: u32,
376
377 pre_max: u32,
380}
381
382impl DominatorTreePreorder {
384 pub fn new() -> Self {
386 Self {
387 nodes: SecondaryMap::new(),
388 stack: Vec::new(),
389 }
390 }
391
392 pub fn compute(&mut self, domtree: &DominatorTree, layout: &Layout) {
394 self.nodes.clear();
395
396 for &block in domtree.cfg_postorder() {
401 if let Some(idom_inst) = domtree.idom(block) {
402 let idom = layout
403 .inst_block(idom_inst)
404 .expect("Instruction not in layout.");
405 let sib = mem::replace(&mut self.nodes[idom].child, block.into());
406 self.nodes[block].sibling = sib;
407 } else {
408 self.stack.push(block);
410 }
411 }
412
413 debug_assert!(self.stack.len() <= 1);
415 let mut n = 0;
416 while let Some(block) = self.stack.pop() {
417 n += 1;
418 let node = &mut self.nodes[block];
419 node.pre_number = n;
420 node.pre_max = n;
421 if let Some(n) = node.sibling.expand() {
422 self.stack.push(n);
423 }
424 if let Some(n) = node.child.expand() {
425 self.stack.push(n);
426 }
427 }
428
429 for &block in domtree.cfg_postorder() {
433 if let Some(idom_inst) = domtree.idom(block) {
434 let idom = layout
435 .inst_block(idom_inst)
436 .expect("Instruction not in layout.");
437 let pre_max = cmp::max(self.nodes[block].pre_max, self.nodes[idom].pre_max);
438 self.nodes[idom].pre_max = pre_max;
439 }
440 }
441 }
442}
443
444pub struct ChildIter<'a> {
446 dtpo: &'a DominatorTreePreorder,
447 next: PackedOption<Block>,
448}
449
450impl<'a> Iterator for ChildIter<'a> {
451 type Item = Block;
452
453 fn next(&mut self) -> Option<Block> {
454 let n = self.next.expand();
455 if let Some(block) = n {
456 self.next = self.dtpo.nodes[block].sibling;
457 }
458 n
459 }
460}
461
462impl DominatorTreePreorder {
464 pub fn children(&self, block: Block) -> ChildIter {
469 ChildIter {
470 dtpo: self,
471 next: self.nodes[block].child,
472 }
473 }
474
475 pub fn dominates(&self, a: Block, b: Block) -> bool {
483 let na = &self.nodes[a];
484 let nb = &self.nodes[b];
485 na.pre_number <= nb.pre_number && na.pre_max >= nb.pre_max
486 }
487
488 pub fn pre_cmp_block(&self, a: Block, b: Block) -> Ordering {
490 self.nodes[a].pre_number.cmp(&self.nodes[b].pre_number)
491 }
492
493 pub fn pre_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering
498 where
499 A: Into<ProgramPoint>,
500 B: Into<ProgramPoint>,
501 {
502 let a = a.into();
503 let b = b.into();
504 self.pre_cmp_block(layout.pp_block(a), layout.pp_block(b))
505 .then_with(|| layout.pp_cmp(a, b))
506 }
507}
508
509#[cfg(test)]
510mod tests {
511 use super::*;
512 use crate::cursor::{Cursor, FuncCursor};
513 use crate::ir::types::*;
514 use crate::ir::{InstBuilder, TrapCode};
515
516 #[test]
517 fn empty() {
518 let func = Function::new();
519 let cfg = ControlFlowGraph::with_function(&func);
520 debug_assert!(cfg.is_valid());
521 let dtree = DominatorTree::with_function(&func, &cfg);
522 assert_eq!(0, dtree.nodes.keys().count());
523 assert_eq!(dtree.cfg_postorder(), &[]);
524
525 let mut dtpo = DominatorTreePreorder::new();
526 dtpo.compute(&dtree, &func.layout);
527 }
528
529 #[test]
530 fn unreachable_node() {
531 let mut func = Function::new();
532 let block0 = func.dfg.make_block();
533 let v0 = func.dfg.append_block_param(block0, I32);
534 let block1 = func.dfg.make_block();
535 let block2 = func.dfg.make_block();
536 let trap_block = func.dfg.make_block();
537
538 let mut cur = FuncCursor::new(&mut func);
539
540 cur.insert_block(block0);
541 cur.ins().brif(v0, block2, &[], trap_block, &[]);
542
543 cur.insert_block(trap_block);
544 cur.ins().trap(TrapCode::User(0));
545
546 cur.insert_block(block1);
547 let v1 = cur.ins().iconst(I32, 1);
548 let v2 = cur.ins().iadd(v0, v1);
549 cur.ins().jump(block0, &[v2]);
550
551 cur.insert_block(block2);
552 cur.ins().return_(&[v0]);
553
554 let cfg = ControlFlowGraph::with_function(cur.func);
555 let dt = DominatorTree::with_function(cur.func, &cfg);
556
557 assert_eq!(dt.cfg_postorder(), &[block2, trap_block, block0]);
567
568 let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
569 assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
570 assert!(!dt.dominates(block0, v2_def, &cur.func.layout));
571
572 let mut dtpo = DominatorTreePreorder::new();
573 dtpo.compute(&dt, &cur.func.layout);
574 assert!(dtpo.dominates(block0, block0));
575 assert!(!dtpo.dominates(block0, block1));
576 assert!(dtpo.dominates(block0, block2));
577 assert!(!dtpo.dominates(block1, block0));
578 assert!(dtpo.dominates(block1, block1));
579 assert!(!dtpo.dominates(block1, block2));
580 assert!(!dtpo.dominates(block2, block0));
581 assert!(!dtpo.dominates(block2, block1));
582 assert!(dtpo.dominates(block2, block2));
583 }
584
585 #[test]
586 fn non_zero_entry_block() {
587 let mut func = Function::new();
588 let block0 = func.dfg.make_block();
589 let block1 = func.dfg.make_block();
590 let block2 = func.dfg.make_block();
591 let block3 = func.dfg.make_block();
592 let cond = func.dfg.append_block_param(block3, I32);
593
594 let mut cur = FuncCursor::new(&mut func);
595
596 cur.insert_block(block3);
597 let jmp_block3_block1 = cur.ins().jump(block1, &[]);
598
599 cur.insert_block(block1);
600 let br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]);
601
602 cur.insert_block(block2);
603 cur.ins().jump(block0, &[]);
604
605 cur.insert_block(block0);
606
607 let cfg = ControlFlowGraph::with_function(cur.func);
608 let dt = DominatorTree::with_function(cur.func, &cfg);
609
610 assert_eq!(dt.cfg_postorder(), &[block0, block2, block1, block3]);
629
630 assert_eq!(cur.func.layout.entry_block().unwrap(), block3);
631 assert_eq!(dt.idom(block3), None);
632 assert_eq!(dt.idom(block1).unwrap(), jmp_block3_block1);
633 assert_eq!(dt.idom(block2).unwrap(), br_block1_block0_block2);
634 assert_eq!(dt.idom(block0).unwrap(), br_block1_block0_block2);
635
636 assert!(dt.dominates(
637 br_block1_block0_block2,
638 br_block1_block0_block2,
639 &cur.func.layout
640 ));
641 assert!(!dt.dominates(br_block1_block0_block2, jmp_block3_block1, &cur.func.layout));
642 assert!(dt.dominates(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout));
643
644 assert_eq!(
645 dt.rpo_cmp(block3, block3, &cur.func.layout),
646 Ordering::Equal
647 );
648 assert_eq!(dt.rpo_cmp(block3, block1, &cur.func.layout), Ordering::Less);
649 assert_eq!(
650 dt.rpo_cmp(block3, jmp_block3_block1, &cur.func.layout),
651 Ordering::Less
652 );
653 assert_eq!(
654 dt.rpo_cmp(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout),
655 Ordering::Less
656 );
657 }
658
659 #[test]
660 fn backwards_layout() {
661 let mut func = Function::new();
662 let block0 = func.dfg.make_block();
663 let block1 = func.dfg.make_block();
664 let block2 = func.dfg.make_block();
665
666 let mut cur = FuncCursor::new(&mut func);
667
668 cur.insert_block(block0);
669 let jmp02 = cur.ins().jump(block2, &[]);
670
671 cur.insert_block(block1);
672 let trap = cur.ins().trap(TrapCode::User(5));
673
674 cur.insert_block(block2);
675 let jmp21 = cur.ins().jump(block1, &[]);
676
677 let cfg = ControlFlowGraph::with_function(cur.func);
678 let dt = DominatorTree::with_function(cur.func, &cfg);
679
680 assert_eq!(cur.func.layout.entry_block(), Some(block0));
681 assert_eq!(dt.idom(block0), None);
682 assert_eq!(dt.idom(block1), Some(jmp21));
683 assert_eq!(dt.idom(block2), Some(jmp02));
684
685 assert!(dt.dominates(block0, block0, &cur.func.layout));
686 assert!(dt.dominates(block0, jmp02, &cur.func.layout));
687 assert!(dt.dominates(block0, block1, &cur.func.layout));
688 assert!(dt.dominates(block0, trap, &cur.func.layout));
689 assert!(dt.dominates(block0, block2, &cur.func.layout));
690 assert!(dt.dominates(block0, jmp21, &cur.func.layout));
691
692 assert!(!dt.dominates(jmp02, block0, &cur.func.layout));
693 assert!(dt.dominates(jmp02, jmp02, &cur.func.layout));
694 assert!(dt.dominates(jmp02, block1, &cur.func.layout));
695 assert!(dt.dominates(jmp02, trap, &cur.func.layout));
696 assert!(dt.dominates(jmp02, block2, &cur.func.layout));
697 assert!(dt.dominates(jmp02, jmp21, &cur.func.layout));
698
699 assert!(!dt.dominates(block1, block0, &cur.func.layout));
700 assert!(!dt.dominates(block1, jmp02, &cur.func.layout));
701 assert!(dt.dominates(block1, block1, &cur.func.layout));
702 assert!(dt.dominates(block1, trap, &cur.func.layout));
703 assert!(!dt.dominates(block1, block2, &cur.func.layout));
704 assert!(!dt.dominates(block1, jmp21, &cur.func.layout));
705
706 assert!(!dt.dominates(trap, block0, &cur.func.layout));
707 assert!(!dt.dominates(trap, jmp02, &cur.func.layout));
708 assert!(!dt.dominates(trap, block1, &cur.func.layout));
709 assert!(dt.dominates(trap, trap, &cur.func.layout));
710 assert!(!dt.dominates(trap, block2, &cur.func.layout));
711 assert!(!dt.dominates(trap, jmp21, &cur.func.layout));
712
713 assert!(!dt.dominates(block2, block0, &cur.func.layout));
714 assert!(!dt.dominates(block2, jmp02, &cur.func.layout));
715 assert!(dt.dominates(block2, block1, &cur.func.layout));
716 assert!(dt.dominates(block2, trap, &cur.func.layout));
717 assert!(dt.dominates(block2, block2, &cur.func.layout));
718 assert!(dt.dominates(block2, jmp21, &cur.func.layout));
719
720 assert!(!dt.dominates(jmp21, block0, &cur.func.layout));
721 assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout));
722 assert!(dt.dominates(jmp21, block1, &cur.func.layout));
723 assert!(dt.dominates(jmp21, trap, &cur.func.layout));
724 assert!(!dt.dominates(jmp21, block2, &cur.func.layout));
725 assert!(dt.dominates(jmp21, jmp21, &cur.func.layout));
726 }
727}