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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user