@@ -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
284368impl PcodeStore for PcodeCfg < ConcretePcodeAddress , PcodeOperation > {
0 commit comments