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