use std::collections::{BTreeMap, BTreeSet};
use crate::decompiler::cfg::{BlockId, Cfg};
mod algorithm;
mod frontier;
mod traversal;
#[cfg(test)]
mod tests;
#[derive(Debug, Clone)]
pub struct DominanceInfo {
pub idom: BTreeMap<BlockId, Option<BlockId>>,
pub dominator_tree: BTreeMap<BlockId, Vec<BlockId>>,
pub dominance_frontier: BTreeMap<BlockId, BTreeSet<BlockId>>,
}
impl DominanceInfo {
#[must_use]
pub fn new() -> Self {
Self {
idom: BTreeMap::new(),
dominator_tree: BTreeMap::new(),
dominance_frontier: BTreeMap::new(),
}
}
#[must_use]
pub fn idom(&self, block: BlockId) -> Option<BlockId> {
let idom = self.idom.get(&block).copied().flatten();
if idom == Some(block) {
None
} else {
idom
}
}
#[must_use]
pub fn children(&self, block: BlockId) -> &[BlockId] {
self.dominator_tree
.get(&block)
.map(Vec::as_slice)
.unwrap_or(&[])
}
#[must_use]
pub fn dominance_frontier_vec(&self, block: BlockId) -> Vec<BlockId> {
self.dominance_frontier
.get(&block)
.map(|set| set.iter().copied().collect())
.unwrap_or_default()
}
#[must_use]
pub fn strictly_dominates(&self, a: BlockId, b: BlockId) -> bool {
if a == b {
return false;
}
let mut current = self.idom(b);
while let Some(idom) = current {
if idom == a {
return true;
}
current = self.idom(idom);
}
false
}
}
impl Default for DominanceInfo {
fn default() -> Self {
Self::new()
}
}
#[must_use]
pub fn compute(cfg: &Cfg) -> DominanceInfo {
if cfg.blocks().count() == 0 {
return DominanceInfo::new();
}
let idom = algorithm::compute_immediate_dominators(cfg);
let dominator_tree = algorithm::build_dominator_tree(&idom);
let dominance_frontier = frontier::compute_df(cfg, &idom);
DominanceInfo {
idom,
dominator_tree,
dominance_frontier,
}
}