Refactor UsageInfo and SubstitutionMap
This commit refactors the `UsageInfo` struct to use a single `HashSet<Address>` for both used and assigned addresses, simplifying the logic. It also updates the `SubstitutionMap` to use a similar approach for tracking assigned values. Key changes include: - `UsageInfo` now has `used` and `assigned` fields of type `HashSet<Address>`. - `SubstitutionMap`'s `add_local`, `add_global`, `add_upvalue`, `remove_local`, `remove_global`, and `remove_upvalue` methods have been replaced with a generic `add_value` and `remove_value` that operate on `Address`. - The `Optimizer`'s `collect_pattern_usage` and `visit_node` methods have been updated to use the new `UsageInfo` and `SubstitutionMap` APIs. - `Get` and `Set` nodes now use `sub.map_address` to transform addresses based on the current substitution.
This commit is contained in:
@@ -118,7 +118,10 @@ impl Binder {
|
|||||||
if self.functions.len() == 1 {
|
if self.functions.len() == 1 {
|
||||||
let mut globals = self.globals.borrow_mut();
|
let mut globals = self.globals.borrow_mut();
|
||||||
if globals.contains_key(name) {
|
if globals.contains_key(name) {
|
||||||
return Err(format!("Global variable '{}' is already defined.", name.name));
|
return Err(format!(
|
||||||
|
"Global variable '{}' is already defined.",
|
||||||
|
name.name
|
||||||
|
));
|
||||||
}
|
}
|
||||||
let idx = globals.len() as u32;
|
let idx = globals.len() as u32;
|
||||||
globals.insert(name.clone(), idx);
|
globals.insert(name.clone(), idx);
|
||||||
@@ -512,7 +515,10 @@ mod tests {
|
|||||||
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
||||||
if let BoundKind::Block { exprs } = &body.kind {
|
if let BoundKind::Block { exprs } = &body.kind {
|
||||||
let x_decl = &exprs[0];
|
let x_decl = &exprs[0];
|
||||||
if let BoundKind::Define { captured_by, addr, .. } = &x_decl.kind {
|
if let BoundKind::Define {
|
||||||
|
captured_by, addr, ..
|
||||||
|
} = &x_decl.kind
|
||||||
|
{
|
||||||
assert!(matches!(addr, Address::Local(_)));
|
assert!(matches!(addr, Address::Local(_)));
|
||||||
assert!(
|
assert!(
|
||||||
!captured_by.is_empty(),
|
!captured_by.is_empty(),
|
||||||
@@ -544,7 +550,10 @@ mod tests {
|
|||||||
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
||||||
if let BoundKind::Block { exprs } = &body.kind {
|
if let BoundKind::Block { exprs } = &body.kind {
|
||||||
let x_decl = &exprs[0];
|
let x_decl = &exprs[0];
|
||||||
if let BoundKind::Define { captured_by, addr, .. } = &x_decl.kind {
|
if let BoundKind::Define {
|
||||||
|
captured_by, addr, ..
|
||||||
|
} = &x_decl.kind
|
||||||
|
{
|
||||||
assert!(matches!(addr, Address::Local(_)));
|
assert!(matches!(addr, Address::Local(_)));
|
||||||
assert!(
|
assert!(
|
||||||
captured_by.is_empty(),
|
captured_by.is_empty(),
|
||||||
|
|||||||
@@ -237,7 +237,9 @@ impl<T> BoundKind<T> {
|
|||||||
BoundKind::Constant(v) => format!("CONST({})", v),
|
BoundKind::Constant(v) => format!("CONST({})", v),
|
||||||
BoundKind::Get { addr, name } => format!("GET({}, {:?})", name.name, addr),
|
BoundKind::Get { addr, name } => format!("GET({}, {:?})", name.name, addr),
|
||||||
BoundKind::Set { addr, .. } => format!("SET({:?})", addr),
|
BoundKind::Set { addr, .. } => format!("SET({:?})", addr),
|
||||||
BoundKind::Define { name, addr, kind, .. } => {
|
BoundKind::Define {
|
||||||
|
name, addr, kind, ..
|
||||||
|
} => {
|
||||||
let k_str = match kind {
|
let k_str = match kind {
|
||||||
DeclarationKind::Variable => "VAR",
|
DeclarationKind::Variable => "VAR",
|
||||||
DeclarationKind::Parameter => "PARAM",
|
DeclarationKind::Parameter => "PARAM",
|
||||||
|
|||||||
+212
-294
@@ -44,13 +44,19 @@ impl PathTracker {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct UsageInfo {
|
struct UsageInfo {
|
||||||
used_locals: HashSet<u32>,
|
used: HashSet<Address>,
|
||||||
used_globals: HashSet<u32>,
|
assigned: HashSet<Address>,
|
||||||
used_upvalues: HashSet<u32>,
|
|
||||||
used_identities: HashSet<crate::ast::types::Identity>,
|
used_identities: HashSet<crate::ast::types::Identity>,
|
||||||
assigned_locals: HashSet<u32>,
|
}
|
||||||
assigned_globals: HashSet<u32>,
|
|
||||||
assigned_upvalues: HashSet<u32>,
|
impl UsageInfo {
|
||||||
|
fn is_assigned(&self, addr: &Address) -> bool {
|
||||||
|
self.assigned.contains(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_used(&self, addr: &Address) -> bool {
|
||||||
|
self.used.contains(addr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Optimizer {
|
impl Optimizer {
|
||||||
@@ -99,26 +105,12 @@ impl Optimizer {
|
|||||||
|
|
||||||
fn collect_pattern_usage(&self, node: &AnalyzedNode, info: &mut UsageInfo) {
|
fn collect_pattern_usage(&self, node: &AnalyzedNode, info: &mut UsageInfo) {
|
||||||
match &node.kind {
|
match &node.kind {
|
||||||
BoundKind::Define { addr, .. } => match addr {
|
BoundKind::Define { addr, .. } => {
|
||||||
Address::Local(slot) => {
|
info.assigned.insert(*addr);
|
||||||
info.assigned_locals.insert(*slot);
|
}
|
||||||
}
|
BoundKind::Set { addr, .. } => {
|
||||||
Address::Global(idx) => {
|
info.assigned.insert(*addr);
|
||||||
info.assigned_globals.insert(*idx);
|
}
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
BoundKind::Set { addr, .. } => match addr {
|
|
||||||
Address::Local(slot) => {
|
|
||||||
info.assigned_locals.insert(*slot);
|
|
||||||
}
|
|
||||||
Address::Global(idx) => {
|
|
||||||
info.assigned_globals.insert(*idx);
|
|
||||||
}
|
|
||||||
Address::Upvalue(idx) => {
|
|
||||||
info.assigned_upvalues.insert(*idx);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
BoundKind::Tuple { elements } => {
|
BoundKind::Tuple { elements } => {
|
||||||
for el in elements {
|
for el in elements {
|
||||||
self.collect_pattern_usage(el, info);
|
self.collect_pattern_usage(el, info);
|
||||||
@@ -136,54 +128,65 @@ impl Optimizer {
|
|||||||
) -> AnalyzedNode {
|
) -> AnalyzedNode {
|
||||||
let (new_kind, metrics) = match node.kind {
|
let (new_kind, metrics) = match node.kind {
|
||||||
BoundKind::Get { addr, ref name } => {
|
BoundKind::Get { addr, ref name } => {
|
||||||
let new_addr = match addr {
|
if !sub.assigned.contains(&addr) {
|
||||||
Address::Local(slot) => {
|
// 1. Try inlining from current substitution map (locals/globals/upvalues)
|
||||||
if !sub.assigned_locals.contains(&slot) {
|
if let Some(val) = sub.get_value(&addr) {
|
||||||
if let Some(inlined_node) = sub.ast_locals.get(&slot) {
|
// For globals, only inline if it's a known "safe" value
|
||||||
return inlined_node.clone();
|
let can_inline = match addr {
|
||||||
}
|
Address::Global(idx) => self.is_inlinable_value(val, Some(idx)),
|
||||||
if let Some(val) = sub.locals.get(&slot) {
|
_ => true,
|
||||||
return self.make_constant_node(val.clone(), &node);
|
};
|
||||||
}
|
if can_inline {
|
||||||
}
|
|
||||||
sub.used_slots.insert(slot);
|
|
||||||
Address::Local(sub.map_slot(slot))
|
|
||||||
}
|
|
||||||
Address::Upvalue(idx) => {
|
|
||||||
if let Some(val) = sub.upvalues.get(&idx) {
|
|
||||||
return self.make_constant_node(val.clone(), &node);
|
return self.make_constant_node(val.clone(), &node);
|
||||||
}
|
}
|
||||||
Address::Upvalue(idx)
|
|
||||||
}
|
}
|
||||||
Address::Global(idx) => {
|
|
||||||
if !sub.assigned_globals.contains(&idx) {
|
// 2. Special case: Local-only AST inlining (for non-constant but pure expressions)
|
||||||
if let Some(val) = sub.globals.get(&idx)
|
if let Address::Local(slot) = addr
|
||||||
&& self.is_inlinable_value(val, Some(idx))
|
&& let Some(inlined_node) = sub.ast_locals.get(&slot)
|
||||||
{
|
{
|
||||||
return self.make_constant_node(val.clone(), &node);
|
return inlined_node.clone();
|
||||||
}
|
}
|
||||||
if let Some(globals_rc) = &self.globals {
|
|
||||||
let globals = globals_rc.borrow();
|
// 3. Fallback for Globals: check the actual VM environment
|
||||||
if let Some(val) = globals.get(idx as usize)
|
if let Address::Global(idx) = addr
|
||||||
&& self.is_inlinable_value(val, Some(idx))
|
&& let Some(globals_rc) = &self.globals
|
||||||
{
|
{
|
||||||
return self.make_constant_node(val.clone(), &node);
|
let globals = globals_rc.borrow();
|
||||||
}
|
if let Some(val) = globals.get(idx as usize)
|
||||||
}
|
&& self.is_inlinable_value(val, Some(idx))
|
||||||
|
{
|
||||||
|
return self.make_constant_node(val.clone(), &node);
|
||||||
}
|
}
|
||||||
sub.used_globals.insert(idx);
|
|
||||||
Address::Global(idx)
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
sub.used.insert(addr);
|
||||||
(
|
(
|
||||||
BoundKind::Get {
|
BoundKind::Get {
|
||||||
addr: new_addr,
|
addr: sub.map_address(addr),
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
},
|
},
|
||||||
node.ty.clone(),
|
node.ty.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoundKind::Set { addr, value } => {
|
||||||
|
let value = Box::new(self.visit_node(*value, sub, path));
|
||||||
|
if let BoundKind::Constant(val) = &value.kind {
|
||||||
|
sub.add_value(addr, val.clone());
|
||||||
|
} else {
|
||||||
|
sub.remove_value(&addr);
|
||||||
|
}
|
||||||
|
(
|
||||||
|
BoundKind::Set {
|
||||||
|
addr: sub.map_address(addr),
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
node.ty.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BoundKind::Define {
|
BoundKind::Define {
|
||||||
ref name,
|
ref name,
|
||||||
addr,
|
addr,
|
||||||
@@ -191,68 +194,41 @@ impl Optimizer {
|
|||||||
ref value,
|
ref value,
|
||||||
ref captured_by,
|
ref captured_by,
|
||||||
} => {
|
} => {
|
||||||
let value = Box::new(self.visit_node(*value.clone(), sub, path));
|
let value_opt = Box::new(self.visit_node((**value).clone(), sub, path));
|
||||||
let new_addr = match addr {
|
|
||||||
Address::Local(slot) => {
|
if let Address::Local(slot) = addr
|
||||||
if let BoundKind::Constant(val) = &value.kind {
|
&& !captured_by.is_empty()
|
||||||
sub.add_local(slot, val.clone());
|
{
|
||||||
} else {
|
sub.captured_slots.insert(slot);
|
||||||
sub.locals.remove(&slot);
|
}
|
||||||
}
|
|
||||||
Address::Local(sub.map_slot(slot))
|
if let BoundKind::Constant(val) = &value_opt.kind {
|
||||||
}
|
sub.add_value(addr, val.clone());
|
||||||
Address::Global(idx) => {
|
} else {
|
||||||
if let BoundKind::Constant(val) = &value.kind {
|
sub.remove_value(&addr);
|
||||||
sub.add_global(idx, val.clone());
|
}
|
||||||
} else {
|
|
||||||
sub.globals.remove(&idx);
|
if let Address::Global(global_index) = addr
|
||||||
}
|
&& value_opt.ty.purity > Purity::Impure
|
||||||
Address::Global(idx)
|
&& let Some(purity_rc) = &self.global_purity
|
||||||
}
|
{
|
||||||
_ => addr,
|
purity_rc
|
||||||
};
|
.borrow_mut()
|
||||||
|
.insert(global_index, value_opt.ty.purity);
|
||||||
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
BoundKind::Define {
|
BoundKind::Define {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
addr: new_addr,
|
addr: sub.map_address(addr),
|
||||||
kind,
|
kind,
|
||||||
value,
|
value: value_opt,
|
||||||
captured_by: captured_by.clone(),
|
captured_by: captured_by.clone(),
|
||||||
},
|
},
|
||||||
node.ty.clone(),
|
node.ty.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundKind::Set { addr, value } => {
|
|
||||||
let value = Box::new(self.visit_node(*value, sub, path));
|
|
||||||
let new_addr = match addr {
|
|
||||||
Address::Local(slot) => {
|
|
||||||
if let BoundKind::Constant(val) = &value.kind {
|
|
||||||
sub.add_local(slot, val.clone());
|
|
||||||
} else {
|
|
||||||
sub.locals.remove(&slot);
|
|
||||||
}
|
|
||||||
Address::Local(sub.map_slot(slot))
|
|
||||||
}
|
|
||||||
Address::Global(idx) => {
|
|
||||||
if let BoundKind::Constant(val) = &value.kind {
|
|
||||||
sub.add_global(idx, val.clone());
|
|
||||||
} else {
|
|
||||||
sub.globals.remove(&idx);
|
|
||||||
}
|
|
||||||
Address::Global(idx)
|
|
||||||
}
|
|
||||||
_ => addr,
|
|
||||||
};
|
|
||||||
(
|
|
||||||
BoundKind::Set {
|
|
||||||
addr: new_addr,
|
|
||||||
value,
|
|
||||||
},
|
|
||||||
node.ty.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
BoundKind::Call { callee, args } => {
|
BoundKind::Call { callee, args } => {
|
||||||
let callee = self.visit_node(*callee, sub, path);
|
let callee = self.visit_node(*callee, sub, path);
|
||||||
let args = self.visit_node(*args, sub, path);
|
let args = self.visit_node(*args, sub, path);
|
||||||
@@ -335,7 +311,8 @@ impl Optimizer {
|
|||||||
{
|
{
|
||||||
let mut closure_sub = SubstitutionMap::new();
|
let mut closure_sub = SubstitutionMap::new();
|
||||||
for (i, cell) in closure.upvalues.iter().enumerate() {
|
for (i, cell) in closure.upvalues.iter().enumerate() {
|
||||||
closure_sub.add_upvalue(i as u32, cell.borrow().clone());
|
closure_sub
|
||||||
|
.add_value(Address::Upvalue(i as u32), cell.borrow().clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
path.inlining_depth += 1;
|
path.inlining_depth += 1;
|
||||||
@@ -424,12 +401,7 @@ impl Optimizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for slot in &info.assigned_locals {
|
sub.assigned.extend(info.assigned.iter().cloned());
|
||||||
sub.assigned_locals.insert(*slot);
|
|
||||||
}
|
|
||||||
for idx in &info.assigned_globals {
|
|
||||||
sub.assigned_globals.insert(*idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut new_exprs = Vec::with_capacity(exprs.len());
|
let mut new_exprs = Vec::with_capacity(exprs.len());
|
||||||
let last_idx = exprs.len().saturating_sub(1);
|
let last_idx = exprs.len().saturating_sub(1);
|
||||||
@@ -438,26 +410,23 @@ impl Optimizer {
|
|||||||
let is_last = i == last_idx;
|
let is_last = i == last_idx;
|
||||||
if self.enabled && !is_last {
|
if self.enabled && !is_last {
|
||||||
let removable = match &e.kind {
|
let removable = match &e.kind {
|
||||||
BoundKind::Define { addr, value, .. } => match addr {
|
BoundKind::Define { addr, value, .. } => {
|
||||||
Address::Local(slot) => {
|
!info.is_used(addr)
|
||||||
!info.used_locals.contains(slot)
|
&& (if let Address::Local(slot) = addr {
|
||||||
&& !sub.captured_slots.contains(slot)
|
!sub.captured_slots.contains(slot)
|
||||||
&& value.ty.purity >= Purity::SideEffectFree
|
} else {
|
||||||
}
|
true
|
||||||
Address::Global(global_index) => {
|
})
|
||||||
!info.used_globals.contains(global_index)
|
&& (value.ty.purity >= Purity::SideEffectFree
|
||||||
&& (value.ty.purity >= Purity::SideEffectFree
|
|| matches!(value.kind, BoundKind::Lambda { .. }))
|
||||||
|| matches!(value.kind, BoundKind::Lambda { .. }))
|
}
|
||||||
}
|
BoundKind::Set { addr, value, .. } => {
|
||||||
_ => false,
|
!info.is_used(addr)
|
||||||
},
|
&& (if let Address::Local(slot) = addr {
|
||||||
BoundKind::Set {
|
!sub.captured_slots.contains(slot)
|
||||||
addr: Address::Local(slot),
|
} else {
|
||||||
value,
|
true
|
||||||
..
|
})
|
||||||
} => {
|
|
||||||
!info.used_locals.contains(slot)
|
|
||||||
&& !sub.captured_slots.contains(slot)
|
|
||||||
&& value.ty.purity >= Purity::SideEffectFree
|
&& value.ty.purity >= Purity::SideEffectFree
|
||||||
}
|
}
|
||||||
_ => e.ty.purity >= Purity::SideEffectFree,
|
_ => e.ty.purity >= Purity::SideEffectFree,
|
||||||
@@ -503,44 +472,26 @@ impl Optimizer {
|
|||||||
let mut info = UsageInfo::default();
|
let mut info = UsageInfo::default();
|
||||||
self.collect_usage(&node, &mut info);
|
self.collect_usage(&node, &mut info);
|
||||||
|
|
||||||
for idx in &info.assigned_globals {
|
|
||||||
sub.assigned_globals.insert(*idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut new_upvalues = Vec::new();
|
let mut new_upvalues = Vec::new();
|
||||||
let mut mapping = Vec::new();
|
let mut mapping = Vec::new();
|
||||||
let mut next_inner_subs = SubstitutionMap::new();
|
let mut next_inner_subs = SubstitutionMap::new();
|
||||||
next_inner_subs.assigned_locals = info.assigned_locals;
|
next_inner_subs.assigned = info.assigned;
|
||||||
next_inner_subs.assigned_globals = info.assigned_globals;
|
|
||||||
|
|
||||||
for (old_idx, capture_addr) in original_upvalues.iter().enumerate() {
|
for (old_idx, capture_addr) in original_upvalues.iter().enumerate() {
|
||||||
let mut inlined_val = None;
|
let mut inlined_val = None;
|
||||||
match capture_addr {
|
if let Some(val) = sub.get_value(capture_addr) {
|
||||||
Address::Local(slot) => {
|
inlined_val = Some(val.clone());
|
||||||
if let Some(val) = sub.locals.get(slot) {
|
}
|
||||||
inlined_val = Some(val.clone());
|
if let Address::Local(slot) = capture_addr {
|
||||||
}
|
sub.captured_slots.insert(*slot);
|
||||||
sub.captured_slots.insert(*slot);
|
|
||||||
}
|
|
||||||
Address::Upvalue(idx) => {
|
|
||||||
if let Some(val) = sub.upvalues.get(idx) {
|
|
||||||
inlined_val = Some(val.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(val) = inlined_val {
|
if let Some(val) = inlined_val {
|
||||||
next_inner_subs.add_upvalue(old_idx as u32, val);
|
next_inner_subs.add_value(Address::Upvalue(old_idx as u32), val);
|
||||||
mapping.push(None);
|
mapping.push(None);
|
||||||
} else {
|
} else {
|
||||||
mapping.push(Some(new_upvalues.len() as u32));
|
mapping.push(Some(new_upvalues.len() as u32));
|
||||||
let new_addr = if let Address::Local(parent_slot) = capture_addr {
|
new_upvalues.push(sub.map_address(*capture_addr));
|
||||||
Address::Local(sub.map_slot(*parent_slot))
|
|
||||||
} else {
|
|
||||||
*capture_addr
|
|
||||||
};
|
|
||||||
new_upvalues.push(new_addr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -575,11 +526,8 @@ impl Optimizer {
|
|||||||
|
|
||||||
let mut info = UsageInfo::default();
|
let mut info = UsageInfo::default();
|
||||||
self.collect_pattern_usage(&pat_opt, &mut info);
|
self.collect_pattern_usage(&pat_opt, &mut info);
|
||||||
for slot in info.assigned_locals {
|
for addr in info.assigned {
|
||||||
sub.locals.remove(&slot);
|
sub.remove_value(&addr);
|
||||||
}
|
|
||||||
for idx in info.assigned_globals {
|
|
||||||
sub.globals.remove(&idx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
@@ -633,7 +581,10 @@ impl Optimizer {
|
|||||||
|
|
||||||
fn collect_parameter_slots(&self, node: &AnalyzedNode, sub: &mut SubstitutionMap) {
|
fn collect_parameter_slots(&self, node: &AnalyzedNode, sub: &mut SubstitutionMap) {
|
||||||
match &node.kind {
|
match &node.kind {
|
||||||
BoundKind::Define { addr: Address::Local(slot), .. } => {
|
BoundKind::Define {
|
||||||
|
addr: Address::Local(slot),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
sub.slot_mapping.insert(*slot, *slot);
|
sub.slot_mapping.insert(*slot, *slot);
|
||||||
sub.next_slot = sub.next_slot.max(*slot + 1);
|
sub.next_slot = sub.next_slot.max(*slot + 1);
|
||||||
}
|
}
|
||||||
@@ -752,26 +703,20 @@ impl Optimizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify that all used/assigned parameters have been substituted.
|
// Verify that all used/assigned parameters have been substituted.
|
||||||
// If a parameter is used in the body but we couldn't substitute it (e.g. because it's assigned),
|
|
||||||
// we cannot inline the function because the parameter slot would become invalid.
|
|
||||||
let mut param_slots = HashSet::new();
|
let mut param_slots = HashSet::new();
|
||||||
self.collect_parameter_slots_set(params, &mut param_slots);
|
self.collect_parameter_slots_set(params, &mut param_slots);
|
||||||
|
|
||||||
for slot in param_slots {
|
for slot in param_slots {
|
||||||
if (body_usage.used_locals.contains(&slot)
|
let addr = Address::Local(slot);
|
||||||
|| body_usage.assigned_locals.contains(&slot))
|
if (body_usage.is_used(&addr) || body_usage.is_assigned(&addr))
|
||||||
&& !sub.locals.contains_key(&slot)
|
&& sub.get_value(&addr).is_none()
|
||||||
&& !sub.ast_locals.contains_key(&slot)
|
&& !sub.ast_locals.contains_key(&slot)
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sub.locals.is_empty()
|
if sub.values.is_empty() && sub.ast_locals.is_empty() && !arg_vals.is_empty() {
|
||||||
&& sub.ast_locals.is_empty()
|
|
||||||
&& sub.upvalues.is_empty()
|
|
||||||
&& !arg_vals.is_empty()
|
|
||||||
{
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -780,7 +725,10 @@ impl Optimizer {
|
|||||||
|
|
||||||
fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet<u32>) {
|
fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet<u32>) {
|
||||||
match &node.kind {
|
match &node.kind {
|
||||||
BoundKind::Define { addr: Address::Local(slot), .. } => {
|
BoundKind::Define {
|
||||||
|
addr: Address::Local(slot),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
slots.insert(*slot);
|
slots.insert(*slot);
|
||||||
}
|
}
|
||||||
BoundKind::Tuple { elements } => {
|
BoundKind::Tuple { elements } => {
|
||||||
@@ -818,26 +766,32 @@ impl Optimizer {
|
|||||||
body_usage: &UsageInfo,
|
body_usage: &UsageInfo,
|
||||||
) {
|
) {
|
||||||
match &pattern.kind {
|
match &pattern.kind {
|
||||||
BoundKind::Define { addr: Address::Local(slot), .. } => {
|
BoundKind::Define { addr, .. } => {
|
||||||
if let Some(arg) = args.get(*offset)
|
if let Some(arg) = args.get(*offset)
|
||||||
&& !body_usage.assigned_locals.contains(slot)
|
&& !body_usage.is_assigned(addr)
|
||||||
{
|
{
|
||||||
if let BoundKind::Constant(val) = &arg.kind {
|
if let BoundKind::Constant(val) = &arg.kind {
|
||||||
sub.add_local(*slot, val.clone());
|
sub.add_value(*addr, val.clone());
|
||||||
} else if let BoundKind::Lambda { upvalues, .. } = &arg.kind {
|
} else if let BoundKind::Lambda { upvalues, .. } = &arg.kind
|
||||||
if upvalues.is_empty() {
|
&& upvalues.is_empty()
|
||||||
sub.add_ast_local(*slot, arg.clone());
|
&& let Address::Local(slot) = addr
|
||||||
}
|
{
|
||||||
|
sub.add_ast_local(*slot, 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_local(*slot, arg.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sub.slot_mapping.insert(*slot, *slot);
|
|
||||||
sub.next_slot = sub.next_slot.max(*slot + 1);
|
// Ensure local slots are mapped even if no constant/AST is substituted
|
||||||
|
if let Address::Local(slot) = addr {
|
||||||
|
sub.slot_mapping.insert(*slot, *slot);
|
||||||
|
sub.next_slot = sub.next_slot.max(*slot + 1);
|
||||||
|
}
|
||||||
*offset += 1;
|
*offset += 1;
|
||||||
}
|
}
|
||||||
BoundKind::Tuple { elements } => {
|
BoundKind::Tuple { elements } => {
|
||||||
@@ -894,29 +848,11 @@ impl Optimizer {
|
|||||||
self.collect_usage(&closure.function_node, info);
|
self.collect_usage(&closure.function_node, info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoundKind::Get { addr, .. } => match addr {
|
BoundKind::Get { addr, .. } => {
|
||||||
Address::Local(slot) => {
|
info.used.insert(*addr);
|
||||||
info.used_locals.insert(*slot);
|
}
|
||||||
}
|
|
||||||
Address::Global(idx) => {
|
|
||||||
info.used_globals.insert(*idx);
|
|
||||||
}
|
|
||||||
Address::Upvalue(idx) => {
|
|
||||||
info.used_upvalues.insert(*idx);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
BoundKind::Set { addr, value } => {
|
BoundKind::Set { addr, value } => {
|
||||||
match addr {
|
info.assigned.insert(*addr);
|
||||||
Address::Local(slot) => {
|
|
||||||
info.assigned_locals.insert(*slot);
|
|
||||||
}
|
|
||||||
Address::Global(idx) => {
|
|
||||||
info.assigned_globals.insert(*idx);
|
|
||||||
}
|
|
||||||
Address::Upvalue(idx) => {
|
|
||||||
info.assigned_upvalues.insert(*idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.collect_usage(value, info);
|
self.collect_usage(value, info);
|
||||||
}
|
}
|
||||||
BoundKind::Lambda {
|
BoundKind::Lambda {
|
||||||
@@ -932,40 +868,32 @@ impl Optimizer {
|
|||||||
self.collect_usage(body, &mut inner_info);
|
self.collect_usage(body, &mut inner_info);
|
||||||
|
|
||||||
// Propagate globals and identities
|
// Propagate globals and identities
|
||||||
info.used_globals.extend(inner_info.used_globals);
|
for addr in &inner_info.used {
|
||||||
info.assigned_globals.extend(inner_info.assigned_globals);
|
if let Address::Global(_) = addr {
|
||||||
|
info.used.insert(*addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for addr in &inner_info.assigned {
|
||||||
|
if let Address::Global(_) = addr {
|
||||||
|
info.assigned.insert(*addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
info.used_identities.extend(inner_info.used_identities);
|
info.used_identities.extend(inner_info.used_identities);
|
||||||
|
|
||||||
// Map used upvalues to parent scope
|
// Map used upvalues to parent scope
|
||||||
for idx in inner_info.used_upvalues {
|
for addr in &inner_info.used {
|
||||||
if let Some(addr) = upvalues.get(idx as usize) {
|
if let Address::Upvalue(idx) = addr
|
||||||
match addr {
|
&& let Some(parent_addr) = upvalues.get(*idx as usize)
|
||||||
Address::Local(slot) => {
|
{
|
||||||
info.used_locals.insert(*slot);
|
info.used.insert(*parent_addr);
|
||||||
}
|
|
||||||
Address::Global(idx) => {
|
|
||||||
info.used_globals.insert(*idx);
|
|
||||||
}
|
|
||||||
Address::Upvalue(idx) => {
|
|
||||||
info.used_upvalues.insert(*idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Map assigned upvalues to parent scope
|
// Map assigned upvalues to parent scope
|
||||||
for idx in inner_info.assigned_upvalues {
|
for addr in &inner_info.assigned {
|
||||||
if let Some(addr) = upvalues.get(idx as usize) {
|
if let Address::Upvalue(idx) = addr
|
||||||
match addr {
|
&& let Some(parent_addr) = upvalues.get(*idx as usize)
|
||||||
Address::Local(slot) => {
|
{
|
||||||
info.assigned_locals.insert(*slot);
|
info.assigned.insert(*parent_addr);
|
||||||
}
|
|
||||||
Address::Global(idx) => {
|
|
||||||
info.assigned_globals.insert(*idx);
|
|
||||||
}
|
|
||||||
Address::Upvalue(idx) => {
|
|
||||||
info.assigned_upvalues.insert(*idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1012,32 +940,24 @@ impl Optimizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct SubstitutionMap {
|
struct SubstitutionMap {
|
||||||
locals: HashMap<u32, Value>,
|
values: HashMap<Address, Value>,
|
||||||
upvalues: HashMap<u32, Value>,
|
|
||||||
globals: HashMap<u32, Value>,
|
|
||||||
ast_locals: HashMap<u32, AnalyzedNode>,
|
ast_locals: HashMap<u32, AnalyzedNode>,
|
||||||
slot_mapping: HashMap<u32, u32>,
|
slot_mapping: HashMap<u32, u32>,
|
||||||
assigned_locals: HashSet<u32>,
|
assigned: HashSet<Address>,
|
||||||
assigned_globals: HashSet<u32>,
|
|
||||||
next_slot: u32,
|
next_slot: u32,
|
||||||
used_slots: HashSet<u32>,
|
used: HashSet<Address>,
|
||||||
used_globals: HashSet<u32>,
|
|
||||||
captured_slots: HashSet<u32>,
|
captured_slots: HashSet<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubstitutionMap {
|
impl SubstitutionMap {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
locals: HashMap::new(),
|
values: HashMap::new(),
|
||||||
upvalues: HashMap::new(),
|
|
||||||
globals: HashMap::new(),
|
|
||||||
ast_locals: HashMap::new(),
|
ast_locals: HashMap::new(),
|
||||||
slot_mapping: HashMap::new(),
|
slot_mapping: HashMap::new(),
|
||||||
assigned_locals: HashSet::new(),
|
assigned: HashSet::new(),
|
||||||
assigned_globals: HashSet::new(),
|
|
||||||
next_slot: 0,
|
next_slot: 0,
|
||||||
used_slots: HashSet::new(),
|
used: HashSet::new(),
|
||||||
used_globals: HashSet::new(),
|
|
||||||
captured_slots: HashSet::new(),
|
captured_slots: HashSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1045,6 +965,7 @@ impl SubstitutionMap {
|
|||||||
fn add_ast_local(&mut self, slot: u32, node: AnalyzedNode) {
|
fn add_ast_local(&mut self, slot: u32, node: AnalyzedNode) {
|
||||||
self.ast_locals.insert(slot, node);
|
self.ast_locals.insert(slot, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_slot(&mut self, old_slot: u32) -> u32 {
|
fn map_slot(&mut self, old_slot: u32) -> u32 {
|
||||||
if let Some(&new_slot) = self.slot_mapping.get(&old_slot) {
|
if let Some(&new_slot) = self.slot_mapping.get(&old_slot) {
|
||||||
return new_slot;
|
return new_slot;
|
||||||
@@ -1054,42 +975,46 @@ impl SubstitutionMap {
|
|||||||
self.next_slot += 1;
|
self.next_slot += 1;
|
||||||
new_slot
|
new_slot
|
||||||
}
|
}
|
||||||
fn add_local(&mut self, slot: u32, val: Value) {
|
|
||||||
self.locals.insert(slot, val);
|
fn map_address(&mut self, addr: Address) -> Address {
|
||||||
|
match addr {
|
||||||
|
Address::Local(slot) => Address::Local(self.map_slot(slot)),
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn add_upvalue(&mut self, idx: u32, val: Value) {
|
|
||||||
self.upvalues.insert(idx, val);
|
fn add_value(&mut self, addr: Address, val: Value) {
|
||||||
|
self.values.insert(addr, val);
|
||||||
}
|
}
|
||||||
fn add_global(&mut self, idx: u32, val: Value) {
|
|
||||||
self.globals.insert(idx, val);
|
fn get_value(&self, addr: &Address) -> Option<&Value> {
|
||||||
|
self.values.get(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_value(&mut self, addr: &Address) {
|
||||||
|
self.values.remove(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reindex_addr(&self, addr: Address, mapping: &[Option<u32>]) -> Address {
|
||||||
|
if let Address::Upvalue(idx) = addr
|
||||||
|
&& let Some(res) = mapping.get(idx as usize)
|
||||||
|
&& let Some(new_idx) = res
|
||||||
|
{
|
||||||
|
Address::Upvalue(*new_idx)
|
||||||
|
} else {
|
||||||
|
addr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reindex_upvalues(&self, node: AnalyzedNode, mapping: &[Option<u32>]) -> AnalyzedNode {
|
fn reindex_upvalues(&self, node: AnalyzedNode, mapping: &[Option<u32>]) -> AnalyzedNode {
|
||||||
let (new_kind, metrics) = match node.kind {
|
let (new_kind, metrics) = match node.kind {
|
||||||
BoundKind::Get {
|
BoundKind::Get { addr, name } => (
|
||||||
addr: Address::Upvalue(idx),
|
BoundKind::Get {
|
||||||
name,
|
addr: self.reindex_addr(addr, mapping),
|
||||||
} => {
|
name,
|
||||||
if let Some(res) = mapping.get(idx as usize)
|
},
|
||||||
&& let Some(new_idx) = res
|
node.ty.clone(),
|
||||||
{
|
),
|
||||||
(
|
|
||||||
BoundKind::Get {
|
|
||||||
addr: Address::Upvalue(*new_idx),
|
|
||||||
name,
|
|
||||||
},
|
|
||||||
node.ty.clone(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
BoundKind::Get {
|
|
||||||
addr: Address::Upvalue(idx),
|
|
||||||
name,
|
|
||||||
},
|
|
||||||
node.ty.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BoundKind::Lambda {
|
BoundKind::Lambda {
|
||||||
params,
|
params,
|
||||||
upvalues,
|
upvalues,
|
||||||
@@ -1098,14 +1023,7 @@ impl SubstitutionMap {
|
|||||||
} => {
|
} => {
|
||||||
let mut next_upvalues = Vec::new();
|
let mut next_upvalues = Vec::new();
|
||||||
for addr in upvalues {
|
for addr in upvalues {
|
||||||
if let Address::Upvalue(idx) = addr
|
next_upvalues.push(self.reindex_addr(addr, mapping));
|
||||||
&& let Some(res) = mapping.get(idx as usize)
|
|
||||||
&& let Some(new_idx) = res
|
|
||||||
{
|
|
||||||
next_upvalues.push(Address::Upvalue(*new_idx));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
next_upvalues.push(addr);
|
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
BoundKind::Lambda {
|
BoundKind::Lambda {
|
||||||
|
|||||||
@@ -294,7 +294,13 @@ impl TypeChecker {
|
|||||||
} else {
|
} else {
|
||||||
ctx.get_type(addr)
|
ctx.get_type(addr)
|
||||||
};
|
};
|
||||||
(BoundKind::Get { addr, name: name.clone() }, ty)
|
(
|
||||||
|
BoundKind::Get {
|
||||||
|
addr,
|
||||||
|
name: name.clone(),
|
||||||
|
},
|
||||||
|
ty,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundKind::Set { addr, value } => {
|
BoundKind::Set { addr, value } => {
|
||||||
|
|||||||
Reference in New Issue
Block a user