use std::collections::{HashMap, HashSet};
use std::fs;
use std::path::Path;
use surge_frontend::pipeline::classifier::FamilyClassifier;
use surge_frontend::pipeline::language::PipelineLanguage;
use surge_frontend::pipeline::produce;
use vyre_primitives::graph::program_graph::ProgramGraphShape;
use weir::callgraph::{callgraph_build, callgraph_build_with_count};
use weir::control_dependence::control_dependence;
use weir::cross_language::cross_language;
use weir::dominators::dominates;
use weir::escape::escape_analyze_with_count;
use weir::escapes::escapes;
use weir::graph_layout::CsrGraph;
use weir::ifds::ifds_reach_step;
use weir::live::prepare_live_graph;
use weir::live_at::live_at;
use weir::loop_sum::{loop_summarize, try_loop_summarize_with_count};
use weir::may_alias::may_alias;
use weir::must_init::must_init;
use weir::points_to::prepare_points_to_subset_graph;
use weir::post_dominates::post_dominates;
use weir::range::{range_propagate, try_range_propagate_with_count};
use weir::range_check::range_check;
use weir::reaching::prepare_reaching_graph;
use weir::reaching_def::reaching_def;
use weir::scc_query::scc_query;
use weir::slice::prepare_slice_graph;
use weir::ssa::{try_compute_dominators, Block, Cfg};
use weir::summary::{summarize_function, summarize_function_with_count};
use weir::traversal_step::forward_cfg_step;
use weir::value_set::value_set;
#[test]
fn cross_language_matrix() {
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let df_micro = manifest_dir.join("tests").join("df_micro");
let mut c_files: Vec<_> = fs::read_dir(&df_micro)
.expect("read df_micro")
.filter_map(|e| e.ok())
.filter(|e| e.path().extension().map(|ext| ext == "c").unwrap_or(false))
.map(|e| e.path())
.collect();
c_files.sort();
assert!(
c_files.len() >= 100,
"expected at least 100 .c files, found {}",
c_files.len()
);
let cls = FamilyClassifier::launch_defaults();
for path in &c_files {
let source = fs::read(path).unwrap_or_else(|_| panic!("read {}", path.display()));
let pg = produce(PipelineLanguage::C, &source, &cls);
assert!(
pg.node_count > 0 || pg.edge_count == 0,
"{}: parsed zero nodes but non-zero edges is suspicious",
path.display()
);
if pg.node_count == 0 {
continue;
}
let mut edges: Vec<(u32, u32, u32)> = pg.iter_edges().collect();
edges.sort_by_key(|(f, t, _k)| (*f, *t));
let n = pg.node_count as usize;
let mut offsets = vec![0u32; n + 1];
let mut targets = Vec::new();
let mut kinds = Vec::new();
let mut succs: Vec<Vec<u32>> = vec![Vec::new(); n];
for (f, t, k) in &edges {
offsets[(*f as usize) + 1] += 1;
targets.push(*t);
kinds.push(*k);
succs[*f as usize].push(*t);
}
for i in 1..=n {
offsets[i] += offsets[i - 1];
}
let shape = ProgramGraphShape::new(pg.node_count, pg.edge_count);
let normalized = CsrGraph::new(pg.node_count, &offsets, &targets, &kinds)
.normalize("test")
.expect("normalize should succeed for valid CSR");
assert_eq!(normalized.node_count(), pg.node_count);
let mut blocks = HashMap::new();
for i in 0..pg.node_count {
blocks.insert(
i,
Block {
id: i,
preds: Vec::new(),
succs: succs[i as usize].clone(),
defs: HashSet::new(),
uses: HashSet::new(),
},
);
}
for i in 0..pg.node_count {
let succ_list = succs[i as usize].clone();
for &s in &succ_list {
if let Some(b) = blocks.get_mut(&s) {
b.preds.push(i);
}
}
}
let cfg = Cfg { entry: 0, blocks };
let _ = try_compute_dominators(&cfg);
let prog = dominates(pg.node_count, "dom", "target", "out");
assert!(!prog.entry().is_empty());
let prog = post_dominates(pg.node_count, "pdom_set", "target_set", "out");
assert!(!prog.entry().is_empty());
let prog = control_dependence(pg.node_count, "succ_pdom", "pdom_n", "out");
assert!(!prog.entry().is_empty());
let prog = escapes(pg.node_count, "pts", "escape_set", "out");
assert!(!prog.entry().is_empty());
let prog = may_alias(pg.node_count, "pts_p", "pts_q", "scratch", "out_scalar");
assert!(!prog.entry().is_empty());
let prog = range_check(pg.node_count, "interval_hi", "bound", "out");
assert!(!prog.entry().is_empty());
let prog = value_set(pg.node_count, "const_in", "query", "out");
assert!(!prog.entry().is_empty());
let prog = scc_query(pg.node_count, "same_scc", "query", "out");
assert!(!prog.entry().is_empty());
let prog = must_init(pg.node_count, "def_dominates", "use_set", "out");
assert!(!prog.entry().is_empty());
let prog = reaching_def(pg.node_count, "gen_kill_in", "use_set", "out");
assert!(!prog.entry().is_empty());
let prog = live_at(pg.node_count, "live_in", "query", "out");
assert!(!prog.entry().is_empty());
let prog = callgraph_build("direct", "indirect", "pts", "cg_out");
assert!(!prog.entry().is_empty());
let prog = callgraph_build_with_count("direct", "indirect", "pts", "cg_out", pg.node_count);
assert!(!prog.entry().is_empty());
let prog = escape_analyze_with_count("pts", "cg", "out", pg.node_count);
assert!(!prog.entry().is_empty());
let prog = loop_summarize("prev", "next", "out");
assert!(!prog.entry().is_empty());
let prog = try_loop_summarize_with_count("prev", "next", "out", pg.node_count)
.expect("loop_summarize_with_count should build for parsed node count");
assert!(!prog.entry().is_empty());
let prog = range_propagate("defs_in", "edges_in", "ranges_out");
assert!(!prog.entry().is_empty());
let prog =
try_range_propagate_with_count("defs_in", "edges_in", "ranges_out", pg.node_count)
.expect("range_propagate_with_count should build for parsed node count");
assert!(!prog.entry().is_empty());
let prog = summarize_function("fn_ast", "callgraph", "cached", "summary");
assert!(!prog.entry().is_empty());
let prog = summarize_function_with_count(
"fn_ast",
"callgraph",
"cached",
"summary",
pg.node_count,
);
assert!(!prog.entry().is_empty());
let prog = cross_language(
pg.node_count,
"source",
"sink",
"post_cross",
"current",
"next",
"changed",
"seed",
"out",
);
assert!(
prog.is_ok(),
"cross_language construction failed: {:?}",
prog.err()
);
assert!(!prog.unwrap().entry().is_empty());
let prog = forward_cfg_step(shape, "fin", "fout");
assert!(!prog.entry().is_empty());
let prog = ifds_reach_step(shape, "fin", "fout");
assert!(!prog.entry().is_empty());
let g = prepare_reaching_graph(pg.node_count, &offsets, &targets, &kinds);
assert!(g.is_ok(), "prepare_reaching_graph failed: {:?}", g.err());
assert_eq!(g.unwrap().node_count(), pg.node_count);
let g = prepare_live_graph(pg.node_count, &offsets, &targets, &kinds);
assert!(g.is_ok(), "prepare_live_graph failed: {:?}", g.err());
assert_eq!(g.unwrap().node_count(), pg.node_count);
let g = prepare_points_to_subset_graph(pg.node_count, &offsets, &targets, &kinds);
assert!(
g.is_ok(),
"prepare_points_to_subset_graph failed: {:?}",
g.err()
);
assert_eq!(g.unwrap().node_count(), pg.node_count);
let g = prepare_slice_graph(pg.node_count, &offsets, &targets, &kinds);
assert!(g.is_ok(), "prepare_slice_graph failed: {:?}", g.err());
assert_eq!(g.unwrap().node_count(), pg.node_count);
}
}