go_brrr/pdg/
types.rs

1//! PDG (Program Dependence Graph) type definitions.
2//!
3//! A PDG combines control flow (CFG) and data flow (DFG) into a unified graph
4//! where slicing operations can follow both types of dependencies.
5//!
6//! Control dependency: "this statement executes only if that condition is true"
7//! Data dependency: "this statement uses a value computed by that statement"
8
9use serde::{Deserialize, Serialize};
10
11use crate::cfg::types::{BlockId, CFGInfo, EdgeType};
12use crate::dfg::types::DFGInfo;
13
14/// A control dependence between two lines.
15///
16/// Represents that `dependent_line` only executes based on the outcome
17/// of the condition at `condition_line`.
18///
19/// For example, in:
20/// ```text
21/// if x > 0:        # Line 2 - condition_line
22///     result = x   # Line 3 - dependent_line
23/// ```
24/// Line 3 is control-dependent on line 2.
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26pub struct ControlDependence {
27    /// Line number of the controlling condition (e.g., if/while condition)
28    pub condition_line: usize,
29    /// Line number that depends on the condition
30    pub dependent_line: usize,
31    /// Type of control dependence (true branch, false branch, etc.)
32    pub branch_type: BranchType,
33}
34
35/// Type of control dependence branch.
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
37pub enum BranchType {
38    /// Dependent on true branch of condition
39    True,
40    /// Dependent on false branch of condition
41    False,
42    /// Loop body depends on loop condition
43    Loop,
44    /// Exception handler depends on try block
45    Exception,
46    /// Unconditional flow (sequential)
47    Unconditional,
48}
49
50impl From<EdgeType> for BranchType {
51    fn from(edge_type: EdgeType) -> Self {
52        match edge_type {
53            EdgeType::True => BranchType::True,
54            EdgeType::False | EdgeType::Else => BranchType::False,
55            EdgeType::BackEdge | EdgeType::Loop | EdgeType::Iterate | EdgeType::Next => {
56                BranchType::Loop
57            }
58            EdgeType::Exception | EdgeType::ExceptionGroup => BranchType::Exception,
59            _ => BranchType::Unconditional,
60        }
61    }
62}
63
64/// Complete Program Dependence Graph combining CFG and DFG.
65///
66/// Provides access to:
67/// - The underlying CFG for control flow analysis
68/// - The underlying DFG for data flow analysis
69/// - Computed control dependencies for slicing operations
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct PDGInfo {
72    /// Function name
73    pub function_name: String,
74
75    /// Control flow graph (provides control structure)
76    pub cfg: CFGInfo,
77
78    /// Data flow graph (provides data dependencies)
79    pub dfg: DFGInfo,
80
81    /// Computed control dependencies.
82    ///
83    /// Maps from dependent line -> condition line.
84    /// These are derived from the CFG structure:
85    /// - Lines in the true branch of an if are control-dependent on the condition
86    /// - Lines in the false branch of an if are control-dependent on the condition
87    /// - Loop body lines are control-dependent on the loop condition
88    pub control_deps: Vec<ControlDependence>,
89}
90
91impl PDGInfo {
92    /// Create a new PDGInfo from CFG and DFG with computed control dependencies.
93    pub fn new(cfg: CFGInfo, dfg: DFGInfo, control_deps: Vec<ControlDependence>) -> Self {
94        let function_name = cfg.function_name.clone();
95        Self {
96            function_name,
97            cfg,
98            dfg,
99            control_deps,
100        }
101    }
102
103    /// Get all control dependency source lines for a given line.
104    ///
105    /// Returns lines that control whether `line` executes.
106    pub fn control_deps_for(&self, line: usize) -> Vec<usize> {
107        self.control_deps
108            .iter()
109            .filter(|dep| dep.dependent_line == line)
110            .map(|dep| dep.condition_line)
111            .collect()
112    }
113
114    /// Get all lines that are control-dependent on a given condition line.
115    ///
116    /// Returns lines whose execution depends on the condition at `condition_line`.
117    pub fn lines_controlled_by(&self, condition_line: usize) -> Vec<usize> {
118        self.control_deps
119            .iter()
120            .filter(|dep| dep.condition_line == condition_line)
121            .map(|dep| dep.dependent_line)
122            .collect()
123    }
124
125    /// Check if line_a is control-dependent on line_b.
126    pub fn is_control_dependent(&self, line_a: usize, line_b: usize) -> bool {
127        self.control_deps
128            .iter()
129            .any(|dep| dep.dependent_line == line_a && dep.condition_line == line_b)
130    }
131
132    /// Get total number of control dependencies.
133    pub fn control_dep_count(&self) -> usize {
134        self.control_deps.len()
135    }
136
137    /// Get total number of data flow edges.
138    pub fn data_edge_count(&self) -> usize {
139        self.dfg.edges.len()
140    }
141
142    /// Get all unique variables in the PDG.
143    pub fn variables(&self) -> Vec<&str> {
144        self.dfg.variables()
145    }
146
147    /// Get the block containing a specific line number.
148    pub fn block_for_line(&self, line: usize) -> Option<BlockId> {
149        self.cfg.block_for_line(line)
150    }
151
152    /// Convert to dictionary for JSON serialization.
153    pub fn to_dict(&self) -> serde_json::Value {
154        serde_json::json!({
155            "function_name": self.function_name,
156            "control_deps": self.control_deps.len(),
157            "data_edges": self.dfg.edges.len(),
158            "cfg_blocks": self.cfg.blocks.len(),
159            "cfg_edges": self.cfg.edges.len(),
160        })
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use super::*;
167
168    fn create_test_control_dep() -> ControlDependence {
169        ControlDependence {
170            condition_line: 2,
171            dependent_line: 3,
172            branch_type: BranchType::True,
173        }
174    }
175
176    #[test]
177    fn test_branch_type_from_edge_type() {
178        assert_eq!(BranchType::from(EdgeType::True), BranchType::True);
179        assert_eq!(BranchType::from(EdgeType::False), BranchType::False);
180        assert_eq!(BranchType::from(EdgeType::Else), BranchType::False);
181        assert_eq!(BranchType::from(EdgeType::BackEdge), BranchType::Loop);
182        assert_eq!(BranchType::from(EdgeType::Loop), BranchType::Loop);
183        assert_eq!(BranchType::from(EdgeType::Exception), BranchType::Exception);
184        assert_eq!(
185            BranchType::from(EdgeType::Unconditional),
186            BranchType::Unconditional
187        );
188    }
189
190    #[test]
191    fn test_control_dependence_equality() {
192        let dep1 = create_test_control_dep();
193        let dep2 = create_test_control_dep();
194        assert_eq!(dep1, dep2);
195
196        let dep3 = ControlDependence {
197            condition_line: 2,
198            dependent_line: 4, // Different line
199            branch_type: BranchType::True,
200        };
201        assert_ne!(dep1, dep3);
202    }
203}