From 81c805f07ea8572da3c77e978e85d1b3dbcaeb3b Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Wed, 25 Feb 2026 21:26:12 +0100 Subject: [PATCH] Refactor optimizer utilities Removes unused `flatten_tuple` function from optimizer utilities. The functionality was moved to the `Folder` struct, making it more contextually appropriate. This change streamlines the utility module and improves code organization. --- src/ast/compiler/optimizer/engine.rs | 34 +- src/ast/compiler/optimizer/folder.rs | 165 ++++---- src/ast/compiler/optimizer/inliner.rs | 9 +- src/ast/compiler/optimizer/mod.rs | 2 +- .../compiler/optimizer/substitution_map.rs | 348 ++++++++--------- src/ast/compiler/optimizer/utils.rs | 358 +++++++++--------- 6 files changed, 456 insertions(+), 460 deletions(-) diff --git a/src/ast/compiler/optimizer/engine.rs b/src/ast/compiler/optimizer/engine.rs index af8dcd7..33b52aa 100644 --- a/src/ast/compiler/optimizer/engine.rs +++ b/src/ast/compiler/optimizer/engine.rs @@ -1,6 +1,4 @@ -use crate::ast::compiler::bound_nodes::{ - Address, AnalyzedNode, BoundKind, GlobalIdx, UpvalueIdx, -}; +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; @@ -8,9 +6,9 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; -use super::substitution_map::SubstitutionMap; use super::folder::Folder; use super::inliner::Inliner; +use super::substitution_map::SubstitutionMap; use super::utils::{PathTracker, UsageInfo}; pub struct Optimizer { @@ -177,6 +175,9 @@ impl Optimizer { let args = self.visit_node(*args, sub, path); if self.enabled { + let mut arg_nodes = Vec::new(); + self.flatten_tuple(args.clone(), &mut arg_nodes); + if let BoundKind::Lambda { params, body, @@ -192,7 +193,7 @@ impl Optimizer { path.inlining_depth += 1; let mut inner_sub = SubstitutionMap::new(); let collapsed = if inliner - .prepare_beta_reduction(params, &args, body, &mut inner_sub) + .prepare_beta_reduction(params, &arg_nodes, body, &mut inner_sub) .is_some() { Some(self.visit_node((**body).clone(), &mut inner_sub, path)) @@ -231,7 +232,7 @@ impl Optimizer { path.inlining_stack.insert(*idx); path.inlining_depth += 1; let collapsed = if inliner - .prepare_beta_reduction(params, &args, body, &mut inner_sub) + .prepare_beta_reduction(params, &arg_nodes, body, &mut inner_sub) .is_some() { Some(self.visit_node((**body).clone(), &mut inner_sub, path)) @@ -273,7 +274,7 @@ impl Optimizer { let collapsed = if inliner .prepare_beta_reduction( &closure.parameter_node, - &args, + &arg_nodes, &inlined_body, &mut closure_sub, ) @@ -291,7 +292,7 @@ impl Optimizer { } } - if let Some(folded) = folder.try_fold_pure(&callee, &args) { + if let Some(folded) = folder.try_fold_pure(&callee, &arg_nodes) { return folded; } } @@ -533,4 +534,21 @@ impl Optimizer { 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::Record { fields } => { + for (_, v) in fields { + self.flatten_tuple(v, into); + } + } + BoundKind::Nop => {} + _ => into.push(node), + } + } } diff --git a/src/ast/compiler/optimizer/folder.rs b/src/ast/compiler/optimizer/folder.rs index 59a7463..fa5281c 100644 --- a/src/ast/compiler/optimizer/folder.rs +++ b/src/ast/compiler/optimizer/folder.rs @@ -1,83 +1,82 @@ -use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, NodeMetrics}; -use crate::ast::nodes::Node; -use crate::ast::types::{Purity, StaticType, Value}; -use std::cell::RefCell; -use std::rc::Rc; - -use super::utils::flatten_tuple; - -pub struct Folder<'a> { - pub globals: &'a Option>>>, -} - -impl<'a> Folder<'a> { - pub fn new(globals: &'a Option>>>) -> Self { - Self { globals } - } - - pub fn make_constant_node(&self, val: Value, template: &AnalyzedNode) -> AnalyzedNode { - let ty = val.static_type(); - let typed_original = Rc::new(Node { - identity: template.identity.clone(), - kind: BoundKind::Constant(val.clone()), - ty: ty.clone(), - }); - Node { - identity: template.identity.clone(), - kind: BoundKind::Constant(val), - ty: NodeMetrics { - original: typed_original, - purity: Purity::Pure, - is_recursive: false, - }, - } - } - - pub fn make_nop_node(&self, template: &AnalyzedNode) -> AnalyzedNode { - let typed_original = Rc::new(Node { - identity: template.identity.clone(), - kind: BoundKind::Nop, - ty: StaticType::Void, - }); - Node { - identity: template.identity.clone(), - kind: BoundKind::Nop, - ty: NodeMetrics { - original: typed_original, - purity: Purity::Pure, - is_recursive: false, - }, - } - } - - pub fn try_fold_pure(&self, callee: &AnalyzedNode, args: &AnalyzedNode) -> Option { - if callee.ty.purity < Purity::Pure || args.ty.purity < Purity::Pure { - return None; - } - - let mut arg_nodes = Vec::new(); - flatten_tuple(args.clone(), &mut arg_nodes); - let mut arg_values = Vec::with_capacity(arg_nodes.len()); - for node in arg_nodes { - if let BoundKind::Constant(val) = node.kind { - arg_values.push(val); - } else { - return None; - } - } - let func_val = match &callee.kind { - BoundKind::Get { - addr: Address::Global(idx), - .. - } => self.globals.as_ref()?.borrow().get(idx.0 as usize)?.clone(), - BoundKind::Constant(val) => val.clone(), - _ => return None, - }; - let result = match func_val { - Value::Function(f) => (f.func)(arg_values), - _ => return None, - }; - Some(self.make_constant_node(result, callee)) - } -} - +use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, NodeMetrics}; +use crate::ast::nodes::Node; +use crate::ast::types::{Purity, StaticType, Value}; +use std::cell::RefCell; +use std::rc::Rc; + +pub struct Folder<'a> { + pub globals: &'a Option>>>, +} + +impl<'a> Folder<'a> { + pub fn new(globals: &'a Option>>>) -> Self { + Self { globals } + } + + pub fn make_constant_node(&self, val: Value, template: &AnalyzedNode) -> AnalyzedNode { + let ty = val.static_type(); + let typed_original = Rc::new(Node { + identity: template.identity.clone(), + kind: BoundKind::Constant(val.clone()), + ty: ty.clone(), + }); + Node { + identity: template.identity.clone(), + kind: BoundKind::Constant(val), + ty: NodeMetrics { + original: typed_original, + purity: Purity::Pure, + is_recursive: false, + }, + } + } + + pub fn make_nop_node(&self, template: &AnalyzedNode) -> AnalyzedNode { + let typed_original = Rc::new(Node { + identity: template.identity.clone(), + kind: BoundKind::Nop, + ty: StaticType::Void, + }); + Node { + identity: template.identity.clone(), + kind: BoundKind::Nop, + ty: NodeMetrics { + original: typed_original, + purity: Purity::Pure, + is_recursive: false, + }, + } + } + + pub fn try_fold_pure( + &self, + callee: &AnalyzedNode, + arg_nodes: &[AnalyzedNode], + ) -> Option { + if callee.ty.purity < Purity::Pure { + return None; + } + + let mut arg_values = Vec::with_capacity(arg_nodes.len()); + for node in arg_nodes { + if let BoundKind::Constant(val) = &node.kind { + arg_values.push(val.clone()); + } else { + return None; + } + } + let func_val = match &callee.kind { + BoundKind::Get { + addr: Address::Global(idx), + .. + } => self.globals.as_ref()?.borrow().get(idx.0 as usize)?.clone(), + BoundKind::Constant(val) => val.clone(), + _ => return None, + }; + let result = match func_val { + Value::Function(f) => (f.func)(arg_values), + _ => return None, + }; + Some(self.make_constant_node(result, callee)) + } +} diff --git a/src/ast/compiler/optimizer/inliner.rs b/src/ast/compiler/optimizer/inliner.rs index b67aa53..36b8b0e 100644 --- a/src/ast/compiler/optimizer/inliner.rs +++ b/src/ast/compiler/optimizer/inliner.rs @@ -6,7 +6,7 @@ use std::collections::{HashMap, HashSet}; use std::rc::Rc; use super::substitution_map::SubstitutionMap; -use super::utils::{flatten_tuple, UsageInfo}; +use super::utils::UsageInfo; pub struct Inliner<'a> { pub globals: &'a Option>>>, @@ -64,18 +64,15 @@ impl<'a> Inliner<'a> { pub fn prepare_beta_reduction( &self, params: &AnalyzedNode, - args: &AnalyzedNode, + arg_vals: &[AnalyzedNode], body: &AnalyzedNode, sub: &mut SubstitutionMap, ) -> Option<()> { let mut body_usage = UsageInfo::default(); body_usage.collect(body); - let mut arg_vals = Vec::new(); - flatten_tuple(args.clone(), &mut arg_vals); - let mut slot_index = 0; - self.map_params_to_args(params, &arg_vals, &mut slot_index, sub, &body_usage); + self.map_params_to_args(params, arg_vals, &mut slot_index, sub, &body_usage); if slot_index != arg_vals.len() { return None; diff --git a/src/ast/compiler/optimizer/mod.rs b/src/ast/compiler/optimizer/mod.rs index 44f8b9f..5b17037 100644 --- a/src/ast/compiler/optimizer/mod.rs +++ b/src/ast/compiler/optimizer/mod.rs @@ -8,4 +8,4 @@ pub use engine::Optimizer; pub use folder::Folder; pub use inliner::Inliner; pub use substitution_map::SubstitutionMap; -pub use utils::{PathTracker, UsageInfo, flatten_tuple}; +pub use utils::{PathTracker, UsageInfo}; diff --git a/src/ast/compiler/optimizer/substitution_map.rs b/src/ast/compiler/optimizer/substitution_map.rs index 36cc520..91b5fb4 100644 --- a/src/ast/compiler/optimizer/substitution_map.rs +++ b/src/ast/compiler/optimizer/substitution_map.rs @@ -1,174 +1,174 @@ -use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot, UpvalueIdx}; -use crate::ast::nodes::Node; -use crate::ast::types::{Value}; -use std::collections::{HashMap, HashSet}; - -#[derive(Default)] -pub struct SubstitutionMap { - pub values: HashMap, - pub ast_substitutions: HashMap, - pub slot_mapping: HashMap, - pub assigned: HashSet
, - pub next_slot: u32, - pub used: HashSet
, - pub captured_slots: HashSet, -} - -impl SubstitutionMap { - pub fn new() -> Self { - Self::default() - } - - pub fn add_ast_substitution(&mut self, addr: Address, node: AnalyzedNode) { - self.ast_substitutions.insert(addr, node); - } - - pub fn map_slot(&mut self, old_slot: LocalSlot) -> LocalSlot { - if let Some(&new_slot) = self.slot_mapping.get(&old_slot) { - return new_slot; - } - let new_slot = LocalSlot(self.next_slot); - self.slot_mapping.insert(old_slot, new_slot); - self.next_slot += 1; - new_slot - } - - pub fn map_address(&mut self, addr: Address) -> Address { - match addr { - Address::Local(slot) => Address::Local(self.map_slot(slot)), - other => other, - } - } - - pub fn add_value(&mut self, addr: Address, val: Value) { - self.values.insert(addr, val); - } - - pub fn get_value(&self, addr: &Address) -> Option<&Value> { - self.values.get(addr) - } - - pub fn remove_value(&mut self, addr: &Address) { - self.values.remove(addr); - } - - fn reindex_addr(&self, addr: Address, mapping: &[Option]) -> Address { - if let Address::Upvalue(idx) = addr - && let Some(res) = mapping.get(idx.0 as usize) - && let Some(new_idx) = res - { - Address::Upvalue(UpvalueIdx(*new_idx)) - } else { - addr - } - } - - pub fn reindex_upvalues(&self, node: AnalyzedNode, mapping: &[Option]) -> AnalyzedNode { - let (new_kind, metrics) = match node.kind { - BoundKind::Get { addr, name } => ( - BoundKind::Get { - addr: self.reindex_addr(addr, mapping), - name, - }, - node.ty.clone(), - ), - BoundKind::Lambda { - params, - upvalues, - body, - positional_count, - } => { - let mut next_upvalues = Vec::new(); - for addr in upvalues { - next_upvalues.push(self.reindex_addr(addr, mapping)); - } - ( - BoundKind::Lambda { - params, - upvalues: next_upvalues, - body, - positional_count, - }, - node.ty.clone(), - ) - } - BoundKind::If { - cond, - then_br, - else_br, - } => { - let cond = Box::new(self.reindex_upvalues(*cond, mapping)); - let then_br = Box::new(self.reindex_upvalues(*then_br, mapping)); - let else_br = else_br.map(|e| Box::new(self.reindex_upvalues(*e, mapping))); - ( - BoundKind::If { - cond, - then_br, - else_br, - }, - node.ty.clone(), - ) - } - BoundKind::Block { exprs } => { - let exprs = exprs - .into_iter() - .map(|e| self.reindex_upvalues(e, mapping)) - .collect(); - (BoundKind::Block { exprs }, node.ty.clone()) - } - BoundKind::Call { callee, args } => { - let callee = Box::new(self.reindex_upvalues(*callee, mapping)); - let args = Box::new(self.reindex_upvalues(*args, mapping)); - (BoundKind::Call { callee, args }, node.ty.clone()) - } - BoundKind::Define { - name, - addr, - kind, - value, - captured_by, - } => { - let value = Box::new(self.reindex_upvalues(*value, mapping)); - ( - BoundKind::Define { - name, - addr, - kind, - value, - captured_by, - }, - node.ty.clone(), - ) - } - BoundKind::Set { addr, value } => { - let value = Box::new(self.reindex_upvalues(*value, mapping)); - (BoundKind::Set { addr, value }, node.ty.clone()) - } - BoundKind::Tuple { elements } => { - let elements = elements - .into_iter() - .map(|e| self.reindex_upvalues(e, mapping)) - .collect(); - (BoundKind::Tuple { elements }, node.ty.clone()) - } - BoundKind::Record { fields } => { - let fields = fields - .into_iter() - .map(|(k, v)| { - ( - self.reindex_upvalues(k, mapping), - self.reindex_upvalues(v, mapping), - ) - }) - .collect(); - (BoundKind::Record { fields }, node.ty.clone()) - } - k => (k, node.ty.clone()), - }; - Node { - identity: node.identity.clone(), - kind: new_kind, - ty: metrics, - } - } -} +use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot, UpvalueIdx}; +use crate::ast::nodes::Node; +use crate::ast::types::Value; +use std::collections::{HashMap, HashSet}; + +#[derive(Default)] +pub struct SubstitutionMap { + pub values: HashMap, + pub ast_substitutions: HashMap, + pub slot_mapping: HashMap, + pub assigned: HashSet
, + pub next_slot: u32, + pub used: HashSet
, + pub captured_slots: HashSet, +} + +impl SubstitutionMap { + pub fn new() -> Self { + Self::default() + } + + pub fn add_ast_substitution(&mut self, addr: Address, node: AnalyzedNode) { + self.ast_substitutions.insert(addr, node); + } + + pub fn map_slot(&mut self, old_slot: LocalSlot) -> LocalSlot { + if let Some(&new_slot) = self.slot_mapping.get(&old_slot) { + return new_slot; + } + let new_slot = LocalSlot(self.next_slot); + self.slot_mapping.insert(old_slot, new_slot); + self.next_slot += 1; + new_slot + } + + pub fn map_address(&mut self, addr: Address) -> Address { + match addr { + Address::Local(slot) => Address::Local(self.map_slot(slot)), + other => other, + } + } + + pub fn add_value(&mut self, addr: Address, val: Value) { + self.values.insert(addr, val); + } + + pub fn get_value(&self, addr: &Address) -> Option<&Value> { + self.values.get(addr) + } + + pub fn remove_value(&mut self, addr: &Address) { + self.values.remove(addr); + } + + fn reindex_addr(&self, addr: Address, mapping: &[Option]) -> Address { + if let Address::Upvalue(idx) = addr + && let Some(res) = mapping.get(idx.0 as usize) + && let Some(new_idx) = res + { + Address::Upvalue(UpvalueIdx(*new_idx)) + } else { + addr + } + } + + pub fn reindex_upvalues(&self, node: AnalyzedNode, mapping: &[Option]) -> AnalyzedNode { + let (new_kind, metrics) = match node.kind { + BoundKind::Get { addr, name } => ( + BoundKind::Get { + addr: self.reindex_addr(addr, mapping), + name, + }, + node.ty.clone(), + ), + BoundKind::Lambda { + params, + upvalues, + body, + positional_count, + } => { + let mut next_upvalues = Vec::new(); + for addr in upvalues { + next_upvalues.push(self.reindex_addr(addr, mapping)); + } + ( + BoundKind::Lambda { + params, + upvalues: next_upvalues, + body, + positional_count, + }, + node.ty.clone(), + ) + } + BoundKind::If { + cond, + then_br, + else_br, + } => { + let cond = Box::new(self.reindex_upvalues(*cond, mapping)); + let then_br = Box::new(self.reindex_upvalues(*then_br, mapping)); + let else_br = else_br.map(|e| Box::new(self.reindex_upvalues(*e, mapping))); + ( + BoundKind::If { + cond, + then_br, + else_br, + }, + node.ty.clone(), + ) + } + BoundKind::Block { exprs } => { + let exprs = exprs + .into_iter() + .map(|e| self.reindex_upvalues(e, mapping)) + .collect(); + (BoundKind::Block { exprs }, node.ty.clone()) + } + BoundKind::Call { callee, args } => { + let callee = Box::new(self.reindex_upvalues(*callee, mapping)); + let args = Box::new(self.reindex_upvalues(*args, mapping)); + (BoundKind::Call { callee, args }, node.ty.clone()) + } + BoundKind::Define { + name, + addr, + kind, + value, + captured_by, + } => { + let value = Box::new(self.reindex_upvalues(*value, mapping)); + ( + BoundKind::Define { + name, + addr, + kind, + value, + captured_by, + }, + node.ty.clone(), + ) + } + BoundKind::Set { addr, value } => { + let value = Box::new(self.reindex_upvalues(*value, mapping)); + (BoundKind::Set { addr, value }, node.ty.clone()) + } + BoundKind::Tuple { elements } => { + let elements = elements + .into_iter() + .map(|e| self.reindex_upvalues(e, mapping)) + .collect(); + (BoundKind::Tuple { elements }, node.ty.clone()) + } + BoundKind::Record { fields } => { + let fields = fields + .into_iter() + .map(|(k, v)| { + ( + self.reindex_upvalues(k, mapping), + self.reindex_upvalues(v, mapping), + ) + }) + .collect(); + (BoundKind::Record { fields }, node.ty.clone()) + } + k => (k, node.ty.clone()), + }; + Node { + identity: node.identity.clone(), + kind: new_kind, + ty: metrics, + } + } +} diff --git a/src/ast/compiler/optimizer/utils.rs b/src/ast/compiler/optimizer/utils.rs index e431e52..e02fbea 100644 --- a/src/ast/compiler/optimizer/utils.rs +++ b/src/ast/compiler/optimizer/utils.rs @@ -1,188 +1,170 @@ -use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, GlobalIdx}; -use crate::ast::types::{Identity, Value}; -use crate::ast::vm::Closure; -use std::collections::HashSet; - -// --- PathTracker --- -#[derive(Default)] -pub struct PathTracker { - pub inlining_depth: usize, - pub inlining_stack: HashSet, - pub identity_stack: HashSet, -} - -impl PathTracker { - pub fn new() -> Self { - Self::default() - } - - pub fn enter_lambda(&mut self, identity: &Identity) -> bool { - if self.identity_stack.contains(identity) { - return false; - } - self.identity_stack.insert(identity.clone()); - true - } - - pub fn exit_lambda(&mut self, identity: &Identity) { - self.identity_stack.remove(identity); - } -} - -// --- UsageInfo --- -#[derive(Default)] -pub struct UsageInfo { - pub used: HashSet
, - pub assigned: HashSet
, - pub used_identities: HashSet, -} - -impl UsageInfo { - pub fn is_assigned(&self, addr: &Address) -> bool { - self.assigned.contains(addr) - } - - pub fn is_used(&self, addr: &Address) -> bool { - self.used.contains(addr) - } - - pub fn collect(&mut self, node: &AnalyzedNode) { - match &node.kind { - BoundKind::Destructure { pattern, value } => { - self.collect_pattern(pattern); - self.collect(value); - } - BoundKind::Constant(v) => { - if let Value::Object(obj) = v - && let Some(closure) = obj.as_any().downcast_ref::() - { - self.used_identities - .insert(closure.function_node.identity.clone()); - self.collect(&closure.function_node); - } - } - BoundKind::Get { addr, .. } => { - self.used.insert(*addr); - } - BoundKind::Set { addr, value } => { - self.assigned.insert(*addr); - self.collect(value); - } - BoundKind::Lambda { - params, - body, - upvalues, - .. - } => { - self.used_identities.insert(node.identity.clone()); - - let mut inner_info = UsageInfo::default(); - inner_info.collect(params); - inner_info.collect(body); - - // Propagate globals and identities - for addr in &inner_info.used { - if let Address::Global(_) = addr { - self.used.insert(*addr); - } - } - for addr in &inner_info.assigned { - if let Address::Global(_) = addr { - self.assigned.insert(*addr); - } - } - self.used_identities.extend(inner_info.used_identities); - - // Map used upvalues to parent scope - for addr in &inner_info.used { - if let Address::Upvalue(idx) = addr - && let Some(parent_addr) = upvalues.get(idx.0 as usize) - { - self.used.insert(*parent_addr); - } - } - // Map assigned upvalues to parent scope - for addr in &inner_info.assigned { - if let Address::Upvalue(idx) = addr - && let Some(parent_addr) = upvalues.get(idx.0 as usize) - { - self.assigned.insert(*parent_addr); - } - } - } - BoundKind::Block { exprs } => { - for e in exprs { - self.collect(e); - } - } - BoundKind::If { - cond, - then_br, - else_br, - } => { - self.collect(cond); - self.collect(then_br); - if let Some(e) = else_br { - self.collect(e); - } - } - BoundKind::Call { callee, args } => { - self.collect(callee); - self.collect(args); - } - BoundKind::Tuple { elements } => { - for e in elements { - self.collect(e); - } - } - BoundKind::Record { fields } => { - for (k, v) in fields { - self.collect(k); - self.collect(v); - } - } - BoundKind::Define { value, .. } => { - self.collect(value); - } - BoundKind::Expansion { bound_expanded, .. } => { - self.collect(bound_expanded); - } - _ => {} - } - } - - pub fn collect_pattern(&mut self, node: &AnalyzedNode) { - match &node.kind { - BoundKind::Define { addr, .. } => { - self.assigned.insert(*addr); - } - BoundKind::Set { addr, .. } => { - self.assigned.insert(*addr); - } - BoundKind::Tuple { elements } => { - for el in elements { - self.collect_pattern(el); - } - } - _ => {} - } - } -} - -// --- Structural Helpers --- -pub fn flatten_tuple(node: AnalyzedNode, into: &mut Vec) { - match node.kind { - BoundKind::Tuple { elements } => { - for el in elements { - flatten_tuple(el, into); - } - } - BoundKind::Record { fields } => { - for (_, v) in fields { - flatten_tuple(v, into); - } - } - BoundKind::Nop => {} - _ => into.push(node), - } -} +use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, GlobalIdx}; +use crate::ast::types::{Identity, Value}; +use crate::ast::vm::Closure; +use std::collections::HashSet; + +// --- PathTracker --- +#[derive(Default)] +pub struct PathTracker { + pub inlining_depth: usize, + pub inlining_stack: HashSet, + pub identity_stack: HashSet, +} + +impl PathTracker { + pub fn new() -> Self { + Self::default() + } + + pub fn enter_lambda(&mut self, identity: &Identity) -> bool { + if self.identity_stack.contains(identity) { + return false; + } + self.identity_stack.insert(identity.clone()); + true + } + + pub fn exit_lambda(&mut self, identity: &Identity) { + self.identity_stack.remove(identity); + } +} + +// --- UsageInfo --- +#[derive(Default)] +pub struct UsageInfo { + pub used: HashSet
, + pub assigned: HashSet
, + pub used_identities: HashSet, +} + +impl UsageInfo { + pub fn is_assigned(&self, addr: &Address) -> bool { + self.assigned.contains(addr) + } + + pub fn is_used(&self, addr: &Address) -> bool { + self.used.contains(addr) + } + + pub fn collect(&mut self, node: &AnalyzedNode) { + match &node.kind { + BoundKind::Destructure { pattern, value } => { + self.collect_pattern(pattern); + self.collect(value); + } + BoundKind::Constant(v) => { + if let Value::Object(obj) = v + && let Some(closure) = obj.as_any().downcast_ref::() + { + self.used_identities + .insert(closure.function_node.identity.clone()); + self.collect(&closure.function_node); + } + } + BoundKind::Get { addr, .. } => { + self.used.insert(*addr); + } + BoundKind::Set { addr, value } => { + self.assigned.insert(*addr); + self.collect(value); + } + BoundKind::Lambda { + params, + body, + upvalues, + .. + } => { + self.used_identities.insert(node.identity.clone()); + + let mut inner_info = UsageInfo::default(); + inner_info.collect(params); + inner_info.collect(body); + + // Propagate globals and identities + for addr in &inner_info.used { + if let Address::Global(_) = addr { + self.used.insert(*addr); + } + } + for addr in &inner_info.assigned { + if let Address::Global(_) = addr { + self.assigned.insert(*addr); + } + } + self.used_identities.extend(inner_info.used_identities); + + // Map used upvalues to parent scope + for addr in &inner_info.used { + if let Address::Upvalue(idx) = addr + && let Some(parent_addr) = upvalues.get(idx.0 as usize) + { + self.used.insert(*parent_addr); + } + } + // Map assigned upvalues to parent scope + for addr in &inner_info.assigned { + if let Address::Upvalue(idx) = addr + && let Some(parent_addr) = upvalues.get(idx.0 as usize) + { + self.assigned.insert(*parent_addr); + } + } + } + BoundKind::Block { exprs } => { + for e in exprs { + self.collect(e); + } + } + BoundKind::If { + cond, + then_br, + else_br, + } => { + self.collect(cond); + self.collect(then_br); + if let Some(e) = else_br { + self.collect(e); + } + } + BoundKind::Call { callee, args } => { + self.collect(callee); + self.collect(args); + } + BoundKind::Tuple { elements } => { + for e in elements { + self.collect(e); + } + } + BoundKind::Record { fields } => { + for (k, v) in fields { + self.collect(k); + self.collect(v); + } + } + BoundKind::Define { value, .. } => { + self.collect(value); + } + BoundKind::Expansion { bound_expanded, .. } => { + self.collect(bound_expanded); + } + _ => {} + } + } + + pub fn collect_pattern(&mut self, node: &AnalyzedNode) { + match &node.kind { + BoundKind::Define { addr, .. } => { + self.assigned.insert(*addr); + } + BoundKind::Set { addr, .. } => { + self.assigned.insert(*addr); + } + BoundKind::Tuple { elements } => { + for el in elements { + self.collect_pattern(el); + } + } + _ => {} + } + } +}