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(®).unwrap_or_default();
168 let uses_list: SmallVec<[Use; 8]> = uses.remove(®).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}