Refactor recursion checking
Introduces a `UsageInfo` struct to consolidate tracking of used and assigned locals, globals, and identities. Replaces multiple `HashSet` arguments in `collect_usage` with a single `UsageInfo` struct. Adds an `is_recursive` method to check for recursive calls within a node, considering global indices, local slots, and identity. Updates inlining logic to use `is_recursive` to prevent inlining of recursive functions or closures. This includes: - Checking for recursion when inlining a call to a lambda. - Checking for recursion when inlining a global variable that is a lambda. - Checking for recursion when inlining a closure. Updates `is_inlinable_value` to also check for closure recursion.
This commit is contained in:
+131
-210
@@ -54,6 +54,15 @@ impl PathTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct UsageInfo {
|
||||||
|
used_locals: HashSet<u32>,
|
||||||
|
used_globals: HashSet<u32>,
|
||||||
|
used_identities: HashSet<crate::ast::types::Identity>,
|
||||||
|
assigned_locals: HashSet<u32>,
|
||||||
|
assigned_globals: HashSet<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Optimizer {
|
impl Optimizer {
|
||||||
pub fn new(enabled: bool) -> Self {
|
pub fn new(enabled: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -98,6 +107,34 @@ impl Optimizer {
|
|||||||
current
|
current
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_recursive(
|
||||||
|
&self,
|
||||||
|
node: &TypedNode,
|
||||||
|
global_idx: Option<u32>,
|
||||||
|
local_slot: Option<u32>,
|
||||||
|
identity: Option<&crate::ast::types::Identity>,
|
||||||
|
) -> bool {
|
||||||
|
let mut info = UsageInfo::default();
|
||||||
|
self.collect_usage(node, &mut info);
|
||||||
|
|
||||||
|
if let Some(idx) = global_idx {
|
||||||
|
if info.used_globals.contains(&idx) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(slot) = local_slot {
|
||||||
|
if info.used_locals.contains(&slot) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(id) = identity {
|
||||||
|
if info.used_identities.contains(id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_node(
|
fn visit_node(
|
||||||
&self,
|
&self,
|
||||||
node: TypedNode,
|
node: TypedNode,
|
||||||
@@ -136,16 +173,18 @@ impl Optimizer {
|
|||||||
Address::Global(idx) => {
|
Address::Global(idx) => {
|
||||||
if !sub.assigned_globals.contains(&idx) {
|
if !sub.assigned_globals.contains(&idx) {
|
||||||
if let Some(val) = sub.globals.get(&idx) {
|
if let Some(val) = sub.globals.get(&idx) {
|
||||||
return Node {
|
if self.is_inlinable_value(val, Some(idx)) {
|
||||||
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()),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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)
|
||||||
&& self.is_inlinable_value(val)
|
&& self.is_inlinable_value(val, Some(idx))
|
||||||
{
|
{
|
||||||
return Node {
|
return Node {
|
||||||
identity: node.identity,
|
identity: node.identity,
|
||||||
@@ -225,7 +264,10 @@ impl Optimizer {
|
|||||||
&& positional_count.is_some()
|
&& positional_count.is_some()
|
||||||
&& path.inlining_depth < 5
|
&& path.inlining_depth < 5
|
||||||
{
|
{
|
||||||
if path.enter_lambda(&callee.identity) {
|
// STRICT RECURSION CHECK: Don't inline if recursive
|
||||||
|
if !self.is_recursive(body, None, None, Some(&callee.identity))
|
||||||
|
&& path.enter_lambda(&callee.identity)
|
||||||
|
{
|
||||||
path.inlining_depth += 1;
|
path.inlining_depth += 1;
|
||||||
let collapsed = self.try_beta_reduce_with_sub(
|
let collapsed = self.try_beta_reduce_with_sub(
|
||||||
params,
|
params,
|
||||||
@@ -263,20 +305,8 @@ impl Optimizer {
|
|||||||
&& upvalues.is_empty()
|
&& upvalues.is_empty()
|
||||||
&& positional_count.is_some()
|
&& positional_count.is_some()
|
||||||
{
|
{
|
||||||
// RECURSION CHECK: Don't inline if the function calls itself
|
// STRICT RECURSION CHECK: Don't inline if recursive (idx or identity)
|
||||||
let mut used_in_body = HashSet::new();
|
if !self.is_recursive(body, Some(*idx), None, Some(&lambda_node.identity)) {
|
||||||
let mut assigned_in_body = HashSet::new();
|
|
||||||
let mut used_globals_in_body = HashSet::new();
|
|
||||||
let mut assigned_globals_in_body = HashSet::new();
|
|
||||||
self.collect_usage(
|
|
||||||
body,
|
|
||||||
&mut used_in_body,
|
|
||||||
&mut used_globals_in_body,
|
|
||||||
&mut assigned_in_body,
|
|
||||||
&mut assigned_globals_in_body,
|
|
||||||
);
|
|
||||||
|
|
||||||
if !used_globals_in_body.contains(idx) {
|
|
||||||
let mut inner_sub = SubstitutionMap::new();
|
let mut inner_sub = SubstitutionMap::new();
|
||||||
path.inlining_stack.insert(*idx);
|
path.inlining_stack.insert(*idx);
|
||||||
path.inlining_depth += 1;
|
path.inlining_depth += 1;
|
||||||
@@ -304,29 +334,39 @@ impl Optimizer {
|
|||||||
&& (closure.upvalues.is_empty()
|
&& (closure.upvalues.is_empty()
|
||||||
|| self.purity_of(&closure.function_node) >= Purity::SideEffectFree)
|
|| self.purity_of(&closure.function_node) >= Purity::SideEffectFree)
|
||||||
{
|
{
|
||||||
let mut closure_sub = SubstitutionMap::new();
|
// STRICT RECURSION CHECK: Don't inline if closure body contains its own identity
|
||||||
for (i, cell) in closure.upvalues.iter().enumerate() {
|
if !self.is_recursive(
|
||||||
closure_sub.add_upvalue(i as u32, cell.borrow().clone());
|
&closure.function_node,
|
||||||
}
|
None,
|
||||||
|
None,
|
||||||
|
Some(&closure.function_node.identity),
|
||||||
|
) && path.enter_lambda(&closure.function_node.identity)
|
||||||
|
{
|
||||||
|
let mut closure_sub = SubstitutionMap::new();
|
||||||
|
for (i, cell) in closure.upvalues.iter().enumerate() {
|
||||||
|
closure_sub.add_upvalue(i as u32, cell.borrow().clone());
|
||||||
|
}
|
||||||
|
|
||||||
path.inlining_depth += 1;
|
path.inlining_depth += 1;
|
||||||
let inlined_body = self.visit_node(
|
let inlined_body = self.visit_node(
|
||||||
(*closure.function_node).clone(),
|
(*closure.function_node).clone(),
|
||||||
&mut closure_sub,
|
&mut closure_sub,
|
||||||
path,
|
path,
|
||||||
);
|
);
|
||||||
|
|
||||||
let collapsed = self.try_beta_reduce_with_sub(
|
let collapsed = self.try_beta_reduce_with_sub(
|
||||||
&closure.parameter_node,
|
&closure.parameter_node,
|
||||||
&args,
|
&args,
|
||||||
inlined_body,
|
inlined_body,
|
||||||
&mut closure_sub,
|
&mut closure_sub,
|
||||||
path,
|
path,
|
||||||
);
|
);
|
||||||
path.inlining_depth -= 1;
|
path.inlining_depth -= 1;
|
||||||
|
path.exit_lambda(&closure.function_node.identity);
|
||||||
|
|
||||||
if let Some(res) = collapsed {
|
if let Some(res) = collapsed {
|
||||||
return res;
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,6 +382,7 @@ impl Optimizer {
|
|||||||
&& (closure.upvalues.is_empty()
|
&& (closure.upvalues.is_empty()
|
||||||
|| self.purity_of(&closure.function_node) >= Purity::SideEffectFree)
|
|| self.purity_of(&closure.function_node) >= Purity::SideEffectFree)
|
||||||
{
|
{
|
||||||
|
// CRACKING fallback (only if not recursive already handled above)
|
||||||
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_upvalue(i as u32, cell.borrow().clone());
|
||||||
@@ -415,20 +456,11 @@ impl Optimizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BoundKind::Block { exprs } => {
|
BoundKind::Block { exprs } => {
|
||||||
let mut used_in_block = HashSet::new();
|
let mut info = UsageInfo::default();
|
||||||
let mut used_globals_in_block = HashSet::new();
|
|
||||||
let mut assigned_locals_in_block = HashSet::new();
|
|
||||||
let mut assigned_globals_in_block = HashSet::new();
|
|
||||||
|
|
||||||
if !exprs.is_empty() {
|
if !exprs.is_empty() {
|
||||||
for e in &exprs {
|
for e in &exprs {
|
||||||
self.collect_usage(
|
self.collect_usage(e, &mut info);
|
||||||
e,
|
|
||||||
&mut used_in_block,
|
|
||||||
&mut used_globals_in_block,
|
|
||||||
&mut assigned_locals_in_block,
|
|
||||||
&mut assigned_globals_in_block,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if let Some(last) = exprs.last() {
|
if let Some(last) = exprs.last() {
|
||||||
match &last.kind {
|
match &last.kind {
|
||||||
@@ -436,13 +468,13 @@ impl Optimizer {
|
|||||||
addr: Address::Local(slot),
|
addr: Address::Local(slot),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
used_in_block.insert(*slot);
|
info.used_locals.insert(*slot);
|
||||||
}
|
}
|
||||||
BoundKind::Get {
|
BoundKind::Get {
|
||||||
addr: Address::Global(idx),
|
addr: Address::Global(idx),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
used_globals_in_block.insert(*idx);
|
info.used_globals.insert(*idx);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -451,10 +483,10 @@ impl Optimizer {
|
|||||||
|
|
||||||
// IMPORTANT: Propagate block assignments to the outer map so we don't
|
// IMPORTANT: Propagate block assignments to the outer map so we don't
|
||||||
// incorrectly inline these variables in the rest of the script.
|
// incorrectly inline these variables in the rest of the script.
|
||||||
for slot in &assigned_locals_in_block {
|
for slot in &info.assigned_locals {
|
||||||
sub.assigned_locals.insert(*slot);
|
sub.assigned_locals.insert(*slot);
|
||||||
}
|
}
|
||||||
for idx in &assigned_globals_in_block {
|
for idx in &info.assigned_globals {
|
||||||
sub.assigned_globals.insert(*idx);
|
sub.assigned_globals.insert(*idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,7 +499,7 @@ impl Optimizer {
|
|||||||
if self.enabled && !is_last {
|
if self.enabled && !is_last {
|
||||||
let removable = match &e.kind {
|
let removable = match &e.kind {
|
||||||
BoundKind::DefLocal { slot, value, .. } => {
|
BoundKind::DefLocal { slot, value, .. } => {
|
||||||
!used_in_block.contains(slot)
|
!info.used_locals.contains(slot)
|
||||||
&& !sub.captured_slots.contains(slot)
|
&& !sub.captured_slots.contains(slot)
|
||||||
&& self.purity_of(value) >= Purity::SideEffectFree
|
&& self.purity_of(value) >= Purity::SideEffectFree
|
||||||
}
|
}
|
||||||
@@ -476,7 +508,7 @@ impl Optimizer {
|
|||||||
value,
|
value,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
!used_globals_in_block.contains(global_index)
|
!info.used_globals.contains(global_index)
|
||||||
&& (self.purity_of(value) >= Purity::SideEffectFree
|
&& (self.purity_of(value) >= Purity::SideEffectFree
|
||||||
|| matches!(value.kind, BoundKind::Lambda { .. }))
|
|| matches!(value.kind, BoundKind::Lambda { .. }))
|
||||||
}
|
}
|
||||||
@@ -485,7 +517,7 @@ impl Optimizer {
|
|||||||
value,
|
value,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
!used_in_block.contains(slot)
|
!info.used_locals.contains(slot)
|
||||||
&& !sub.captured_slots.contains(slot)
|
&& !sub.captured_slots.contains(slot)
|
||||||
&& self.purity_of(value) >= Purity::SideEffectFree
|
&& self.purity_of(value) >= Purity::SideEffectFree
|
||||||
}
|
}
|
||||||
@@ -538,29 +570,19 @@ impl Optimizer {
|
|||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut used_in_lambda = HashSet::new();
|
let mut info = UsageInfo::default();
|
||||||
let mut used_globals_in_lambda = HashSet::new();
|
self.collect_usage(&node, &mut info);
|
||||||
let mut assigned_locals_in_lambda = HashSet::new();
|
|
||||||
let mut assigned_globals_in_lambda = HashSet::new();
|
|
||||||
|
|
||||||
self.collect_usage(
|
|
||||||
&node,
|
|
||||||
&mut used_in_lambda,
|
|
||||||
&mut used_globals_in_lambda,
|
|
||||||
&mut assigned_locals_in_lambda,
|
|
||||||
&mut assigned_globals_in_lambda,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Track captures from outer scope that are assigned in this lambda
|
// Track captures from outer scope that are assigned in this lambda
|
||||||
for idx in &assigned_globals_in_lambda {
|
for idx in &info.assigned_globals {
|
||||||
sub.assigned_globals.insert(*idx);
|
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 = assigned_locals_in_lambda;
|
next_inner_subs.assigned_locals = info.assigned_locals;
|
||||||
next_inner_subs.assigned_globals = assigned_globals_in_lambda;
|
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;
|
||||||
@@ -660,7 +682,9 @@ impl Optimizer {
|
|||||||
} => {
|
} => {
|
||||||
let value = Box::new(self.visit_node(*value, sub, path));
|
let value = Box::new(self.visit_node(*value, sub, path));
|
||||||
if let BoundKind::Constant(val) = &value.kind {
|
if let BoundKind::Constant(val) = &value.kind {
|
||||||
sub.add_global(global_index, val.clone());
|
if self.is_inlinable_value(val, Some(global_index)) {
|
||||||
|
sub.add_global(global_index, val.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let p = self.purity_of(&value);
|
let p = self.purity_of(&value);
|
||||||
if p > Purity::Impure
|
if p > Purity::Impure
|
||||||
@@ -738,7 +762,7 @@ impl Optimizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_inlinable_value(&self, val: &Value) -> bool {
|
fn is_inlinable_value(&self, val: &Value, global_idx: Option<u32>) -> bool {
|
||||||
match val {
|
match val {
|
||||||
Value::Int(_)
|
Value::Int(_)
|
||||||
| Value::Float(_)
|
| Value::Float(_)
|
||||||
@@ -748,7 +772,14 @@ impl Optimizer {
|
|||||||
| Value::DateTime(_) => true,
|
| Value::DateTime(_) => true,
|
||||||
Value::Object(obj) => {
|
Value::Object(obj) => {
|
||||||
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
||||||
|
// A closure is only inlinable if it's not recursive
|
||||||
closure.upvalues.is_empty()
|
closure.upvalues.is_empty()
|
||||||
|
&& !self.is_recursive(
|
||||||
|
&closure.function_node,
|
||||||
|
global_idx,
|
||||||
|
None,
|
||||||
|
Some(&closure.function_node.identity),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -951,96 +982,46 @@ impl Optimizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_usage(
|
fn collect_usage(&self, node: &TypedNode, info: &mut UsageInfo) {
|
||||||
&self,
|
|
||||||
node: &TypedNode,
|
|
||||||
used_locals: &mut HashSet<u32>,
|
|
||||||
used_globals: &mut HashSet<u32>,
|
|
||||||
assigned_locals: &mut HashSet<u32>,
|
|
||||||
assigned_globals: &mut HashSet<u32>,
|
|
||||||
) {
|
|
||||||
match &node.kind {
|
match &node.kind {
|
||||||
BoundKind::Constant(v) => {
|
BoundKind::Constant(v) => {
|
||||||
if let Value::Object(obj) = v
|
if let Value::Object(obj) = v
|
||||||
&& let Some(closure) = obj.as_any().downcast_ref::<Closure>()
|
&& let Some(closure) = obj.as_any().downcast_ref::<Closure>()
|
||||||
{
|
{
|
||||||
self.collect_usage(
|
info.used_identities
|
||||||
&closure.function_node,
|
.insert(closure.function_node.identity.clone());
|
||||||
used_locals,
|
self.collect_usage(&closure.function_node, info);
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoundKind::Get { addr, .. } => match addr {
|
BoundKind::Get { addr, .. } => match addr {
|
||||||
Address::Local(slot) => {
|
Address::Local(slot) => {
|
||||||
used_locals.insert(*slot);
|
info.used_locals.insert(*slot);
|
||||||
}
|
}
|
||||||
Address::Global(idx) => {
|
Address::Global(idx) => {
|
||||||
used_globals.insert(*idx);
|
info.used_globals.insert(*idx);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
BoundKind::Set { addr, value } => {
|
BoundKind::Set { addr, value } => {
|
||||||
match addr {
|
match addr {
|
||||||
Address::Local(slot) => {
|
Address::Local(slot) => {
|
||||||
assigned_locals.insert(*slot);
|
info.assigned_locals.insert(*slot);
|
||||||
}
|
}
|
||||||
Address::Global(idx) => {
|
Address::Global(idx) => {
|
||||||
assigned_globals.insert(*idx);
|
info.assigned_globals.insert(*idx);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.collect_usage(
|
self.collect_usage(value, info);
|
||||||
value,
|
|
||||||
used_locals,
|
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
BoundKind::Lambda {
|
BoundKind::Lambda { params, body, .. } => {
|
||||||
params,
|
info.used_identities.insert(node.identity.clone());
|
||||||
body,
|
self.collect_usage(params, info);
|
||||||
upvalues,
|
self.collect_usage(body, info);
|
||||||
..
|
|
||||||
} => {
|
|
||||||
self.collect_usage(
|
|
||||||
params,
|
|
||||||
used_locals,
|
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
self.collect_usage(
|
|
||||||
body,
|
|
||||||
used_locals,
|
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
for addr in upvalues {
|
|
||||||
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 {
|
for e in exprs {
|
||||||
self.collect_usage(
|
self.collect_usage(e, info);
|
||||||
e,
|
|
||||||
used_locals,
|
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoundKind::If {
|
BoundKind::If {
|
||||||
@@ -1048,92 +1029,32 @@ impl Optimizer {
|
|||||||
then_br,
|
then_br,
|
||||||
else_br,
|
else_br,
|
||||||
} => {
|
} => {
|
||||||
self.collect_usage(
|
self.collect_usage(cond, info);
|
||||||
cond,
|
self.collect_usage(then_br, info);
|
||||||
used_locals,
|
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
self.collect_usage(
|
|
||||||
then_br,
|
|
||||||
used_locals,
|
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
if let Some(e) = else_br {
|
if let Some(e) = else_br {
|
||||||
self.collect_usage(
|
self.collect_usage(e, info);
|
||||||
e,
|
|
||||||
used_locals,
|
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoundKind::Call { callee, args } => {
|
BoundKind::Call { callee, args } => {
|
||||||
self.collect_usage(
|
self.collect_usage(callee, info);
|
||||||
callee,
|
self.collect_usage(args, info);
|
||||||
used_locals,
|
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
self.collect_usage(
|
|
||||||
args,
|
|
||||||
used_locals,
|
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
BoundKind::Tuple { elements } => {
|
BoundKind::Tuple { elements } => {
|
||||||
for e in elements {
|
for e in elements {
|
||||||
self.collect_usage(
|
self.collect_usage(e, info);
|
||||||
e,
|
|
||||||
used_locals,
|
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoundKind::Record { fields } => {
|
BoundKind::Record { fields } => {
|
||||||
for (k, v) in fields {
|
for (k, v) in fields {
|
||||||
self.collect_usage(
|
self.collect_usage(k, info);
|
||||||
k,
|
self.collect_usage(v, info);
|
||||||
used_locals,
|
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
self.collect_usage(
|
|
||||||
v,
|
|
||||||
used_locals,
|
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoundKind::DefLocal { value, .. } | BoundKind::DefGlobal { value, .. } => {
|
BoundKind::DefLocal { value, .. } | BoundKind::DefGlobal { value, .. } => {
|
||||||
self.collect_usage(
|
self.collect_usage(value, info);
|
||||||
value,
|
|
||||||
used_locals,
|
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
BoundKind::Expansion { bound_expanded, .. } => {
|
BoundKind::Expansion { bound_expanded, .. } => {
|
||||||
self.collect_usage(
|
self.collect_usage(bound_expanded, info);
|
||||||
bound_expanded,
|
|
||||||
used_locals,
|
|
||||||
used_globals,
|
|
||||||
assigned_locals,
|
|
||||||
assigned_globals,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -1,12 +1,13 @@
|
|||||||
---
|
---
|
||||||
source: src/ast/compiler/optimizer.rs
|
source: src/ast/compiler/optimizer.rs
|
||||||
assertion_line: 727
|
assertion_line: 1333
|
||||||
expression: dump
|
expression: dump
|
||||||
---
|
---
|
||||||
Lambda (Upvalues: 0) <Metadata: Metadata { ty: Function(Signature { params: Tuple([]), ret: Function(Signature { params: Tuple([]), ret: Int }) }), is_tail: true }>
|
Lambda (Upvalues: 0) <Metadata: Metadata { ty: Function(Signature { params: Tuple([]), ret: Function(Signature { params: Tuple([]), ret: Int }) }), is_tail: true }>
|
||||||
Parameters:
|
Parameters:
|
||||||
Tuple <Metadata: Metadata { ty: Tuple([]), is_tail: false }>
|
Tuple <Metadata: Metadata { ty: Tuple([]), is_tail: false }>
|
||||||
Lambda (Upvalues: 0) <Metadata: Metadata { ty: Function(Signature { params: Tuple([]), ret: Int }), is_tail: true }>
|
Lambda (Upvalues: 1) <Metadata: Metadata { ty: Function(Signature { params: Tuple([]), ret: Int }), is_tail: true }>
|
||||||
Parameters:
|
Parameters:
|
||||||
Tuple <Metadata: Metadata { ty: Tuple([]), is_tail: false }>
|
Tuple <Metadata: Metadata { ty: Tuple([]), is_tail: false }>
|
||||||
Constant: 10 <Metadata: Metadata { ty: Int, is_tail: true }>
|
Upvalues: [Local(0)]
|
||||||
|
Get: x (Upvalue(0)) <Metadata: Metadata { ty: Int, is_tail: true }>
|
||||||
|
|||||||
Reference in New Issue
Block a user