diff --git a/.gitignore b/.gitignore index b3c1d0b..3468236 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target Delphi +*.snap.new diff --git a/src/ast/compiler/optimizer.rs b/src/ast/compiler/optimizer.rs index aa7012b..58c4b38 100644 --- a/src/ast/compiler/optimizer.rs +++ b/src/ast/compiler/optimizer.rs @@ -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) { + fn collect_usage(&self, node: &TypedNode, used_locals: &mut HashSet, used_globals: &mut HashSet) { 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::() + { + 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")); + } }