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