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