use crate::entity::SecondaryMap;
use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
use crate::ir::{Block, Function, Layout, ProgramPoint};
use crate::packed_option::PackedOption;
use crate::timing;
use crate::traversals::Dfs;
use alloc::vec::Vec;
use core::cmp::Ordering;
const STRIDE: u32 = 4;
#[derive(Clone, Default)]
struct DomNode {
rpo_number: u32,
idom: PackedOption<Block>,
}
pub struct SimpleDominatorTree {
nodes: SecondaryMap<Block, DomNode>,
postorder: Vec<Block>,
dfs: Dfs,
valid: bool,
}
impl SimpleDominatorTree {
pub fn is_reachable(&self, block: Block) -> bool {
self.nodes[block].rpo_number != 0
}
pub fn cfg_postorder(&self) -> &[Block] {
debug_assert!(self.is_valid());
&self.postorder
}
pub fn idom(&self, block: Block) -> Option<Block> {
self.nodes[block].idom.into()
}
pub fn rpo_cmp_block(&self, a: Block, b: Block) -> Ordering {
self.nodes[a].rpo_number.cmp(&self.nodes[b].rpo_number)
}
pub fn rpo_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering
where
A: Into<ProgramPoint>,
B: Into<ProgramPoint>,
{
let a = a.into();
let b = b.into();
self.rpo_cmp_block(layout.pp_block(a), layout.pp_block(b))
.then_with(|| layout.pp_cmp(a, b))
}
pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool
where
A: Into<ProgramPoint>,
B: Into<ProgramPoint>,
{
let a = a.into();
let b = b.into();
match a {
ProgramPoint::Block(block_a) => match b {
ProgramPoint::Block(block_b) => self.block_dominates(block_a, block_b),
ProgramPoint::Inst(inst_b) => {
let block_b = layout
.inst_block(inst_b)
.expect("Instruction not in layout.");
self.block_dominates(block_a, block_b)
}
},
ProgramPoint::Inst(inst_a) => {
let block_a: Block = layout
.inst_block(inst_a)
.expect("Instruction not in layout.");
match b {
ProgramPoint::Block(block_b) => {
block_a != block_b && self.block_dominates(block_a, block_b)
}
ProgramPoint::Inst(inst_b) => {
let block_b = layout
.inst_block(inst_b)
.expect("Instruction not in layout.");
if block_a == block_b {
layout.pp_cmp(a, b) != Ordering::Greater
} else {
self.block_dominates(block_a, block_b)
}
}
}
}
}
}
fn block_dominates(&self, block_a: Block, mut block_b: Block) -> bool {
let rpo_a = self.nodes[block_a].rpo_number;
while rpo_a < self.nodes[block_b].rpo_number {
let idom = match self.idom(block_b) {
Some(idom) => idom,
None => return false, };
block_b = idom;
}
block_a == block_b
}
fn common_dominator(&self, mut a: Block, mut b: Block) -> Block {
loop {
match self.rpo_cmp_block(a, b) {
Ordering::Less => {
let idom = self.nodes[b].idom.expect("Unreachable basic block?");
b = idom;
}
Ordering::Greater => {
let idom = self.nodes[a].idom.expect("Unreachable basic block?");
a = idom;
}
Ordering::Equal => break,
}
}
debug_assert_eq!(a, b, "Unreachable block passed to common_dominator?");
a
}
}
impl SimpleDominatorTree {
pub fn new() -> Self {
Self {
nodes: SecondaryMap::new(),
postorder: Vec::new(),
dfs: Dfs::new(),
valid: false,
}
}
pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self {
let block_capacity = func.layout.block_capacity();
let mut domtree = Self {
nodes: SecondaryMap::with_capacity(block_capacity),
postorder: Vec::with_capacity(block_capacity),
dfs: Dfs::new(),
valid: false,
};
domtree.compute(func, cfg);
domtree
}
pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) {
let _tt = timing::domtree();
debug_assert!(cfg.is_valid());
self.compute_postorder(func);
self.compute_domtree(func, cfg);
self.valid = true;
}
pub fn clear(&mut self) {
self.nodes.clear();
self.postorder.clear();
self.valid = false;
}
pub fn is_valid(&self) -> bool {
self.valid
}
fn compute_postorder(&mut self, func: &Function) {
self.clear();
self.nodes.resize(func.dfg.num_blocks());
self.postorder.extend(self.dfs.post_order_iter(func));
}
fn compute_domtree(&mut self, func: &Function, cfg: &ControlFlowGraph) {
let (entry_block, postorder) = match self.postorder.as_slice().split_last() {
Some((&eb, rest)) => (eb, rest),
None => return,
};
debug_assert_eq!(Some(entry_block), func.layout.entry_block());
self.nodes[entry_block].rpo_number = 2 * STRIDE;
for (rpo_idx, &block) in postorder.iter().rev().enumerate() {
self.nodes[block] = DomNode {
idom: self.compute_idom(block, cfg).into(),
rpo_number: (rpo_idx as u32 + 3) * STRIDE,
}
}
let mut changed = true;
while changed {
changed = false;
for &block in postorder.iter().rev() {
let idom = self.compute_idom(block, cfg).into();
if self.nodes[block].idom != idom {
self.nodes[block].idom = idom;
changed = true;
}
}
}
}
fn compute_idom(&self, block: Block, cfg: &ControlFlowGraph) -> Block {
let mut reachable_preds = cfg
.pred_iter(block)
.filter(|&BlockPredecessor { block: pred, .. }| self.nodes[pred].rpo_number > 1)
.map(|pred| pred.block);
let mut idom = reachable_preds
.next()
.expect("block node must have one reachable predecessor");
for pred in reachable_preds {
idom = self.common_dominator(idom, pred);
}
idom
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cursor::{Cursor, FuncCursor};
use crate::ir::types::*;
use crate::ir::{InstBuilder, TrapCode};
#[test]
fn empty() {
let func = Function::new();
let cfg = ControlFlowGraph::with_function(&func);
debug_assert!(cfg.is_valid());
let dtree = SimpleDominatorTree::with_function(&func, &cfg);
assert_eq!(0, dtree.nodes.keys().count());
assert_eq!(dtree.cfg_postorder(), &[]);
}
#[test]
fn unreachable_node() {
let mut func = Function::new();
let block0 = func.dfg.make_block();
let v0 = func.dfg.append_block_param(block0, I32);
let block1 = func.dfg.make_block();
let block2 = func.dfg.make_block();
let trap_block = func.dfg.make_block();
let mut cur = FuncCursor::new(&mut func);
cur.insert_block(block0);
cur.ins().brif(v0, block2, &[], trap_block, &[]);
cur.insert_block(trap_block);
cur.ins().trap(TrapCode::unwrap_user(1));
cur.insert_block(block1);
let v1 = cur.ins().iconst(I32, 1);
let v2 = cur.ins().iadd(v0, v1);
cur.ins().jump(block0, &[v2.into()]);
cur.insert_block(block2);
cur.ins().return_(&[v0]);
let cfg = ControlFlowGraph::with_function(cur.func);
let dt = SimpleDominatorTree::with_function(cur.func, &cfg);
assert_eq!(dt.cfg_postorder(), &[block2, trap_block, block0]);
let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
assert!(!dt.dominates(block0, v2_def, &cur.func.layout));
assert!(dt.dominates(block0, block0, &cur.func.layout));
assert!(!dt.dominates(block0, block1, &cur.func.layout));
assert!(dt.dominates(block0, block2, &cur.func.layout));
assert!(!dt.dominates(block1, block0, &cur.func.layout));
assert!(dt.dominates(block1, block1, &cur.func.layout));
assert!(!dt.dominates(block1, block2, &cur.func.layout));
assert!(!dt.dominates(block2, block0, &cur.func.layout));
assert!(!dt.dominates(block2, block1, &cur.func.layout));
assert!(dt.dominates(block2, block2, &cur.func.layout));
}
#[test]
fn non_zero_entry_block() {
let mut func = Function::new();
let block0 = func.dfg.make_block();
let block1 = func.dfg.make_block();
let block2 = func.dfg.make_block();
let block3 = func.dfg.make_block();
let cond = func.dfg.append_block_param(block3, I32);
let mut cur = FuncCursor::new(&mut func);
cur.insert_block(block3);
let jmp_block3_block1 = cur.ins().jump(block1, &[]);
cur.insert_block(block1);
let br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]);
cur.insert_block(block2);
cur.ins().jump(block0, &[]);
cur.insert_block(block0);
let cfg = ControlFlowGraph::with_function(cur.func);
let dt = SimpleDominatorTree::with_function(cur.func, &cfg);
assert_eq!(dt.cfg_postorder(), &[block0, block2, block1, block3]);
assert_eq!(cur.func.layout.entry_block().unwrap(), block3);
assert_eq!(dt.idom(block3), None);
assert_eq!(dt.idom(block1).unwrap(), block3);
assert_eq!(dt.idom(block2).unwrap(), block1);
assert_eq!(dt.idom(block0).unwrap(), block1);
assert!(dt.dominates(
br_block1_block0_block2,
br_block1_block0_block2,
&cur.func.layout
));
assert!(!dt.dominates(br_block1_block0_block2, jmp_block3_block1, &cur.func.layout));
assert!(dt.dominates(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout));
assert_eq!(
dt.rpo_cmp(block3, block3, &cur.func.layout),
Ordering::Equal
);
assert_eq!(dt.rpo_cmp(block3, block1, &cur.func.layout), Ordering::Less);
assert_eq!(
dt.rpo_cmp(block3, jmp_block3_block1, &cur.func.layout),
Ordering::Less
);
assert_eq!(
dt.rpo_cmp(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout),
Ordering::Less
);
}
#[test]
fn backwards_layout() {
let mut func = Function::new();
let block0 = func.dfg.make_block();
let block1 = func.dfg.make_block();
let block2 = func.dfg.make_block();
let mut cur = FuncCursor::new(&mut func);
cur.insert_block(block0);
let jmp02 = cur.ins().jump(block2, &[]);
cur.insert_block(block1);
let trap = cur.ins().trap(TrapCode::unwrap_user(5));
cur.insert_block(block2);
let jmp21 = cur.ins().jump(block1, &[]);
let cfg = ControlFlowGraph::with_function(cur.func);
let dt = SimpleDominatorTree::with_function(cur.func, &cfg);
assert_eq!(cur.func.layout.entry_block(), Some(block0));
assert_eq!(dt.idom(block0), None);
assert_eq!(dt.idom(block1), Some(block2));
assert_eq!(dt.idom(block2), Some(block0));
assert!(dt.dominates(block0, block0, &cur.func.layout));
assert!(dt.dominates(block0, jmp02, &cur.func.layout));
assert!(dt.dominates(block0, block1, &cur.func.layout));
assert!(dt.dominates(block0, trap, &cur.func.layout));
assert!(dt.dominates(block0, block2, &cur.func.layout));
assert!(dt.dominates(block0, jmp21, &cur.func.layout));
assert!(!dt.dominates(jmp02, block0, &cur.func.layout));
assert!(dt.dominates(jmp02, jmp02, &cur.func.layout));
assert!(dt.dominates(jmp02, block1, &cur.func.layout));
assert!(dt.dominates(jmp02, trap, &cur.func.layout));
assert!(dt.dominates(jmp02, block2, &cur.func.layout));
assert!(dt.dominates(jmp02, jmp21, &cur.func.layout));
assert!(!dt.dominates(block1, block0, &cur.func.layout));
assert!(!dt.dominates(block1, jmp02, &cur.func.layout));
assert!(dt.dominates(block1, block1, &cur.func.layout));
assert!(dt.dominates(block1, trap, &cur.func.layout));
assert!(!dt.dominates(block1, block2, &cur.func.layout));
assert!(!dt.dominates(block1, jmp21, &cur.func.layout));
assert!(!dt.dominates(trap, block0, &cur.func.layout));
assert!(!dt.dominates(trap, jmp02, &cur.func.layout));
assert!(!dt.dominates(trap, block1, &cur.func.layout));
assert!(dt.dominates(trap, trap, &cur.func.layout));
assert!(!dt.dominates(trap, block2, &cur.func.layout));
assert!(!dt.dominates(trap, jmp21, &cur.func.layout));
assert!(!dt.dominates(block2, block0, &cur.func.layout));
assert!(!dt.dominates(block2, jmp02, &cur.func.layout));
assert!(dt.dominates(block2, block1, &cur.func.layout));
assert!(dt.dominates(block2, trap, &cur.func.layout));
assert!(dt.dominates(block2, block2, &cur.func.layout));
assert!(dt.dominates(block2, jmp21, &cur.func.layout));
assert!(!dt.dominates(jmp21, block0, &cur.func.layout));
assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout));
assert!(dt.dominates(jmp21, block1, &cur.func.layout));
assert!(dt.dominates(jmp21, trap, &cur.func.layout));
assert!(!dt.dominates(jmp21, block2, &cur.func.layout));
assert!(dt.dominates(jmp21, jmp21, &cur.func.layout));
}
#[test]
fn insts_same_block() {
let mut func = Function::new();
let block0 = func.dfg.make_block();
let mut cur = FuncCursor::new(&mut func);
cur.insert_block(block0);
let v1 = cur.ins().iconst(I32, 1);
let v2 = cur.ins().iadd(v1, v1);
let v3 = cur.ins().iadd(v2, v2);
cur.ins().return_(&[]);
let cfg = ControlFlowGraph::with_function(cur.func);
let dt = SimpleDominatorTree::with_function(cur.func, &cfg);
let v1_def = cur.func.dfg.value_def(v1).unwrap_inst();
let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
let v3_def = cur.func.dfg.value_def(v3).unwrap_inst();
assert!(dt.dominates(v1_def, v2_def, &cur.func.layout));
assert!(dt.dominates(v2_def, v3_def, &cur.func.layout));
assert!(dt.dominates(v1_def, v3_def, &cur.func.layout));
assert!(!dt.dominates(v2_def, v1_def, &cur.func.layout));
assert!(!dt.dominates(v3_def, v2_def, &cur.func.layout));
assert!(!dt.dominates(v3_def, v1_def, &cur.func.layout));
assert!(dt.dominates(v2_def, v2_def, &cur.func.layout));
assert!(dt.dominates(block0, block0, &cur.func.layout));
assert!(dt.dominates(block0, v1_def, &cur.func.layout));
assert!(dt.dominates(block0, v2_def, &cur.func.layout));
assert!(dt.dominates(block0, v3_def, &cur.func.layout));
assert!(!dt.dominates(v1_def, block0, &cur.func.layout));
assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
assert!(!dt.dominates(v3_def, block0, &cur.func.layout));
}
}