Skip to main content

gcrecomp_core/recompiler/analysis/
data_flow.rs

1//! Data Flow Analysis
2//!
3//! This module provides data flow analysis for PowerPC instructions, including:
4//! - Def-use chain construction (tracking where variables are defined and used)
5//! - Live variable analysis (determining which variables are live at each point)
6//! - Dead code elimination (removing unused definitions)
7//!
8//! # Memory Optimizations
9//! - `DefUseChain.definitions` and `uses` use `SmallVec` (typically small lists)
10//! - `Definition` and `Use` structs are packed to minimize padding
11//! - Live variable sets use `BitVec` instead of `HashSet<u8>` for efficiency
12//!   - Saves memory: 1 bit per register instead of 8 bytes (pointer) + overhead
13//!   - Faster membership tests: O(1) bit access with better cache locality
14//!
15//! # Data Flow Analysis Algorithms
16//! ## Def-Use Chain Construction
17//! Scans instructions to identify:
18//! - **Definitions**: Instructions that write to registers
19//! - **Uses**: Instructions that read from registers
20//!
21//! ## Live Variable Analysis
22//! Uses iterative data flow analysis (worklist algorithm):
23//! - **Live at exit**: Union of live at entry of all successors
24//! - **Live at entry**: (Live at exit - Killed) ∪ Generated
25//!   - **Killed**: Registers defined in this block
26//!   - **Generated**: Registers used in this block
27//!
28//! Iterates until fixed point (no changes).
29
30use crate::recompiler::decoder::{DecodedInstruction, Operand};
31use crate::recompiler::analysis::control_flow::ControlFlowGraph;
32use bitvec::prelude::*;
33use smallvec::SmallVec;
34use std::collections::HashMap;
35
36/// Definition-use chain for a register.
37///
38/// Tracks all definitions and uses of a register, enabling data flow optimizations.
39///
40/// # Memory Optimization
41/// - `definitions` and `uses`: Use `SmallVec` - typically small lists (most registers have few defs/uses)
42/// - `register`: Uses `u8` (PowerPC has 32 GPRs, fits in 5 bits)
43#[derive(Debug, Clone)]
44pub struct DefUseChain {
45    /// Register number (0-31 for PowerPC GPRs)
46    pub register: u8,
47    /// Definitions of this register (instructions that write to it)
48    /// Uses SmallVec - most registers have few definitions
49    pub definitions: SmallVec<[Definition; 4]>,
50    /// Uses of this register (instructions that read from it)
51    /// Uses SmallVec - most registers have few uses
52    pub uses: SmallVec<[Use; 8]>,
53}
54
55/// Definition of a register (instruction that writes to it).
56///
57/// # Memory Optimization
58/// Packed struct to minimize padding:
59/// - `instruction_index`: `usize` (required for indexing)
60/// - `address`: `u32` (32-bit address space)
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
62#[repr(C)] // Ensure C-compatible layout, minimize padding
63pub struct Definition {
64    /// Index of defining instruction in instruction sequence
65    pub instruction_index: usize,
66    /// Address of defining instruction in original binary
67    pub address: u32,
68}
69
70/// Use of a register (instruction that reads from it).
71///
72/// # Memory Optimization
73/// Packed struct to minimize padding:
74/// - `instruction_index`: `usize` (required for indexing)
75/// - `address`: `u32` (32-bit address space)
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
77#[repr(C)] // Ensure C-compatible layout, minimize padding
78pub struct Use {
79    /// Index of using instruction in instruction sequence
80    pub instruction_index: usize,
81    /// Address of using instruction in original binary
82    pub address: u32,
83}
84
85/// Live variable analysis results.
86///
87/// # Memory Optimization
88/// - Live variable sets use `BitVec<u32>` instead of `HashSet<u8>`
89///   - Saves memory: 1 bit per register (32 bits = 4 bytes) vs 8 bytes per register + overhead
90///   - Faster membership tests: O(1) bit access with better cache locality
91///   - Efficient set operations: bitwise AND/OR for intersection/union
92///
93/// # Algorithm
94/// Uses iterative data flow analysis:
95/// - Initialize all blocks with empty live sets
96/// - Iterate until fixed point:
97///   - Live at exit = union of live at entry of successors
98///   - Live at entry = (Live at exit - Killed) ∪ Generated
99#[derive(Debug, Clone)]
100pub struct LiveVariableAnalysis {
101    /// Live variables at entry of each basic block
102    /// Key: block ID (u32), Value: bit vector of live registers (1 bit per register)
103    pub live_at_entry: HashMap<u32, BitVec<u32>>,
104    /// Live variables at exit of each basic block
105    /// Key: block ID (u32), Value: bit vector of live registers (1 bit per register)
106    pub live_at_exit: HashMap<u32, BitVec<u32>>,
107}
108
109/// Data flow analyzer for building def-use chains and performing live variable analysis.
110pub struct DataFlowAnalyzer;
111
112impl DataFlowAnalyzer {
113    /// Build definition-use chains for all registers.
114    ///
115    /// # Algorithm
116    /// Scans all instructions to identify:
117    /// - **Definitions**: Instructions that write to registers (destination operands)
118    /// - **Uses**: Instructions that read from registers (source operands)
119    ///
120    /// # Arguments
121    /// * `instructions` - Sequence of decoded PowerPC instructions
122    ///
123    /// # Returns
124    /// `HashMap<u8, DefUseChain>` - Map from register number to def-use chain
125    ///
126    /// # Examples
127    /// ```rust
128    /// let instructions = vec![/* decoded instructions */];
129    /// let chains = DataFlowAnalyzer::build_def_use_chains(&instructions);
130    /// if let Some(chain) = chains.get(&3) {
131    ///     println!("Register r3 has {} definitions and {} uses", 
132    ///              chain.definitions.len(), chain.uses.len());
133    /// }
134    /// ```
135    #[inline] // May be called frequently
136    pub fn build_def_use_chains(
137        instructions: &[DecodedInstruction],
138    ) -> HashMap<u8, DefUseChain> {
139        let mut chains: HashMap<u8, DefUseChain> = HashMap::new();
140        let mut definitions: HashMap<u8, SmallVec<[Definition; 4]>> = HashMap::new();
141        let mut uses: HashMap<u8, SmallVec<[Use; 8]>> = HashMap::new();
142        
143        let mut instruction_address: u32 = 0u32;
144        for (idx, inst) in instructions.iter().enumerate() {
145            // Find definitions (instructions that write to registers)
146            if let Some(def_reg) = Self::get_definition_register(inst) {
147                definitions.entry(def_reg).or_insert_with(SmallVec::new).push(Definition {
148                    instruction_index: idx,
149                    address: instruction_address,
150                });
151            }
152            
153            // Find uses (instructions that read from registers)
154            for use_reg in Self::get_use_registers(inst) {
155                uses.entry(use_reg).or_insert_with(SmallVec::new).push(Use {
156                    instruction_index: idx,
157                    address: instruction_address,
158                });
159            }
160            
161            instruction_address = instruction_address.wrapping_add(4); // PowerPC instructions are 4 bytes
162        }
163        
164        // Combine definitions and uses into chains
165        // PowerPC has 32 GPRs (r0-r31)
166        for reg in 0u8..32u8 {
167            let defs: SmallVec<[Definition; 4]> = definitions.remove(&reg).unwrap_or_default();
168            let uses_list: SmallVec<[Use; 8]> = uses.remove(&reg).unwrap_or_default();
169            
170            if !defs.is_empty() || !uses_list.is_empty() {
171                chains.insert(reg, DefUseChain {
172                    register: reg,
173                    definitions: defs,
174                    uses: uses_list,
175                });
176            }
177        }
178        
179        chains
180    }
181    
182    /// Extract the register that is defined (written to) by an instruction.
183    ///
184    /// # Algorithm
185    /// Checks if instruction writes to a register:
186    /// - Arithmetic instructions: first operand is destination
187    /// - Load instructions: first operand is destination
188    /// - Move instructions: first operand is destination
189    ///
190    /// # Arguments
191    /// * `inst` - Decoded instruction to analyze
192    ///
193    /// # Returns
194    /// `Option<u8>` - Register number if instruction defines a register, None otherwise
195    #[inline] // Hot path - called for every instruction
196    fn get_definition_register(inst: &DecodedInstruction) -> Option<u8> {
197        // Check if instruction writes to a register (first operand is usually destination)
198        if !inst.instruction.operands.is_empty() {
199            if let Operand::Register(reg) = &inst.instruction.operands[0] {
200                // Check if this is a write operation
201                match inst.instruction.instruction_type {
202                    crate::recompiler::decoder::InstructionType::Arithmetic
203                    | crate::recompiler::decoder::InstructionType::Load
204                    | crate::recompiler::decoder::InstructionType::Move => {
205                        return Some(*reg);
206                    }
207                    _ => {}
208                }
209            }
210        }
211        None
212    }
213    
214    /// Extract all registers that are used (read from) by an instruction.
215    ///
216    /// # Algorithm
217    /// Scans all operands for register uses, excluding the definition register (if any).
218    ///
219    /// # Arguments
220    /// * `inst` - Decoded instruction to analyze
221    ///
222    /// # Returns
223    /// `SmallVec<[u8; 4]>` - List of register numbers used by this instruction
224    #[inline] // Hot path - called for every instruction
225    fn get_use_registers(inst: &DecodedInstruction) -> SmallVec<[u8; 4]> {
226        let mut uses: SmallVec<[u8; 4]> = SmallVec::new();
227        
228        // Check all operands for register uses
229        let def_reg: Option<u8> = Self::get_definition_register(inst);
230        for operand in inst.instruction.operands.iter() {
231            match operand {
232                Operand::Register(reg) => {
233                    // Skip if this is the definition register
234                    if def_reg != Some(*reg) {
235                        uses.push(*reg);
236                    }
237                }
238                Operand::FpRegister(_) => {
239                    // Floating-point registers are also uses
240                    // (for now, we only track GPRs)
241                }
242                _ => {}
243            }
244        }
245        
246        uses
247    }
248    
249    /// Perform live variable analysis on a control flow graph.
250    ///
251    /// # Algorithm
252    /// Uses iterative data flow analysis (worklist algorithm):
253    /// 1. Initialize all blocks with empty live sets
254    /// 2. Iterate until fixed point:
255    ///    - Live at exit = union of live at entry of all successors
256    ///    - Live at entry = (Live at exit - Killed) ∪ Generated
257    ///      - **Killed**: Registers defined in this block
258    ///      - **Generated**: Registers used in this block
259    ///
260    /// # Arguments
261    /// * `cfg` - Control flow graph to analyze
262    ///
263    /// # Returns
264    /// `LiveVariableAnalysis` - Live variable sets at entry and exit of each block
265    ///
266    /// # Examples
267    /// ```rust
268    /// let live_analysis = DataFlowAnalyzer::live_variable_analysis(&cfg);
269    /// if let Some(live) = live_analysis.live_at_entry.get(&0) {
270    ///     println!("Block 0 has {} live variables at entry", live.count_ones());
271    /// }
272    /// ```
273    #[inline] // May be called frequently
274    pub fn live_variable_analysis(cfg: &ControlFlowGraph) -> LiveVariableAnalysis {
275        let mut live_at_entry: HashMap<u32, BitVec<u32>> = HashMap::new();
276        let mut live_at_exit: HashMap<u32, BitVec<u32>> = HashMap::new();
277        let mut changed: bool = true;
278        
279        // Initialize all blocks with empty live sets
280        // Use BitVec with 32 bits (one per PowerPC GPR)
281        for block in cfg.nodes.iter() {
282            live_at_entry.insert(block.id, bitvec![u32, Lsb0; 0; 32]);
283            live_at_exit.insert(block.id, bitvec![u32, Lsb0; 0; 32]);
284        }
285        
286        // Iterative data flow analysis
287        while changed {
288            changed = false;
289            
290            for block in cfg.nodes.iter() {
291                // Compute live at exit (union of live at entry of successors)
292                let mut exit_live: BitVec<u32> = bitvec![u32, Lsb0; 0; 32];
293                for &succ in block.successors.iter() {
294                    if let Some(entry_live) = live_at_entry.get(&succ) {
295                        exit_live |= entry_live; // Bitwise OR for union
296                    }
297                }
298                
299                // Compute live at entry (gen ∪ (exit - kill))
300                let mut entry_live: BitVec<u32> = exit_live.clone();
301                
302                // Remove killed registers (defined in this block)
303                for inst in block.instructions.iter() {
304                    if let Some(killed) = Self::get_definition_register(inst) {
305                        entry_live.set(killed as usize, false);
306                    }
307                }
308                
309                // Add generated registers (used in this block)
310                for inst in block.instructions.iter() {
311                    for used in Self::get_use_registers(inst).iter() {
312                        entry_live.set(*used as usize, true);
313                    }
314                }
315                
316                // Check if changed
317                let old_entry: BitVec<u32> = live_at_entry.get(&block.id).cloned().unwrap_or_else(|| bitvec![u32, Lsb0; 0; 32]);
318                if old_entry != entry_live {
319                    changed = true;
320                    live_at_entry.insert(block.id, entry_live);
321                    live_at_exit.insert(block.id, exit_live);
322                }
323            }
324        }
325        
326        LiveVariableAnalysis {
327            live_at_entry,
328            live_at_exit,
329        }
330    }
331    
332    /// Eliminate dead code using live variable analysis.
333    ///
334    /// # Algorithm
335    /// Removes instructions that define registers that are never used.
336    /// This is a simple implementation - a full implementation would need to track
337    /// uses across basic blocks using live variable analysis.
338    ///
339    /// # Arguments
340    /// * `instructions` - Sequence of decoded PowerPC instructions
341    /// * `live_analysis` - Live variable analysis results
342    ///
343    /// # Returns
344    /// `Vec<DecodedInstruction>` - Optimized instruction sequence with dead code removed
345    ///
346    /// # Examples
347    /// ```rust
348    /// let optimized = DataFlowAnalyzer::eliminate_dead_code(&instructions, &live_analysis);
349    /// println!("Removed {} dead instructions", instructions.len() - optimized.len());
350    /// ```
351    #[inline] // May be called frequently
352    pub fn eliminate_dead_code(
353        instructions: &[DecodedInstruction],
354        _live_analysis: &LiveVariableAnalysis,
355    ) -> Vec<DecodedInstruction> {
356        let mut optimized: Vec<DecodedInstruction> = Vec::new();
357        
358        // Simple dead code elimination: remove definitions that are never used
359        // In a full implementation, would use live_analysis to track uses across blocks
360        for inst in instructions.iter() {
361            if let Some(def_reg) = Self::get_definition_register(inst) {
362                // Check if register is ever used
363                let mut is_used: bool = false;
364                for other_inst in instructions.iter() {
365                    if Self::get_use_registers(other_inst).contains(&def_reg) {
366                        is_used = true;
367                        break;
368                    }
369                }
370                
371                if is_used {
372                    optimized.push(inst.clone());
373                }
374                // Otherwise, skip (dead code)
375            } else {
376                optimized.push(inst.clone());
377            }
378        }
379        
380        optimized
381    }
382}