feat: Implement closure cracking and inlining

Introduces a new optimizer pass that can "crack" closures, allowing for
more aggressive specialization. It also enables inlining of upvalues
that point to immutable global variables. This removes overhead for
higher-order functions and currying when arguments are statically
resolvable.
This commit is contained in:
Michael Schimmel
2026-02-21 18:49:55 +01:00
parent 74ea38248e
commit 0bbe35eeec
11 changed files with 386 additions and 286 deletions
+21 -9
View File
@@ -13,6 +13,7 @@ use crate::ast::compiler::lambda_collector::LambdaCollector;
use crate::ast::compiler::dumper::Dumper;
use crate::ast::compiler::macros::{MacroExpander, MacroRegistry, MacroEvaluator};
use crate::ast::compiler::specializer::{Specializer, MonoCache, FunctionRegistry};
use crate::ast::compiler::optimizer::Optimizer;
use crate::ast::rtl;
use crate::ast::rtl::intrinsics;
use crate::ast::compiler::bound_nodes::{Address, BoundNode};
@@ -24,6 +25,7 @@ pub struct Environment {
pub function_registry: Rc<RefCell<HashMap<u32, BoundNode>>>,
pub monomorph_cache: Rc<RefCell<MonoCache>>,
pub debug_mode: bool,
pub optimization_level: u32,
}
struct EnvFunctionRegistry {
@@ -81,6 +83,7 @@ impl Environment {
function_registry: Rc::new(RefCell::new(HashMap::new())),
monomorph_cache: Rc::new(RefCell::new(HashMap::new())),
debug_mode: false,
optimization_level: 0,
};
env.register_stdlib();
env
@@ -161,11 +164,15 @@ impl Environment {
/// Backend: Optimization (TCO, etc.)
pub fn link(&self, node: TypedNode) -> TypedNode {
// 1. Specialize
// 1. Specialize (Always performed for correctness)
let specialized = self.specialize_node(node);
// 2. Optimize
TCO::optimize(specialized)
// 2. Optimize (Level 1: Cracking, Level 2: Collapsing)
let optimizer = Optimizer::new(self.optimization_level);
let optimized = optimizer.optimize(specialized);
// 3. TCO (Always performed)
TCO::optimize(optimized)
}
fn specialize_node(&self, node: TypedNode) -> TypedNode {
@@ -179,6 +186,7 @@ impl Environment {
let mono_cache = self.monomorph_cache.clone();
let global_values = self.global_values.clone();
let global_types = self.global_types.clone();
let opt_level = self.optimization_level;
let compiler = Rc::new(move |func_template: BoundNode, arg_types: &[StaticType]| -> Result<(Value, StaticType), String> {
// 1. Re-TypeCheck the template with concrete argument types
@@ -198,18 +206,22 @@ impl Environment {
let specialized_ast = sub_specializer.specialize(retyped_ast);
// 3. Optimize (TCO)
let optimized_ast = TCO::optimize(specialized_ast);
// 3. Optimize (Phase 2: Cracking & Folding)
let optimizer = Optimizer::new(opt_level);
let optimized_ast = optimizer.optimize(specialized_ast);
// 4. Compile to Value (VM)
// 4. TCO
let tco_ast = TCO::optimize(optimized_ast);
// 5. Compile to Value (VM)
let mut vm = VM::new(global_values.clone());
let compiled_val = match vm.run(&optimized_ast) {
let compiled_val = match vm.run(&tco_ast) {
Ok(v) => v,
Err(e) => return Err(format!("VM Error during specialization: {}", e)),
};
// 5. Determine correct return type from the newly inferred function signature
let ret_type = if let StaticType::Function(sig) = &optimized_ast.ty {
// 6. Determine correct return type from the newly inferred function signature
let ret_type = if let StaticType::Function(sig) = &tco_ast.ty {
sig.ret.clone()
} else {
StaticType::Any