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:
Selfhosting Dev
2025-09-17 03:32:05 +09:00
parent b60d3a0305
commit 56463c75f6
3 changed files with 85 additions and 2 deletions

View File

@ -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
View 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
}

View File

@ -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);
}