diff --git a/gemini.md b/gemini.md index 5422745..8bc46e0 100644 --- a/gemini.md +++ b/gemini.md @@ -53,9 +53,11 @@ Arguments: [FILE] The script file to run or benchmark Options: - -e, --eval Run a script string directly - -b, --bench Run benchmarks (Only allowed in Release mode) - -u, --update-bench Update the benchmark baseline (Only allowed in Release mode) - -d, --dump Dump the compiled AST - -h, --help Print help - -V, --version Print version +-e, --eval Run a script string directly +-b, --bench Run benchmarks (Only allowed in Release mode) +-u, --update-bench Update the benchmark baseline (Only allowed in Release mode) +-d, --dump Dump the compiled AST +-t, --trace Run with TracingObserver enabled + --no-opt Disable optimization +-h, --help Print help +-V, --version Print version diff --git a/src/ast/compiler/optimizer.rs b/src/ast/compiler/optimizer.rs index 080b2ec..aa7012b 100644 --- a/src/ast/compiler/optimizer.rs +++ b/src/ast/compiler/optimizer.rs @@ -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>>>, pub global_purity: Option>>>, } 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>>) -> 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::() { 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); } } diff --git a/src/ast/environment.rs b/src/ast/environment.rs index b898f86..c8bfb8c 100644 --- a/src/ast/environment.rs +++ b/src/ast/environment.rs @@ -26,7 +26,7 @@ pub struct Environment { pub function_registry: Rc>>, pub monomorph_cache: Rc>, pub debug_mode: bool, - pub optimization_level: u32, + pub optimization: bool, } struct EnvFunctionRegistry { @@ -91,7 +91,7 @@ impl Environment { function_registry: Rc::new(RefCell::new(HashMap::new())), monomorph_cache: Rc::new(RefCell::new(HashMap::new())), debug_mode: false, - optimization_level: 2, + optimization: true, }; env.register_stdlib(); env @@ -189,7 +189,7 @@ impl Environment { let specialized = self.specialize_node(node); // 2. Optimize (Level 1: Cracking, Level 2: Collapsing) - let optimizer = Optimizer::new(self.optimization_level) + let optimizer = Optimizer::new(self.optimization) .with_globals(self.global_values.clone()) .with_purity(self.global_purity.clone()); let optimized = optimizer.optimize(specialized); @@ -210,7 +210,7 @@ impl Environment { let global_values = self.global_values.clone(); let global_types = self.global_types.clone(); let global_purity = self.global_purity.clone(); - let opt_level = self.optimization_level; + let optimization = self.optimization; let compiler = Rc::new( move |func_template: BoundNode, @@ -237,7 +237,7 @@ impl Environment { let specialized_ast = sub_specializer.specialize(retyped_ast); // 3. Optimize (Phase 2: Cracking & Folding) - let optimizer = Optimizer::new(opt_level) + let optimizer = Optimizer::new(optimization) .with_globals(global_values.clone()) .with_purity(global_purity.clone()); let optimized_ast = optimizer.optimize(specialized_ast); diff --git a/src/bin/ast.rs b/src/bin/ast.rs index 26fe7fb..14839ed 100644 --- a/src/bin/ast.rs +++ b/src/bin/ast.rs @@ -31,15 +31,15 @@ struct Cli { #[arg(short, long)] trace: bool, - /// Optimization level (0: None, 1: Cracking, 2: Aggressive) - #[arg(short, long, default_value_t = 0)] - opt: u32, + /// Disable optimization + #[arg(long)] + no_opt: bool, } fn main() { let cli = Cli::parse(); let mut env = Environment::new(); - env.optimization_level = cli.opt; + env.optimization = !cli.no_opt; if cli.bench || cli.update_bench { if cfg!(debug_assertions) { diff --git a/src/integration_test.rs b/src/integration_test.rs index 4058f86..f7902b5 100644 --- a/src/integration_test.rs +++ b/src/integration_test.rs @@ -82,13 +82,13 @@ mod tests { #[test] fn test_examples() { - for level in 0..=2 { - let results = crate::utils::tester::run_functional_tests_with_level(level); + for opt in [false, true] { + let results = crate::utils::tester::run_functional_tests_with_optimization(opt); for res in results { assert!( res.success, - "Example {} failed at level {}: {}", - res.name, level, res.message + "Example {} failed at opt {}: {}", + res.name, opt, res.message ); } } @@ -97,7 +97,7 @@ mod tests { #[test] fn test_debug_mode_logging() { let mut env = Environment::new(); - env.optimization_level = 0; + env.optimization = false; let source = "(+ 10 20)"; let result = env.run_debug(source).expect("Failed to run debug"); diff --git a/src/main.rs b/src/main.rs index e435ee8..32b1fe0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -95,7 +95,7 @@ impl CompilerApp { // Reset environment for a fresh run self.env = Environment::new(); - self.env.optimization_level = 2; + self.env.optimization = true; let res = (|| -> Result<(myc::ast::types::Value, std::time::Duration), String> { let start_run = std::time::Instant::now(); @@ -157,7 +157,7 @@ impl CompilerApp { fn dump_ast(&mut self) { self.env = Environment::new(); - self.env.optimization_level = 2; // Enable optimization for AST dump + self.env.optimization = true; // Enable optimization for AST dump match self.env.dump_ast(&self.source_code) { Ok(dump) => { self.output_log = format!("--- BOUND AST DUMP ---\n\n{}", dump); diff --git a/src/utils/tester.rs b/src/utils/tester.rs index 724f28d..0f3aaae 100644 --- a/src/utils/tester.rs +++ b/src/utils/tester.rs @@ -18,17 +18,17 @@ pub struct BenchmarkResult { } pub fn run_functional_tests() -> Vec { - run_functional_tests_with_level(0) + run_functional_tests_with_optimization(false) } -pub fn run_functional_tests_with_level(level: u32) -> Vec { +pub fn run_functional_tests_with_optimization(enabled: bool) -> Vec { let mut results = Vec::new(); let entries = fs::read_dir("examples").unwrap(); let output_re = Regex::new(r";; Output: (.*)").unwrap(); for entry in entries.filter_map(|e| e.ok()) { let mut env = Environment::new(); // Fresh environment per test file - env.optimization_level = level; + env.optimization = enabled; let path = entry.path(); if path.extension().is_some_and(|ext| ext == "myc") { let content = fs::read_to_string(&path).unwrap(); @@ -53,8 +53,8 @@ pub fn run_functional_tests_with_level(level: u32) -> Vec { name, success: false, message: format!( - "Level {}: Expected {}, got {}", - level, expected, val_str + "Opt {}: Expected {}, got {}", + if enabled { "ON" } else { "OFF" }, expected, val_str ), }); } @@ -62,7 +62,7 @@ pub fn run_functional_tests_with_level(level: u32) -> Vec { Err(e) => results.push(TestResult { name, success: false, - message: format!("Level {}: Error: {}", level, e), + message: format!("Opt {}: Error: {}", if enabled { "ON" } else { "OFF" }, e), }), } }