diff --git a/src/mir/optimizer.rs b/src/mir/optimizer.rs index 90f52c72..49f686ae 100644 --- a/src/mir/optimizer.rs +++ b/src/mir/optimizer.rs @@ -62,8 +62,11 @@ impl MirOptimizer { // Normalize Python helper form: py.getattr(obj, name) → obj.getattr(name) stats.merge(self.normalize_python_helper_calls(module)); - // Pass 1: Dead code elimination - stats.merge(self.eliminate_dead_code(module)); + // Pass 1: Dead code elimination (modularized pass) + { + let eliminated = crate::mir::passes::dce::eliminate_dead_code(module); + stats.dead_code_eliminated += eliminated; + } // Pass 2: Pure instruction CSE (Common Subexpression Elimination) stats.merge(self.common_subexpression_elimination(module)); diff --git a/src/mir/passes/dce.rs b/src/mir/passes/dce.rs new file mode 100644 index 00000000..731d3708 --- /dev/null +++ b/src/mir/passes/dce.rs @@ -0,0 +1,72 @@ +//! Dead Code Elimination (pure instruction DCE) +//! +//! Extracted from the monolithic optimizer to enable modular pass composition. + +use crate::mir::{MirModule, MirFunction, MirInstruction, ValueId}; +use std::collections::HashSet; + +/// Eliminate dead code (unused results of pure instructions) across the module. +/// Returns the number of eliminated instructions. +pub fn eliminate_dead_code(module: &mut MirModule) -> usize { + let mut eliminated_total = 0usize; + for (_func_name, func) in &mut module.functions { + eliminated_total += eliminate_dead_code_in_function(func); + } + eliminated_total +} + +fn eliminate_dead_code_in_function(function: &mut MirFunction) -> usize { + // Collect values that must be kept (used results + effects) + let mut used_values: HashSet = HashSet::new(); + + // Mark values used by side-effecting instructions and terminators + for (_bid, block) in &function.blocks { + for instruction in &block.instructions { + if !instruction.effects().is_pure() { + if let Some(dst) = instruction.dst_value() { used_values.insert(dst); } + for u in instruction.used_values() { used_values.insert(u); } + } + } + if let Some(term) = &block.terminator { + for u in term.used_values() { used_values.insert(u); } + } + } + + // Backward propagation: if a value is used, mark its operands as used + let mut changed = true; + while changed { + changed = false; + for (_bid, block) in &function.blocks { + for instruction in &block.instructions { + if let Some(dst) = instruction.dst_value() { + if used_values.contains(&dst) { + for u in instruction.used_values() { + if used_values.insert(u) { changed = true; } + } + } + } + } + } + } + + // Remove unused pure instructions + let mut eliminated = 0usize; + for (bbid, block) in &mut function.blocks { + block.instructions.retain(|inst| { + if inst.effects().is_pure() { + if let Some(dst) = inst.dst_value() { + if !used_values.contains(&dst) { + // Keep indices stable is not required here; remove entirely + // Logging is suppressed to keep pass quiet by default + eliminated += 1; + return false; + } + } + } + true + }); + } + if eliminated > 0 { function.update_cfg(); } + eliminated +} + diff --git a/src/mir/passes/mod.rs b/src/mir/passes/mod.rs index 71e7a857..ac9a711e 100644 --- a/src/mir/passes/mod.rs +++ b/src/mir/passes/mod.rs @@ -4,3 +4,11 @@ pub mod type_hints; pub mod escape; pub mod method_id_inject; +pub mod dce; + +/// Minimal pass trait for future expansion. Currently unused by the main +/// optimizer pipeline but provided to guide modularization. +pub trait MirPass { + fn name(&self) -> &'static str; + fn run(&mut self, module: &mut crate::mir::MirModule); +}