1use crate::entity::SecondaryMap;
4use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
5use crate::ir::{Block, Function, Layout, ProgramPoint};
6use crate::packed_option::PackedOption;
7use crate::timing;
8use alloc::vec::Vec;
9use core::cmp;
10use core::cmp::Ordering;
11use core::mem;
12
13mod simple;
14
15pub use simple::SimpleDominatorTree;
16
17pub(crate) trait DomTreeGraph {
30 fn num_blocks(&self) -> usize;
33
34 fn roots(&self) -> impl Iterator<Item = Block>;
40
41 fn successors(&self, block: Block) -> impl Iterator<Item = Block>;
43
44 fn predecessors(&self, block: Block) -> impl Iterator<Item = Block>;
46}
47
48struct ForwardGraph<'a> {
51 func: &'a Function,
52 cfg: &'a ControlFlowGraph,
53}
54
55impl DomTreeGraph for ForwardGraph<'_> {
56 fn num_blocks(&self) -> usize {
57 self.func.dfg.num_blocks()
58 }
59
60 fn roots(&self) -> impl Iterator<Item = Block> {
61 self.func.layout.entry_block().into_iter()
62 }
63
64 fn successors(&self, block: Block) -> impl Iterator<Item = Block> {
65 self.func.block_successors(block).rev()
73 }
74
75 fn predecessors(&self, block: Block) -> impl Iterator<Item = Block> {
76 self.cfg
77 .pred_iter(block)
78 .map(|pred: BlockPredecessor| pred.block)
79 }
80}
81
82#[derive(Clone, Default)]
84struct SpanningTreeNode {
85 block: PackedOption<Block>,
87 ancestor: u32,
90 label: u32,
94 semi: u32,
96 idom: u32,
99}
100
101const NOT_VISITED: u32 = 0;
103
104#[derive(Clone, Default)]
111struct SpanningTree {
112 nodes: Vec<SpanningTreeNode>,
113}
114
115impl SpanningTree {
116 fn new() -> Self {
117 Self {
119 nodes: vec![Default::default()],
120 }
121 }
122
123 fn with_capacity(capacity: usize) -> Self {
124 let mut nodes = Vec::with_capacity(capacity + 1);
126 nodes.push(Default::default());
127 Self { nodes }
128 }
129
130 fn len(&self) -> usize {
131 self.nodes.len()
132 }
133
134 fn reserve(&mut self, capacity: usize) {
135 self.nodes.reserve(capacity);
137 }
138
139 fn clear(&mut self) {
140 self.nodes.resize(1, Default::default());
141 }
142
143 fn push(&mut self, ancestor: u32, block: Block) -> u32 {
145 debug_assert!(!self.nodes.is_empty());
147
148 let pre_number = self.nodes.len() as u32;
149
150 self.nodes.push(SpanningTreeNode {
151 block: block.into(),
152 ancestor,
153 label: pre_number,
154 semi: pre_number,
155 idom: ancestor,
156 });
157
158 pre_number
159 }
160}
161
162impl core::ops::Index<u32> for SpanningTree {
163 type Output = SpanningTreeNode;
164
165 fn index(&self, idx: u32) -> &Self::Output {
166 &self.nodes[idx as usize]
167 }
168}
169
170impl core::ops::IndexMut<u32> for SpanningTree {
171 fn index_mut(&mut self, idx: u32) -> &mut Self::Output {
172 &mut self.nodes[idx as usize]
173 }
174}
175
176enum TraversalEvent {
180 Enter(u32, Block),
181 Exit(Block),
182}
183
184#[derive(Clone, Default)]
186struct DominatorTreeNode {
187 idom: PackedOption<Block>,
189 pre_number: u32,
191 post_number: u32,
194
195 child: PackedOption<Block>,
197
198 sibling: PackedOption<Block>,
201
202 dom_pre_number: u32,
205
206 dom_pre_max: u32,
209}
210
211pub struct DominatorTree {
214 stree: SpanningTree,
216 postorder: Vec<Block>,
218 nodes: SecondaryMap<Block, DominatorTreeNode>,
220
221 dfs_worklist: Vec<TraversalEvent>,
223 eval_worklist: Vec<u32>,
226
227 valid: bool,
228}
229
230impl core::fmt::Debug for DominatorTree {
231 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
232 if !self.is_valid() {
233 return f.write_str("DominatorTree { <invalid> }");
234 }
235
236 let mut s = f.debug_tuple("DominatorTree");
237
238 if let Some((mut root, _)) = self.nodes.iter().find(|n| n.1.pre_number != NOT_VISITED) {
239 loop {
240 if let Some(b) = self.idom(root)
241 && b != root
242 {
243 root = b;
244 } else {
245 break;
246 }
247 }
248
249 fn fmt_block(domtree: &DominatorTree, block: Block) -> impl core::fmt::Debug {
250 core::fmt::from_fn(move |f| {
251 let children = domtree
252 .children(block)
253 .map(|c| fmt_block(domtree, c))
254 .collect::<Vec<_>>();
255 let mut s = f.debug_tuple(&format!("{block}"));
256 if !children.is_empty() {
257 s.field(&children);
258 }
259 s.finish()
260 })
261 }
262
263 s.field(&fmt_block(self, root));
264 }
265
266 s.finish()
267 }
268}
269
270impl DominatorTree {
272 pub fn is_reachable(&self, block: Block) -> bool {
274 self.nodes[block].pre_number != NOT_VISITED
275 }
276
277 pub fn cfg_postorder(&self) -> &[Block] {
282 debug_assert!(self.is_valid());
283 &self.postorder
284 }
285
286 pub fn cfg_rpo(&self) -> impl Iterator<Item = &Block> {
291 debug_assert!(self.is_valid());
292 self.postorder.iter().rev()
293 }
294
295 pub fn idom(&self, block: Block) -> Option<Block> {
306 self.nodes[block].idom.into()
307 }
308
309 pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool
320 where
321 A: Into<ProgramPoint>,
322 B: Into<ProgramPoint>,
323 {
324 let a = a.into();
325 let b = b.into();
326 match a {
327 ProgramPoint::Block(block_a) => match b {
328 ProgramPoint::Block(block_b) => self.block_dominates(block_a, block_b),
329 ProgramPoint::Inst(inst_b) => {
330 let block_b = layout
331 .inst_block(inst_b)
332 .expect("Instruction not in layout.");
333 self.block_dominates(block_a, block_b)
334 }
335 },
336 ProgramPoint::Inst(inst_a) => {
337 let block_a: Block = layout
338 .inst_block(inst_a)
339 .expect("Instruction not in layout.");
340 match b {
341 ProgramPoint::Block(block_b) => {
342 block_a != block_b && self.block_dominates(block_a, block_b)
343 }
344 ProgramPoint::Inst(inst_b) => {
345 let block_b = layout
346 .inst_block(inst_b)
347 .expect("Instruction not in layout.");
348 if block_a == block_b {
349 layout.pp_cmp(a, b) != Ordering::Greater
350 } else {
351 self.block_dominates(block_a, block_b)
352 }
353 }
354 }
355 }
356 }
357 }
358
359 pub fn block_dominates(&self, block_a: Block, block_b: Block) -> bool {
364 let na = &self.nodes[block_a];
365 let nb = &self.nodes[block_b];
366 na.dom_pre_number <= nb.dom_pre_number && na.dom_pre_max >= nb.dom_pre_max
367 }
368
369 pub fn is_retreating_edge(&self, from: Block, to: Block) -> bool {
380 debug_assert!(self.is_reachable(from));
381 debug_assert!(self.is_reachable(to));
382 self.nodes[to].post_number >= self.nodes[from].post_number
383 }
384
385 pub fn children(&self, block: Block) -> ChildIter<'_> {
390 ChildIter {
391 domtree: self,
392 next: self.nodes[block].child,
393 }
394 }
395}
396
397impl DominatorTree {
398 pub fn new() -> Self {
401 Self {
402 stree: SpanningTree::new(),
403 nodes: SecondaryMap::new(),
404 postorder: Vec::new(),
405 dfs_worklist: Vec::new(),
406 eval_worklist: Vec::new(),
407 valid: false,
408 }
409 }
410
411 pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self {
413 let block_capacity = func.layout.block_capacity();
414 let mut domtree = Self {
415 stree: SpanningTree::with_capacity(block_capacity),
416 nodes: SecondaryMap::with_capacity(block_capacity),
417 postorder: Vec::with_capacity(block_capacity),
418 dfs_worklist: Vec::new(),
419 eval_worklist: Vec::new(),
420 valid: false,
421 };
422 domtree.compute(func, cfg);
423 domtree
424 }
425
426 pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) {
435 let _tt = timing::domtree();
436 debug_assert!(cfg.is_valid());
437 self.compute_from_graph(&ForwardGraph { func, cfg });
438 }
439
440 pub(crate) fn compute_from_graph(&mut self, graph: &impl DomTreeGraph) {
447 self.clear();
448 self.compute_spanning_tree(graph);
449 self.compute_domtree(graph);
450 self.compute_domtree_preorder();
451
452 self.valid = true;
453 }
454
455 pub fn clear(&mut self) {
458 self.stree.clear();
459 self.nodes.clear();
460 self.postorder.clear();
461 self.valid = false;
462 }
463
464 pub fn is_valid(&self) -> bool {
470 self.valid
471 }
472
473 fn compute_spanning_tree(&mut self, graph: &impl DomTreeGraph) {
476 self.nodes.resize(graph.num_blocks());
477 self.stree.reserve(graph.num_blocks());
478
479 for root in graph.roots() {
480 self.dfs_worklist.push(TraversalEvent::Enter(0, root));
481 }
482
483 loop {
484 match self.dfs_worklist.pop() {
485 Some(TraversalEvent::Enter(parent, block)) => {
486 let node = &mut self.nodes[block];
487 if node.pre_number != NOT_VISITED {
488 continue;
489 }
490
491 self.dfs_worklist.push(TraversalEvent::Exit(block));
492
493 let pre_number = self.stree.push(parent, block);
494 node.pre_number = pre_number;
495
496 self.dfs_worklist.extend(
500 graph
501 .successors(block)
502 .filter(|successor| self.nodes[*successor].pre_number == NOT_VISITED)
504 .map(|successor| TraversalEvent::Enter(pre_number, successor)),
505 );
506 }
507 Some(TraversalEvent::Exit(block)) => {
508 self.nodes[block].post_number = u32::try_from(self.postorder.len()).unwrap();
509 self.postorder.push(block);
510 }
511 None => break,
512 }
513 }
514 }
515
516 fn eval(&mut self, v: u32, last_linked: u32) -> u32 {
521 if self.stree[v].ancestor < last_linked {
522 return self.stree[v].label;
523 }
524
525 let mut root = v;
527 loop {
528 self.eval_worklist.push(root);
529 root = self.stree[root].ancestor;
530
531 if self.stree[root].ancestor < last_linked {
532 break;
533 }
534 }
535
536 let mut prev = root;
537 let root = self.stree[prev].ancestor;
538
539 while let Some(curr) = self.eval_worklist.pop() {
542 if self.stree[prev].label < self.stree[curr].label {
543 self.stree[curr].label = self.stree[prev].label;
544 }
545
546 self.stree[curr].ancestor = root;
547 prev = curr;
548 }
549
550 self.stree[v].label
551 }
552
553 fn compute_domtree(&mut self, graph: &impl DomTreeGraph) {
554 for w in (1..self.stree.len() as u32).rev() {
556 let w_node = &mut self.stree[w];
557 let block = w_node.block.expect("Virtual root must have been excluded");
558 let mut semi = w_node.ancestor;
559
560 let last_linked = w + 1;
561
562 for pred in graph.predecessors(block) {
563 if self.nodes[pred].pre_number == NOT_VISITED {
565 continue;
566 }
567
568 let semi_candidate = self.eval(self.nodes[pred].pre_number, last_linked);
569 semi = core::cmp::min(semi, semi_candidate);
570 }
571
572 let w_node = &mut self.stree[w];
573 w_node.label = semi;
574 w_node.semi = semi;
575 }
576
577 for v in 1..self.stree.len() as u32 {
579 let semi = self.stree[v].semi;
580 let block = self.stree[v]
581 .block
582 .expect("Virtual root must have been excluded");
583 let mut idom = self.stree[v].idom;
584
585 while idom > semi {
586 idom = self.stree[idom].idom;
587 }
588
589 self.stree[v].idom = idom;
590
591 self.nodes[block].idom = self.stree[idom].block;
592 }
593 }
594
595 fn compute_domtree_preorder(&mut self) {
599 for &block in &self.postorder {
605 if let Some(idom) = self.idom(block) {
606 let sib = mem::replace(&mut self.nodes[idom].child, block.into());
607 self.nodes[block].sibling = sib;
608 } else {
609 self.dfs_worklist.push(TraversalEvent::Enter(0, block));
613 }
614 }
615
616 let mut n = 0;
624 while let Some(event) = self.dfs_worklist.pop() {
625 if let TraversalEvent::Enter(_, block) = event {
626 n += 1;
627 let node = &mut self.nodes[block];
628 node.dom_pre_number = n;
629 node.dom_pre_max = n;
630 if let Some(sibling) = node.sibling.expand() {
631 self.dfs_worklist.push(TraversalEvent::Enter(0, sibling));
632 }
633 if let Some(child) = node.child.expand() {
634 self.dfs_worklist.push(TraversalEvent::Enter(0, child));
635 }
636 }
637 }
638
639 for &block in &self.postorder {
643 if let Some(idom) = self.idom(block) {
644 let pre_max = cmp::max(self.nodes[block].dom_pre_max, self.nodes[idom].dom_pre_max);
645 self.nodes[idom].dom_pre_max = pre_max;
646 }
647 }
648 }
649}
650
651pub struct ChildIter<'a> {
653 domtree: &'a DominatorTree,
654 next: PackedOption<Block>,
655}
656
657impl<'a> Iterator for ChildIter<'a> {
658 type Item = Block;
659
660 fn next(&mut self) -> Option<Block> {
661 let n = self.next.expand();
662 if let Some(block) = n {
663 self.next = self.domtree.nodes[block].sibling;
664 }
665 n
666 }
667}
668
669#[cfg(test)]
670mod tests {
671 use super::*;
672 use crate::cursor::{Cursor, FuncCursor};
673 use crate::ir::types::*;
674 use crate::ir::{InstBuilder, TrapCode};
675
676 #[test]
677 fn empty() {
678 let func = Function::new();
679 let cfg = ControlFlowGraph::with_function(&func);
680 debug_assert!(cfg.is_valid());
681 let dtree = DominatorTree::with_function(&func, &cfg);
682 assert_eq!(0, dtree.nodes.keys().count());
683 assert_eq!(dtree.cfg_postorder(), &[]);
684 }
685
686 #[test]
687 fn unreachable_node() {
688 let mut func = Function::new();
689 let block0 = func.dfg.make_block();
690 let v0 = func.dfg.append_block_param(block0, I32);
691 let block1 = func.dfg.make_block();
692 let block2 = func.dfg.make_block();
693 let trap_block = func.dfg.make_block();
694
695 let mut cur = FuncCursor::new(&mut func);
696
697 cur.insert_block(block0);
698 cur.ins().brif(v0, block2, &[], trap_block, &[]);
699
700 cur.insert_block(trap_block);
701 cur.ins().trap(TrapCode::unwrap_user(1));
702
703 cur.insert_block(block1);
704 let v1 = cur.ins().iconst(I32, 1);
705 let v2 = cur.ins().iadd(v0, v1);
706 cur.ins().jump(block0, &[v2.into()]);
707
708 cur.insert_block(block2);
709 cur.ins().return_(&[v0]);
710
711 let cfg = ControlFlowGraph::with_function(cur.func);
712 let dt = DominatorTree::with_function(cur.func, &cfg);
713
714 assert_eq!(dt.cfg_postorder(), &[block2, trap_block, block0]);
724
725 let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
726 assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
727 assert!(!dt.dominates(block0, v2_def, &cur.func.layout));
728
729 assert!(dt.block_dominates(block0, block0));
730 assert!(!dt.block_dominates(block0, block1));
731 assert!(dt.block_dominates(block0, block2));
732 assert!(!dt.block_dominates(block1, block0));
733 assert!(dt.block_dominates(block1, block1));
734 assert!(!dt.block_dominates(block1, block2));
735 assert!(!dt.block_dominates(block2, block0));
736 assert!(!dt.block_dominates(block2, block1));
737 assert!(dt.block_dominates(block2, block2));
738 }
739
740 #[test]
741 fn non_zero_entry_block() {
742 let mut func = Function::new();
743 let block0 = func.dfg.make_block();
744 let block1 = func.dfg.make_block();
745 let block2 = func.dfg.make_block();
746 let block3 = func.dfg.make_block();
747 let cond = func.dfg.append_block_param(block3, I32);
748
749 let mut cur = FuncCursor::new(&mut func);
750
751 cur.insert_block(block3);
752 let jmp_block3_block1 = cur.ins().jump(block1, &[]);
753
754 cur.insert_block(block1);
755 let br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]);
756
757 cur.insert_block(block2);
758 cur.ins().jump(block0, &[]);
759
760 cur.insert_block(block0);
761
762 let cfg = ControlFlowGraph::with_function(cur.func);
763 let dt = DominatorTree::with_function(cur.func, &cfg);
764
765 assert_eq!(dt.cfg_postorder(), &[block0, block2, block1, block3]);
784
785 assert_eq!(cur.func.layout.entry_block().unwrap(), block3);
786 assert_eq!(dt.idom(block3), None);
787 assert_eq!(dt.idom(block1).unwrap(), block3);
788 assert_eq!(dt.idom(block2).unwrap(), block1);
789 assert_eq!(dt.idom(block0).unwrap(), block1);
790
791 assert!(dt.dominates(
792 br_block1_block0_block2,
793 br_block1_block0_block2,
794 &cur.func.layout
795 ));
796 assert!(!dt.dominates(br_block1_block0_block2, jmp_block3_block1, &cur.func.layout));
797 assert!(dt.dominates(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout));
798 }
799
800 #[test]
801 fn backwards_layout() {
802 let mut func = Function::new();
803 let block0 = func.dfg.make_block();
804 let block1 = func.dfg.make_block();
805 let block2 = func.dfg.make_block();
806
807 let mut cur = FuncCursor::new(&mut func);
808
809 cur.insert_block(block0);
810 let jmp02 = cur.ins().jump(block2, &[]);
811
812 cur.insert_block(block1);
813 let trap = cur.ins().trap(TrapCode::unwrap_user(5));
814
815 cur.insert_block(block2);
816 let jmp21 = cur.ins().jump(block1, &[]);
817
818 let cfg = ControlFlowGraph::with_function(cur.func);
819 let dt = DominatorTree::with_function(cur.func, &cfg);
820
821 assert_eq!(cur.func.layout.entry_block(), Some(block0));
822 assert_eq!(dt.idom(block0), None);
823 assert_eq!(dt.idom(block1), Some(block2));
824 assert_eq!(dt.idom(block2), Some(block0));
825
826 assert!(dt.dominates(block0, block0, &cur.func.layout));
827 assert!(dt.dominates(block0, jmp02, &cur.func.layout));
828 assert!(dt.dominates(block0, block1, &cur.func.layout));
829 assert!(dt.dominates(block0, trap, &cur.func.layout));
830 assert!(dt.dominates(block0, block2, &cur.func.layout));
831 assert!(dt.dominates(block0, jmp21, &cur.func.layout));
832
833 assert!(!dt.dominates(jmp02, block0, &cur.func.layout));
834 assert!(dt.dominates(jmp02, jmp02, &cur.func.layout));
835 assert!(dt.dominates(jmp02, block1, &cur.func.layout));
836 assert!(dt.dominates(jmp02, trap, &cur.func.layout));
837 assert!(dt.dominates(jmp02, block2, &cur.func.layout));
838 assert!(dt.dominates(jmp02, jmp21, &cur.func.layout));
839
840 assert!(!dt.dominates(block1, block0, &cur.func.layout));
841 assert!(!dt.dominates(block1, jmp02, &cur.func.layout));
842 assert!(dt.dominates(block1, block1, &cur.func.layout));
843 assert!(dt.dominates(block1, trap, &cur.func.layout));
844 assert!(!dt.dominates(block1, block2, &cur.func.layout));
845 assert!(!dt.dominates(block1, jmp21, &cur.func.layout));
846
847 assert!(!dt.dominates(trap, block0, &cur.func.layout));
848 assert!(!dt.dominates(trap, jmp02, &cur.func.layout));
849 assert!(!dt.dominates(trap, block1, &cur.func.layout));
850 assert!(dt.dominates(trap, trap, &cur.func.layout));
851 assert!(!dt.dominates(trap, block2, &cur.func.layout));
852 assert!(!dt.dominates(trap, jmp21, &cur.func.layout));
853
854 assert!(!dt.dominates(block2, block0, &cur.func.layout));
855 assert!(!dt.dominates(block2, jmp02, &cur.func.layout));
856 assert!(dt.dominates(block2, block1, &cur.func.layout));
857 assert!(dt.dominates(block2, trap, &cur.func.layout));
858 assert!(dt.dominates(block2, block2, &cur.func.layout));
859 assert!(dt.dominates(block2, jmp21, &cur.func.layout));
860
861 assert!(!dt.dominates(jmp21, block0, &cur.func.layout));
862 assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout));
863 assert!(dt.dominates(jmp21, block1, &cur.func.layout));
864 assert!(dt.dominates(jmp21, trap, &cur.func.layout));
865 assert!(!dt.dominates(jmp21, block2, &cur.func.layout));
866 assert!(dt.dominates(jmp21, jmp21, &cur.func.layout));
867 }
868
869 #[test]
870 fn retreating_edges() {
871 let mut func = Function::new();
872 let block0 = func.dfg.make_block();
873 let v0 = func.dfg.append_block_param(block0, I32);
874 let block1 = func.dfg.make_block();
875 let block2 = func.dfg.make_block();
876 let block3 = func.dfg.make_block();
877 let block4 = func.dfg.make_block();
878 let block5 = func.dfg.make_block();
879
880 let mut cur = FuncCursor::new(&mut func);
881
882 cur.insert_block(block0);
885 cur.ins().brif(v0, block1, &[], block2, &[]);
886
887 cur.insert_block(block1);
888 cur.ins().brif(v0, block2, &[], block3, &[]);
889
890 cur.insert_block(block2);
891 cur.ins().jump(block1, &[]);
892
893 cur.insert_block(block3);
895 cur.ins().jump(block4, &[]);
896
897 cur.insert_block(block4);
898 cur.ins().brif(v0, block3, &[], block5, &[]);
899
900 cur.insert_block(block5);
902 cur.ins().brif(v0, block5, &[], block1, &[]);
903
904 let cfg = ControlFlowGraph::with_function(cur.func);
905 let dt = DominatorTree::with_function(cur.func, &cfg);
906
907 assert!(!dt.block_dominates(block1, block2));
911 assert!(!dt.block_dominates(block2, block1));
912 assert_ne!(
913 dt.is_retreating_edge(block1, block2),
914 dt.is_retreating_edge(block2, block1),
915 );
916
917 assert!(dt.is_retreating_edge(block4, block3));
918 assert!(dt.is_retreating_edge(block5, block5));
919 assert!(dt.is_retreating_edge(block5, block1));
920
921 assert!(!dt.is_retreating_edge(block0, block1));
922 assert!(!dt.is_retreating_edge(block0, block2));
923 assert!(!dt.is_retreating_edge(block1, block3));
924 assert!(!dt.is_retreating_edge(block3, block4));
925 assert!(!dt.is_retreating_edge(block4, block5));
926 }
927
928 #[test]
929 fn insts_same_block() {
930 let mut func = Function::new();
931 let block0 = func.dfg.make_block();
932
933 let mut cur = FuncCursor::new(&mut func);
934
935 cur.insert_block(block0);
936 let v1 = cur.ins().iconst(I32, 1);
937 let v2 = cur.ins().iadd(v1, v1);
938 let v3 = cur.ins().iadd(v2, v2);
939 cur.ins().return_(&[]);
940
941 let cfg = ControlFlowGraph::with_function(cur.func);
942 let dt = DominatorTree::with_function(cur.func, &cfg);
943
944 let v1_def = cur.func.dfg.value_def(v1).unwrap_inst();
945 let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
946 let v3_def = cur.func.dfg.value_def(v3).unwrap_inst();
947
948 assert!(dt.dominates(v1_def, v2_def, &cur.func.layout));
949 assert!(dt.dominates(v2_def, v3_def, &cur.func.layout));
950 assert!(dt.dominates(v1_def, v3_def, &cur.func.layout));
951
952 assert!(!dt.dominates(v2_def, v1_def, &cur.func.layout));
953 assert!(!dt.dominates(v3_def, v2_def, &cur.func.layout));
954 assert!(!dt.dominates(v3_def, v1_def, &cur.func.layout));
955
956 assert!(dt.dominates(v2_def, v2_def, &cur.func.layout));
957 assert!(dt.dominates(block0, block0, &cur.func.layout));
958
959 assert!(dt.dominates(block0, v1_def, &cur.func.layout));
960 assert!(dt.dominates(block0, v2_def, &cur.func.layout));
961 assert!(dt.dominates(block0, v3_def, &cur.func.layout));
962
963 assert!(!dt.dominates(v1_def, block0, &cur.func.layout));
964 assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
965 assert!(!dt.dominates(v3_def, block0, &cur.func.layout));
966 }
967}