use itertools::Itertools;
use llvm_ir::{Module, Name};
use llvm_ir_analysis::*;
fn init_logging() {
let _ = env_logger::builder().is_test(true).try_init();
}
const PANIC_BC_PATH: &'static str = "tests/bcfiles/panic.bc";
#[test]
fn begin_panic_cfg() {
init_logging();
let module = Module::from_bc_path(PANIC_BC_PATH)
.unwrap_or_else(|e| panic!("Failed to parse module: {}", e));
let analysis = ModuleAnalysis::new(&module);
let cfg = analysis
.fn_analysis("_ZN3std9panicking11begin_panic17h5ae0871c3ba84f98E")
.control_flow_graph();
let bbstart_name = Name::from("start");
let _bbstart_node = CFGNode::Block(&bbstart_name);
let bb1_name = Name::from("bb1");
let bb1_node = CFGNode::Block(&bb1_name);
let bb2_name = Name::from("bb2");
let bb2_node = CFGNode::Block(&bb2_name);
let bb3_name = Name::from("bb3");
let bb3_node = CFGNode::Block(&bb3_name);
let bb4_name = Name::from("bb4");
let bb4_node = CFGNode::Block(&bb4_name);
let bb5_name = Name::from("bb5");
let bb5_node = CFGNode::Block(&bb5_name);
let bb6_name = Name::from("bb6");
let bb6_node = CFGNode::Block(&bb6_name);
let bbcleanup_name = Name::from("cleanup");
let bbcleanup_node = CFGNode::Block(&bbcleanup_name);
let bbcleanup1_name = Name::from("cleanup1");
let bbcleanup1_node = CFGNode::Block(&bbcleanup1_name);
let bbunreachable_name = Name::from("unreachable");
let bbunreachable_node = CFGNode::Block(&bbunreachable_name);
let bbstart_preds: Vec<&Name> = cfg.preds(&bbstart_name).sorted().collect();
assert!(bbstart_preds.is_empty());
let bbstart_succs: Vec<CFGNode> = cfg.succs(&bbstart_name).sorted().collect();
assert_eq!(bbstart_succs, vec![bb2_node, bbcleanup_node]);
let bb1_preds: Vec<&Name> = cfg.preds(&bb1_name).sorted().collect();
assert_eq!(bb1_preds, vec![&bb5_name, &bb6_name]);
let bb1_succs: Vec<CFGNode> = cfg.succs(&bb1_name).sorted().collect();
assert_eq!(bb1_succs, vec![CFGNode::Return]);
let bb2_preds: Vec<&Name> = cfg.preds(&bb2_name).sorted().collect();
assert_eq!(bb2_preds, vec![&bbstart_name]);
let bb2_succs: Vec<CFGNode> = cfg.succs(&bb2_name).sorted().collect();
assert_eq!(bb2_succs, vec![bb4_node, bbcleanup1_node]);
let bb3_preds: Vec<&Name> = cfg.preds(&bb3_name).sorted().collect();
assert_eq!(bb3_preds, vec![&bbcleanup1_name]);
let bb3_succs: Vec<CFGNode> = cfg.succs(&bb3_name).sorted().collect();
assert_eq!(bb3_succs, vec![bb6_node]);
let bb4_preds: Vec<&Name> = cfg.preds(&bb4_name).sorted().collect();
assert_eq!(bb4_preds, vec![&bb2_name]);
let bb4_succs: Vec<CFGNode> = cfg.succs(&bb4_name).sorted().collect();
assert_eq!(bb4_succs, vec![bbcleanup1_node, bbunreachable_node]);
let bb5_preds: Vec<&Name> = cfg.preds(&bb5_name).sorted().collect();
assert_eq!(bb5_preds, vec![&bb6_name]);
let bb5_succs: Vec<CFGNode> = cfg.succs(&bb5_name).sorted().collect();
assert_eq!(bb5_succs, vec![bb1_node]);
let bb6_preds: Vec<&Name> = cfg.preds(&bb6_name).sorted().collect();
assert_eq!(bb6_preds, vec![&bb3_name, &bbcleanup_name]);
let bb6_succs: Vec<CFGNode> = cfg.succs(&bb6_name).sorted().collect();
assert_eq!(bb6_succs, vec![bb1_node, bb5_node]);
let bbcleanup_preds: Vec<&Name> = cfg.preds(&bbcleanup_name).sorted().collect();
assert_eq!(bbcleanup_preds, vec![&bbstart_name]);
let bbcleanup_succs: Vec<CFGNode> = cfg.succs(&bbcleanup_name).sorted().collect();
assert_eq!(bbcleanup_succs, vec![bb6_node]);
let bbcleanup1_preds: Vec<&Name> = cfg.preds(&bbcleanup1_name).sorted().collect();
assert_eq!(bbcleanup1_preds, vec![&bb2_name, &bb4_name]);
let bbcleanup1_succs: Vec<CFGNode> = cfg.succs(&bbcleanup1_name).sorted().collect();
assert_eq!(bbcleanup1_succs, vec![bb3_node]);
let bbunreachable_preds: Vec<&Name> = cfg.preds(&bbunreachable_name).sorted().collect();
assert_eq!(bbunreachable_preds, vec![&bb4_name]);
let bbunreachable_succs: Vec<CFGNode> = cfg.succs(&bbunreachable_name).sorted().collect();
assert!(bbunreachable_succs.is_empty());
let return_preds: Vec<&Name> = cfg.preds_of_return().sorted().collect();
assert_eq!(return_preds, vec![&bb1_name]);
}
#[test]
fn begin_panic_domtree() {
init_logging();
let module = Module::from_bc_path(PANIC_BC_PATH)
.unwrap_or_else(|e| panic!("Failed to parse module: {}", e));
let analysis = ModuleAnalysis::new(&module);
let fn_analysis = analysis.fn_analysis("_ZN3std9panicking11begin_panic17h5ae0871c3ba84f98E");
let domtree = fn_analysis.dominator_tree();
assert_eq!(domtree.idom(&Name::from("start")), None);
assert_eq!(domtree.idom(&Name::from("bb1")), Some(&Name::from("bb6")));
assert_eq!(domtree.idom(&Name::from("bb2")), Some(&Name::from("start")));
assert_eq!(
domtree.idom(&Name::from("bb3")),
Some(&Name::from("cleanup1"))
);
assert_eq!(domtree.idom(&Name::from("bb4")), Some(&Name::from("bb2")));
assert_eq!(domtree.idom(&Name::from("bb5")), Some(&Name::from("bb6")));
assert_eq!(domtree.idom(&Name::from("bb6")), Some(&Name::from("start")));
assert_eq!(
domtree.idom(&Name::from("cleanup")),
Some(&Name::from("start"))
);
assert_eq!(
domtree.idom(&Name::from("cleanup1")),
Some(&Name::from("bb2"))
);
assert_eq!(
domtree.idom(&Name::from("unreachable")),
Some(&Name::from("bb4"))
);
let postdomtree = fn_analysis.postdominator_tree();
assert_eq!(
postdomtree.ipostdom(&Name::from("start")),
Some(CFGNode::Block(&Name::from("bb6")))
);
assert_eq!(
postdomtree.ipostdom(&Name::from("bb1")),
Some(CFGNode::Return)
);
assert_eq!(
postdomtree.ipostdom(&Name::from("bb2")),
Some(CFGNode::Block(&Name::from("cleanup1")))
);
assert_eq!(
postdomtree.ipostdom(&Name::from("bb3")),
Some(CFGNode::Block(&Name::from("bb6")))
);
assert_eq!(
postdomtree.ipostdom(&Name::from("bb4")),
Some(CFGNode::Block(&Name::from("cleanup1")))
);
assert_eq!(
postdomtree.ipostdom(&Name::from("bb5")),
Some(CFGNode::Block(&Name::from("bb1")))
);
assert_eq!(
postdomtree.ipostdom(&Name::from("bb6")),
Some(CFGNode::Block(&Name::from("bb1")))
);
assert_eq!(
postdomtree.ipostdom(&Name::from("cleanup")),
Some(CFGNode::Block(&Name::from("bb6")))
);
assert_eq!(
postdomtree.ipostdom(&Name::from("cleanup1")),
Some(CFGNode::Block(&Name::from("bb3")))
);
assert_eq!(postdomtree.ipostdom(&Name::from("unreachable")), None);
}
#[test]
fn begin_panic_cdg() {
init_logging();
let module = Module::from_bc_path(PANIC_BC_PATH)
.unwrap_or_else(|e| panic!("Failed to parse module: {}", e));
let analysis = ModuleAnalysis::new(&module);
let cdg = analysis
.fn_analysis("_ZN3std9panicking11begin_panic17h5ae0871c3ba84f98E")
.control_dependence_graph();
assert_eq!(
cdg.get_imm_control_dependencies(&Name::from("start"))
.count(),
0
);
assert_eq!(
cdg.get_imm_control_dependencies(&Name::from("bb1")).count(),
0
);
assert_eq!(
cdg.get_imm_control_dependencies(&Name::from("bb2"))
.collect::<Vec<_>>(),
vec![&Name::from("start")]
);
assert_eq!(
cdg.get_imm_control_dependencies(&Name::from("bb3"))
.collect::<Vec<_>>(),
vec![&Name::from("start")]
);
assert_eq!(
cdg.get_imm_control_dependencies(&Name::from("bb4"))
.collect::<Vec<_>>(),
vec![&Name::from("bb2")]
);
assert_eq!(
cdg.get_imm_control_dependencies(&Name::from("bb5"))
.collect::<Vec<_>>(),
vec![&Name::from("bb6")]
);
assert_eq!(
cdg.get_imm_control_dependencies(&Name::from("bb6")).count(),
0
);
assert_eq!(
cdg.get_imm_control_dependencies(&Name::from("cleanup"))
.collect::<Vec<_>>(),
vec![&Name::from("start")]
);
assert_eq!(
cdg.get_imm_control_dependencies(&Name::from("cleanup1"))
.collect::<Vec<_>>(),
vec![&Name::from("start")]
);
assert_eq!(
cdg.get_imm_control_dependencies(&Name::from("unreachable"))
.count(),
0
);
}