Skip to main content

cranelift_codegen/
dominator_tree.rs

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