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
|
/target
|
||||||
Delphi
|
Delphi
|
||||||
|
*.snap.new
|
||||||
|
|||||||
@@ -82,14 +82,14 @@ impl Optimizer {
|
|||||||
}
|
}
|
||||||
if let Some(globals_rc) = &self.globals {
|
if let Some(globals_rc) = &self.globals {
|
||||||
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)
|
||||||
if self.is_inlinable_value(val) {
|
&& self.is_inlinable_value(val)
|
||||||
return Node {
|
{
|
||||||
identity: node.identity,
|
return Node {
|
||||||
ty: val.static_type(),
|
identity: node.identity,
|
||||||
kind: BoundKind::Constant(val.clone()),
|
ty: val.static_type(),
|
||||||
};
|
kind: BoundKind::Constant(val.clone()),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sub.used_globals.insert(idx);
|
sub.used_globals.insert(idx);
|
||||||
@@ -207,13 +207,16 @@ impl Optimizer {
|
|||||||
|
|
||||||
BoundKind::Block { exprs } => {
|
BoundKind::Block { exprs } => {
|
||||||
let mut used_in_block = HashSet::new();
|
let mut used_in_block = HashSet::new();
|
||||||
|
let mut used_globals_in_block = HashSet::new();
|
||||||
if !exprs.is_empty() {
|
if !exprs.is_empty() {
|
||||||
for e in &exprs {
|
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 Some(last) = exprs.last() {
|
||||||
if let BoundKind::Get { addr: Address::Local(slot), .. } = &last.kind {
|
match &last.kind {
|
||||||
used_in_block.insert(*slot);
|
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, .. } => {
|
BoundKind::DefLocal { slot, value, .. } => {
|
||||||
!used_in_block.contains(slot) && !sub.captured_slots.contains(slot) && self.is_pure(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, .. } => {
|
BoundKind::Set { addr: Address::Local(slot), value, .. } => {
|
||||||
!used_in_block.contains(slot) && !sub.captured_slots.contains(slot) && self.is_pure(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 {
|
if let BoundKind::Constant(val) = &value.kind {
|
||||||
sub.add_global(global_index, val.clone());
|
sub.add_global(global_index, val.clone());
|
||||||
}
|
}
|
||||||
if self.is_pure(&value) {
|
if self.is_pure(&value)
|
||||||
if let Some(purity_rc) = &self.global_purity {
|
&& let Some(purity_rc) = &self.global_purity {
|
||||||
purity_rc.borrow_mut().insert(global_index, true);
|
purity_rc.borrow_mut().insert(global_index, true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(BoundKind::DefGlobal { name, global_index, value }, node.ty)
|
(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 {
|
match &node.kind {
|
||||||
BoundKind::Get { addr: Address::Local(slot), .. } | BoundKind::Set { addr: Address::Local(slot), .. } => {
|
BoundKind::Constant(v) => {
|
||||||
used.insert(*slot);
|
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 {
|
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 } => {
|
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 } => {
|
BoundKind::If { cond, then_br, else_br } => {
|
||||||
self.collect_local_usage(cond, used);
|
self.collect_usage(cond, used_locals, used_globals);
|
||||||
self.collect_local_usage(then_br, used);
|
self.collect_usage(then_br, used_locals, used_globals);
|
||||||
if let Some(e) = else_br { self.collect_local_usage(e, used); }
|
if let Some(e) = else_br { self.collect_usage(e, used_locals, used_globals); }
|
||||||
}
|
}
|
||||||
BoundKind::Call { callee, args } => {
|
BoundKind::Call { callee, args } => {
|
||||||
self.collect_local_usage(callee, used);
|
self.collect_usage(callee, used_locals, used_globals);
|
||||||
self.collect_local_usage(args, used);
|
self.collect_usage(args, used_locals, used_globals);
|
||||||
}
|
}
|
||||||
BoundKind::Tuple { elements } => {
|
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 } => {
|
BoundKind::Record { fields } => {
|
||||||
for (k, v) in fields {
|
for (k, v) in fields {
|
||||||
self.collect_local_usage(k, used);
|
self.collect_usage(k, used_locals, used_globals);
|
||||||
self.collect_local_usage(v, used);
|
self.collect_usage(v, used_locals, used_globals);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoundKind::DefLocal { value, .. } | BoundKind::Set { value, .. } => {
|
BoundKind::DefLocal { value, .. } | BoundKind::DefGlobal { value, .. } => {
|
||||||
self.collect_local_usage(value, used);
|
self.collect_usage(value, used_locals, used_globals);
|
||||||
}
|
}
|
||||||
BoundKind::Expansion { bound_expanded, .. } => {
|
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);
|
let dump = get_optimized_dump(source, true);
|
||||||
insta::assert_snapshot!(dump);
|
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