gcrecomp_core/recompiler/analysis/
inter_procedural.rs1use std::collections::{HashMap, HashSet};
3
4#[derive(Debug, Clone)]
5pub struct CallGraph {
6 pub nodes: Vec<FunctionNode>,
7 pub edges: Vec<CallEdge>,
8}
9
10#[derive(Debug, Clone)]
11pub struct FunctionNode {
12 pub address: u32,
13 pub name: String,
14 pub is_entry_point: bool,
15 pub callers: Vec<usize>,
16 pub callees: Vec<usize>,
17}
18
19#[derive(Debug, Clone)]
20pub struct CallEdge {
21 pub caller: usize,
22 pub callee: usize,
23 pub call_sites: Vec<u32>,
24}
25
26pub struct InterProceduralAnalyzer;
27
28impl InterProceduralAnalyzer {
29 pub fn build_call_graph(functions: &[crate::recompiler::ghidra::FunctionInfo]) -> CallGraph {
30 let mut nodes = Vec::new();
31 let mut edges = Vec::new();
32 let mut address_to_index: HashMap<u32, usize> = HashMap::new();
33
34 for (idx, func) in functions.iter().enumerate() {
36 address_to_index.insert(func.address, idx);
37 nodes.push(FunctionNode {
38 address: func.address,
39 name: func.name.clone(),
40 is_entry_point: idx == 0, callers: vec![],
42 callees: vec![],
43 });
44 }
45
46 CallGraph { nodes, edges }
51 }
52
53 pub fn find_unreachable_functions(call_graph: &CallGraph) -> Vec<usize> {
54 let mut reachable = HashSet::new();
55 let mut queue = Vec::new();
56
57 for (idx, node) in call_graph.nodes.iter().enumerate() {
59 if node.is_entry_point {
60 queue.push(idx);
61 reachable.insert(idx);
62 }
63 }
64
65 while let Some(node_idx) = queue.pop() {
67 for &callee in &call_graph.nodes[node_idx].callees {
68 if !reachable.contains(&callee) {
69 reachable.insert(callee);
70 queue.push(callee);
71 }
72 }
73 }
74
75 (0..call_graph.nodes.len())
77 .filter(|idx| !reachable.contains(idx))
78 .collect()
79 }
80}
81