Path: blob/main/cranelift/codegen/src/dominator_tree/simple.rs
1693 views
//! A Dominator Tree represented as mappings of Blocks to their immediate dominator.1//! Computed using Keith D. Cooper's "Simple, Fast Dominator Algorithm."2//! This version have been used in Cranelift for a very long time3//! and should be quite stable. Used as a baseline i.e. in verification.45use crate::entity::SecondaryMap;6use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};7use crate::ir::{Block, Function, Layout, ProgramPoint};8use crate::packed_option::PackedOption;9use crate::timing;10use crate::traversals::Dfs;11use alloc::vec::Vec;12use core::cmp::Ordering;1314/// RPO numbers are not first assigned in a contiguous way but as multiples of STRIDE, to leave15/// room for modifications of the dominator tree.16const STRIDE: u32 = 4;1718/// Dominator tree node. We keep one of these per block.19#[derive(Clone, Default)]20struct DomNode {21/// Number of this node in a reverse post-order traversal of the CFG, starting from 1.22/// This number is monotonic in the reverse postorder but not contiguous, since we leave23/// holes for later localized modifications of the dominator tree.24/// Unreachable nodes get number 0, all others are positive.25rpo_number: u32,2627/// The immediate dominator of this block.28///29/// This is `None` for unreachable blocks and the entry block which doesn't have an immediate30/// dominator.31idom: PackedOption<Block>,32}3334/// The dominator tree for a single function.35pub struct SimpleDominatorTree {36nodes: SecondaryMap<Block, DomNode>,3738/// CFG post-order of all reachable blocks.39postorder: Vec<Block>,4041/// Scratch traversal state used by `compute_postorder()`.42dfs: Dfs,4344valid: bool,45}4647/// Methods for querying the dominator tree.48impl SimpleDominatorTree {49/// Is `block` reachable from the entry block?50pub fn is_reachable(&self, block: Block) -> bool {51self.nodes[block].rpo_number != 052}5354/// Get the CFG post-order of blocks that was used to compute the dominator tree.55///56/// Note that this post-order is not updated automatically when the CFG is modified. It is57/// computed from scratch and cached by `compute()`.58pub fn cfg_postorder(&self) -> &[Block] {59debug_assert!(self.is_valid());60&self.postorder61}6263/// Returns the immediate dominator of `block`.64///65/// `block_a` is said to *dominate* `block_b` if all control flow paths from the function66/// entry to `block_b` must go through `block_a`.67///68/// The *immediate dominator* is the dominator that is closest to `block`. All other dominators69/// also dominate the immediate dominator.70///71/// This returns `None` if `block` is not reachable from the entry block, or if it is the entry block72/// which has no dominators.73pub fn idom(&self, block: Block) -> Option<Block> {74self.nodes[block].idom.into()75}7677/// Compare two blocks relative to the reverse post-order.78pub fn rpo_cmp_block(&self, a: Block, b: Block) -> Ordering {79self.nodes[a].rpo_number.cmp(&self.nodes[b].rpo_number)80}8182/// Compare two program points relative to a reverse post-order traversal of the control-flow83/// graph.84///85/// Return `Ordering::Less` if `a` comes before `b` in the RPO.86///87/// If `a` and `b` belong to the same block, compare their relative position in the block.88pub fn rpo_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering89where90A: Into<ProgramPoint>,91B: Into<ProgramPoint>,92{93let a = a.into();94let b = b.into();95self.rpo_cmp_block(layout.pp_block(a), layout.pp_block(b))96.then_with(|| layout.pp_cmp(a, b))97}9899/// Returns `true` if `a` dominates `b`.100///101/// This means that every control-flow path from the function entry to `b` must go through `a`.102///103/// Dominance is ill defined for unreachable blocks. This function can always determine104/// dominance for instructions in the same block, but otherwise returns `false` if either block105/// is unreachable.106///107/// An instruction is considered to dominate itself.108/// A block is also considered to dominate itself.109pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool110where111A: Into<ProgramPoint>,112B: Into<ProgramPoint>,113{114let a = a.into();115let b = b.into();116match a {117ProgramPoint::Block(block_a) => match b {118ProgramPoint::Block(block_b) => self.block_dominates(block_a, block_b),119ProgramPoint::Inst(inst_b) => {120let block_b = layout121.inst_block(inst_b)122.expect("Instruction not in layout.");123self.block_dominates(block_a, block_b)124}125},126ProgramPoint::Inst(inst_a) => {127let block_a: Block = layout128.inst_block(inst_a)129.expect("Instruction not in layout.");130match b {131ProgramPoint::Block(block_b) => {132block_a != block_b && self.block_dominates(block_a, block_b)133}134ProgramPoint::Inst(inst_b) => {135let block_b = layout136.inst_block(inst_b)137.expect("Instruction not in layout.");138if block_a == block_b {139layout.pp_cmp(a, b) != Ordering::Greater140} else {141self.block_dominates(block_a, block_b)142}143}144}145}146}147}148149/// Returns `true` if `block_a` dominates `block_b`.150///151/// A block is considered to dominate itself.152fn block_dominates(&self, block_a: Block, mut block_b: Block) -> bool {153let rpo_a = self.nodes[block_a].rpo_number;154155// Run a finger up the dominator tree from b until we see a.156// Do nothing if b is unreachable.157while rpo_a < self.nodes[block_b].rpo_number {158let idom = match self.idom(block_b) {159Some(idom) => idom,160None => return false, // a is unreachable, so we climbed past the entry161};162block_b = idom;163}164165block_a == block_b166}167168/// Compute the common dominator of two basic blocks.169///170/// Both basic blocks are assumed to be reachable.171fn common_dominator(&self, mut a: Block, mut b: Block) -> Block {172loop {173match self.rpo_cmp_block(a, b) {174Ordering::Less => {175// `a` comes before `b` in the RPO. Move `b` up.176let idom = self.nodes[b].idom.expect("Unreachable basic block?");177b = idom;178}179Ordering::Greater => {180// `b` comes before `a` in the RPO. Move `a` up.181let idom = self.nodes[a].idom.expect("Unreachable basic block?");182a = idom;183}184Ordering::Equal => break,185}186}187188debug_assert_eq!(a, b, "Unreachable block passed to common_dominator?");189190a191}192}193194impl SimpleDominatorTree {195/// Allocate a new blank dominator tree. Use `compute` to compute the dominator tree for a196/// function.197pub fn new() -> Self {198Self {199nodes: SecondaryMap::new(),200postorder: Vec::new(),201dfs: Dfs::new(),202valid: false,203}204}205206/// Allocate and compute a dominator tree.207pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self {208let block_capacity = func.layout.block_capacity();209let mut domtree = Self {210nodes: SecondaryMap::with_capacity(block_capacity),211postorder: Vec::with_capacity(block_capacity),212dfs: Dfs::new(),213valid: false,214};215domtree.compute(func, cfg);216domtree217}218219/// Reset and compute a CFG post-order and dominator tree.220pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) {221let _tt = timing::domtree();222debug_assert!(cfg.is_valid());223self.compute_postorder(func);224self.compute_domtree(func, cfg);225self.valid = true;226}227228/// Clear the data structures used to represent the dominator tree. This will leave the tree in229/// a state where `is_valid()` returns false.230pub fn clear(&mut self) {231self.nodes.clear();232self.postorder.clear();233self.valid = false;234}235236/// Check if the dominator tree is in a valid state.237///238/// Note that this doesn't perform any kind of validity checks. It simply checks if the239/// `compute()` method has been called since the last `clear()`. It does not check that the240/// dominator tree is consistent with the CFG.241pub fn is_valid(&self) -> bool {242self.valid243}244245/// Reset all internal data structures and compute a post-order of the control flow graph.246///247/// This leaves `rpo_number == 1` for all reachable blocks, 0 for unreachable ones.248fn compute_postorder(&mut self, func: &Function) {249self.clear();250self.nodes.resize(func.dfg.num_blocks());251self.postorder.extend(self.dfs.post_order_iter(func));252}253254/// Build a dominator tree from a control flow graph using Keith D. Cooper's255/// "Simple, Fast Dominator Algorithm."256fn compute_domtree(&mut self, func: &Function, cfg: &ControlFlowGraph) {257// During this algorithm, `rpo_number` has the following values:258//259// 0: block is not reachable.260// 1: block is reachable, but has not yet been visited during the first pass. This is set by261// `compute_postorder`.262// 2+: block is reachable and has an assigned RPO number.263264// We'll be iterating over a reverse post-order of the CFG, skipping the entry block.265let (entry_block, postorder) = match self.postorder.as_slice().split_last() {266Some((&eb, rest)) => (eb, rest),267None => return,268};269debug_assert_eq!(Some(entry_block), func.layout.entry_block());270271// Do a first pass where we assign RPO numbers to all reachable nodes.272self.nodes[entry_block].rpo_number = 2 * STRIDE;273for (rpo_idx, &block) in postorder.iter().rev().enumerate() {274// Update the current node and give it an RPO number.275// The entry block got 2, the rest start at 3 by multiples of STRIDE to leave276// room for future dominator tree modifications.277//278// Since `compute_idom` will only look at nodes with an assigned RPO number, the279// function will never see an uninitialized predecessor.280//281// Due to the nature of the post-order traversal, every node we visit will have at282// least one predecessor that has previously been visited during this RPO.283self.nodes[block] = DomNode {284idom: self.compute_idom(block, cfg).into(),285rpo_number: (rpo_idx as u32 + 3) * STRIDE,286}287}288289// Now that we have RPO numbers for everything and initial immediate dominator estimates,290// iterate until convergence.291//292// If the function is free of irreducible control flow, this will exit after one iteration.293let mut changed = true;294while changed {295changed = false;296for &block in postorder.iter().rev() {297let idom = self.compute_idom(block, cfg).into();298if self.nodes[block].idom != idom {299self.nodes[block].idom = idom;300changed = true;301}302}303}304}305306// Compute the immediate dominator for `block` using the current `idom` states for the reachable307// nodes.308fn compute_idom(&self, block: Block, cfg: &ControlFlowGraph) -> Block {309// Get an iterator with just the reachable, already visited predecessors to `block`.310// Note that during the first pass, `rpo_number` is 1 for reachable blocks that haven't311// been visited yet, 0 for unreachable blocks.312let mut reachable_preds = cfg313.pred_iter(block)314.filter(|&BlockPredecessor { block: pred, .. }| self.nodes[pred].rpo_number > 1)315.map(|pred| pred.block);316317// The RPO must visit at least one predecessor before this node.318let mut idom = reachable_preds319.next()320.expect("block node must have one reachable predecessor");321322for pred in reachable_preds {323idom = self.common_dominator(idom, pred);324}325326idom327}328}329330#[cfg(test)]331mod tests {332use super::*;333use crate::cursor::{Cursor, FuncCursor};334use crate::ir::types::*;335use crate::ir::{InstBuilder, TrapCode};336337#[test]338fn empty() {339let func = Function::new();340let cfg = ControlFlowGraph::with_function(&func);341debug_assert!(cfg.is_valid());342let dtree = SimpleDominatorTree::with_function(&func, &cfg);343assert_eq!(0, dtree.nodes.keys().count());344assert_eq!(dtree.cfg_postorder(), &[]);345}346347#[test]348fn unreachable_node() {349let mut func = Function::new();350let block0 = func.dfg.make_block();351let v0 = func.dfg.append_block_param(block0, I32);352let block1 = func.dfg.make_block();353let block2 = func.dfg.make_block();354let trap_block = func.dfg.make_block();355356let mut cur = FuncCursor::new(&mut func);357358cur.insert_block(block0);359cur.ins().brif(v0, block2, &[], trap_block, &[]);360361cur.insert_block(trap_block);362cur.ins().trap(TrapCode::unwrap_user(1));363364cur.insert_block(block1);365let v1 = cur.ins().iconst(I32, 1);366let v2 = cur.ins().iadd(v0, v1);367cur.ins().jump(block0, &[v2.into()]);368369cur.insert_block(block2);370cur.ins().return_(&[v0]);371372let cfg = ControlFlowGraph::with_function(cur.func);373let dt = SimpleDominatorTree::with_function(cur.func, &cfg);374375// Fall-through-first, prune-at-source DFT:376//377// block0 {378// brif block2 {379// trap380// block2 {381// return382// } block2383// } block0384assert_eq!(dt.cfg_postorder(), &[block2, trap_block, block0]);385386let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();387assert!(!dt.dominates(v2_def, block0, &cur.func.layout));388assert!(!dt.dominates(block0, v2_def, &cur.func.layout));389390assert!(dt.dominates(block0, block0, &cur.func.layout));391assert!(!dt.dominates(block0, block1, &cur.func.layout));392assert!(dt.dominates(block0, block2, &cur.func.layout));393assert!(!dt.dominates(block1, block0, &cur.func.layout));394assert!(dt.dominates(block1, block1, &cur.func.layout));395assert!(!dt.dominates(block1, block2, &cur.func.layout));396assert!(!dt.dominates(block2, block0, &cur.func.layout));397assert!(!dt.dominates(block2, block1, &cur.func.layout));398assert!(dt.dominates(block2, block2, &cur.func.layout));399}400401#[test]402fn non_zero_entry_block() {403let mut func = Function::new();404let block0 = func.dfg.make_block();405let block1 = func.dfg.make_block();406let block2 = func.dfg.make_block();407let block3 = func.dfg.make_block();408let cond = func.dfg.append_block_param(block3, I32);409410let mut cur = FuncCursor::new(&mut func);411412cur.insert_block(block3);413let jmp_block3_block1 = cur.ins().jump(block1, &[]);414415cur.insert_block(block1);416let br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]);417418cur.insert_block(block2);419cur.ins().jump(block0, &[]);420421cur.insert_block(block0);422423let cfg = ControlFlowGraph::with_function(cur.func);424let dt = SimpleDominatorTree::with_function(cur.func, &cfg);425426// Fall-through-first, prune-at-source DFT:427//428// block3 {429// block3:jump block1 {430// block1 {431// block1:brif block0 {432// block1:jump block2 {433// block2 {434// block2:jump block0 (seen)435// } block2436// } block1:jump block2437// block0 {438// } block0439// } block1:brif block0440// } block1441// } block3:jump block1442// } block3443444assert_eq!(dt.cfg_postorder(), &[block0, block2, block1, block3]);445446assert_eq!(cur.func.layout.entry_block().unwrap(), block3);447assert_eq!(dt.idom(block3), None);448assert_eq!(dt.idom(block1).unwrap(), block3);449assert_eq!(dt.idom(block2).unwrap(), block1);450assert_eq!(dt.idom(block0).unwrap(), block1);451452assert!(dt.dominates(453br_block1_block0_block2,454br_block1_block0_block2,455&cur.func.layout456));457assert!(!dt.dominates(br_block1_block0_block2, jmp_block3_block1, &cur.func.layout));458assert!(dt.dominates(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout));459460assert_eq!(461dt.rpo_cmp(block3, block3, &cur.func.layout),462Ordering::Equal463);464assert_eq!(dt.rpo_cmp(block3, block1, &cur.func.layout), Ordering::Less);465assert_eq!(466dt.rpo_cmp(block3, jmp_block3_block1, &cur.func.layout),467Ordering::Less468);469assert_eq!(470dt.rpo_cmp(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout),471Ordering::Less472);473}474475#[test]476fn backwards_layout() {477let mut func = Function::new();478let block0 = func.dfg.make_block();479let block1 = func.dfg.make_block();480let block2 = func.dfg.make_block();481482let mut cur = FuncCursor::new(&mut func);483484cur.insert_block(block0);485let jmp02 = cur.ins().jump(block2, &[]);486487cur.insert_block(block1);488let trap = cur.ins().trap(TrapCode::unwrap_user(5));489490cur.insert_block(block2);491let jmp21 = cur.ins().jump(block1, &[]);492493let cfg = ControlFlowGraph::with_function(cur.func);494let dt = SimpleDominatorTree::with_function(cur.func, &cfg);495496assert_eq!(cur.func.layout.entry_block(), Some(block0));497assert_eq!(dt.idom(block0), None);498assert_eq!(dt.idom(block1), Some(block2));499assert_eq!(dt.idom(block2), Some(block0));500501assert!(dt.dominates(block0, block0, &cur.func.layout));502assert!(dt.dominates(block0, jmp02, &cur.func.layout));503assert!(dt.dominates(block0, block1, &cur.func.layout));504assert!(dt.dominates(block0, trap, &cur.func.layout));505assert!(dt.dominates(block0, block2, &cur.func.layout));506assert!(dt.dominates(block0, jmp21, &cur.func.layout));507508assert!(!dt.dominates(jmp02, block0, &cur.func.layout));509assert!(dt.dominates(jmp02, jmp02, &cur.func.layout));510assert!(dt.dominates(jmp02, block1, &cur.func.layout));511assert!(dt.dominates(jmp02, trap, &cur.func.layout));512assert!(dt.dominates(jmp02, block2, &cur.func.layout));513assert!(dt.dominates(jmp02, jmp21, &cur.func.layout));514515assert!(!dt.dominates(block1, block0, &cur.func.layout));516assert!(!dt.dominates(block1, jmp02, &cur.func.layout));517assert!(dt.dominates(block1, block1, &cur.func.layout));518assert!(dt.dominates(block1, trap, &cur.func.layout));519assert!(!dt.dominates(block1, block2, &cur.func.layout));520assert!(!dt.dominates(block1, jmp21, &cur.func.layout));521522assert!(!dt.dominates(trap, block0, &cur.func.layout));523assert!(!dt.dominates(trap, jmp02, &cur.func.layout));524assert!(!dt.dominates(trap, block1, &cur.func.layout));525assert!(dt.dominates(trap, trap, &cur.func.layout));526assert!(!dt.dominates(trap, block2, &cur.func.layout));527assert!(!dt.dominates(trap, jmp21, &cur.func.layout));528529assert!(!dt.dominates(block2, block0, &cur.func.layout));530assert!(!dt.dominates(block2, jmp02, &cur.func.layout));531assert!(dt.dominates(block2, block1, &cur.func.layout));532assert!(dt.dominates(block2, trap, &cur.func.layout));533assert!(dt.dominates(block2, block2, &cur.func.layout));534assert!(dt.dominates(block2, jmp21, &cur.func.layout));535536assert!(!dt.dominates(jmp21, block0, &cur.func.layout));537assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout));538assert!(dt.dominates(jmp21, block1, &cur.func.layout));539assert!(dt.dominates(jmp21, trap, &cur.func.layout));540assert!(!dt.dominates(jmp21, block2, &cur.func.layout));541assert!(dt.dominates(jmp21, jmp21, &cur.func.layout));542}543544#[test]545fn insts_same_block() {546let mut func = Function::new();547let block0 = func.dfg.make_block();548549let mut cur = FuncCursor::new(&mut func);550551cur.insert_block(block0);552let v1 = cur.ins().iconst(I32, 1);553let v2 = cur.ins().iadd(v1, v1);554let v3 = cur.ins().iadd(v2, v2);555cur.ins().return_(&[]);556557let cfg = ControlFlowGraph::with_function(cur.func);558let dt = SimpleDominatorTree::with_function(cur.func, &cfg);559560let v1_def = cur.func.dfg.value_def(v1).unwrap_inst();561let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();562let v3_def = cur.func.dfg.value_def(v3).unwrap_inst();563564assert!(dt.dominates(v1_def, v2_def, &cur.func.layout));565assert!(dt.dominates(v2_def, v3_def, &cur.func.layout));566assert!(dt.dominates(v1_def, v3_def, &cur.func.layout));567568assert!(!dt.dominates(v2_def, v1_def, &cur.func.layout));569assert!(!dt.dominates(v3_def, v2_def, &cur.func.layout));570assert!(!dt.dominates(v3_def, v1_def, &cur.func.layout));571572assert!(dt.dominates(v2_def, v2_def, &cur.func.layout));573assert!(dt.dominates(block0, block0, &cur.func.layout));574575assert!(dt.dominates(block0, v1_def, &cur.func.layout));576assert!(dt.dominates(block0, v2_def, &cur.func.layout));577assert!(dt.dominates(block0, v3_def, &cur.func.layout));578579assert!(!dt.dominates(v1_def, block0, &cur.func.layout));580assert!(!dt.dominates(v2_def, block0, &cur.func.layout));581assert!(!dt.dominates(v3_def, block0, &cur.func.layout));582}583}584585586