Refactor AST inlining and global purity checks

Rename `ast_locals` to `ast_substitutions` for clarity.
Update `is_inlinable_value` to accept `Address` and check global purity
separately.
Simplify inlining logic in `optimize_node`.
This commit is contained in:
Michael Schimmel
2026-02-25 11:09:22 +01:00
parent 283cdf61a4
commit 6034216524
+39 -27
View File
@@ -129,22 +129,15 @@ impl Optimizer {
let (new_kind, metrics) = match node.kind {
BoundKind::Get { addr, ref name } => {
if !sub.assigned.contains(&addr) {
// 1. Try inlining from current substitution map (locals/globals/upvalues)
if let Some(val) = sub.get_value(&addr) {
// For globals, only inline if it's a known "safe" value
let can_inline = match addr {
Address::Global(idx) => self.is_inlinable_value(val, Some(idx)),
_ => true,
};
if can_inline {
return self.make_constant_node(val.clone(), &node);
}
// 1. Try inlining from current value substitution map (locals/globals/upvalues)
if let Some(val) = sub.get_value(&addr)
&& self.is_inlinable_value(val, addr)
{
return self.make_constant_node(val.clone(), &node);
}
// 2. Special case: Local-only AST inlining (for non-constant but pure expressions)
if let Address::Local(slot) = addr
&& let Some(inlined_node) = sub.ast_locals.get(&slot)
{
// 2. Try inlining from AST substitution map (pure expressions)
if let Some(inlined_node) = sub.ast_substitutions.get(&addr) {
return inlined_node.clone();
}
@@ -154,7 +147,7 @@ impl Optimizer {
{
let globals = globals_rc.borrow();
if let Some(val) = globals.get(idx as usize)
&& self.is_inlinable_value(val, Some(idx))
&& self.is_inlinable_value(val, addr)
{
return self.make_constant_node(val.clone(), &node);
}
@@ -597,8 +590,9 @@ impl Optimizer {
}
}
fn is_inlinable_value(&self, val: &Value, _global_idx: Option<u32>) -> bool {
match val {
fn is_inlinable_value(&self, val: &Value, addr: Address) -> bool {
// 1. Basic check: is the type itself inlinable (Constants, pure Closures)?
let type_ok = match val {
Value::Int(_)
| Value::Float(_)
| Value::Bool(_)
@@ -613,7 +607,27 @@ impl Optimizer {
}
}
_ => false,
};
if !type_ok {
return false;
}
// 2. For Globals: check if they are actually pure/stable
if let Address::Global(idx) = addr {
if let Some(purity_rc) = &self.global_purity {
let purity = purity_rc
.borrow()
.get(&idx)
.cloned()
.unwrap_or(Purity::Impure);
return purity >= Purity::Pure;
}
// If no purity info is available, we assume the global is unstable (safer)
return false;
}
true // Locals/Upvalues that reach here and passed the type check are inlinable
}
fn make_constant_node(&self, val: Value, template: &AnalyzedNode) -> AnalyzedNode {
@@ -710,13 +724,13 @@ impl Optimizer {
let addr = Address::Local(slot);
if (body_usage.is_used(&addr) || body_usage.is_assigned(&addr))
&& sub.get_value(&addr).is_none()
&& !sub.ast_locals.contains_key(&slot)
&& !sub.ast_substitutions.contains_key(&addr)
{
return None;
}
}
if sub.values.is_empty() && sub.ast_locals.is_empty() && !arg_vals.is_empty() {
if sub.values.is_empty() && sub.ast_substitutions.is_empty() && !arg_vals.is_empty() {
return None;
}
@@ -774,16 +788,14 @@ impl Optimizer {
sub.add_value(*addr, val.clone());
} else if let BoundKind::Lambda { upvalues, .. } = &arg.kind
&& upvalues.is_empty()
&& let Address::Local(slot) = addr
{
sub.add_ast_local(*slot, arg.clone());
sub.add_ast_substitution(*addr, arg.clone());
} else if let BoundKind::Get {
addr: Address::Global(_),
..
} = &arg.kind
&& let Address::Local(slot) = addr
{
sub.add_ast_local(*slot, arg.clone());
sub.add_ast_substitution(*addr, arg.clone());
}
}
@@ -941,7 +953,7 @@ impl Optimizer {
struct SubstitutionMap {
values: HashMap<Address, Value>,
ast_locals: HashMap<u32, AnalyzedNode>,
ast_substitutions: HashMap<Address, AnalyzedNode>,
slot_mapping: HashMap<u32, u32>,
assigned: HashSet<Address>,
next_slot: u32,
@@ -953,7 +965,7 @@ impl SubstitutionMap {
fn new() -> Self {
Self {
values: HashMap::new(),
ast_locals: HashMap::new(),
ast_substitutions: HashMap::new(),
slot_mapping: HashMap::new(),
assigned: HashSet::new(),
next_slot: 0,
@@ -962,8 +974,8 @@ impl SubstitutionMap {
}
}
fn add_ast_local(&mut self, slot: u32, node: AnalyzedNode) {
self.ast_locals.insert(slot, node);
fn add_ast_substitution(&mut self, addr: Address, node: AnalyzedNode) {
self.ast_substitutions.insert(addr, node);
}
fn map_slot(&mut self, old_slot: u32) -> u32 {