Refactor: Collect global usage in optimizer
Introduce `collect_usage` to gather both local and global variable usage. This enables dead code elimination for unused global definitions.
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
/target
|
||||
Delphi
|
||||
*.snap.new
|
||||
|
||||
@@ -82,14 +82,14 @@ impl Optimizer {
|
||||
}
|
||||
if let Some(globals_rc) = &self.globals {
|
||||
let globals = globals_rc.borrow();
|
||||
if let Some(val) = globals.get(idx as usize) {
|
||||
if self.is_inlinable_value(val) {
|
||||
return Node {
|
||||
identity: node.identity,
|
||||
ty: val.static_type(),
|
||||
kind: BoundKind::Constant(val.clone()),
|
||||
};
|
||||
}
|
||||
if let Some(val) = globals.get(idx as usize)
|
||||
&& self.is_inlinable_value(val)
|
||||
{
|
||||
return Node {
|
||||
identity: node.identity,
|
||||
ty: val.static_type(),
|
||||
kind: BoundKind::Constant(val.clone()),
|
||||
};
|
||||
}
|
||||
}
|
||||
sub.used_globals.insert(idx);
|
||||
@@ -207,13 +207,16 @@ impl Optimizer {
|
||||
|
||||
BoundKind::Block { exprs } => {
|
||||
let mut used_in_block = HashSet::new();
|
||||
let mut used_globals_in_block = HashSet::new();
|
||||
if !exprs.is_empty() {
|
||||
for e in &exprs {
|
||||
self.collect_local_usage(e, &mut used_in_block);
|
||||
self.collect_usage(e, &mut used_in_block, &mut used_globals_in_block);
|
||||
}
|
||||
if let Some(last) = exprs.last() {
|
||||
if let BoundKind::Get { addr: Address::Local(slot), .. } = &last.kind {
|
||||
used_in_block.insert(*slot);
|
||||
match &last.kind {
|
||||
BoundKind::Get { addr: Address::Local(slot), .. } => { used_in_block.insert(*slot); }
|
||||
BoundKind::Get { addr: Address::Global(idx), .. } => { used_globals_in_block.insert(*idx); }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,6 +232,9 @@ impl Optimizer {
|
||||
BoundKind::DefLocal { slot, value, .. } => {
|
||||
!used_in_block.contains(slot) && !sub.captured_slots.contains(slot) && self.is_pure(value)
|
||||
}
|
||||
BoundKind::DefGlobal { global_index, value, .. } => {
|
||||
!used_globals_in_block.contains(global_index) && self.is_pure(value)
|
||||
}
|
||||
BoundKind::Set { addr: Address::Local(slot), value, .. } => {
|
||||
!used_in_block.contains(slot) && !sub.captured_slots.contains(slot) && self.is_pure(value)
|
||||
}
|
||||
@@ -340,10 +346,9 @@ impl Optimizer {
|
||||
if let BoundKind::Constant(val) = &value.kind {
|
||||
sub.add_global(global_index, val.clone());
|
||||
}
|
||||
if self.is_pure(&value) {
|
||||
if let Some(purity_rc) = &self.global_purity {
|
||||
if self.is_pure(&value)
|
||||
&& let Some(purity_rc) = &self.global_purity {
|
||||
purity_rc.borrow_mut().insert(global_index, true);
|
||||
}
|
||||
}
|
||||
(BoundKind::DefGlobal { name, global_index, value }, node.ty)
|
||||
},
|
||||
@@ -515,42 +520,62 @@ impl Optimizer {
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_local_usage(&self, node: &TypedNode, used: &mut HashSet<u32>) {
|
||||
fn collect_usage(&self, node: &TypedNode, used_locals: &mut HashSet<u32>, used_globals: &mut HashSet<u32>) {
|
||||
match &node.kind {
|
||||
BoundKind::Get { addr: Address::Local(slot), .. } | BoundKind::Set { addr: Address::Local(slot), .. } => {
|
||||
used.insert(*slot);
|
||||
BoundKind::Constant(v) => {
|
||||
if let Value::Object(obj) = v
|
||||
&& let Some(closure) = obj.as_any().downcast_ref::<Closure>()
|
||||
{
|
||||
self.collect_usage(&closure.function_node, used_locals, used_globals);
|
||||
}
|
||||
}
|
||||
BoundKind::Lambda { upvalues, .. } => {
|
||||
BoundKind::Get { addr, .. } | BoundKind::Set { addr, .. } => {
|
||||
match addr {
|
||||
Address::Local(slot) => { used_locals.insert(*slot); }
|
||||
Address::Global(idx) => { used_globals.insert(*idx); }
|
||||
_ => {}
|
||||
}
|
||||
if let BoundKind::Set { value, .. } = &node.kind {
|
||||
self.collect_usage(value, used_locals, used_globals);
|
||||
}
|
||||
}
|
||||
BoundKind::Lambda { params, body, upvalues, .. } => {
|
||||
self.collect_usage(params, used_locals, used_globals);
|
||||
self.collect_usage(body, used_locals, used_globals);
|
||||
for addr in upvalues {
|
||||
if let Address::Local(slot) = addr { used.insert(*slot); }
|
||||
match addr {
|
||||
Address::Local(slot) => { used_locals.insert(*slot); }
|
||||
Address::Global(idx) => { used_globals.insert(*idx); }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
BoundKind::Block { exprs } => {
|
||||
for e in exprs { self.collect_local_usage(e, used); }
|
||||
for e in exprs { self.collect_usage(e, used_locals, used_globals); }
|
||||
}
|
||||
BoundKind::If { cond, then_br, else_br } => {
|
||||
self.collect_local_usage(cond, used);
|
||||
self.collect_local_usage(then_br, used);
|
||||
if let Some(e) = else_br { self.collect_local_usage(e, used); }
|
||||
self.collect_usage(cond, used_locals, used_globals);
|
||||
self.collect_usage(then_br, used_locals, used_globals);
|
||||
if let Some(e) = else_br { self.collect_usage(e, used_locals, used_globals); }
|
||||
}
|
||||
BoundKind::Call { callee, args } => {
|
||||
self.collect_local_usage(callee, used);
|
||||
self.collect_local_usage(args, used);
|
||||
self.collect_usage(callee, used_locals, used_globals);
|
||||
self.collect_usage(args, used_locals, used_globals);
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
for e in elements { self.collect_local_usage(e, used); }
|
||||
for e in elements { self.collect_usage(e, used_locals, used_globals); }
|
||||
}
|
||||
BoundKind::Record { fields } => {
|
||||
for (k, v) in fields {
|
||||
self.collect_local_usage(k, used);
|
||||
self.collect_local_usage(v, used);
|
||||
self.collect_usage(k, used_locals, used_globals);
|
||||
self.collect_usage(v, used_locals, used_globals);
|
||||
}
|
||||
}
|
||||
BoundKind::DefLocal { value, .. } | BoundKind::Set { value, .. } => {
|
||||
self.collect_local_usage(value, used);
|
||||
BoundKind::DefLocal { value, .. } | BoundKind::DefGlobal { value, .. } => {
|
||||
self.collect_usage(value, used_locals, used_globals);
|
||||
}
|
||||
BoundKind::Expansion { bound_expanded, .. } => {
|
||||
self.collect_local_usage(bound_expanded, used);
|
||||
self.collect_usage(bound_expanded, used_locals, used_globals);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -727,4 +752,12 @@ mod tests {
|
||||
let dump = get_optimized_dump(source, true);
|
||||
insta::assert_snapshot!(dump);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt_global_unused_dce() {
|
||||
let source = "(do (def f (fn [x] x)) 42)";
|
||||
let dump = get_optimized_dump(source, true);
|
||||
assert!(!dump.contains("DefGlobal"), "Unused global helper 'f' should be optimized away. Dump: \n{}", dump);
|
||||
assert!(dump.contains("Constant: 42"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user