1 //! A Dominator Tree represented as mappings of Blocks to their immediate dominator.
2 
3 use crate::entity::SecondaryMap;
4 use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
5 use crate::ir::{Block, Function, Layout, ProgramPoint};
6 use crate::packed_option::PackedOption;
7 use crate::timing;
8 use crate::traversals::Dfs;
9 use alloc::vec::Vec;
10 use core::cmp;
11 use core::cmp::Ordering;
12 use core::mem;
13 
14 /// RPO numbers are not first assigned in a contiguous way but as multiples of STRIDE, to leave
15 /// room for modifications of the dominator tree.
16 const STRIDE: u32 = 4;
17 
18 /// Dominator tree node. We keep one of these per block.
19 #[derive(Clone, Default)]
20 struct DomNode {
21     /// Number of this node in a reverse post-order traversal of the CFG, starting from 1.
22     /// This number is monotonic in the reverse postorder but not contiguous, since we leave
23     /// holes for later localized modifications of the dominator tree.
24     /// Unreachable nodes get number 0, all others are positive.
25     rpo_number: u32,
26 
27     /// The immediate dominator of this block.
28     ///
29     /// This is `None` for unreachable blocks and the entry block which doesn't have an immediate
30     /// dominator.
31     idom: PackedOption<Block>,
32 }
33 
34 /// The dominator tree for a single function.
35 pub struct DominatorTree {
36     nodes: SecondaryMap<Block, DomNode>,
37 
38     /// CFG post-order of all reachable blocks.
39     postorder: Vec<Block>,
40 
41     /// Scratch traversal state used by `compute_postorder()`.
42     dfs: Dfs,
43 
44     valid: bool,
45 }
46 
47 /// Methods for querying the dominator tree.
48 impl DominatorTree {
49     /// Is `block` reachable from the entry block?
50     pub fn is_reachable(&self, block: Block) -> bool {
51         self.nodes[block].rpo_number != 0
52     }
53 
54     /// Get the CFG post-order of blocks that was used to compute the dominator tree.
55     ///
56     /// Note that this post-order is not updated automatically when the CFG is modified. It is
57     /// computed from scratch and cached by `compute()`.
58     pub fn cfg_postorder(&self) -> &[Block] {
59         debug_assert!(self.is_valid());
60         &self.postorder
61     }
62 
63     /// Returns the immediate dominator of `block`.
64     ///
65     /// `block_a` is said to *dominate* `block_b` if all control flow paths from the function
66     /// entry to `block_b` must go through `block_a`.
67     ///
68     /// The *immediate dominator* is the dominator that is closest to `block`. All other dominators
69     /// also dominate the immediate dominator.
70     ///
71     /// This returns `None` if `block` is not reachable from the entry block, or if it is the entry block
72     /// which has no dominators.
73     pub fn idom(&self, block: Block) -> Option<Block> {
74         self.nodes[block].idom.into()
75     }
76 
77     /// Compare two blocks relative to the reverse post-order.
78     pub fn rpo_cmp_block(&self, a: Block, b: Block) -> Ordering {
79         self.nodes[a].rpo_number.cmp(&self.nodes[b].rpo_number)
80     }
81 
82     /// Compare two program points relative to a reverse post-order traversal of the control-flow
83     /// graph.
84     ///
85     /// Return `Ordering::Less` if `a` comes before `b` in the RPO.
86     ///
87     /// If `a` and `b` belong to the same block, compare their relative position in the block.
88     pub fn rpo_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering
89     where
90         A: Into<ProgramPoint>,
91         B: Into<ProgramPoint>,
92     {
93         let a = a.into();
94         let b = b.into();
95         self.rpo_cmp_block(layout.pp_block(a), layout.pp_block(b))
96             .then_with(|| layout.pp_cmp(a, b))
97     }
98 
99     /// Returns `true` if `a` dominates `b`.
100     ///
101     /// This means that every control-flow path from the function entry to `b` must go through `a`.
102     ///
103     /// Dominance is ill defined for unreachable blocks. This function can always determine
104     /// dominance for instructions in the same block, but otherwise returns `false` if either block
105     /// is unreachable.
106     ///
107     /// An instruction is considered to dominate itself.
108     /// A block is also considered to dominate itself.
109     pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool
110     where
111         A: Into<ProgramPoint>,
112         B: Into<ProgramPoint>,
113     {
114         let a = a.into();
115         let b = b.into();
116         match a {
117             ProgramPoint::Block(block_a) => match b {
118                 ProgramPoint::Block(block_b) => self.block_dominates(block_a, block_b),
119                 ProgramPoint::Inst(inst_b) => {
120                     let block_b = layout
121                         .inst_block(inst_b)
122                         .expect("Instruction not in layout.");
123                     self.block_dominates(block_a, block_b)
124                 }
125             },
126             ProgramPoint::Inst(inst_a) => {
127                 let block_a: Block = layout
128                     .inst_block(inst_a)
129                     .expect("Instruction not in layout.");
130                 match b {
131                     ProgramPoint::Block(block_b) => {
132                         block_a != block_b && self.block_dominates(block_a, block_b)
133                     }
134                     ProgramPoint::Inst(inst_b) => {
135                         let block_b = layout
136                             .inst_block(inst_b)
137                             .expect("Instruction not in layout.");
138                         if block_a == block_b {
139                             layout.pp_cmp(a, b) != Ordering::Greater
140                         } else {
141                             self.block_dominates(block_a, block_b)
142                         }
143                     }
144                 }
145             }
146         }
147     }
148 
149     /// Returns `true` if `block_a` dominates `block_b`.
150     ///
151     /// A block is considered to dominate itself.
152     fn block_dominates(&self, block_a: Block, mut block_b: Block) -> bool {
153         let rpo_a = self.nodes[block_a].rpo_number;
154 
155         // Run a finger up the dominator tree from b until we see a.
156         // Do nothing if b is unreachable.
157         while rpo_a < self.nodes[block_b].rpo_number {
158             let idom = match self.idom(block_b) {
159                 Some(idom) => idom,
160                 None => return false, // a is unreachable, so we climbed past the entry
161             };
162             block_b = idom;
163         }
164 
165         block_a == block_b
166     }
167 
168     /// Compute the common dominator of two basic blocks.
169     ///
170     /// Both basic blocks are assumed to be reachable.
171     fn common_dominator(&self, mut a: Block, mut b: Block) -> Block {
172         loop {
173             match self.rpo_cmp_block(a, b) {
174                 Ordering::Less => {
175                     // `a` comes before `b` in the RPO. Move `b` up.
176                     let idom = self.nodes[b].idom.expect("Unreachable basic block?");
177                     b = idom;
178                 }
179                 Ordering::Greater => {
180                     // `b` comes before `a` in the RPO. Move `a` up.
181                     let idom = self.nodes[a].idom.expect("Unreachable basic block?");
182                     a = idom;
183                 }
184                 Ordering::Equal => break,
185             }
186         }
187 
188         debug_assert_eq!(a, b, "Unreachable block passed to common_dominator?");
189 
190         a
191     }
192 }
193 
194 impl DominatorTree {
195     /// Allocate a new blank dominator tree. Use `compute` to compute the dominator tree for a
196     /// function.
197     pub fn new() -> Self {
198         Self {
199             nodes: SecondaryMap::new(),
200             postorder: Vec::new(),
201             dfs: Dfs::new(),
202             valid: false,
203         }
204     }
205 
206     /// Allocate and compute a dominator tree.
207     pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self {
208         let block_capacity = func.layout.block_capacity();
209         let mut domtree = Self {
210             nodes: SecondaryMap::with_capacity(block_capacity),
211             postorder: Vec::with_capacity(block_capacity),
212             dfs: Dfs::new(),
213             valid: false,
214         };
215         domtree.compute(func, cfg);
216         domtree
217     }
218 
219     /// Reset and compute a CFG post-order and dominator tree.
220     pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) {
221         let _tt = timing::domtree();
222         debug_assert!(cfg.is_valid());
223         self.compute_postorder(func);
224         self.compute_domtree(func, cfg);
225         self.valid = true;
226     }
227 
228     /// Clear the data structures used to represent the dominator tree. This will leave the tree in
229     /// a state where `is_valid()` returns false.
230     pub fn clear(&mut self) {
231         self.nodes.clear();
232         self.postorder.clear();
233         self.valid = false;
234     }
235 
236     /// Check if the dominator tree is in a valid state.
237     ///
238     /// Note that this doesn't perform any kind of validity checks. It simply checks if the
239     /// `compute()` method has been called since the last `clear()`. It does not check that the
240     /// dominator tree is consistent with the CFG.
241     pub fn is_valid(&self) -> bool {
242         self.valid
243     }
244 
245     /// Reset all internal data structures and compute a post-order of the control flow graph.
246     ///
247     /// This leaves `rpo_number == 1` for all reachable blocks, 0 for unreachable ones.
248     fn compute_postorder(&mut self, func: &Function) {
249         self.clear();
250         self.nodes.resize(func.dfg.num_blocks());
251         self.postorder.extend(self.dfs.post_order_iter(func));
252     }
253 
254     /// Build a dominator tree from a control flow graph using Keith D. Cooper's
255     /// "Simple, Fast Dominator Algorithm."
256     fn compute_domtree(&mut self, func: &Function, cfg: &ControlFlowGraph) {
257         // During this algorithm, `rpo_number` has the following values:
258         //
259         // 0: block is not reachable.
260         // 1: block is reachable, but has not yet been visited during the first pass. This is set by
261         // `compute_postorder`.
262         // 2+: block is reachable and has an assigned RPO number.
263 
264         // We'll be iterating over a reverse post-order of the CFG, skipping the entry block.
265         let (entry_block, postorder) = match self.postorder.as_slice().split_last() {
266             Some((&eb, rest)) => (eb, rest),
267             None => return,
268         };
269         debug_assert_eq!(Some(entry_block), func.layout.entry_block());
270 
271         // Do a first pass where we assign RPO numbers to all reachable nodes.
272         self.nodes[entry_block].rpo_number = 2 * STRIDE;
273         for (rpo_idx, &block) in postorder.iter().rev().enumerate() {
274             // Update the current node and give it an RPO number.
275             // The entry block got 2, the rest start at 3 by multiples of STRIDE to leave
276             // room for future dominator tree modifications.
277             //
278             // Since `compute_idom` will only look at nodes with an assigned RPO number, the
279             // function will never see an uninitialized predecessor.
280             //
281             // Due to the nature of the post-order traversal, every node we visit will have at
282             // least one predecessor that has previously been visited during this RPO.
283             self.nodes[block] = DomNode {
284                 idom: self.compute_idom(block, cfg).into(),
285                 rpo_number: (rpo_idx as u32 + 3) * STRIDE,
286             }
287         }
288 
289         // Now that we have RPO numbers for everything and initial immediate dominator estimates,
290         // iterate until convergence.
291         //
292         // If the function is free of irreducible control flow, this will exit after one iteration.
293         let mut changed = true;
294         while changed {
295             changed = false;
296             for &block in postorder.iter().rev() {
297                 let idom = self.compute_idom(block, cfg).into();
298                 if self.nodes[block].idom != idom {
299                     self.nodes[block].idom = idom;
300                     changed = true;
301                 }
302             }
303         }
304     }
305 
306     // Compute the immediate dominator for `block` using the current `idom` states for the reachable
307     // nodes.
308     fn compute_idom(&self, block: Block, cfg: &ControlFlowGraph) -> Block {
309         // Get an iterator with just the reachable, already visited predecessors to `block`.
310         // Note that during the first pass, `rpo_number` is 1 for reachable blocks that haven't
311         // been visited yet, 0 for unreachable blocks.
312         let mut reachable_preds = cfg
313             .pred_iter(block)
314             .filter(|&BlockPredecessor { block: pred, .. }| self.nodes[pred].rpo_number > 1)
315             .map(|pred| pred.block);
316 
317         // The RPO must visit at least one predecessor before this node.
318         let mut idom = reachable_preds
319             .next()
320             .expect("block node must have one reachable predecessor");
321 
322         for pred in reachable_preds {
323             idom = self.common_dominator(idom, pred);
324         }
325 
326         idom
327     }
328 }
329 
330 /// Optional pre-order information that can be computed for a dominator tree.
331 ///
332 /// This data structure is computed from a `DominatorTree` and provides:
333 ///
334 /// - A forward traversable dominator tree through the `children()` iterator.
335 /// - An ordering of blocks according to a dominator tree pre-order.
336 /// - Constant time dominance checks at the block granularity.
337 ///
338 /// The information in this auxiliary data structure is not easy to update when the control flow
339 /// graph changes, which is why it is kept separate.
340 pub struct DominatorTreePreorder {
341     nodes: SecondaryMap<Block, ExtraNode>,
342 
343     // Scratch memory used by `compute_postorder()`.
344     stack: Vec<Block>,
345 }
346 
347 #[derive(Default, Clone)]
348 struct ExtraNode {
349     /// First child node in the domtree.
350     child: PackedOption<Block>,
351 
352     /// Next sibling node in the domtree. This linked list is ordered according to the CFG RPO.
353     sibling: PackedOption<Block>,
354 
355     /// Sequence number for this node in a pre-order traversal of the dominator tree.
356     /// Unreachable blocks have number 0, the entry block is 1.
357     pre_number: u32,
358 
359     /// Maximum `pre_number` for the sub-tree of the dominator tree that is rooted at this node.
360     /// This is always >= `pre_number`.
361     pre_max: u32,
362 }
363 
364 /// Creating and computing the dominator tree pre-order.
365 impl DominatorTreePreorder {
366     /// Create a new blank `DominatorTreePreorder`.
367     pub fn new() -> Self {
368         Self {
369             nodes: SecondaryMap::new(),
370             stack: Vec::new(),
371         }
372     }
373 
374     /// Recompute this data structure to match `domtree`.
375     pub fn compute(&mut self, domtree: &DominatorTree) {
376         self.nodes.clear();
377 
378         // Step 1: Populate the child and sibling links.
379         //
380         // By following the CFG post-order and pushing to the front of the lists, we make sure that
381         // sibling lists are ordered according to the CFG reverse post-order.
382         for &block in domtree.cfg_postorder() {
383             if let Some(idom) = domtree.idom(block) {
384                 let sib = mem::replace(&mut self.nodes[idom].child, block.into());
385                 self.nodes[block].sibling = sib;
386             } else {
387                 // The only block without an immediate dominator is the entry.
388                 self.stack.push(block);
389             }
390         }
391 
392         // Step 2. Assign pre-order numbers from a DFS of the dominator tree.
393         debug_assert!(self.stack.len() <= 1);
394         let mut n = 0;
395         while let Some(block) = self.stack.pop() {
396             n += 1;
397             let node = &mut self.nodes[block];
398             node.pre_number = n;
399             node.pre_max = n;
400             if let Some(n) = node.sibling.expand() {
401                 self.stack.push(n);
402             }
403             if let Some(n) = node.child.expand() {
404                 self.stack.push(n);
405             }
406         }
407 
408         // Step 3. Propagate the `pre_max` numbers up the tree.
409         // The CFG post-order is topologically ordered w.r.t. dominance so a node comes after all
410         // its dominator tree children.
411         for &block in domtree.cfg_postorder() {
412             if let Some(idom) = domtree.idom(block) {
413                 let pre_max = cmp::max(self.nodes[block].pre_max, self.nodes[idom].pre_max);
414                 self.nodes[idom].pre_max = pre_max;
415             }
416         }
417     }
418 }
419 
420 /// An iterator that enumerates the direct children of a block in the dominator tree.
421 pub struct ChildIter<'a> {
422     dtpo: &'a DominatorTreePreorder,
423     next: PackedOption<Block>,
424 }
425 
426 impl<'a> Iterator for ChildIter<'a> {
427     type Item = Block;
428 
429     fn next(&mut self) -> Option<Block> {
430         let n = self.next.expand();
431         if let Some(block) = n {
432             self.next = self.dtpo.nodes[block].sibling;
433         }
434         n
435     }
436 }
437 
438 /// Query interface for the dominator tree pre-order.
439 impl DominatorTreePreorder {
440     /// Get an iterator over the direct children of `block` in the dominator tree.
441     ///
442     /// These are the block's whose immediate dominator is an instruction in `block`, ordered according
443     /// to the CFG reverse post-order.
444     pub fn children(&self, block: Block) -> ChildIter {
445         ChildIter {
446             dtpo: self,
447             next: self.nodes[block].child,
448         }
449     }
450 
451     /// Fast, constant time dominance check with block granularity.
452     ///
453     /// This computes the same result as `domtree.dominates(a, b)`, but in guaranteed fast constant
454     /// time. This is less general than the `DominatorTree` method because it only works with block
455     /// program points.
456     ///
457     /// A block is considered to dominate itself.
458     pub fn dominates(&self, a: Block, b: Block) -> bool {
459         let na = &self.nodes[a];
460         let nb = &self.nodes[b];
461         na.pre_number <= nb.pre_number && na.pre_max >= nb.pre_max
462     }
463 
464     /// Compare two blocks according to the dominator pre-order.
465     pub fn pre_cmp_block(&self, a: Block, b: Block) -> Ordering {
466         self.nodes[a].pre_number.cmp(&self.nodes[b].pre_number)
467     }
468 
469     /// Compare two program points according to the dominator tree pre-order.
470     ///
471     /// This ordering of program points have the property that given a program point, pp, all the
472     /// program points dominated by pp follow immediately and contiguously after pp in the order.
473     pub fn pre_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering
474     where
475         A: Into<ProgramPoint>,
476         B: Into<ProgramPoint>,
477     {
478         let a = a.into();
479         let b = b.into();
480         self.pre_cmp_block(layout.pp_block(a), layout.pp_block(b))
481             .then_with(|| layout.pp_cmp(a, b))
482     }
483 }
484 
485 #[cfg(test)]
486 mod tests {
487     use super::*;
488     use crate::cursor::{Cursor, FuncCursor};
489     use crate::ir::types::*;
490     use crate::ir::{InstBuilder, TrapCode};
491 
492     #[test]
493     fn empty() {
494         let func = Function::new();
495         let cfg = ControlFlowGraph::with_function(&func);
496         debug_assert!(cfg.is_valid());
497         let dtree = DominatorTree::with_function(&func, &cfg);
498         assert_eq!(0, dtree.nodes.keys().count());
499         assert_eq!(dtree.cfg_postorder(), &[]);
500 
501         let mut dtpo = DominatorTreePreorder::new();
502         dtpo.compute(&dtree);
503     }
504 
505     #[test]
506     fn unreachable_node() {
507         let mut func = Function::new();
508         let block0 = func.dfg.make_block();
509         let v0 = func.dfg.append_block_param(block0, I32);
510         let block1 = func.dfg.make_block();
511         let block2 = func.dfg.make_block();
512         let trap_block = func.dfg.make_block();
513 
514         let mut cur = FuncCursor::new(&mut func);
515 
516         cur.insert_block(block0);
517         cur.ins().brif(v0, block2, &[], trap_block, &[]);
518 
519         cur.insert_block(trap_block);
520         cur.ins().trap(TrapCode::unwrap_user(1));
521 
522         cur.insert_block(block1);
523         let v1 = cur.ins().iconst(I32, 1);
524         let v2 = cur.ins().iadd(v0, v1);
525         cur.ins().jump(block0, &[v2]);
526 
527         cur.insert_block(block2);
528         cur.ins().return_(&[v0]);
529 
530         let cfg = ControlFlowGraph::with_function(cur.func);
531         let dt = DominatorTree::with_function(cur.func, &cfg);
532 
533         // Fall-through-first, prune-at-source DFT:
534         //
535         // block0 {
536         //   brif block2 {
537         //     trap
538         //     block2 {
539         //       return
540         //     } block2
541         // } block0
542         assert_eq!(dt.cfg_postorder(), &[block2, trap_block, block0]);
543 
544         let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
545         assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
546         assert!(!dt.dominates(block0, v2_def, &cur.func.layout));
547 
548         let mut dtpo = DominatorTreePreorder::new();
549         dtpo.compute(&dt);
550         assert!(dtpo.dominates(block0, block0));
551         assert!(!dtpo.dominates(block0, block1));
552         assert!(dtpo.dominates(block0, block2));
553         assert!(!dtpo.dominates(block1, block0));
554         assert!(dtpo.dominates(block1, block1));
555         assert!(!dtpo.dominates(block1, block2));
556         assert!(!dtpo.dominates(block2, block0));
557         assert!(!dtpo.dominates(block2, block1));
558         assert!(dtpo.dominates(block2, block2));
559     }
560 
561     #[test]
562     fn non_zero_entry_block() {
563         let mut func = Function::new();
564         let block0 = func.dfg.make_block();
565         let block1 = func.dfg.make_block();
566         let block2 = func.dfg.make_block();
567         let block3 = func.dfg.make_block();
568         let cond = func.dfg.append_block_param(block3, I32);
569 
570         let mut cur = FuncCursor::new(&mut func);
571 
572         cur.insert_block(block3);
573         let jmp_block3_block1 = cur.ins().jump(block1, &[]);
574 
575         cur.insert_block(block1);
576         let br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]);
577 
578         cur.insert_block(block2);
579         cur.ins().jump(block0, &[]);
580 
581         cur.insert_block(block0);
582 
583         let cfg = ControlFlowGraph::with_function(cur.func);
584         let dt = DominatorTree::with_function(cur.func, &cfg);
585 
586         // Fall-through-first, prune-at-source DFT:
587         //
588         // block3 {
589         //   block3:jump block1 {
590         //     block1 {
591         //       block1:brif block0 {
592         //         block1:jump block2 {
593         //           block2 {
594         //             block2:jump block0 (seen)
595         //           } block2
596         //         } block1:jump block2
597         //         block0 {
598         //         } block0
599         //       } block1:brif block0
600         //     } block1
601         //   } block3:jump block1
602         // } block3
603 
604         assert_eq!(dt.cfg_postorder(), &[block0, block2, block1, block3]);
605 
606         assert_eq!(cur.func.layout.entry_block().unwrap(), block3);
607         assert_eq!(dt.idom(block3), None);
608         assert_eq!(dt.idom(block1).unwrap(), block3);
609         assert_eq!(dt.idom(block2).unwrap(), block1);
610         assert_eq!(dt.idom(block0).unwrap(), block1);
611 
612         assert!(dt.dominates(
613             br_block1_block0_block2,
614             br_block1_block0_block2,
615             &cur.func.layout
616         ));
617         assert!(!dt.dominates(br_block1_block0_block2, jmp_block3_block1, &cur.func.layout));
618         assert!(dt.dominates(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout));
619 
620         assert_eq!(
621             dt.rpo_cmp(block3, block3, &cur.func.layout),
622             Ordering::Equal
623         );
624         assert_eq!(dt.rpo_cmp(block3, block1, &cur.func.layout), Ordering::Less);
625         assert_eq!(
626             dt.rpo_cmp(block3, jmp_block3_block1, &cur.func.layout),
627             Ordering::Less
628         );
629         assert_eq!(
630             dt.rpo_cmp(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout),
631             Ordering::Less
632         );
633     }
634 
635     #[test]
636     fn backwards_layout() {
637         let mut func = Function::new();
638         let block0 = func.dfg.make_block();
639         let block1 = func.dfg.make_block();
640         let block2 = func.dfg.make_block();
641 
642         let mut cur = FuncCursor::new(&mut func);
643 
644         cur.insert_block(block0);
645         let jmp02 = cur.ins().jump(block2, &[]);
646 
647         cur.insert_block(block1);
648         let trap = cur.ins().trap(TrapCode::unwrap_user(5));
649 
650         cur.insert_block(block2);
651         let jmp21 = cur.ins().jump(block1, &[]);
652 
653         let cfg = ControlFlowGraph::with_function(cur.func);
654         let dt = DominatorTree::with_function(cur.func, &cfg);
655 
656         assert_eq!(cur.func.layout.entry_block(), Some(block0));
657         assert_eq!(dt.idom(block0), None);
658         assert_eq!(dt.idom(block1), Some(block2));
659         assert_eq!(dt.idom(block2), Some(block0));
660 
661         assert!(dt.dominates(block0, block0, &cur.func.layout));
662         assert!(dt.dominates(block0, jmp02, &cur.func.layout));
663         assert!(dt.dominates(block0, block1, &cur.func.layout));
664         assert!(dt.dominates(block0, trap, &cur.func.layout));
665         assert!(dt.dominates(block0, block2, &cur.func.layout));
666         assert!(dt.dominates(block0, jmp21, &cur.func.layout));
667 
668         assert!(!dt.dominates(jmp02, block0, &cur.func.layout));
669         assert!(dt.dominates(jmp02, jmp02, &cur.func.layout));
670         assert!(dt.dominates(jmp02, block1, &cur.func.layout));
671         assert!(dt.dominates(jmp02, trap, &cur.func.layout));
672         assert!(dt.dominates(jmp02, block2, &cur.func.layout));
673         assert!(dt.dominates(jmp02, jmp21, &cur.func.layout));
674 
675         assert!(!dt.dominates(block1, block0, &cur.func.layout));
676         assert!(!dt.dominates(block1, jmp02, &cur.func.layout));
677         assert!(dt.dominates(block1, block1, &cur.func.layout));
678         assert!(dt.dominates(block1, trap, &cur.func.layout));
679         assert!(!dt.dominates(block1, block2, &cur.func.layout));
680         assert!(!dt.dominates(block1, jmp21, &cur.func.layout));
681 
682         assert!(!dt.dominates(trap, block0, &cur.func.layout));
683         assert!(!dt.dominates(trap, jmp02, &cur.func.layout));
684         assert!(!dt.dominates(trap, block1, &cur.func.layout));
685         assert!(dt.dominates(trap, trap, &cur.func.layout));
686         assert!(!dt.dominates(trap, block2, &cur.func.layout));
687         assert!(!dt.dominates(trap, jmp21, &cur.func.layout));
688 
689         assert!(!dt.dominates(block2, block0, &cur.func.layout));
690         assert!(!dt.dominates(block2, jmp02, &cur.func.layout));
691         assert!(dt.dominates(block2, block1, &cur.func.layout));
692         assert!(dt.dominates(block2, trap, &cur.func.layout));
693         assert!(dt.dominates(block2, block2, &cur.func.layout));
694         assert!(dt.dominates(block2, jmp21, &cur.func.layout));
695 
696         assert!(!dt.dominates(jmp21, block0, &cur.func.layout));
697         assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout));
698         assert!(dt.dominates(jmp21, block1, &cur.func.layout));
699         assert!(dt.dominates(jmp21, trap, &cur.func.layout));
700         assert!(!dt.dominates(jmp21, block2, &cur.func.layout));
701         assert!(dt.dominates(jmp21, jmp21, &cur.func.layout));
702     }
703 
704     #[test]
705     fn insts_same_block() {
706         let mut func = Function::new();
707         let block0 = func.dfg.make_block();
708 
709         let mut cur = FuncCursor::new(&mut func);
710 
711         cur.insert_block(block0);
712         let v1 = cur.ins().iconst(I32, 1);
713         let v2 = cur.ins().iadd(v1, v1);
714         let v3 = cur.ins().iadd(v2, v2);
715         cur.ins().return_(&[]);
716 
717         let cfg = ControlFlowGraph::with_function(cur.func);
718         let dt = DominatorTree::with_function(cur.func, &cfg);
719 
720         let v1_def = cur.func.dfg.value_def(v1).unwrap_inst();
721         let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
722         let v3_def = cur.func.dfg.value_def(v3).unwrap_inst();
723 
724         assert!(dt.dominates(v1_def, v2_def, &cur.func.layout));
725         assert!(dt.dominates(v2_def, v3_def, &cur.func.layout));
726         assert!(dt.dominates(v1_def, v3_def, &cur.func.layout));
727 
728         assert!(!dt.dominates(v2_def, v1_def, &cur.func.layout));
729         assert!(!dt.dominates(v3_def, v2_def, &cur.func.layout));
730         assert!(!dt.dominates(v3_def, v1_def, &cur.func.layout));
731 
732         assert!(dt.dominates(v2_def, v2_def, &cur.func.layout));
733         assert!(dt.dominates(block0, block0, &cur.func.layout));
734 
735         assert!(dt.dominates(block0, v1_def, &cur.func.layout));
736         assert!(dt.dominates(block0, v2_def, &cur.func.layout));
737         assert!(dt.dominates(block0, v3_def, &cur.func.layout));
738 
739         assert!(!dt.dominates(v1_def, block0, &cur.func.layout));
740         assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
741         assert!(!dt.dominates(v3_def, block0, &cur.func.layout));
742     }
743 }
744