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 alloc::vec::Vec;
9 use core::cmp;
10 use core::cmp::Ordering;
11 use core::mem;
12 
13 mod simple;
14 
15 pub use simple::SimpleDominatorTree;
16 
17 /// Spanning tree node, used during domtree computation.
18 #[derive(Clone, Default)]
19 struct SpanningTreeNode {
20     /// This node's block in function CFG.
21     block: PackedOption<Block>,
22     /// Node's ancestor in the spanning tree.
23     /// Gets invalidated during semi-dominator computation.
24     ancestor: u32,
25     /// The smallest semi value discovered on any semi-dominator path
26     /// that went through the node up till the moment.
27     /// Gets updated in the course of semi-dominator computation.
28     label: u32,
29     /// Semidominator value for the node.
30     semi: u32,
31     /// Immediate dominator value for the node.
32     /// Initialized to node's ancestor in the spanning tree.
33     idom: u32,
34 }
35 
36 /// DFS preorder number for unvisited nodes and the virtual root in the spanning tree.
37 const NOT_VISITED: u32 = 0;
38 
39 /// Spanning tree, in CFG preorder.
40 /// Node 0 is the virtual root and doesn't have a corresponding block.
41 /// It's not required because function's CFG in Cranelift always have
42 /// a singular root, but helps to avoid additional checks.
43 /// Numbering nodes from 0 also follows the convention in
44 /// `SimpleDominatorTree` and `DominatorTreePreorder`.
45 #[derive(Clone, Default)]
46 struct SpanningTree {
47     nodes: Vec<SpanningTreeNode>,
48 }
49 
50 impl SpanningTree {
51     fn new() -> Self {
52         // Include the virtual root.
53         Self {
54             nodes: vec![Default::default()],
55         }
56     }
57 
58     fn with_capacity(capacity: usize) -> Self {
59         // Include the virtual root.
60         let mut nodes = Vec::with_capacity(capacity + 1);
61         nodes.push(Default::default());
62         Self { nodes }
63     }
64 
65     fn len(&self) -> usize {
66         self.nodes.len()
67     }
68 
69     fn reserve(&mut self, capacity: usize) {
70         // Virtual root should be already included.
71         self.nodes.reserve(capacity);
72     }
73 
74     fn clear(&mut self) {
75         self.nodes.resize(1, Default::default());
76     }
77 
78     /// Returns pre_number for the new node.
79     fn push(&mut self, ancestor: u32, block: Block) -> u32 {
80         // Virtual root should be already included.
81         debug_assert!(!self.nodes.is_empty());
82 
83         let pre_number = self.nodes.len() as u32;
84 
85         self.nodes.push(SpanningTreeNode {
86             block: block.into(),
87             ancestor: ancestor,
88             label: pre_number,
89             semi: pre_number,
90             idom: ancestor,
91         });
92 
93         pre_number
94     }
95 }
96 
97 impl std::ops::Index<u32> for SpanningTree {
98     type Output = SpanningTreeNode;
99 
100     fn index(&self, idx: u32) -> &Self::Output {
101         &self.nodes[idx as usize]
102     }
103 }
104 
105 impl std::ops::IndexMut<u32> for SpanningTree {
106     fn index_mut(&mut self, idx: u32) -> &mut Self::Output {
107         &mut self.nodes[idx as usize]
108     }
109 }
110 
111 /// Traversal event to compute both preorder spanning tree
112 /// and postorder block list. Can't use `Dfs` from traversals.rs
113 /// here because of the need for parent links.
114 enum TraversalEvent {
115     Enter(u32, Block),
116     Exit(Block),
117 }
118 
119 /// Dominator tree node. We keep one of these per block.
120 #[derive(Clone, Default)]
121 struct DominatorTreeNode {
122     /// Immediate dominator for the block, `None` for unreachable blocks.
123     idom: PackedOption<Block>,
124     /// Preorder traversal number, zero for unreachable blocks.
125     pre_number: u32,
126 }
127 
128 /// The dominator tree for a single function,
129 /// computed using Semi-NCA algorithm.
130 pub struct DominatorTree {
131     /// DFS spanning tree.
132     stree: SpanningTree,
133     /// List of CFG blocks in postorder.
134     postorder: Vec<Block>,
135     /// Dominator tree nodes.
136     nodes: SecondaryMap<Block, DominatorTreeNode>,
137 
138     /// Stack for building the spanning tree.
139     dfs_worklist: Vec<TraversalEvent>,
140     /// Stack used for processing semidominator paths
141     /// in link-eval procedure.
142     eval_worklist: Vec<u32>,
143 
144     valid: bool,
145 }
146 
147 /// Methods for querying the dominator tree.
148 impl DominatorTree {
149     /// Is `block` reachable from the entry block?
150     pub fn is_reachable(&self, block: Block) -> bool {
151         self.nodes[block].pre_number != NOT_VISITED
152     }
153 
154     /// Get the CFG post-order of blocks that was used to compute the dominator tree.
155     ///
156     /// Note that this post-order is not updated automatically when the CFG is modified. It is
157     /// computed from scratch and cached by `compute()`.
158     pub fn cfg_postorder(&self) -> &[Block] {
159         debug_assert!(self.is_valid());
160         &self.postorder
161     }
162 
163     /// Get an iterator over CFG reverse post-order of blocks used to compute the dominator tree.
164     ///
165     /// Note that the post-order is not updated automatically when the CFG is modified. It is
166     /// computed from scratch and cached by `compute()`.
167     pub fn cfg_rpo(&self) -> impl Iterator<Item = &Block> {
168         debug_assert!(self.is_valid());
169         self.postorder.iter().rev()
170     }
171 
172     /// Returns the immediate dominator of `block`.
173     ///
174     /// `block_a` is said to *dominate* `block_b` if all control flow paths from the function
175     /// entry to `block_b` must go through `block_a`.
176     ///
177     /// The *immediate dominator* is the dominator that is closest to `block`. All other dominators
178     /// also dominate the immediate dominator.
179     ///
180     /// This returns `None` if `block` is not reachable from the entry block, or if it is the entry block
181     /// which has no dominators.
182     pub fn idom(&self, block: Block) -> Option<Block> {
183         self.nodes[block].idom.into()
184     }
185 
186     /// Returns `true` if `a` dominates `b`.
187     ///
188     /// This means that every control-flow path from the function entry to `b` must go through `a`.
189     ///
190     /// Dominance is ill defined for unreachable blocks. This function can always determine
191     /// dominance for instructions in the same block, but otherwise returns `false` if either block
192     /// is unreachable.
193     ///
194     /// An instruction is considered to dominate itself.
195     /// A block is also considered to dominate itself.
196     pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool
197     where
198         A: Into<ProgramPoint>,
199         B: Into<ProgramPoint>,
200     {
201         let a = a.into();
202         let b = b.into();
203         match a {
204             ProgramPoint::Block(block_a) => match b {
205                 ProgramPoint::Block(block_b) => self.block_dominates(block_a, block_b),
206                 ProgramPoint::Inst(inst_b) => {
207                     let block_b = layout
208                         .inst_block(inst_b)
209                         .expect("Instruction not in layout.");
210                     self.block_dominates(block_a, block_b)
211                 }
212             },
213             ProgramPoint::Inst(inst_a) => {
214                 let block_a: Block = layout
215                     .inst_block(inst_a)
216                     .expect("Instruction not in layout.");
217                 match b {
218                     ProgramPoint::Block(block_b) => {
219                         block_a != block_b && self.block_dominates(block_a, block_b)
220                     }
221                     ProgramPoint::Inst(inst_b) => {
222                         let block_b = layout
223                             .inst_block(inst_b)
224                             .expect("Instruction not in layout.");
225                         if block_a == block_b {
226                             layout.pp_cmp(a, b) != Ordering::Greater
227                         } else {
228                             self.block_dominates(block_a, block_b)
229                         }
230                     }
231                 }
232             }
233         }
234     }
235 
236     /// Returns `true` if `block_a` dominates `block_b`.
237     ///
238     /// A block is considered to dominate itself.
239     fn block_dominates(&self, block_a: Block, mut block_b: Block) -> bool {
240         let pre_a = self.nodes[block_a].pre_number;
241 
242         // Run a finger up the dominator tree from b until we see a.
243         // Do nothing if b is unreachable.
244         while pre_a < self.nodes[block_b].pre_number {
245             let idom = match self.idom(block_b) {
246                 Some(idom) => idom,
247                 None => return false, // a is unreachable, so we climbed past the entry
248             };
249             block_b = idom;
250         }
251 
252         block_a == block_b
253     }
254 }
255 
256 impl DominatorTree {
257     /// Allocate a new blank dominator tree. Use `compute` to compute the dominator tree for a
258     /// function.
259     pub fn new() -> Self {
260         Self {
261             stree: SpanningTree::new(),
262             nodes: SecondaryMap::new(),
263             postorder: Vec::new(),
264             dfs_worklist: Vec::new(),
265             eval_worklist: Vec::new(),
266             valid: false,
267         }
268     }
269 
270     /// Allocate and compute a dominator tree.
271     pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self {
272         let block_capacity = func.layout.block_capacity();
273         let mut domtree = Self {
274             stree: SpanningTree::with_capacity(block_capacity),
275             nodes: SecondaryMap::with_capacity(block_capacity),
276             postorder: Vec::with_capacity(block_capacity),
277             dfs_worklist: Vec::new(),
278             eval_worklist: Vec::new(),
279             valid: false,
280         };
281         domtree.compute(func, cfg);
282         domtree
283     }
284 
285     /// Reset and compute a CFG post-order and dominator tree,
286     /// using Semi-NCA algorithm, described in the paper:
287     ///
288     /// Linear-Time Algorithms for Dominators and Related Problems.
289     /// Loukas Georgiadis, Princeton University, November 2005.
290     ///
291     /// The same algorithm is used by Julia, SpiderMonkey and LLVM,
292     /// the implementation is heavily inspired by them.
293     pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) {
294         let _tt = timing::domtree();
295         debug_assert!(cfg.is_valid());
296 
297         self.clear();
298         self.compute_spanning_tree(func);
299         self.compute_domtree(cfg);
300 
301         self.valid = true;
302     }
303 
304     /// Clear the data structures used to represent the dominator tree. This will leave the tree in
305     /// a state where `is_valid()` returns false.
306     pub fn clear(&mut self) {
307         self.stree.clear();
308         self.nodes.clear();
309         self.postorder.clear();
310         self.valid = false;
311     }
312 
313     /// Check if the dominator tree is in a valid state.
314     ///
315     /// Note that this doesn't perform any kind of validity checks. It simply checks if the
316     /// `compute()` method has been called since the last `clear()`. It does not check that the
317     /// dominator tree is consistent with the CFG.
318     pub fn is_valid(&self) -> bool {
319         self.valid
320     }
321 
322     /// Reset all internal data structures, build spanning tree
323     /// and compute a post-order of the control flow graph.
324     fn compute_spanning_tree(&mut self, func: &Function) {
325         self.nodes.resize(func.dfg.num_blocks());
326         self.stree.reserve(func.dfg.num_blocks());
327 
328         if let Some(block) = func.layout.entry_block() {
329             self.dfs_worklist.push(TraversalEvent::Enter(0, block));
330         }
331 
332         loop {
333             match self.dfs_worklist.pop() {
334                 Some(TraversalEvent::Enter(parent, block)) => {
335                     let node = &mut self.nodes[block];
336                     if node.pre_number != NOT_VISITED {
337                         continue;
338                     }
339 
340                     self.dfs_worklist.push(TraversalEvent::Exit(block));
341 
342                     let pre_number = self.stree.push(parent, block);
343                     node.pre_number = pre_number;
344 
345                     // Use the same traversal heuristics as in traversals.rs.
346                     self.dfs_worklist.extend(
347                         func.block_successors(block)
348                             // Heuristic: chase the children in reverse. This puts
349                             // the first successor block first in the postorder, all
350                             // other things being equal, which tends to prioritize
351                             // loop backedges over out-edges, putting the edge-block
352                             // closer to the loop body and minimizing live-ranges in
353                             // linear instruction space. This heuristic doesn't have
354                             // any effect on the computation of dominators, and is
355                             // purely for other consumers of the postorder we cache
356                             // here.
357                             .rev()
358                             // A simple optimization: push less items to the stack.
359                             .filter(|successor| self.nodes[*successor].pre_number == NOT_VISITED)
360                             .map(|successor| TraversalEvent::Enter(pre_number, successor)),
361                     );
362                 }
363                 Some(TraversalEvent::Exit(block)) => self.postorder.push(block),
364                 None => break,
365             }
366         }
367     }
368 
369     /// Eval-link procedure from the paper.
370     /// For a predecessor V of node W returns V if V < W, otherwise the minimum of sdom(U),
371     /// where U > W and U is on a semi-dominator path for W in CFG.
372     /// Use path compression to bring complexity down to O(m*log(n)).
373     fn eval(&mut self, v: u32, last_linked: u32) -> u32 {
374         if self.stree[v].ancestor < last_linked {
375             return self.stree[v].label;
376         }
377 
378         // Follow semi-dominator path.
379         let mut root = v;
380         loop {
381             self.eval_worklist.push(root);
382             root = self.stree[root].ancestor;
383 
384             if self.stree[root].ancestor < last_linked {
385                 break;
386             }
387         }
388 
389         let mut prev = root;
390         let root = self.stree[prev].ancestor;
391 
392         // Perform path compression. Point all ancestors to the root
393         // and propagate minimal sdom(U) value from ancestors to children.
394         while let Some(curr) = self.eval_worklist.pop() {
395             if self.stree[prev].label < self.stree[curr].label {
396                 self.stree[curr].label = self.stree[prev].label;
397             }
398 
399             self.stree[curr].ancestor = root;
400             prev = curr;
401         }
402 
403         self.stree[v].label
404     }
405 
406     fn compute_domtree(&mut self, cfg: &ControlFlowGraph) {
407         // Compute semi-dominators.
408         for w in (1..self.stree.len() as u32).rev() {
409             let w_node = &mut self.stree[w];
410             let block = w_node.block.expect("Virtual root must have been excluded");
411             let mut semi = w_node.ancestor;
412 
413             let last_linked = w + 1;
414 
415             for pred in cfg
416                 .pred_iter(block)
417                 .map(|pred: BlockPredecessor| pred.block)
418             {
419                 // Skip unreachable nodes.
420                 if self.nodes[pred].pre_number == NOT_VISITED {
421                     continue;
422                 }
423 
424                 let semi_candidate = self.eval(self.nodes[pred].pre_number, last_linked);
425                 semi = std::cmp::min(semi, semi_candidate);
426             }
427 
428             let w_node = &mut self.stree[w];
429             w_node.label = semi;
430             w_node.semi = semi;
431         }
432 
433         // Compute immediate dominators.
434         for v in 1..self.stree.len() as u32 {
435             let semi = self.stree[v].semi;
436             let block = self.stree[v]
437                 .block
438                 .expect("Virtual root must have been excluded");
439             let mut idom = self.stree[v].idom;
440 
441             while idom > semi {
442                 idom = self.stree[idom].idom;
443             }
444 
445             self.stree[v].idom = idom;
446 
447             self.nodes[block].idom = self.stree[idom].block;
448         }
449     }
450 }
451 
452 /// Optional pre-order information that can be computed for a dominator tree.
453 ///
454 /// This data structure is computed from a `DominatorTree` and provides:
455 ///
456 /// - A forward traversable dominator tree through the `children()` iterator.
457 /// - An ordering of blocks according to a dominator tree pre-order.
458 /// - Constant time dominance checks at the block granularity.
459 ///
460 /// The information in this auxiliary data structure is not easy to update when the control flow
461 /// graph changes, which is why it is kept separate.
462 pub struct DominatorTreePreorder {
463     nodes: SecondaryMap<Block, ExtraNode>,
464 
465     // Scratch memory used by `compute_postorder()`.
466     stack: Vec<Block>,
467 }
468 
469 #[derive(Default, Clone)]
470 struct ExtraNode {
471     /// First child node in the domtree.
472     child: PackedOption<Block>,
473 
474     /// Next sibling node in the domtree. This linked list is ordered according to the CFG RPO.
475     sibling: PackedOption<Block>,
476 
477     /// Sequence number for this node in a pre-order traversal of the dominator tree.
478     /// Unreachable blocks have number 0, the entry block is 1.
479     pre_number: u32,
480 
481     /// Maximum `pre_number` for the sub-tree of the dominator tree that is rooted at this node.
482     /// This is always >= `pre_number`.
483     pre_max: u32,
484 }
485 
486 /// Creating and computing the dominator tree pre-order.
487 impl DominatorTreePreorder {
488     /// Create a new blank `DominatorTreePreorder`.
489     pub fn new() -> Self {
490         Self {
491             nodes: SecondaryMap::new(),
492             stack: Vec::new(),
493         }
494     }
495 
496     /// Recompute this data structure to match `domtree`.
497     pub fn compute(&mut self, domtree: &DominatorTree) {
498         self.nodes.clear();
499 
500         // Step 1: Populate the child and sibling links.
501         //
502         // By following the CFG post-order and pushing to the front of the lists, we make sure that
503         // sibling lists are ordered according to the CFG reverse post-order.
504         for &block in domtree.cfg_postorder() {
505             if let Some(idom) = domtree.idom(block) {
506                 let sib = mem::replace(&mut self.nodes[idom].child, block.into());
507                 self.nodes[block].sibling = sib;
508             } else {
509                 // The only block without an immediate dominator is the entry.
510                 self.stack.push(block);
511             }
512         }
513 
514         // Step 2. Assign pre-order numbers from a DFS of the dominator tree.
515         debug_assert!(self.stack.len() <= 1);
516         let mut n = 0;
517         while let Some(block) = self.stack.pop() {
518             n += 1;
519             let node = &mut self.nodes[block];
520             node.pre_number = n;
521             node.pre_max = n;
522             if let Some(n) = node.sibling.expand() {
523                 self.stack.push(n);
524             }
525             if let Some(n) = node.child.expand() {
526                 self.stack.push(n);
527             }
528         }
529 
530         // Step 3. Propagate the `pre_max` numbers up the tree.
531         // The CFG post-order is topologically ordered w.r.t. dominance so a node comes after all
532         // its dominator tree children.
533         for &block in domtree.cfg_postorder() {
534             if let Some(idom) = domtree.idom(block) {
535                 let pre_max = cmp::max(self.nodes[block].pre_max, self.nodes[idom].pre_max);
536                 self.nodes[idom].pre_max = pre_max;
537             }
538         }
539     }
540 }
541 
542 /// An iterator that enumerates the direct children of a block in the dominator tree.
543 pub struct ChildIter<'a> {
544     dtpo: &'a DominatorTreePreorder,
545     next: PackedOption<Block>,
546 }
547 
548 impl<'a> Iterator for ChildIter<'a> {
549     type Item = Block;
550 
551     fn next(&mut self) -> Option<Block> {
552         let n = self.next.expand();
553         if let Some(block) = n {
554             self.next = self.dtpo.nodes[block].sibling;
555         }
556         n
557     }
558 }
559 
560 /// Query interface for the dominator tree pre-order.
561 impl DominatorTreePreorder {
562     /// Get an iterator over the direct children of `block` in the dominator tree.
563     ///
564     /// These are the block's whose immediate dominator is an instruction in `block`, ordered according
565     /// to the CFG reverse post-order.
566     pub fn children(&self, block: Block) -> ChildIter {
567         ChildIter {
568             dtpo: self,
569             next: self.nodes[block].child,
570         }
571     }
572 
573     /// Fast, constant time dominance check with block granularity.
574     ///
575     /// This computes the same result as `domtree.dominates(a, b)`, but in guaranteed fast constant
576     /// time. This is less general than the `DominatorTree` method because it only works with block
577     /// program points.
578     ///
579     /// A block is considered to dominate itself.
580     pub fn dominates(&self, a: Block, b: Block) -> bool {
581         let na = &self.nodes[a];
582         let nb = &self.nodes[b];
583         na.pre_number <= nb.pre_number && na.pre_max >= nb.pre_max
584     }
585 
586     /// Compare two blocks according to the dominator pre-order.
587     pub fn pre_cmp_block(&self, a: Block, b: Block) -> Ordering {
588         self.nodes[a].pre_number.cmp(&self.nodes[b].pre_number)
589     }
590 
591     /// Compare two program points according to the dominator tree pre-order.
592     ///
593     /// This ordering of program points have the property that given a program point, pp, all the
594     /// program points dominated by pp follow immediately and contiguously after pp in the order.
595     pub fn pre_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering
596     where
597         A: Into<ProgramPoint>,
598         B: Into<ProgramPoint>,
599     {
600         let a = a.into();
601         let b = b.into();
602         self.pre_cmp_block(layout.pp_block(a), layout.pp_block(b))
603             .then_with(|| layout.pp_cmp(a, b))
604     }
605 }
606 
607 #[cfg(test)]
608 mod tests {
609     use super::*;
610     use crate::cursor::{Cursor, FuncCursor};
611     use crate::ir::types::*;
612     use crate::ir::{InstBuilder, TrapCode};
613 
614     #[test]
615     fn empty() {
616         let func = Function::new();
617         let cfg = ControlFlowGraph::with_function(&func);
618         debug_assert!(cfg.is_valid());
619         let dtree = DominatorTree::with_function(&func, &cfg);
620         assert_eq!(0, dtree.nodes.keys().count());
621         assert_eq!(dtree.cfg_postorder(), &[]);
622 
623         let mut dtpo = DominatorTreePreorder::new();
624         dtpo.compute(&dtree);
625     }
626 
627     #[test]
628     fn unreachable_node() {
629         let mut func = Function::new();
630         let block0 = func.dfg.make_block();
631         let v0 = func.dfg.append_block_param(block0, I32);
632         let block1 = func.dfg.make_block();
633         let block2 = func.dfg.make_block();
634         let trap_block = func.dfg.make_block();
635 
636         let mut cur = FuncCursor::new(&mut func);
637 
638         cur.insert_block(block0);
639         cur.ins().brif(v0, block2, &[], trap_block, &[]);
640 
641         cur.insert_block(trap_block);
642         cur.ins().trap(TrapCode::unwrap_user(1));
643 
644         cur.insert_block(block1);
645         let v1 = cur.ins().iconst(I32, 1);
646         let v2 = cur.ins().iadd(v0, v1);
647         cur.ins().jump(block0, &[v2]);
648 
649         cur.insert_block(block2);
650         cur.ins().return_(&[v0]);
651 
652         let cfg = ControlFlowGraph::with_function(cur.func);
653         let dt = DominatorTree::with_function(cur.func, &cfg);
654 
655         // Fall-through-first, prune-at-source DFT:
656         //
657         // block0 {
658         //   brif block2 {
659         //     trap
660         //     block2 {
661         //       return
662         //     } block2
663         // } block0
664         assert_eq!(dt.cfg_postorder(), &[block2, trap_block, block0]);
665 
666         let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
667         assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
668         assert!(!dt.dominates(block0, v2_def, &cur.func.layout));
669 
670         let mut dtpo = DominatorTreePreorder::new();
671         dtpo.compute(&dt);
672         assert!(dtpo.dominates(block0, block0));
673         assert!(!dtpo.dominates(block0, block1));
674         assert!(dtpo.dominates(block0, block2));
675         assert!(!dtpo.dominates(block1, block0));
676         assert!(dtpo.dominates(block1, block1));
677         assert!(!dtpo.dominates(block1, block2));
678         assert!(!dtpo.dominates(block2, block0));
679         assert!(!dtpo.dominates(block2, block1));
680         assert!(dtpo.dominates(block2, block2));
681     }
682 
683     #[test]
684     fn non_zero_entry_block() {
685         let mut func = Function::new();
686         let block0 = func.dfg.make_block();
687         let block1 = func.dfg.make_block();
688         let block2 = func.dfg.make_block();
689         let block3 = func.dfg.make_block();
690         let cond = func.dfg.append_block_param(block3, I32);
691 
692         let mut cur = FuncCursor::new(&mut func);
693 
694         cur.insert_block(block3);
695         let jmp_block3_block1 = cur.ins().jump(block1, &[]);
696 
697         cur.insert_block(block1);
698         let br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]);
699 
700         cur.insert_block(block2);
701         cur.ins().jump(block0, &[]);
702 
703         cur.insert_block(block0);
704 
705         let cfg = ControlFlowGraph::with_function(cur.func);
706         let dt = DominatorTree::with_function(cur.func, &cfg);
707 
708         // Fall-through-first, prune-at-source DFT:
709         //
710         // block3 {
711         //   block3:jump block1 {
712         //     block1 {
713         //       block1:brif block0 {
714         //         block1:jump block2 {
715         //           block2 {
716         //             block2:jump block0 (seen)
717         //           } block2
718         //         } block1:jump block2
719         //         block0 {
720         //         } block0
721         //       } block1:brif block0
722         //     } block1
723         //   } block3:jump block1
724         // } block3
725 
726         assert_eq!(dt.cfg_postorder(), &[block0, block2, block1, block3]);
727 
728         assert_eq!(cur.func.layout.entry_block().unwrap(), block3);
729         assert_eq!(dt.idom(block3), None);
730         assert_eq!(dt.idom(block1).unwrap(), block3);
731         assert_eq!(dt.idom(block2).unwrap(), block1);
732         assert_eq!(dt.idom(block0).unwrap(), block1);
733 
734         assert!(dt.dominates(
735             br_block1_block0_block2,
736             br_block1_block0_block2,
737             &cur.func.layout
738         ));
739         assert!(!dt.dominates(br_block1_block0_block2, jmp_block3_block1, &cur.func.layout));
740         assert!(dt.dominates(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout));
741     }
742 
743     #[test]
744     fn backwards_layout() {
745         let mut func = Function::new();
746         let block0 = func.dfg.make_block();
747         let block1 = func.dfg.make_block();
748         let block2 = func.dfg.make_block();
749 
750         let mut cur = FuncCursor::new(&mut func);
751 
752         cur.insert_block(block0);
753         let jmp02 = cur.ins().jump(block2, &[]);
754 
755         cur.insert_block(block1);
756         let trap = cur.ins().trap(TrapCode::unwrap_user(5));
757 
758         cur.insert_block(block2);
759         let jmp21 = cur.ins().jump(block1, &[]);
760 
761         let cfg = ControlFlowGraph::with_function(cur.func);
762         let dt = DominatorTree::with_function(cur.func, &cfg);
763 
764         assert_eq!(cur.func.layout.entry_block(), Some(block0));
765         assert_eq!(dt.idom(block0), None);
766         assert_eq!(dt.idom(block1), Some(block2));
767         assert_eq!(dt.idom(block2), Some(block0));
768 
769         assert!(dt.dominates(block0, block0, &cur.func.layout));
770         assert!(dt.dominates(block0, jmp02, &cur.func.layout));
771         assert!(dt.dominates(block0, block1, &cur.func.layout));
772         assert!(dt.dominates(block0, trap, &cur.func.layout));
773         assert!(dt.dominates(block0, block2, &cur.func.layout));
774         assert!(dt.dominates(block0, jmp21, &cur.func.layout));
775 
776         assert!(!dt.dominates(jmp02, block0, &cur.func.layout));
777         assert!(dt.dominates(jmp02, jmp02, &cur.func.layout));
778         assert!(dt.dominates(jmp02, block1, &cur.func.layout));
779         assert!(dt.dominates(jmp02, trap, &cur.func.layout));
780         assert!(dt.dominates(jmp02, block2, &cur.func.layout));
781         assert!(dt.dominates(jmp02, jmp21, &cur.func.layout));
782 
783         assert!(!dt.dominates(block1, block0, &cur.func.layout));
784         assert!(!dt.dominates(block1, jmp02, &cur.func.layout));
785         assert!(dt.dominates(block1, block1, &cur.func.layout));
786         assert!(dt.dominates(block1, trap, &cur.func.layout));
787         assert!(!dt.dominates(block1, block2, &cur.func.layout));
788         assert!(!dt.dominates(block1, jmp21, &cur.func.layout));
789 
790         assert!(!dt.dominates(trap, block0, &cur.func.layout));
791         assert!(!dt.dominates(trap, jmp02, &cur.func.layout));
792         assert!(!dt.dominates(trap, block1, &cur.func.layout));
793         assert!(dt.dominates(trap, trap, &cur.func.layout));
794         assert!(!dt.dominates(trap, block2, &cur.func.layout));
795         assert!(!dt.dominates(trap, jmp21, &cur.func.layout));
796 
797         assert!(!dt.dominates(block2, block0, &cur.func.layout));
798         assert!(!dt.dominates(block2, jmp02, &cur.func.layout));
799         assert!(dt.dominates(block2, block1, &cur.func.layout));
800         assert!(dt.dominates(block2, trap, &cur.func.layout));
801         assert!(dt.dominates(block2, block2, &cur.func.layout));
802         assert!(dt.dominates(block2, jmp21, &cur.func.layout));
803 
804         assert!(!dt.dominates(jmp21, block0, &cur.func.layout));
805         assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout));
806         assert!(dt.dominates(jmp21, block1, &cur.func.layout));
807         assert!(dt.dominates(jmp21, trap, &cur.func.layout));
808         assert!(!dt.dominates(jmp21, block2, &cur.func.layout));
809         assert!(dt.dominates(jmp21, jmp21, &cur.func.layout));
810     }
811 
812     #[test]
813     fn insts_same_block() {
814         let mut func = Function::new();
815         let block0 = func.dfg.make_block();
816 
817         let mut cur = FuncCursor::new(&mut func);
818 
819         cur.insert_block(block0);
820         let v1 = cur.ins().iconst(I32, 1);
821         let v2 = cur.ins().iadd(v1, v1);
822         let v3 = cur.ins().iadd(v2, v2);
823         cur.ins().return_(&[]);
824 
825         let cfg = ControlFlowGraph::with_function(cur.func);
826         let dt = DominatorTree::with_function(cur.func, &cfg);
827 
828         let v1_def = cur.func.dfg.value_def(v1).unwrap_inst();
829         let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
830         let v3_def = cur.func.dfg.value_def(v3).unwrap_inst();
831 
832         assert!(dt.dominates(v1_def, v2_def, &cur.func.layout));
833         assert!(dt.dominates(v2_def, v3_def, &cur.func.layout));
834         assert!(dt.dominates(v1_def, v3_def, &cur.func.layout));
835 
836         assert!(!dt.dominates(v2_def, v1_def, &cur.func.layout));
837         assert!(!dt.dominates(v3_def, v2_def, &cur.func.layout));
838         assert!(!dt.dominates(v3_def, v1_def, &cur.func.layout));
839 
840         assert!(dt.dominates(v2_def, v2_def, &cur.func.layout));
841         assert!(dt.dominates(block0, block0, &cur.func.layout));
842 
843         assert!(dt.dominates(block0, v1_def, &cur.func.layout));
844         assert!(dt.dominates(block0, v2_def, &cur.func.layout));
845         assert!(dt.dominates(block0, v3_def, &cur.func.layout));
846 
847         assert!(!dt.dominates(v1_def, block0, &cur.func.layout));
848         assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
849         assert!(!dt.dominates(v3_def, block0, &cur.func.layout));
850     }
851 }
852