mir: extract CSE into passes::cse and wire it in optimizer. Behavior preserved (count-only, no SSA rewrites). Build + PyVM Stage-2 smokes PASS.

This commit is contained in:
Selfhosting Dev
2025-09-17 03:41:04 +09:00
parent 56463c75f6
commit 154778fc57
3 changed files with 62 additions and 2 deletions

View File

@ -68,8 +68,11 @@ impl MirOptimizer {
stats.dead_code_eliminated += eliminated; stats.dead_code_eliminated += eliminated;
} }
// Pass 2: Pure instruction CSE (Common Subexpression Elimination) // Pass 2: Pure instruction CSE (modularized)
stats.merge(self.common_subexpression_elimination(module)); {
let eliminated = crate::mir::passes::cse::eliminate_common_subexpressions(module);
stats.cse_eliminated += eliminated;
}
// Pass 3: Pure instruction reordering for better locality // Pass 3: Pure instruction reordering for better locality
stats.merge(self.reorder_pure_instructions(module)); stats.merge(self.reorder_pure_instructions(module));

56
src/mir/passes/cse.rs Normal file
View File

@ -0,0 +1,56 @@
//! Common Subexpression Elimination (CSE) for pure MIR instructions.
//!
//! Note: Current implementation mirrors the prior monolithic behavior and
//! counts eliminations without rewriting uses (SSA update is TODO). This keeps
//! behavior identical while modularizing the pass for future enhancement.
use crate::mir::{MirInstruction, MirModule, MirFunction, ValueId};
use std::collections::HashMap;
/// Run CSE across the module. Returns the number of eliminated expressions.
pub fn eliminate_common_subexpressions(module: &mut MirModule) -> usize {
let mut eliminated = 0usize;
for (_name, func) in module.functions.iter_mut() {
eliminated += cse_in_function(func);
}
eliminated
}
fn cse_in_function(function: &mut MirFunction) -> usize {
let mut expression_map: HashMap<String, ValueId> = HashMap::new();
let mut eliminated = 0usize;
for (_bid, block) in &mut function.blocks {
for inst in &mut block.instructions {
if inst.effects().is_pure() {
let key = instruction_key(inst);
if let Some(&existing) = expression_map.get(&key) {
if let Some(dst) = inst.dst_value() {
// Count as eliminated; rewriting uses is a future improvement.
let _ = (existing, dst); // keep variables referenced
eliminated += 1;
}
} else if let Some(dst) = inst.dst_value() {
expression_map.insert(key, dst);
}
}
}
}
eliminated
}
fn instruction_key(i: &MirInstruction) -> String {
match i {
MirInstruction::Const { value, .. } => format!("const_{:?}", value),
MirInstruction::BinOp { op, lhs, rhs, .. } =>
format!("binop_{:?}_{}_{}", op, lhs.as_u32(), rhs.as_u32()),
MirInstruction::Compare { op, lhs, rhs, .. } =>
format!("cmp_{:?}_{}_{}", op, lhs.as_u32(), rhs.as_u32()),
MirInstruction::Call { func, args, .. } => {
let args_str = args.iter().map(|v| v.as_u32().to_string()).collect::<Vec<_>>().join(",");
format!("call_{}_{}", func.as_u32(), args_str)
}
other => format!("other_{:?}", other),
}
}

View File

@ -5,6 +5,7 @@ pub mod type_hints;
pub mod escape; pub mod escape;
pub mod method_id_inject; pub mod method_id_inject;
pub mod dce; pub mod dce;
pub mod cse;
/// Minimal pass trait for future expansion. Currently unused by the main /// Minimal pass trait for future expansion. Currently unused by the main
/// optimizer pipeline but provided to guide modularization. /// optimizer pipeline but provided to guide modularization.