use std::collections::BTreeSet;
use super::cfg::{BlockRef, EdgeRef};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GraphFacts {
pub rpo: Vec<BlockRef>,
pub dominator_tree: DominatorTree,
pub post_dominator_tree: PostDominatorTree,
pub dominance_frontier: Vec<BTreeSet<BlockRef>>,
pub backedges: Vec<EdgeRef>,
pub loop_headers: BTreeSet<BlockRef>,
pub natural_loops: Vec<NaturalLoop>,
pub children: Vec<GraphFacts>,
}
impl GraphFacts {
pub fn dominance_frontier_blocks(
&self,
block: BlockRef,
) -> impl Iterator<Item = BlockRef> + '_ {
self.dominance_frontier
.get(block.index())
.into_iter()
.flat_map(|frontier| frontier.iter().copied())
}
pub fn dominance_frontier_is_empty(&self, block: BlockRef) -> bool {
self.dominance_frontier
.get(block.index())
.is_none_or(BTreeSet::is_empty)
}
pub fn dominates(&self, dom: BlockRef, block: BlockRef) -> bool {
self.dominator_tree.dominates(dom, block)
}
pub fn post_dominates(&self, dom: BlockRef, block: BlockRef) -> bool {
self.post_dominator_tree.dominates(dom, block)
}
pub fn nearest_common_postdom(&self, left: BlockRef, right: BlockRef) -> Option<BlockRef> {
self.post_dominator_tree
.nearest_common_ancestor(left, right)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct DominatorTree {
pub parent: Vec<Option<BlockRef>>,
pub children: Vec<Vec<BlockRef>>,
pub order: Vec<BlockRef>,
}
impl DominatorTree {
pub fn dominates(&self, dom: BlockRef, block: BlockRef) -> bool {
tree_dominates(&self.parent, dom, block)
}
pub fn nearest_common_ancestor(&self, left: BlockRef, right: BlockRef) -> Option<BlockRef> {
nearest_common_tree_ancestor(&self.parent, left, right)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct PostDominatorTree {
pub parent: Vec<Option<BlockRef>>,
pub children: Vec<Vec<BlockRef>>,
pub order: Vec<BlockRef>,
}
impl PostDominatorTree {
pub fn dominates(&self, dom: BlockRef, block: BlockRef) -> bool {
tree_dominates(&self.parent, dom, block)
}
pub fn nearest_common_ancestor(&self, left: BlockRef, right: BlockRef) -> Option<BlockRef> {
nearest_common_tree_ancestor(&self.parent, left, right)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NaturalLoop {
pub header: BlockRef,
pub backedge: EdgeRef,
pub blocks: BTreeSet<BlockRef>,
}
fn tree_dominates(parent: &[Option<BlockRef>], dom: BlockRef, mut block: BlockRef) -> bool {
if dom == block {
return true;
}
while let Some(next) = parent[block.index()] {
if next == dom {
return true;
}
block = next;
}
false
}
fn nearest_common_tree_ancestor(
parent: &[Option<BlockRef>],
left: BlockRef,
right: BlockRef,
) -> Option<BlockRef> {
let mut ancestors = BTreeSet::new();
let mut cursor = Some(left);
while let Some(block) = cursor {
ancestors.insert(block);
cursor = parent[block.index()];
}
let mut cursor = Some(right);
while let Some(block) = cursor {
if ancestors.contains(&block) {
return Some(block);
}
cursor = parent[block.index()];
}
None
}