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 {
|
let (new_kind, metrics) = match node.kind {
|
||||||
BoundKind::Get { addr, ref name } => {
|
BoundKind::Get { addr, ref name } => {
|
||||||
if !sub.assigned.contains(&addr) {
|
if !sub.assigned.contains(&addr) {
|
||||||
// 1. Try inlining from current substitution map (locals/globals/upvalues)
|
// 1. Try inlining from current value substitution map (locals/globals/upvalues)
|
||||||
if let Some(val) = sub.get_value(&addr) {
|
if let Some(val) = sub.get_value(&addr)
|
||||||
// For globals, only inline if it's a known "safe" value
|
&& self.is_inlinable_value(val, addr)
|
||||||
let can_inline = match addr {
|
{
|
||||||
Address::Global(idx) => self.is_inlinable_value(val, Some(idx)),
|
return self.make_constant_node(val.clone(), &node);
|
||||||
_ => true,
|
|
||||||
};
|
|
||||||
if can_inline {
|
|
||||||
return self.make_constant_node(val.clone(), &node);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Special case: Local-only AST inlining (for non-constant but pure expressions)
|
// 2. Try inlining from AST substitution map (pure expressions)
|
||||||
if let Address::Local(slot) = addr
|
if let Some(inlined_node) = sub.ast_substitutions.get(&addr) {
|
||||||
&& let Some(inlined_node) = sub.ast_locals.get(&slot)
|
|
||||||
{
|
|
||||||
return inlined_node.clone();
|
return inlined_node.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +147,7 @@ impl Optimizer {
|
|||||||
{
|
{
|
||||||
let globals = globals_rc.borrow();
|
let globals = globals_rc.borrow();
|
||||||
if let Some(val) = globals.get(idx as usize)
|
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);
|
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 {
|
fn is_inlinable_value(&self, val: &Value, addr: Address) -> bool {
|
||||||
match val {
|
// 1. Basic check: is the type itself inlinable (Constants, pure Closures)?
|
||||||
|
let type_ok = match val {
|
||||||
Value::Int(_)
|
Value::Int(_)
|
||||||
| Value::Float(_)
|
| Value::Float(_)
|
||||||
| Value::Bool(_)
|
| Value::Bool(_)
|
||||||
@@ -613,7 +607,27 @@ impl Optimizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => 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 {
|
fn make_constant_node(&self, val: Value, template: &AnalyzedNode) -> AnalyzedNode {
|
||||||
@@ -710,13 +724,13 @@ impl Optimizer {
|
|||||||
let addr = Address::Local(slot);
|
let addr = Address::Local(slot);
|
||||||
if (body_usage.is_used(&addr) || body_usage.is_assigned(&addr))
|
if (body_usage.is_used(&addr) || body_usage.is_assigned(&addr))
|
||||||
&& sub.get_value(&addr).is_none()
|
&& sub.get_value(&addr).is_none()
|
||||||
&& !sub.ast_locals.contains_key(&slot)
|
&& !sub.ast_substitutions.contains_key(&addr)
|
||||||
{
|
{
|
||||||
return None;
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -774,16 +788,14 @@ impl Optimizer {
|
|||||||
sub.add_value(*addr, val.clone());
|
sub.add_value(*addr, val.clone());
|
||||||
} else if let BoundKind::Lambda { upvalues, .. } = &arg.kind
|
} else if let BoundKind::Lambda { upvalues, .. } = &arg.kind
|
||||||
&& upvalues.is_empty()
|
&& 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 {
|
} else if let BoundKind::Get {
|
||||||
addr: Address::Global(_),
|
addr: Address::Global(_),
|
||||||
..
|
..
|
||||||
} = &arg.kind
|
} = &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 {
|
struct SubstitutionMap {
|
||||||
values: HashMap<Address, Value>,
|
values: HashMap<Address, Value>,
|
||||||
ast_locals: HashMap<u32, AnalyzedNode>,
|
ast_substitutions: HashMap<Address, AnalyzedNode>,
|
||||||
slot_mapping: HashMap<u32, u32>,
|
slot_mapping: HashMap<u32, u32>,
|
||||||
assigned: HashSet<Address>,
|
assigned: HashSet<Address>,
|
||||||
next_slot: u32,
|
next_slot: u32,
|
||||||
@@ -953,7 +965,7 @@ impl SubstitutionMap {
|
|||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
values: HashMap::new(),
|
values: HashMap::new(),
|
||||||
ast_locals: HashMap::new(),
|
ast_substitutions: HashMap::new(),
|
||||||
slot_mapping: HashMap::new(),
|
slot_mapping: HashMap::new(),
|
||||||
assigned: HashSet::new(),
|
assigned: HashSet::new(),
|
||||||
next_slot: 0,
|
next_slot: 0,
|
||||||
@@ -962,8 +974,8 @@ impl SubstitutionMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_ast_local(&mut self, slot: u32, node: AnalyzedNode) {
|
fn add_ast_substitution(&mut self, addr: Address, node: AnalyzedNode) {
|
||||||
self.ast_locals.insert(slot, node);
|
self.ast_substitutions.insert(addr, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_slot(&mut self, old_slot: u32) -> u32 {
|
fn map_slot(&mut self, old_slot: u32) -> u32 {
|
||||||
|
|||||||
Reference in New Issue
Block a user