use crate::{
graph::{iter::EdgesWalker, Edge, Edges, Node, Nodes},
map::{IntKeyMap, Map},
FrozenGraph,
};
use alloc::vec;
use core::fmt::Debug;
pub trait WithImmDom {
type Ty: Copy + Eq + Debug + 'static;
fn imm_dom(&self) -> Option<Self::Ty>;
fn set_imm_dom(&mut self, dom: Self::Ty);
}
fn calc_imm_dominators_impl<N, E, NI, EI, NM, EM>(
nodes: &mut Nodes<N, EI, NM>,
edges: &Edges<E, NI, EI, EM>,
node_index: NI,
entry_idx: NI,
) where
N: WithImmDom<Ty = NI>,
NI: Copy + Eq + Debug + 'static,
EI: Copy + Eq + Debug + 'static,
NM: Map<Node<N, EI>, Key = NI>,
EM: IntKeyMap<Edge<E, NI, EI>, Key = EI>,
{
let mut successors = nodes.walk_successors(node_index);
while let Some(successor_idx) = successors.walk_edges_next(edges) {
if successor_idx == entry_idx {
continue;
}
match nodes[successor_idx].weight().imm_dom() {
None => {
nodes[successor_idx].weight_mut().set_imm_dom(node_index);
calc_imm_dominators_impl(nodes, edges, successor_idx, entry_idx);
}
Some(dominator) => {
assert_ne!(dominator, node_index);
let mut left_idx = dominator;
let mut left_path = vec![dominator];
let mut right_idx = node_index;
let mut right_path = vec![node_index];
loop {
if let Some(left_dominator) = nodes[left_idx].weight_mut().imm_dom() {
if right_path.contains(&left_dominator) {
nodes[successor_idx]
.weight_mut()
.set_imm_dom(left_dominator);
break;
}
left_idx = left_dominator;
left_path.push(left_dominator);
}
if let Some(right_dominator) = nodes[right_idx].weight_mut().imm_dom() {
if right_dominator == successor_idx {
break;
} else if left_path.contains(&right_dominator) {
nodes[successor_idx]
.weight_mut()
.set_imm_dom(right_dominator);
break;
}
right_idx = right_dominator;
right_path.push(right_dominator);
}
}
}
}
}
}
pub fn calc_imm_dominators<N, E, NI, EI, NM, EM>(
graph: &mut FrozenGraph<N, E, NI, EI, NM, EM>,
entry_index: NI,
) where
N: WithImmDom<Ty = NI>,
NI: Copy + Eq + Debug + 'static,
EI: Copy + Eq + Debug + 'static,
NM: Map<Node<N, EI>, Key = NI>,
EM: IntKeyMap<Edge<E, NI, EI>, Key = EI>,
{
let (nodes, edges) = graph.as_nodes_mut_and_edges();
calc_imm_dominators_impl(nodes, edges, entry_index, entry_index)
}
#[cfg(all(test, feature = "slotmap"))]
mod tests {
use super::*;
use crate::{aliases::SlotMapGraph, map::slotmap::NodeIndex};
struct TestNode(Option<NodeIndex>);
impl Default for TestNode {
fn default() -> Self {
TestNode(None)
}
}
impl WithImmDom for TestNode {
type Ty = NodeIndex;
fn imm_dom(&self) -> Option<Self::Ty> {
self.0
}
fn set_imm_dom(&mut self, dom: Self::Ty) {
self.0 = Some(dom);
}
}
#[test]
fn test_imm_dom_simple_diamond() {
let mut graph = SlotMapGraph::<TestNode, ()>::with_capacities(4, 4);
let entry_idx = graph.add_default_node();
let left_idx = graph.add_default_node();
let right_idx = graph.add_default_node();
let end_idx = graph.add_default_node();
graph.add_edge((), entry_idx, left_idx).unwrap();
graph.add_edge((), entry_idx, right_idx).unwrap();
graph.add_edge((), left_idx, end_idx).unwrap();
graph.add_edge((), right_idx, end_idx).unwrap();
calc_imm_dominators(&mut graph, entry_idx);
assert_eq!(graph.node_weight(entry_idx).unwrap().imm_dom(), None);
assert_eq!(
graph.node_weight(left_idx).unwrap().imm_dom(),
Some(entry_idx)
);
assert_eq!(
graph.node_weight(right_idx).unwrap().imm_dom(),
Some(entry_idx)
);
assert_eq!(
graph.node_weight(end_idx).unwrap().imm_dom(),
Some(entry_idx)
);
}
#[test]
fn test_imm_dom_simple_loop() {
let mut graph = SlotMapGraph::<TestNode, ()>::with_capacities(5, 5);
let entry_idx = graph.add_default_node();
let loop_entry_idx = graph.add_default_node();
let loop_body_idx = graph.add_default_node();
let loop_exit_idx = graph.add_default_node();
let end_idx = graph.add_default_node();
graph.add_edge((), entry_idx, loop_entry_idx).unwrap();
graph.add_edge((), loop_entry_idx, loop_body_idx).unwrap();
graph.add_edge((), loop_body_idx, loop_exit_idx).unwrap();
graph.add_edge((), loop_exit_idx, loop_entry_idx).unwrap();
graph.add_edge((), loop_entry_idx, end_idx).unwrap();
calc_imm_dominators(&mut graph, entry_idx);
assert_eq!(graph.node_weight(entry_idx).unwrap().imm_dom(), None);
assert_eq!(
graph.node_weight(loop_entry_idx).unwrap().imm_dom(),
Some(entry_idx)
);
assert_eq!(
graph.node_weight(loop_body_idx).unwrap().imm_dom(),
Some(loop_entry_idx)
);
assert_eq!(
graph.node_weight(loop_exit_idx).unwrap().imm_dom(),
Some(loop_body_idx)
);
assert_eq!(
graph.node_weight(end_idx).unwrap().imm_dom(),
Some(loop_entry_idx)
);
}
#[test]
fn test_imm_dom_nested_loop() {
let mut graph = SlotMapGraph::<TestNode, ()>::with_capacities(9, 10);
let entry_idx = graph.add_default_node();
let outer_loop_entry_idx = graph.add_default_node();
let outer_loop_start_idx = graph.add_default_node();
let inner_loop_entry_idx = graph.add_default_node();
let inner_loop_body_idx = graph.add_default_node();
let inner_loop_exit_idx = graph.add_default_node();
let outer_loop_end_idx = graph.add_default_node();
let outer_loop_exit_idx = graph.add_default_node();
let end_idx = graph.add_default_node();
graph.add_edge((), entry_idx, outer_loop_entry_idx).unwrap();
graph
.add_edge((), outer_loop_entry_idx, outer_loop_start_idx)
.unwrap();
graph.add_edge((), outer_loop_entry_idx, end_idx).unwrap();
graph
.add_edge((), outer_loop_start_idx, inner_loop_entry_idx)
.unwrap();
graph
.add_edge((), inner_loop_entry_idx, inner_loop_body_idx)
.unwrap();
graph
.add_edge((), inner_loop_entry_idx, outer_loop_end_idx)
.unwrap();
graph
.add_edge((), inner_loop_body_idx, inner_loop_exit_idx)
.unwrap();
graph
.add_edge((), inner_loop_exit_idx, inner_loop_entry_idx)
.unwrap();
graph
.add_edge((), outer_loop_end_idx, outer_loop_exit_idx)
.unwrap();
graph
.add_edge((), outer_loop_exit_idx, outer_loop_entry_idx)
.unwrap();
calc_imm_dominators(&mut graph, entry_idx);
assert_eq!(graph.node_weight(entry_idx).unwrap().imm_dom(), None);
assert_eq!(
graph.node_weight(outer_loop_entry_idx).unwrap().imm_dom(),
Some(entry_idx)
);
assert_eq!(
graph.node_weight(outer_loop_start_idx).unwrap().imm_dom(),
Some(outer_loop_entry_idx)
);
assert_eq!(
graph.node_weight(inner_loop_entry_idx).unwrap().imm_dom(),
Some(outer_loop_start_idx)
);
assert_eq!(
graph.node_weight(inner_loop_body_idx).unwrap().imm_dom(),
Some(inner_loop_entry_idx)
);
assert_eq!(
graph.node_weight(inner_loop_exit_idx).unwrap().imm_dom(),
Some(inner_loop_body_idx)
);
assert_eq!(
graph.node_weight(outer_loop_end_idx).unwrap().imm_dom(),
Some(inner_loop_entry_idx)
);
assert_eq!(
graph.node_weight(outer_loop_exit_idx).unwrap().imm_dom(),
Some(outer_loop_end_idx)
);
assert_eq!(
graph.node_weight(end_idx).unwrap().imm_dom(),
Some(outer_loop_entry_idx)
);
}
}