134 lines
5.6 KiB
Rust
134 lines
5.6 KiB
Rust
//! 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::{MirFunction, MirInstruction, MirModule, MirType, 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;
|
|
let fast_int = std::env::var("NYASH_LLVM_FAST_INT").ok().as_deref() == Some("1");
|
|
|
|
// Helper: check if both operands are numeric (Integer/Float) via value type hints
|
|
let is_numeric = |vid: ValueId| -> bool {
|
|
match function.metadata.value_types.get(&vid) {
|
|
Some(MirType::Integer) | Some(MirType::Float) => true,
|
|
_ => false,
|
|
}
|
|
};
|
|
|
|
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() {
|
|
// Prefer existing SSA value in the same block when FAST_INT is enabled.
|
|
if fast_int {
|
|
match inst {
|
|
MirInstruction::BinOp { op, lhs, rhs, .. } => {
|
|
// Only rewrite Add when both operands are numeric (avoid String + String)
|
|
let allow = match op {
|
|
crate::mir::BinaryOp::Add => {
|
|
is_numeric(*lhs) && is_numeric(*rhs)
|
|
}
|
|
_ => true,
|
|
};
|
|
if allow {
|
|
*inst = MirInstruction::Copy { dst, src: existing };
|
|
}
|
|
}
|
|
MirInstruction::Compare { .. }
|
|
| MirInstruction::UnaryOp { .. }
|
|
| MirInstruction::Cast { .. } => {
|
|
*inst = MirInstruction::Copy { dst, src: existing };
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
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 {
|
|
callee, func, args, ..
|
|
} => {
|
|
let args_str = args
|
|
.iter()
|
|
.map(|v| v.as_u32().to_string())
|
|
.collect::<Vec<_>>()
|
|
.join(",");
|
|
|
|
// Include callee information to distinguish different call targets
|
|
if let Some(c) = callee {
|
|
use crate::mir::Callee;
|
|
match c {
|
|
Callee::Global(name) => {
|
|
format!("call_global_{}_{}", name, args_str)
|
|
}
|
|
Callee::Method {
|
|
box_name,
|
|
method,
|
|
receiver,
|
|
..
|
|
} => {
|
|
let recv_str = receiver
|
|
.map(|r| r.as_u32().to_string())
|
|
.unwrap_or_else(|| "static".to_string());
|
|
format!(
|
|
"call_method_{}.{}_{}_{}",
|
|
box_name, method, recv_str, args_str
|
|
)
|
|
}
|
|
Callee::Value(v) => {
|
|
format!("call_value_{}_{}", v.as_u32(), args_str)
|
|
}
|
|
Callee::Extern(name) => {
|
|
format!("call_extern_{}_{}", name, args_str)
|
|
}
|
|
Callee::Constructor { box_type } => {
|
|
format!("call_ctor_{}_{}", box_type, args_str)
|
|
}
|
|
Callee::Closure { .. } => {
|
|
// Closures are unique by definition (captures, params may differ)
|
|
// Use func as distinguisher
|
|
format!("call_closure_{}_{}", func.as_u32(), args_str)
|
|
}
|
|
}
|
|
} else {
|
|
// Legacy path: no callee information, use func
|
|
format!("call_legacy_{}_{}", func.as_u32(), args_str)
|
|
}
|
|
}
|
|
other => format!("other_{:?}", other),
|
|
}
|
|
}
|