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