mir: introduce passes::dce and minimal MirPass trait; rewire optimizer to use modular DCE pass. Build + PyVM Stage-2 smokes PASS.
This commit is contained in:
@ -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));
|
||||
|
||||
72
src/mir/passes/dce.rs
Normal file
72
src/mir/passes/dce.rs
Normal file
@ -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<ValueId> = 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
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user