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