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:
Michael Schimmel
2026-02-25 10:57:58 +01:00
parent c256a8a992
commit 283cdf61a4
4 changed files with 234 additions and 299 deletions
+12 -3
View File
@@ -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(),
+3 -1
View File
@@ -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
View File
@@ -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 {
+7 -1
View File
@@ -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 } => {