Refactor: Replace optimization level with boolean flag

The optimizer now uses a simple boolean flag (`optimization`) instead of
an `optimization_level` (u32). This simplifies the optimizer's logic and
makes it easier to enable or disable optimizations.

The command-line interface and internal testing have been updated to
reflect this change.
This commit is contained in:
Michael Schimmel
2026-02-22 01:50:14 +01:00
parent d3cdafbb85
commit 7c997ca841
7 changed files with 52 additions and 49 deletions
+22 -21
View File
@@ -9,15 +9,15 @@ use std::collections::{HashMap, HashSet};
/// The Optimizer performs Phase 2 (Cracking), Phase 2.5/2.6 (Aggressive Collapsing & DCE)
/// and Phase 4 (Global Inlining).
pub struct Optimizer {
pub level: u32,
pub enabled: bool,
max_passes: usize,
pub globals: Option<Rc<RefCell<Vec<Value>>>>,
pub global_purity: Option<Rc<RefCell<HashMap<u32, bool>>>>,
}
impl Optimizer {
pub fn new(level: u32) -> Self {
Self { level, max_passes: 5, globals: None, global_purity: None }
pub fn new(enabled: bool) -> Self {
Self { enabled, max_passes: 5, globals: None, global_purity: None }
}
pub fn with_globals(mut self, globals: Rc<RefCell<Vec<Value>>>) -> Self {
@@ -31,7 +31,7 @@ impl Optimizer {
}
pub fn optimize(&self, node: TypedNode) -> TypedNode {
if self.level == 0 {
if !self.enabled {
return node;
}
@@ -132,7 +132,7 @@ impl Optimizer {
let callee = self.visit_node(*callee, sub);
let args = self.visit_node(*args, sub);
if self.level >= 2 {
if self.enabled {
// Case 1: Beta-Reduction for Lambda Literals
if let BoundKind::Lambda { params, body, .. } = &callee.kind
&& let Some(collapsed) = self.try_beta_reduce(params, &args, (**body).clone()) {
@@ -158,7 +158,7 @@ impl Optimizer {
}
}
if self.level >= 1
if self.enabled
&& let BoundKind::Constant(Value::Object(ref obj)) = callee.kind
&& let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
let mut closure_sub = SubstitutionMap::new();
@@ -189,7 +189,7 @@ impl Optimizer {
BoundKind::If { cond, then_br, else_br } => {
let cond = self.visit_node(*cond, sub);
if self.level >= 2
if self.enabled
&& let BoundKind::Constant(ref val) = cond.kind {
if val.is_truthy() {
return self.visit_node(*then_br, sub);
@@ -224,7 +224,7 @@ impl Optimizer {
for (i, e) in exprs.into_iter().enumerate() {
let is_last = i == last_idx;
if self.level >= 2 && !is_last {
if self.enabled && !is_last {
let removable = match &e.kind {
BoundKind::DefLocal { slot, value, .. } => {
!used_in_block.contains(slot) && !sub.captured_slots.contains(slot) && self.is_pure(value)
@@ -241,13 +241,13 @@ impl Optimizer {
}
let opt = self.visit_node(e, sub);
if self.level >= 2 && matches!(opt.kind, BoundKind::Nop) && !is_last {
if self.enabled && matches!(opt.kind, BoundKind::Nop) && !is_last {
continue;
}
new_exprs.push(opt);
}
if self.level >= 2 {
if self.enabled {
if new_exprs.is_empty() {
return Node { identity: node.identity, kind: BoundKind::Nop, ty: StaticType::Void };
} else if new_exprs.len() == 1 {
@@ -259,6 +259,7 @@ impl Optimizer {
(BoundKind::Block { exprs: new_exprs }, ty)
},
BoundKind::Lambda { params, upvalues: original_upvalues, body, positional_count } => {
let mut new_upvalues = Vec::new();
let mut mapping = Vec::new();
@@ -675,55 +676,55 @@ impl SubstitutionMap {
mod tests {
use crate::ast::environment::Environment;
fn get_optimized_dump(source: &str, level: u32) -> String {
fn get_optimized_dump(source: &str, enabled: bool) -> String {
let mut env = Environment::new();
env.optimization_level = level;
env.optimization = enabled;
env.dump_ast(source).expect("Compilation failed during test")
}
#[test]
fn test_opt_folding() {
let dump = get_optimized_dump("(+ 10 20)", 2);
let dump = get_optimized_dump("(+ 10 20)", true);
insta::assert_snapshot!(dump);
}
#[test]
fn test_opt_folding_complex() {
let dump_float = get_optimized_dump("(+ 1.5 2.5)", 2);
let dump_float = get_optimized_dump("(+ 1.5 2.5)", true);
assert!(dump_float.contains("Constant: 4"));
let dump_text = get_optimized_dump("(+ \"hello \" \"world\")", 2);
let dump_text = get_optimized_dump("(+ \"hello \" \"world\")", true);
assert!(dump_text.contains("Constant: \"hello world\""));
let dump_date = get_optimized_dump("(date \"2023-01-01\")", 2);
let dump_date = get_optimized_dump("(date \"2023-01-01\")", true);
assert!(dump_date.contains("Constant: #2023-01-01 00:00:00#"));
let dump_cmp = get_optimized_dump("(> 10 5)", 2);
let dump_cmp = get_optimized_dump("(> 10 5)", true);
assert!(dump_cmp.contains("Constant: true"));
}
#[test]
fn test_opt_local_inlining() {
let source = "(fn [] (do (def x 10) (+ x 5)))";
let dump = get_optimized_dump(source, 2);
let dump = get_optimized_dump(source, true);
insta::assert_snapshot!(dump);
}
#[test]
fn test_opt_dce_unused() {
let source = "(fn [] (do (def x 10) 42))";
let dump = get_optimized_dump(source, 2);
let dump = get_optimized_dump(source, true);
insta::assert_snapshot!(dump);
}
#[test]
fn test_opt_assignment_safety() {
let source = "(fn [] (do (def x 10) (assign x 20) x))";
let dump = get_optimized_dump(source, 2);
let dump = get_optimized_dump(source, true);
insta::assert_snapshot!(dump);
}
#[test]
fn test_opt_lambda_cracking() {
let source = "(fn [] (do (def x 10) (fn [] x)))";
let dump = get_optimized_dump(source, 2);
let dump = get_optimized_dump(source, true);
insta::assert_snapshot!(dump);
}
}