feat: Add AST analysis pass for purity and recursion
This commit introduces a new AST analysis pass that identifies function purity and recursion. This information is then used by the optimizer and specializer to make more informed decisions, particularly regarding inlining. The `Analyzer` struct and its associated `Analysis` struct are responsible for traversing the AST and collecting this data. Key changes include: - A new `analyzer` module is added to `ast::compiler`. - `Analyzer::analyze` performs a two-pass traversal to collect global-to-lambda mappings and then analyze purity and recursion. - The `Optimizer` and `Specializer` are updated to accept and utilize the `Analysis` data. - Recursion checks in `Optimizer` and `Specializer` are replaced with checks against the pre-computed `Analysis.is_recursive` set. - The `Environment` now stores and passes the `Analysis` results to the compiler stages.
This commit is contained in:
+15
-2
@@ -1,3 +1,4 @@
|
||||
use crate::ast::compiler::analyzer::{Analysis, Analyzer};
|
||||
use crate::ast::compiler::binder::Binder;
|
||||
use crate::ast::compiler::{TypeChecker, TypedNode};
|
||||
use crate::ast::nodes::{Node, Symbol, UntypedKind};
|
||||
@@ -29,6 +30,7 @@ pub struct Environment {
|
||||
pub monomorph_cache: Rc<RefCell<MonoCache>>,
|
||||
pub debug_mode: bool,
|
||||
pub optimization: bool,
|
||||
pub last_analysis: RefCell<Analysis>,
|
||||
}
|
||||
|
||||
struct EnvFunctionRegistry {
|
||||
@@ -96,6 +98,7 @@ impl Environment {
|
||||
monomorph_cache: Rc::new(RefCell::new(HashMap::new())),
|
||||
debug_mode: false,
|
||||
optimization: true,
|
||||
last_analysis: RefCell::new(Analysis::default()),
|
||||
};
|
||||
env.register_stdlib();
|
||||
env
|
||||
@@ -204,6 +207,10 @@ impl Environment {
|
||||
// 7. Collect Typed Lambdas
|
||||
LambdaCollector::collect(&typed_ast, &mut self.typed_function_registry.borrow_mut());
|
||||
|
||||
// 8. Analyze (Purity, Recursion)
|
||||
let analysis = Analyzer::analyze(&typed_ast, &self.global_purity.borrow());
|
||||
*self.last_analysis.borrow_mut() = analysis;
|
||||
|
||||
Ok(typed_ast)
|
||||
}
|
||||
|
||||
@@ -216,7 +223,8 @@ impl Environment {
|
||||
let optimizer = Optimizer::new(self.optimization)
|
||||
.with_globals(self.global_values.clone())
|
||||
.with_purity(self.global_purity.clone())
|
||||
.with_registry(self.typed_function_registry.clone());
|
||||
.with_registry(self.typed_function_registry.clone())
|
||||
.with_analysis(self.last_analysis.borrow().clone());
|
||||
let optimized = optimizer.optimize(specialized);
|
||||
|
||||
// 3. TCO (Always performed, converts to ExecNode)
|
||||
@@ -302,7 +310,9 @@ impl Environment {
|
||||
let global_types = self.global_types.clone();
|
||||
let global_purity = self.global_purity.clone();
|
||||
let optimization = self.optimization;
|
||||
let analysis = self.last_analysis.borrow().clone();
|
||||
|
||||
let compiler_analysis = analysis.clone();
|
||||
let compiler = Rc::new(
|
||||
move |func_template: BoundNode,
|
||||
arg_types: &[StaticType]|
|
||||
@@ -323,6 +333,7 @@ impl Environment {
|
||||
None,
|
||||
Some(sub_rtl_lookup),
|
||||
Some(mono_cache.clone()),
|
||||
compiler_analysis.clone(),
|
||||
);
|
||||
|
||||
let specialized_ast = sub_specializer.specialize(retyped_ast);
|
||||
@@ -330,7 +341,8 @@ impl Environment {
|
||||
// 3. Optimize (Phase 2: Cracking & Folding)
|
||||
let optimizer = Optimizer::new(optimization)
|
||||
.with_globals(global_values.clone())
|
||||
.with_purity(global_purity.clone());
|
||||
.with_purity(global_purity.clone())
|
||||
.with_analysis(compiler_analysis.clone());
|
||||
let optimized_ast = optimizer.optimize(specialized_ast);
|
||||
|
||||
// 4. TCO (converts to ExecNode)
|
||||
@@ -359,6 +371,7 @@ impl Environment {
|
||||
Some(compiler),
|
||||
Some(rtl_lookup),
|
||||
Some(self.monomorph_cache.clone()),
|
||||
analysis,
|
||||
);
|
||||
|
||||
specializer.specialize(node)
|
||||
|
||||
Reference in New Issue
Block a user