use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, GlobalIdx, UpvalueIdx}; use crate::ast::nodes::Node; use crate::ast::types::{Purity, Value}; use crate::ast::vm::Closure; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use super::folder::Folder; use super::inliner::Inliner; use super::substitution_map::SubstitutionMap; use super::utils::{PathTracker, UsageInfo}; pub struct Optimizer { pub enabled: bool, max_passes: usize, pub globals: Option>>>, pub global_purity: Option>>>, pub lambda_registry: Option>>>, } impl Optimizer { pub fn new(enabled: bool) -> Self { Self { enabled, max_passes: 5, globals: None, global_purity: None, lambda_registry: None, } } pub fn with_globals(mut self, globals: Rc>>) -> Self { self.globals = Some(globals); self } pub fn with_purity(mut self, purity: Rc>>) -> Self { self.global_purity = Some(purity); self } pub fn with_registry( mut self, registry: Rc>>, ) -> Self { self.lambda_registry = Some(registry); self } pub fn optimize(&self, node: AnalyzedNode) -> AnalyzedNode { if !self.enabled { return node; } let mut current = node; for _ in 0..self.max_passes { let mut sub = SubstitutionMap::new(); let mut path = PathTracker::new(); let next = self.visit_node(current.clone(), &mut sub, &mut path); if next == current { break; } current = next; } current } fn visit_node( &self, node: AnalyzedNode, sub: &mut SubstitutionMap, path: &mut PathTracker, ) -> AnalyzedNode { let folder = Folder::new(&self.globals); let inliner = Inliner::new(&self.globals, &self.global_purity); let (new_kind, metrics) = match node.kind { BoundKind::Get { addr, ref name } => { if !sub.assigned.contains(&addr) { // 1. Try inlining from current value substitution map (locals/globals/upvalues) if let Some(val) = sub.get_value(&addr) && inliner.is_inlinable_value(val, addr) { return folder.make_constant_node(val.clone(), &node); } // 2. Try inlining from AST substitution map (pure expressions) if let Some(inlined_node) = sub.ast_substitutions.get(&addr) { return inlined_node.clone(); } // 3. Fallback for Globals: check the actual VM environment if let Address::Global(idx) = addr && let Some(globals_rc) = &self.globals { let globals = globals_rc.borrow(); if let Some(val) = globals.get(idx.0 as usize) && inliner.is_inlinable_value(val, addr) { return folder.make_constant_node(val.clone(), &node); } } } sub.used.insert(addr); ( BoundKind::Get { addr: sub.map_address(addr), name: name.clone(), }, node.ty.clone(), ) } BoundKind::FieldAccessor(k) => (BoundKind::FieldAccessor(k), node.ty.clone()), BoundKind::GetField { ref rec, field } => { let rec_opt = self.visit_node((**rec).clone(), sub, path); // Constant folding for Field Access if let BoundKind::Constant(Value::Record(layout, values)) = &rec_opt.kind && let Some(idx) = layout.index_of(field) { return folder.make_constant_node(values[idx].clone(), &node); } ( BoundKind::GetField { rec: Box::new(rec_opt), field, }, node.ty.clone(), ) } BoundKind::Set { addr, value } => { let value = Box::new(self.visit_node(*value, sub, path)); if let BoundKind::Constant(val) = &value.kind { sub.add_value(addr, val.clone()); } else { sub.remove_value(&addr); } ( BoundKind::Set { addr: sub.map_address(addr), value, }, node.ty.clone(), ) } BoundKind::Define { ref name, addr, kind, ref value, ref captured_by, } => { let value_opt = Box::new(self.visit_node((**value).clone(), sub, path)); if let Address::Local(slot) = addr && !captured_by.is_empty() { sub.captured_slots.insert(slot); } if let BoundKind::Constant(val) = &value_opt.kind { sub.add_value(addr, val.clone()); } else { sub.remove_value(&addr); } if let Address::Global(global_index) = addr && value_opt.ty.purity > Purity::Impure && let Some(purity_rc) = &self.global_purity { purity_rc .borrow_mut() .insert(global_index, value_opt.ty.purity); } ( BoundKind::Define { name: name.clone(), addr: sub.map_address(addr), kind, value: value_opt, captured_by: captured_by.clone(), }, node.ty.clone(), ) } BoundKind::Call { callee, args } => { let callee = self.visit_node(*callee, sub, path); let args = self.visit_node(*args, sub, path); if self.enabled { // Optimized Field Access Transformation if let BoundKind::FieldAccessor(k) = &callee.kind && let BoundKind::Tuple { elements } = &args.kind && elements.len() == 1 { let rec = elements[0].clone(); let metrics = node.ty.clone(); return Node { identity: node.identity, kind: BoundKind::GetField { rec: Box::new(rec), field: *k, }, ty: metrics, }; } let mut arg_nodes = Vec::new(); self.flatten_tuple(args.clone(), &mut arg_nodes); if let BoundKind::Lambda { params, body, upvalues, positional_count, } = &callee.kind && upvalues.is_empty() && positional_count.is_some() && path.inlining_depth < 5 && !callee.ty.is_recursive && path.enter_lambda(&callee.identity) { path.inlining_depth += 1; let mut inner_sub = sub.new_inner(); let collapsed = if inliner .prepare_beta_reduction(params, &arg_nodes, body, &mut inner_sub) .is_some() { Some(self.visit_node((**body).clone(), &mut inner_sub, path)) } else { None }; path.inlining_depth -= 1; path.exit_lambda(&callee.identity); if let Some(res) = collapsed { return res; } } if let BoundKind::Get { addr: Address::Global(idx), .. } = &callee.kind && let Some(registry_rc) = &self.lambda_registry && path.inlining_depth < 5 && !path.inlining_stack.contains(idx) { let registry = registry_rc.borrow(); if let Some(lambda_node) = registry.get(idx) && let BoundKind::Lambda { params, body, upvalues, positional_count, } = &lambda_node.kind && upvalues.is_empty() && positional_count.is_some() && !lambda_node.ty.is_recursive { let mut inner_sub = sub.new_inner(); path.inlining_stack.insert(*idx); path.inlining_depth += 1; let collapsed = if inliner .prepare_beta_reduction(params, &arg_nodes, body, &mut inner_sub) .is_some() { Some(self.visit_node((**body).clone(), &mut inner_sub, path)) } else { None }; path.inlining_depth -= 1; path.inlining_stack.remove(idx); if let Some(res) = collapsed { return res; } } } if let BoundKind::Constant(Value::Object(ref obj)) = callee.kind && path.inlining_depth < 5 && let Some(closure) = obj.as_any().downcast_ref::() && (closure.upvalues.is_empty() || closure.function_node.ty.purity >= Purity::SideEffectFree) && !closure.function_node.ty.is_recursive && path.enter_lambda(&closure.function_node.identity) { let mut closure_sub = SubstitutionMap::new(); for (i, cell) in closure.upvalues.iter().enumerate() { closure_sub.add_value( Address::Upvalue(UpvalueIdx(i as u32)), cell.borrow().clone(), ); } path.inlining_depth += 1; let inlined_body = self.visit_node( (*closure.function_node).clone(), &mut closure_sub, path, ); let collapsed = if inliner .prepare_beta_reduction( &closure.parameter_node, &arg_nodes, &inlined_body, &mut closure_sub, ) .is_some() { Some(self.visit_node(inlined_body, &mut closure_sub, path)) } else { None }; path.inlining_depth -= 1; path.exit_lambda(&closure.function_node.identity); if let Some(res) = collapsed { return res; } } if let Some(folded) = folder.try_fold_pure(&callee, &arg_nodes) { return folded; } } ( BoundKind::Call { callee: Box::new(callee), args: Box::new(args), }, node.ty.clone(), ) } BoundKind::Again { args } => { let args = self.visit_node(*args, sub, path); ( BoundKind::Again { args: Box::new(args), }, node.ty.clone(), ) } BoundKind::If { ref cond, ref then_br, ref else_br, } => { let cond_opt = self.visit_node((**cond).clone(), sub, path); if self.enabled && let BoundKind::Constant(ref val) = cond_opt.kind { if val.is_truthy() { return self.visit_node((**then_br).clone(), sub, path); } else if let Some(else_node) = else_br { return self.visit_node((**else_node).clone(), sub, path); } else { return folder.make_nop_node(&node); } } let then_br = Box::new(self.visit_node((**then_br).clone(), sub, path)); let else_br = else_br .as_ref() .map(|e| Box::new(self.visit_node((**e).clone(), sub, path))); ( BoundKind::If { cond: Box::new(cond_opt), then_br, else_br, }, node.ty.clone(), ) } BoundKind::Pipe { inputs, lambda, out_type, } => { let mut o_inputs = Vec::with_capacity(inputs.len()); for input in inputs { o_inputs.push(self.visit_node(input, sub, path)); } let o_lambda = Box::new(self.visit_node(*lambda, sub, path)); ( BoundKind::Pipe { inputs: o_inputs, lambda: o_lambda, out_type: out_type.clone(), }, node.ty.clone(), ) } BoundKind::Block { ref exprs } => { let mut info = UsageInfo::default(); if !exprs.is_empty() { for e in exprs { info.collect(e); } } sub.assigned.extend(info.assigned.iter().cloned()); let mut new_exprs = Vec::with_capacity(exprs.len()); let last_idx = exprs.len().saturating_sub(1); for (i, e) in exprs.iter().enumerate() { let is_last = i == last_idx; if self.enabled && !is_last { let removable = match &e.kind { BoundKind::Define { addr, value, .. } => { !info.is_used(addr) && (if let Address::Local(slot) = addr { !sub.captured_slots.contains(slot) } else { true }) && (value.ty.purity >= Purity::SideEffectFree || matches!(value.kind, BoundKind::Lambda { .. })) } BoundKind::Set { addr, value, .. } => { !info.is_used(addr) && (if let Address::Local(slot) = addr { !sub.captured_slots.contains(slot) } else { true }) && value.ty.purity >= Purity::SideEffectFree } _ => e.ty.purity >= Purity::SideEffectFree, }; if removable { continue; } } let opt = self.visit_node(e.clone(), sub, path); if self.enabled && matches!(opt.kind, BoundKind::Nop) && !is_last { continue; } new_exprs.push(opt); } if self.enabled { if new_exprs.is_empty() { return folder.make_nop_node(&node); } else if new_exprs.len() == 1 { return new_exprs.pop().unwrap(); } } (BoundKind::Block { exprs: new_exprs }, node.ty.clone()) } BoundKind::Lambda { .. } => { let (params, original_upvalues, body, positional_count) = if let BoundKind::Lambda { params, upvalues, body, positional_count, } = &node.kind { (params, upvalues, body, positional_count) } else { unreachable!() }; let mut info = UsageInfo::default(); info.collect(&node); let mut new_upvalues = Vec::new(); let mut mapping = Vec::new(); let mut next_inner_subs = sub.new_inner(); next_inner_subs.assigned = info.assigned; for (old_idx, capture_addr) in original_upvalues.iter().enumerate() { let mut inlined_val = None; if !sub.assigned.contains(capture_addr) && let Some(val) = sub.get_value(capture_addr) { inlined_val = Some(val.clone()); } if let Address::Local(slot) = capture_addr { sub.captured_slots.insert(*slot); } if let Some(val) = inlined_val { next_inner_subs .add_value(Address::Upvalue(UpvalueIdx(old_idx as u32)), val); mapping.push(None); } else { mapping.push(Some(new_upvalues.len() as u32)); new_upvalues.push(sub.map_address(*capture_addr)); } } let params_node = self.visit_node(params.as_ref().clone(), &mut next_inner_subs, path); inliner.collect_parameter_slots(¶ms_node, &mut next_inner_subs); let body_node = self.visit_node(body.as_ref().clone(), &mut next_inner_subs, path); let reindexed_body = if new_upvalues.len() != original_upvalues.len() { sub.reindex_upvalues(body_node, &mapping) } else { body_node }; ( BoundKind::Lambda { params: Rc::new(params_node), upvalues: new_upvalues, body: Rc::new(reindexed_body), positional_count: *positional_count, }, node.ty.clone(), ) } BoundKind::Destructure { ref pattern, ref value, } => { let val_opt = Box::new(self.visit_node((**value).clone(), sub, path)); let pat_opt = Box::new(self.visit_node((**pattern).clone(), sub, path)); let mut info = UsageInfo::default(); info.collect_pattern(&pat_opt); for addr in info.assigned { sub.remove_value(&addr); } ( BoundKind::Destructure { pattern: pat_opt, value: val_opt, }, node.ty.clone(), ) } BoundKind::Tuple { elements } => { let elements = elements .into_iter() .map(|e| self.visit_node(e, sub, path)) .collect(); (BoundKind::Tuple { elements }, node.ty.clone()) } BoundKind::Record { ref layout, ref values, } => { let mapped_values: Vec<_> = values .iter() .map(|v| self.visit_node(v.clone(), sub, path)) .collect(); if self.enabled && let Some(folded) = folder.try_fold_record(layout, &mapped_values, &node) { return folded; } ( BoundKind::Record { layout: layout.clone(), values: mapped_values, }, node.ty.clone(), ) } BoundKind::Expansion { ref original_call, ref bound_expanded, } => { path.inlining_depth += 1; let bound_expanded = Box::new(self.visit_node((**bound_expanded).clone(), sub, path)); path.inlining_depth -= 1; ( BoundKind::Expansion { original_call: original_call.clone(), bound_expanded, }, node.ty.clone(), ) } k => (k, node.ty.clone()), }; Node { identity: node.identity, kind: new_kind, ty: metrics, } } fn flatten_tuple(&self, node: AnalyzedNode, into: &mut Vec) { match node.kind { BoundKind::Tuple { elements } => { for el in elements { self.flatten_tuple(el, into); } } BoundKind::Nop => {} _ => into.push(node), } } }