Skip to content

Commit 269b0df

Browse files
authored
feat: add an api to get a sub-graph (#141)
* feat: add PcodeCfg::sub_cfg_from_indices to build sub-CFG from a set of nodes and immediate successors * Fix sub_cfg_from_indices to exclude successors outside selection * Include destination in Call inputs for PcodeOperation
1 parent 6d9e211 commit 269b0df

File tree

2 files changed

+87
-3
lines changed

2 files changed

+87
-3
lines changed

jingle/src/analysis/cfg/mod.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,90 @@ impl<N: CfgState, D: ModelTransition<N::Model>> PcodeCfg<N, D> {
279279
pub fn smt_model(self, info: SleighArchInfo) -> ModeledPcodeCfg<N, D> {
280280
ModeledPcodeCfg::new(self, info)
281281
}
282+
283+
/// Build a sub-CFG from a set of `NodeIndex` values.
284+
///
285+
/// This will include:
286+
/// - all nodes whose indices are in `selected`
287+
/// - all edges between those nodes
288+
///
289+
/// If the resulting subgraph has more than one connected component (treating edges as undirected
290+
/// for connectivity), this function will panic.
291+
pub fn sub_cfg_from_indices(&self, selected: &HashSet<NodeIndex>) -> PcodeCfg<N, D> {
292+
// Build the set of NodeIndex that we will include: exactly the selected nodes.
293+
// Do NOT include outgoing neighbors or nodes that only have incoming edges into the selected set.
294+
let mut include: HashSet<NodeIndex> = HashSet::new();
295+
for &idx in selected {
296+
include.insert(idx);
297+
}
298+
299+
// Collect the nodes to work with
300+
let nodes: Vec<NodeIndex> = include.iter().copied().collect();
301+
302+
// If there are nodes, verify the induced subgraph is a single (undirected) component.
303+
if !nodes.is_empty() {
304+
let mut visited: HashSet<NodeIndex> = HashSet::new();
305+
let mut stack: Vec<NodeIndex> = Vec::new();
306+
307+
// Start from the first node
308+
stack.push(nodes[0]);
309+
visited.insert(nodes[0]);
310+
311+
while let Some(u) = stack.pop() {
312+
// Outgoing neighbors
313+
for e in self.graph.edges_directed(u, Direction::Outgoing) {
314+
let v = e.target();
315+
if include.contains(&v) && visited.insert(v) {
316+
stack.push(v);
317+
}
318+
}
319+
// Incoming neighbors (treat graph as undirected for connectivity)
320+
for e in self.graph.edges_directed(u, Direction::Incoming) {
321+
let v = e.source();
322+
if include.contains(&v) && visited.insert(v) {
323+
stack.push(v);
324+
}
325+
}
326+
}
327+
328+
if visited.len() != include.len() {
329+
panic!("sub_cfg_from_indices produced more than one component");
330+
}
331+
}
332+
333+
// Build the new graph and maps containing only the included nodes and edges between them
334+
let mut new_graph = StableDiGraph::<N, EmptyEdge>::default();
335+
let mut new_indices: HashMap<N, NodeIndex> = HashMap::new();
336+
let mut new_ops: HashMap<N, D> = HashMap::new();
337+
338+
// Add nodes and their ops
339+
for &old_idx in &nodes {
340+
let n = self.graph.node_weight(old_idx).unwrap().clone();
341+
let new_idx = new_graph.add_node(n.clone());
342+
new_indices.insert(n.clone(), new_idx);
343+
if let Some(op) = self.ops.get(&n) {
344+
new_ops.insert(n.clone(), op.clone());
345+
}
346+
}
347+
348+
// Add edges where both endpoints are included
349+
for edge in self.graph.edge_indices() {
350+
let (from, to) = self.graph.edge_endpoints(edge).unwrap();
351+
if include.contains(&from) && include.contains(&to) {
352+
let from_w = self.graph.node_weight(from).unwrap();
353+
let to_w = self.graph.node_weight(to).unwrap();
354+
let from_new = *new_indices.get(from_w).unwrap();
355+
let to_new = *new_indices.get(to_w).unwrap();
356+
new_graph.add_edge(from_new, to_new, EmptyEdge);
357+
}
358+
}
359+
360+
PcodeCfg {
361+
graph: new_graph,
362+
ops: new_ops,
363+
indices: new_indices,
364+
}
365+
}
282366
}
283367

284368
impl PcodeStore for PcodeCfg<ConcretePcodeAddress, PcodeOperation> {

jingle_sleigh/src/pcode/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -416,9 +416,9 @@ impl PcodeOperation {
416416
BranchInd { input, .. } => {
417417
vec![input.into()]
418418
}
419-
Call { args, .. } => {
420-
let b: Vec<_> = args.iter().map(GeneralizedVarNode::from).collect();
421-
419+
Call { args, dest, .. } => {
420+
let mut b = vec![GeneralizedVarNode::from(dest)];
421+
b.extend(args.iter().map(GeneralizedVarNode::from));
422422
b
423423
}
424424
CallInd { input, .. } => {

0 commit comments

Comments
 (0)