use crate::{
analysis::PredecessorTable,
ir::{prelude::*, ValueData},
table::TableKey,
};
use hibitset::BitSet;
use std::{
collections::{HashMap, HashSet},
sync::atomic::{AtomicU64, Ordering},
time::Instant,
};
#[derive(Debug, Clone)]
pub struct DominatorTree {
dominates: HashMap<Block, HashSet<Block>>,
dominated: HashMap<Block, HashSet<Block>>,
doms: Vec<Block>,
post_order: Vec<Block>,
inv_post_order: Vec<u32>,
}
impl DominatorTree {
#[deprecated(since = "0.13.0", note = "use unit.domtree() instead")]
pub fn new(unit: &Unit, pred: &PredecessorTable) -> Self {
let t0 = Instant::now();
let post_order = Self::compute_blocks_post_order(unit, pred);
let length = post_order.len();
let undef = std::u32::MAX;
let mut doms = vec![undef; length];
let mut inv_post_order = vec![undef; unit.block_id_bound()];
for (i, &bb) in post_order.iter().enumerate() {
inv_post_order[bb.index()] = i as u32;
}
for root in Some(unit.entry())
.into_iter()
.chain(unit.blocks().filter(|&id| pred.pred_set(id).is_empty()))
{
let poidx = inv_post_order[root.index()];
doms[poidx as usize] = poidx; }
let mut changed = true;
while changed {
changed = false;
for idx in (0..length).rev() {
if doms[idx] == idx as u32 {
continue; }
let bb = post_order[idx];
let mut preds = pred
.pred_set(bb)
.iter()
.map(|id| inv_post_order[id.index()])
.filter(|&p| doms[p as usize] != undef);
let new_idom = preds.next().unwrap();
let new_idom = preds.fold(new_idom, |mut i1, mut i2| {
let i1_init = i1;
while i1 != i2 {
if i1 < i2 {
if i1 == doms[i1 as usize] {
return i1;
}
i1 = doms[i1 as usize];
} else if i2 < i1 {
if i2 == doms[i2 as usize] {
return i1_init;
}
i2 = doms[i2 as usize];
}
}
i1
});
debug_assert!(new_idom < length as u32);
if doms[idx] != new_idom {
doms[idx] = new_idom;
changed = true;
}
}
}
let mut doms_final = vec![Block::invalid(); unit.block_id_bound()];
for bb in &post_order {
doms_final[bb.index()] = post_order[doms[inv_post_order[bb.index()] as usize] as usize];
}
let mut dominated = HashMap::new();
for block in unit.blocks() {
let mut s = HashSet::new();
let mut bb = block;
loop {
s.insert(bb);
let next = doms_final[bb.index()];
if next == bb {
break;
}
bb = next;
}
dominated.insert(block, s);
}
let mut dominates: HashMap<Block, HashSet<Block>> =
unit.blocks().map(|bb| (bb, HashSet::new())).collect();
for (&bb, dom) in &dominated {
for d in dom {
dominates.get_mut(d).unwrap().insert(bb);
}
}
let t1 = Instant::now();
DOMINATOR_TREE_TIME.fetch_add((t1 - t0).as_nanos() as u64, Ordering::Relaxed);
Self {
dominates,
dominated,
doms: doms_final,
post_order,
inv_post_order,
}
}
fn compute_blocks_post_order(unit: &Unit, pred: &PredecessorTable) -> Vec<Block> {
let mut order = Vec::with_capacity(pred.all_pred_sets().len());
let mut stack = Vec::with_capacity(8);
let mut discovered = BitSet::with_capacity(pred.all_pred_sets().len() as u32);
let mut finished = BitSet::with_capacity(pred.all_pred_sets().len() as u32);
stack.push(unit.entry());
stack.extend(unit.blocks().filter(|&id| pred.pred_set(id).is_empty()));
while let Some(&next) = stack.last() {
if !discovered.add(next.index() as u32) {
for &succ in pred.succ_set(next) {
if !discovered.contains(succ.index() as u32) {
stack.push(succ);
}
}
} else {
stack.pop();
if !finished.add(next.index() as u32) {
order.push(next);
}
}
}
order
}
pub fn blocks_post_order(&self) -> &[Block] {
&self.post_order
}
pub fn block_order(&self, block: Block) -> usize {
self.inv_post_order[block.index()] as usize
}
pub fn dominates(&self, dominator: Block, follower: Block) -> bool {
self.dominates
.get(&dominator)
.map(|d| d.contains(&follower))
.unwrap_or(false)
}
pub fn dominator(&self, block: Block) -> Block {
self.doms[block.index()]
}
pub fn dominators(&self, follower: Block) -> &HashSet<Block> {
&self.dominated[&follower]
}
pub fn dominated_by(&self, dominator: Block) -> &HashSet<Block> {
&self.dominates[&dominator]
}
pub fn block_dominates_block(&self, parent: Block, mut child: Block) -> bool {
while parent != child {
let next = self.dominator(child);
if next == child {
return false;
}
child = next;
}
true
}
pub fn inst_dominates_block(&self, unit: &Unit, parent: Inst, child: Block) -> bool {
match unit.inst_block(parent) {
Some(bb) => self.block_dominates_block(bb, child),
None => false,
}
}
pub fn value_dominates_block(&self, unit: &Unit, parent: Value, child: Block) -> bool {
match unit[parent] {
ValueData::Inst { inst, .. } => self.inst_dominates_block(unit, inst, child),
ValueData::Arg { .. } => true,
_ => false,
}
}
pub fn block_dominates_inst(&self, unit: &Unit, parent: Block, child: Inst) -> bool {
match unit.inst_block(child) {
Some(bb) => self.block_dominates_block(parent, bb),
None => false,
}
}
pub fn inst_dominates_inst(&self, unit: &Unit, parent: Inst, child: Inst) -> bool {
if parent == child {
return true;
}
let parent_bb = unit.inst_block(parent);
let child_bb = unit.inst_block(child);
let (parent_bb, child_bb) = match (parent_bb, child_bb) {
(Some(a), Some(b)) => (a, b),
_ => return false,
};
let data = &unit[child];
if let (Opcode::Phi, Some(parent_result)) = (data.opcode(), unit.get_inst_result(parent)) {
for (&v, &bb) in data.args().iter().zip(data.blocks().iter()) {
if v == parent_result {
return parent_bb == bb || self.inst_dominates_block(unit, parent, bb);
}
}
}
if parent_bb == child_bb {
let mut pi = parent;
let mut ci = child;
loop {
if let Some(pci) = unit.prev_inst(ci) {
if pci == parent {
return true;
}
ci = pci;
} else {
return false;
}
if let Some(ppi) = unit.prev_inst(pi) {
if ppi == child {
return false;
}
pi = ppi;
} else {
return true;
}
}
}
self.block_dominates_block(parent_bb, child_bb)
}
pub fn value_dominates_inst(&self, unit: &Unit, parent: Value, child: Inst) -> bool {
match unit[parent] {
ValueData::Inst { inst, .. } => self.inst_dominates_inst(unit, inst, child),
ValueData::Arg { .. } => true,
_ => false,
}
}
pub fn block_dominates_value(&self, unit: &Unit, parent: Block, child: Value) -> bool {
unit.get_value_inst(child)
.map(move |inst| self.block_dominates_inst(unit, parent, inst))
.unwrap_or(false)
}
pub fn inst_dominates_value(&self, unit: &Unit, parent: Inst, child: Value) -> bool {
unit.get_value_inst(child)
.map(|inst| self.inst_dominates_inst(unit, parent, inst))
.unwrap_or(false)
}
pub fn value_dominates_value(&self, unit: &Unit, parent: Value, child: Value) -> bool {
unit.get_value_inst(child)
.map(|inst| self.value_dominates_inst(unit, parent, inst))
.unwrap_or(false)
}
}
pub static DOMINATOR_TREE_TIME: AtomicU64 = AtomicU64::new(0);