use alloc::collections::VecDeque;
use super::*;
use crate::{
BlockArgument, BlockRef, ValueRef,
adt::{SmallDenseMap, SmallSet},
};
#[derive(Default)]
pub struct DominanceFrontier {
dfs: SmallDenseMap<BlockRef, SmallSet<BlockRef, 2>, 8>,
}
impl DominanceFrontier {
pub fn new(domtree: &DominanceTree) -> Self {
let mut this = Self::default();
for node in domtree.postorder() {
let Some(node_block) = node.block() else {
continue;
};
let block = node_block.borrow();
let has_multiple_predecessors = block.predecessors().enumerate().any(|(i, _)| i > 1);
if !has_multiple_predecessors {
continue;
}
let idom = node
.idom()
.expect("expected immediate dominator for block with multiple predecessors");
let idom_block = idom.block().unwrap();
for pred in block.predecessors() {
let mut p = pred.predecessor();
while p != idom_block {
this.dfs.entry(p).or_default().insert(node_block);
let node_p = domtree.get(Some(p)).unwrap();
let Some(idom_p) = node_p.idom() else {
break;
};
p = idom_p.block().unwrap();
}
}
}
this
}
pub fn iterate(&self, block: BlockRef) -> SmallSet<BlockRef, 4> {
self.iterate_all([block])
}
pub fn iterate_all<I>(&self, blocks: I) -> SmallSet<BlockRef, 4>
where
I: IntoIterator<Item = BlockRef>,
{
let mut block_q = VecDeque::default();
let mut idf = SmallSet::<_, 4>::default();
let mut visit_block = |block: BlockRef, block_q: &mut VecDeque<BlockRef>| {
let Some(df) = self.dfs.get(&block) else {
return;
};
let added = df.difference(&idf);
if added.is_empty() {
return;
}
for block in added {
idf.insert(block);
if !block_q.contains(&block) {
block_q.push_back(block);
}
}
};
for block in blocks {
visit_block(block, &mut block_q);
}
while let Some(block) = block_q.pop_front() {
visit_block(block, &mut block_q);
}
idf
}
pub fn iter(&self, block: BlockRef) -> impl Iterator<Item = BlockRef> + '_ {
DominanceFrontierIter {
df: self.dfs.get(&block).map(|set| set.iter().copied()),
}
}
pub fn iter_by_value(&self, value: ValueRef) -> impl Iterator<Item = BlockRef> + '_ {
let v = value.borrow();
let defining_block = match v.get_defining_op() {
Some(op) => op.parent().unwrap(),
None => v.downcast_ref::<BlockArgument>().unwrap().owner(),
};
DominanceFrontierIter {
df: self.dfs.get(&defining_block).map(|set| set.iter().copied()),
}
}
#[inline]
pub fn get(&self, block: &BlockRef) -> Option<&SmallSet<BlockRef, 2>> {
self.dfs.get(block)
}
pub fn get_by_value(&self, value: ValueRef) -> Option<&SmallSet<BlockRef, 2>> {
let v = value.borrow();
let defining_block = match v.get_defining_op() {
Some(op) => op.parent().unwrap(),
None => v.downcast_ref::<BlockArgument>().unwrap().owner(),
};
self.dfs.get(&defining_block)
}
}
struct DominanceFrontierIter<I> {
df: Option<I>,
}
impl<I> Iterator for DominanceFrontierIter<I>
where
I: Iterator<Item = BlockRef>,
{
type Item = BlockRef;
fn next(&mut self) -> Option<Self::Item> {
if let Some(i) = self.df.as_mut() {
i.next()
} else {
None
}
}
}