mod node;
pub use node::CFGNode;
use crate::util::unique_vec::UniqueVec;
use std::fmt;
use std::collections::HashMap;
use llvm_ir::{
Function,
Name,
Terminator
};
#[derive(Clone)]
pub struct ControlFlowGraph {
entry : CFGNode,
nodes : UniqueVec<CFGNode>,
preds : HashMap<CFGNode, UniqueVec<CFGNode>>,
succs : HashMap<CFGNode, UniqueVec<CFGNode>>,
temps : UniqueVec<Name>,
next_temp : u128
}
impl ControlFlowGraph {
pub fn new(function : &Function) -> Self {
let mut cfg = ControlFlowGraph {
entry : (&function.basic_blocks[0].name).into(),
nodes : UniqueVec::new(),
preds : HashMap::new(),
succs : HashMap::new(),
temps : UniqueVec::new(),
next_temp : 0
};
for block in &function.basic_blocks { match (&block.term) {
Terminator::Br(term) => {
cfg.add_edge(&block.name, &term.dest);
},
Terminator::CondBr(term) => {
cfg.add_edge(&block.name, &term.true_dest);
cfg.add_edge(&block.name, &term.false_dest);
},
Terminator::Switch(term) => {
for (_, dest) in &term.dests {
cfg.add_edge(&block.name, dest);
}
cfg.add_edge(&block.name, &term.default_dest);
},
Terminator::IndirectBr(term) => {
for dest in &term.possible_dests {
cfg.add_edge(&block.name, dest);
}
},
Terminator::Ret(_) | Terminator::Unreachable(_) => { },
term @ Terminator::Invoke (_) |
term @ Terminator::Resume (_) |
term @ Terminator::CleanupRet (_) |
term @ Terminator::CatchRet (_) |
term @ Terminator::CatchSwitch (_) |
term @ Terminator::CallBr (_)
=> { panic!("Unsupported terminator: {}", term) }
} }
cfg
}
pub fn entry(&self) -> &CFGNode { &self.entry }
pub(crate) fn set_entry<N : Into<CFGNode>>(&mut self, node : N) -> () { self.entry = node.into(); }
pub fn nodes(&self) -> &UniqueVec<CFGNode> { &self.nodes }
pub fn preds<N : Into<CFGNode>>(&self, node : N) -> Option<&UniqueVec<CFGNode>> { self.preds.get(&node.into()) }
pub fn succs<N : Into<CFGNode>>(&self, node : N) -> Option<&UniqueVec<CFGNode>> { self.succs.get(&node.into()) }
pub fn temps(&self) -> &UniqueVec<Name> { &self.temps }
pub fn add_edge<F : Into<CFGNode>, T : Into<CFGNode>>(&mut self, from : F, to : T) -> () {
let from = from.into();
let to = to.into();
self.preds.entry(to.clone()).or_insert_with(|| UniqueVec::new()).insert(from.clone());
self.succs.entry(from.clone()).or_insert_with(|| UniqueVec::new()).insert(to.clone());
self.nodes.insert(from.clone());
self.nodes.insert(to.clone());
}
pub fn remove_node<N : Into<CFGNode>>(&mut self, node : N) -> () {
let node = node.into();
self.nodes.remove(&node);
self.preds.remove(&node);
self.succs.remove(&node);
for (_, preds) in &mut self.preds {
preds.remove(&node);
}
for (_, succs) in &mut self.succs {
succs.remove(&node);
}
}
pub fn insert_node<N : Into<CFGNode>, A : Into<CFGNode>, B : Into<CFGNode>>(&mut self, node : N, after : A, before : B) -> () {
let node = node.into();
let after = after.into();
let before = before.into();
if let Some(succs) = self.succs.get_mut(&after) {
succs.remove(&before);
}
if let Some(preds) = self.preds.get_mut(&before) {
preds.remove(&after);
}
self.add_edge(&after, &node);
self.add_edge(&node, &before);
}
pub fn create_temporary_node(&mut self) -> Name {
let mut name;
loop {
name = Name::Name(Box::new(format!("@{}_TEMPORARY_{}", crate::MODULE_NAME.to_uppercase(), self.next_temp)));
self.next_temp += 1;
if (! self.nodes.contains(&(&name).into())) { break; }
}
self.temps.insert(name.clone());
name
}
pub fn dominates<H : Into<CFGNode>, O : Into<CFGNode>>(&self, through : H, to : O) -> bool {
let through = through.into();
let to = to.into();
if (through == to) { return true; }
self.dominates_inner(&self.entry, &through, &to, &mut Vec::new())
}
fn dominates_inner<'l>(&'l self, at : &'l CFGNode, through : &CFGNode, to : &CFGNode, already_checked : &mut Vec<&'l CFGNode>) -> bool {
already_checked.push(at);
let Some(succs) = self.succs.get(at) else { return true };
for succ in succs {
if (already_checked.contains(&succ)) { continue; }
if (succ == through) { continue; }
if (! self.dominates_inner(succ, through, to, already_checked)) {
return false;
}
}
true
}
}
impl fmt::Display for ControlFlowGraph {
fn fmt(&self, f : &mut fmt::Formatter<'_>) -> fmt::Result {
let mut first = true;
for node in &self.nodes {
if (first) { first = false; }
else { writeln!(f)?; }
if let Some(preds) = self.preds.get(node) { if (preds.len() > 0) {
write!(f, " ↙‾")?;
for pred in preds {
write!(f, " {}", pred)?;
}
writeln!(f)?;
} }
if (node == &self.entry) {
writeln!(f, " \x1b[92m\x1b[1m{}\x1b[0m", node)?;
} else {
writeln!(f, " \x1b[97m\x1b[1m{}\x1b[0m", node)?;
}
if let Some(succs) = self.succs.get(node) { if (succs.len() > 0) {
write!(f, " ↘_")?;
for succ in succs {
write!(f, " {}", succ)?;
}
writeln!(f)?;
} }
}
Ok(())
}
}