Refactor AST nodes to use Rc
This commit refactors various AST node types to use `Rc` (Reference Counting) instead of `Box`. This change is primarily driven by the need for shared ownership and more efficient handling of potentially recursive or shared data structures within the AST. The main benefits of this change include: - **Reduced cloning:** `Rc` allows multiple parts of the AST to refer to the same node without needing to clone the entire node. This is particularly beneficial for optimization passes where nodes might be visited multiple times. - **Enabling sharing:** It makes it easier to represent scenarios where a single AST node might be a child of multiple parent nodes. - **Performance improvements:** By avoiding unnecessary deep copies of AST nodes, this change can lead to performance gains, especially during complex compilation phases.
This commit is contained in:
@@ -55,35 +55,40 @@ impl Optimizer {
|
||||
return node;
|
||||
}
|
||||
|
||||
let mut current = node;
|
||||
let mut current = Rc::new(node);
|
||||
for _ in 0..self.max_passes {
|
||||
let mut sub = SubstitutionMap::new();
|
||||
let mut path = PathTracker::new();
|
||||
let next = self.visit_node(current.clone(), &mut sub, &mut path);
|
||||
if next == current {
|
||||
if Rc::ptr_eq(&next, ¤t) {
|
||||
break;
|
||||
}
|
||||
current = next;
|
||||
}
|
||||
current
|
||||
// Unwrap the final Rc if we are at the end, or return a clone of the inner node.
|
||||
// Since AnalyzedNode is small now (header + Rcs), cloning is cheap.
|
||||
(*current).clone()
|
||||
}
|
||||
|
||||
fn visit_node(
|
||||
&self,
|
||||
node: AnalyzedNode,
|
||||
node_rc: Rc<AnalyzedNode>,
|
||||
sub: &mut SubstitutionMap,
|
||||
path: &mut PathTracker,
|
||||
) -> AnalyzedNode {
|
||||
) -> Rc<AnalyzedNode> {
|
||||
let node = &*node_rc;
|
||||
let folder = Folder::new(&self.globals);
|
||||
let inliner = Inliner::new(&self.globals, &self.global_purity);
|
||||
let (new_kind, metrics) = match node.kind {
|
||||
BoundKind::Get { addr, ref name } => {
|
||||
|
||||
let (new_kind, metrics) = match &node.kind {
|
||||
BoundKind::Get { addr, name } => {
|
||||
let addr = *addr;
|
||||
if !sub.assigned.contains(&addr) {
|
||||
// 1. Try inlining from current value substitution map (locals/globals/upvalues)
|
||||
if let Some(val) = sub.get_value(&addr)
|
||||
&& inliner.is_inlinable_value(val, addr)
|
||||
{
|
||||
return folder.make_constant_node(val.clone(), &node);
|
||||
return Rc::new(folder.make_constant_node(val.clone(), &node));
|
||||
}
|
||||
|
||||
// 2. Try inlining from AST substitution map (pure expressions)
|
||||
@@ -99,7 +104,7 @@ impl Optimizer {
|
||||
if let Some(val) = globals.get(idx.0 as usize)
|
||||
&& inliner.is_inlinable_value(val, addr)
|
||||
{
|
||||
return folder.make_constant_node(val.clone(), &node);
|
||||
return Rc::new(folder.make_constant_node(val.clone(), &node));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,62 +119,71 @@ impl Optimizer {
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::FieldAccessor(k) => (BoundKind::FieldAccessor(k), node.ty.clone()),
|
||||
BoundKind::FieldAccessor(k) => (BoundKind::FieldAccessor(*k), node.ty.clone()),
|
||||
|
||||
BoundKind::GetField { ref rec, field } => {
|
||||
let rec_opt = self.visit_node((**rec).clone(), sub, path);
|
||||
BoundKind::GetField { rec, field } => {
|
||||
let rec_opt = self.visit_node(rec.clone(), sub, path);
|
||||
|
||||
// Constant folding for Field Access
|
||||
if let BoundKind::Constant(Value::Record(layout, values)) = &rec_opt.kind
|
||||
&& let Some(idx) = layout.index_of(field)
|
||||
&& let Some(idx) = layout.index_of(*field)
|
||||
{
|
||||
return folder.make_constant_node(values[idx].clone(), &node);
|
||||
return Rc::new(folder.make_constant_node(values[idx].clone(), &node));
|
||||
}
|
||||
|
||||
if Rc::ptr_eq(&rec_opt, rec) {
|
||||
return node_rc;
|
||||
}
|
||||
|
||||
(
|
||||
BoundKind::GetField {
|
||||
rec: Box::new(rec_opt),
|
||||
field,
|
||||
rec: rec_opt,
|
||||
field: *field,
|
||||
},
|
||||
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());
|
||||
let value_opt = self.visit_node(value.clone(), sub, path);
|
||||
if let BoundKind::Constant(val) = &value_opt.kind {
|
||||
sub.add_value(*addr, val.clone());
|
||||
} else {
|
||||
sub.remove_value(&addr);
|
||||
sub.remove_value(addr);
|
||||
}
|
||||
|
||||
if Rc::ptr_eq(&value_opt, value) {
|
||||
return node_rc;
|
||||
}
|
||||
|
||||
(
|
||||
BoundKind::Set {
|
||||
addr: sub.map_address(addr),
|
||||
value,
|
||||
addr: sub.map_address(*addr),
|
||||
value: value_opt,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Define {
|
||||
ref name,
|
||||
name,
|
||||
addr,
|
||||
kind,
|
||||
ref value,
|
||||
ref captured_by,
|
||||
value,
|
||||
captured_by,
|
||||
} => {
|
||||
let value_opt = Box::new(self.visit_node((**value).clone(), sub, path));
|
||||
let value_opt = self.visit_node(value.clone(), sub, path);
|
||||
|
||||
if let Address::Local(slot) = addr
|
||||
&& !captured_by.is_empty()
|
||||
{
|
||||
sub.captured_slots.insert(slot);
|
||||
sub.captured_slots.insert(*slot);
|
||||
}
|
||||
|
||||
if let BoundKind::Constant(val) = &value_opt.kind {
|
||||
sub.add_value(addr, val.clone());
|
||||
sub.add_value(*addr, val.clone());
|
||||
} else {
|
||||
sub.remove_value(&addr);
|
||||
sub.remove_value(addr);
|
||||
}
|
||||
|
||||
if let Address::Global(global_index) = addr
|
||||
@@ -178,14 +192,18 @@ impl Optimizer {
|
||||
{
|
||||
purity_rc
|
||||
.borrow_mut()
|
||||
.insert(global_index, value_opt.ty.purity);
|
||||
.insert(*global_index, value_opt.ty.purity);
|
||||
}
|
||||
|
||||
if Rc::ptr_eq(&value_opt, value) {
|
||||
return node_rc;
|
||||
}
|
||||
|
||||
(
|
||||
BoundKind::Define {
|
||||
name: name.clone(),
|
||||
addr: sub.map_address(addr),
|
||||
kind,
|
||||
addr: sub.map_address(*addr),
|
||||
kind: *kind,
|
||||
value: value_opt,
|
||||
captured_by: captured_by.clone(),
|
||||
},
|
||||
@@ -194,41 +212,41 @@ impl Optimizer {
|
||||
}
|
||||
|
||||
BoundKind::Call { callee, args } => {
|
||||
let callee = self.visit_node(*callee, sub, path);
|
||||
let args = self.visit_node(*args, sub, path);
|
||||
let callee_opt = self.visit_node(callee.clone(), sub, path);
|
||||
let args_opt = self.visit_node(args.clone(), sub, path);
|
||||
|
||||
if self.enabled {
|
||||
// Optimized Field Access Transformation
|
||||
if let BoundKind::FieldAccessor(k) = &callee.kind
|
||||
&& let BoundKind::Tuple { elements } = &args.kind
|
||||
if let BoundKind::FieldAccessor(k) = &callee_opt.kind
|
||||
&& let BoundKind::Tuple { elements } = &args_opt.kind
|
||||
&& elements.len() == 1
|
||||
{
|
||||
let rec = elements[0].clone();
|
||||
let metrics = node.ty.clone();
|
||||
return Node {
|
||||
identity: node.identity,
|
||||
return Rc::new(Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: BoundKind::GetField {
|
||||
rec: Box::new(rec),
|
||||
rec,
|
||||
field: *k,
|
||||
},
|
||||
ty: metrics,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
let mut arg_nodes = Vec::new();
|
||||
self.flatten_tuple(args.clone(), &mut arg_nodes);
|
||||
self.flatten_tuple(args_opt.clone(), &mut arg_nodes);
|
||||
|
||||
if let BoundKind::Lambda {
|
||||
params,
|
||||
body,
|
||||
upvalues,
|
||||
positional_count,
|
||||
} = &callee.kind
|
||||
} = &callee_opt.kind
|
||||
&& upvalues.is_empty()
|
||||
&& positional_count.is_some()
|
||||
&& path.inlining_depth < 5
|
||||
&& !callee.ty.is_recursive
|
||||
&& path.enter_lambda(&callee.identity)
|
||||
&& !callee_opt.ty.is_recursive
|
||||
&& path.enter_lambda(&callee_opt.identity)
|
||||
{
|
||||
path.inlining_depth += 1;
|
||||
let mut inner_sub = sub.new_inner();
|
||||
@@ -236,12 +254,12 @@ impl Optimizer {
|
||||
.prepare_beta_reduction(params, &arg_nodes, body, &mut inner_sub)
|
||||
.is_some()
|
||||
{
|
||||
Some(self.visit_node((**body).clone(), &mut inner_sub, path))
|
||||
Some(self.visit_node(body.clone(), &mut inner_sub, path))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
path.inlining_depth -= 1;
|
||||
path.exit_lambda(&callee.identity);
|
||||
path.exit_lambda(&callee_opt.identity);
|
||||
|
||||
if let Some(res) = collapsed {
|
||||
return res;
|
||||
@@ -251,7 +269,7 @@ impl Optimizer {
|
||||
if let BoundKind::Get {
|
||||
addr: Address::Global(idx),
|
||||
..
|
||||
} = &callee.kind
|
||||
} = &callee_opt.kind
|
||||
&& let Some(registry_rc) = &self.lambda_registry
|
||||
&& path.inlining_depth < 5
|
||||
&& !path.inlining_stack.contains(idx)
|
||||
@@ -275,7 +293,7 @@ impl Optimizer {
|
||||
.prepare_beta_reduction(params, &arg_nodes, body, &mut inner_sub)
|
||||
.is_some()
|
||||
{
|
||||
Some(self.visit_node((**body).clone(), &mut inner_sub, path))
|
||||
Some(self.visit_node(body.clone(), &mut inner_sub, path))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -288,7 +306,7 @@ impl Optimizer {
|
||||
}
|
||||
}
|
||||
|
||||
if let BoundKind::Constant(Value::Object(ref obj)) = callee.kind
|
||||
if let BoundKind::Constant(Value::Object(ref obj)) = callee_opt.kind
|
||||
&& path.inlining_depth < 5
|
||||
&& let Some(closure) = obj.as_any().downcast_ref::<Closure>()
|
||||
&& (closure.upvalues.is_empty()
|
||||
@@ -306,7 +324,7 @@ impl Optimizer {
|
||||
|
||||
path.inlining_depth += 1;
|
||||
let inlined_body = self.visit_node(
|
||||
(*closure.function_node).clone(),
|
||||
closure.function_node.clone(),
|
||||
&mut closure_sub,
|
||||
path,
|
||||
);
|
||||
@@ -332,57 +350,75 @@ impl Optimizer {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(folded) = folder.try_fold_pure(&callee, &arg_nodes) {
|
||||
return folded;
|
||||
if let Some(folded) = folder.try_fold_pure(&callee_opt, &arg_nodes) {
|
||||
return Rc::new(folded);
|
||||
}
|
||||
}
|
||||
|
||||
if Rc::ptr_eq(&callee_opt, callee) && Rc::ptr_eq(&args_opt, args) {
|
||||
return node_rc;
|
||||
}
|
||||
|
||||
(
|
||||
BoundKind::Call {
|
||||
callee: Box::new(callee),
|
||||
args: Box::new(args),
|
||||
callee: callee_opt,
|
||||
args: args_opt,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Again { args } => {
|
||||
let args = self.visit_node(*args, sub, path);
|
||||
let args_opt = self.visit_node(args.clone(), sub, path);
|
||||
if Rc::ptr_eq(&args_opt, args) {
|
||||
return node_rc;
|
||||
}
|
||||
(
|
||||
BoundKind::Again {
|
||||
args: Box::new(args),
|
||||
args: args_opt,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::If {
|
||||
ref cond,
|
||||
ref then_br,
|
||||
ref else_br,
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
let cond_opt = self.visit_node((**cond).clone(), sub, path);
|
||||
let cond_opt = self.visit_node(cond.clone(), sub, path);
|
||||
if self.enabled
|
||||
&& let BoundKind::Constant(ref val) = cond_opt.kind
|
||||
{
|
||||
if val.is_truthy() {
|
||||
return self.visit_node((**then_br).clone(), sub, path);
|
||||
return self.visit_node(then_br.clone(), sub, path);
|
||||
} else if let Some(else_node) = else_br {
|
||||
return self.visit_node((**else_node).clone(), sub, path);
|
||||
return self.visit_node(else_node.clone(), sub, path);
|
||||
} else {
|
||||
return folder.make_nop_node(&node);
|
||||
return Rc::new(folder.make_nop_node(&node));
|
||||
}
|
||||
}
|
||||
|
||||
let then_br = Box::new(self.visit_node((**then_br).clone(), sub, path));
|
||||
let else_br = else_br
|
||||
let then_br_opt = self.visit_node(then_br.clone(), sub, path);
|
||||
let else_br_opt = else_br
|
||||
.as_ref()
|
||||
.map(|e| Box::new(self.visit_node((**e).clone(), sub, path)));
|
||||
.map(|e| self.visit_node(e.clone(), sub, path));
|
||||
|
||||
let unchanged = Rc::ptr_eq(&cond_opt, cond) && Rc::ptr_eq(&then_br_opt, then_br) && match (&else_br_opt, else_br) {
|
||||
(Some(a), Some(b)) => Rc::ptr_eq(a, b),
|
||||
(None, None) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if unchanged {
|
||||
return node_rc;
|
||||
}
|
||||
|
||||
(
|
||||
BoundKind::If {
|
||||
cond: Box::new(cond_opt),
|
||||
then_br,
|
||||
else_br,
|
||||
cond: cond_opt,
|
||||
then_br: then_br_opt,
|
||||
else_br: else_br_opt,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
@@ -394,10 +430,20 @@ impl Optimizer {
|
||||
out_type,
|
||||
} => {
|
||||
let mut o_inputs = Vec::with_capacity(inputs.len());
|
||||
let mut inputs_changed = false;
|
||||
for input in inputs {
|
||||
o_inputs.push(self.visit_node(input, sub, path));
|
||||
let opt = self.visit_node(input.clone(), sub, path);
|
||||
if !Rc::ptr_eq(&opt, input) {
|
||||
inputs_changed = true;
|
||||
}
|
||||
o_inputs.push(opt);
|
||||
}
|
||||
let o_lambda = Box::new(self.visit_node(*lambda, sub, path));
|
||||
let o_lambda = self.visit_node(lambda.clone(), sub, path);
|
||||
|
||||
if !inputs_changed && Rc::ptr_eq(&o_lambda, lambda) {
|
||||
return node_rc;
|
||||
}
|
||||
|
||||
(
|
||||
BoundKind::Pipe {
|
||||
inputs: o_inputs,
|
||||
@@ -408,7 +454,7 @@ impl Optimizer {
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Block { ref exprs } => {
|
||||
BoundKind::Block { exprs } => {
|
||||
let mut info = UsageInfo::default();
|
||||
if !exprs.is_empty() {
|
||||
for e in exprs {
|
||||
@@ -420,6 +466,7 @@ impl Optimizer {
|
||||
|
||||
let mut new_exprs = Vec::with_capacity(exprs.len());
|
||||
let last_idx = exprs.len().saturating_sub(1);
|
||||
let mut block_changed = false;
|
||||
|
||||
for (i, e) in exprs.iter().enumerate() {
|
||||
let is_last = i == last_idx;
|
||||
@@ -448,20 +495,29 @@ impl Optimizer {
|
||||
};
|
||||
|
||||
if removable {
|
||||
block_changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let opt = self.visit_node(e.clone(), sub, path);
|
||||
if self.enabled && matches!(opt.kind, BoundKind::Nop) && !is_last {
|
||||
block_changed = true;
|
||||
continue;
|
||||
}
|
||||
if !Rc::ptr_eq(&opt, e) {
|
||||
block_changed = true;
|
||||
}
|
||||
new_exprs.push(opt);
|
||||
}
|
||||
|
||||
if !block_changed {
|
||||
return node_rc;
|
||||
}
|
||||
|
||||
if self.enabled {
|
||||
if new_exprs.is_empty() {
|
||||
return folder.make_nop_node(&node);
|
||||
return Rc::new(folder.make_nop_node(&node));
|
||||
} else if new_exprs.len() == 1 {
|
||||
return new_exprs.pop().unwrap();
|
||||
}
|
||||
@@ -470,20 +526,12 @@ impl Optimizer {
|
||||
(BoundKind::Block { exprs: new_exprs }, node.ty.clone())
|
||||
}
|
||||
|
||||
BoundKind::Lambda { .. } => {
|
||||
let (params, original_upvalues, body, positional_count) =
|
||||
if let BoundKind::Lambda {
|
||||
params,
|
||||
upvalues,
|
||||
body,
|
||||
positional_count,
|
||||
} = &node.kind
|
||||
{
|
||||
(params, upvalues, body, positional_count)
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
BoundKind::Lambda {
|
||||
params,
|
||||
upvalues,
|
||||
body,
|
||||
positional_count,
|
||||
} => {
|
||||
let mut info = UsageInfo::default();
|
||||
info.collect(&node);
|
||||
|
||||
@@ -491,8 +539,9 @@ impl Optimizer {
|
||||
let mut mapping = Vec::new();
|
||||
let mut next_inner_subs = sub.new_inner();
|
||||
next_inner_subs.assigned = info.assigned;
|
||||
let mut upvalues_changed = false;
|
||||
|
||||
for (old_idx, capture_addr) in original_upvalues.iter().enumerate() {
|
||||
for (old_idx, capture_addr) in upvalues.iter().enumerate() {
|
||||
let mut inlined_val = None;
|
||||
if !sub.assigned.contains(capture_addr)
|
||||
&& let Some(val) = sub.get_value(capture_addr)
|
||||
@@ -508,40 +557,45 @@ impl Optimizer {
|
||||
next_inner_subs
|
||||
.add_value(Address::Upvalue(UpvalueIdx(old_idx as u32)), val);
|
||||
mapping.push(None);
|
||||
upvalues_changed = true;
|
||||
} else {
|
||||
mapping.push(Some(new_upvalues.len() as u32));
|
||||
new_upvalues.push(sub.map_address(*capture_addr));
|
||||
let mapped = sub.map_address(*capture_addr);
|
||||
if mapped != *capture_addr {
|
||||
upvalues_changed = true;
|
||||
}
|
||||
new_upvalues.push(mapped);
|
||||
}
|
||||
}
|
||||
|
||||
let params_node =
|
||||
self.visit_node(params.as_ref().clone(), &mut next_inner_subs, path);
|
||||
inliner.collect_parameter_slots(¶ms_node, &mut next_inner_subs);
|
||||
let params_opt = self.visit_node(params.clone(), &mut next_inner_subs, path);
|
||||
inliner.collect_parameter_slots(¶ms_opt, &mut next_inner_subs);
|
||||
|
||||
let body_node = self.visit_node(body.as_ref().clone(), &mut next_inner_subs, path);
|
||||
let reindexed_body = if new_upvalues.len() != original_upvalues.len() {
|
||||
sub.reindex_upvalues(body_node, &mapping)
|
||||
let body_opt = self.visit_node(body.clone(), &mut next_inner_subs, path);
|
||||
let reindexed_body = if new_upvalues.len() != upvalues.len() {
|
||||
sub.reindex_upvalues(body_opt, &mapping)
|
||||
} else {
|
||||
body_node
|
||||
body_opt
|
||||
};
|
||||
|
||||
if !upvalues_changed && Rc::ptr_eq(¶ms_opt, params) && Rc::ptr_eq(&reindexed_body, body) {
|
||||
return node_rc;
|
||||
}
|
||||
|
||||
(
|
||||
BoundKind::Lambda {
|
||||
params: Rc::new(params_node),
|
||||
params: params_opt,
|
||||
upvalues: new_upvalues,
|
||||
body: Rc::new(reindexed_body),
|
||||
body: reindexed_body,
|
||||
positional_count: *positional_count,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Destructure {
|
||||
ref pattern,
|
||||
ref value,
|
||||
} => {
|
||||
let val_opt = Box::new(self.visit_node((**value).clone(), sub, path));
|
||||
let pat_opt = Box::new(self.visit_node((**pattern).clone(), sub, path));
|
||||
BoundKind::Destructure { pattern, value } => {
|
||||
let val_opt = self.visit_node(value.clone(), sub, path);
|
||||
let pat_opt = self.visit_node(pattern.clone(), sub, path);
|
||||
|
||||
let mut info = UsageInfo::default();
|
||||
info.collect_pattern(&pat_opt);
|
||||
@@ -549,6 +603,10 @@ impl Optimizer {
|
||||
sub.remove_value(&addr);
|
||||
}
|
||||
|
||||
if Rc::ptr_eq(&val_opt, value) && Rc::ptr_eq(&pat_opt, pattern) {
|
||||
return node_rc;
|
||||
}
|
||||
|
||||
(
|
||||
BoundKind::Destructure {
|
||||
pattern: pat_opt,
|
||||
@@ -559,25 +617,39 @@ impl Optimizer {
|
||||
}
|
||||
|
||||
BoundKind::Tuple { elements } => {
|
||||
let elements = elements
|
||||
.into_iter()
|
||||
.map(|e| self.visit_node(e, sub, path))
|
||||
.collect();
|
||||
(BoundKind::Tuple { elements }, node.ty.clone())
|
||||
let mut new_elements = Vec::with_capacity(elements.len());
|
||||
let mut changed = false;
|
||||
for e in elements {
|
||||
let opt = self.visit_node(e.clone(), sub, path);
|
||||
if !Rc::ptr_eq(&opt, e) {
|
||||
changed = true;
|
||||
}
|
||||
new_elements.push(opt);
|
||||
}
|
||||
if !changed {
|
||||
return node_rc;
|
||||
}
|
||||
(BoundKind::Tuple { elements: new_elements }, node.ty.clone())
|
||||
}
|
||||
BoundKind::Record {
|
||||
ref layout,
|
||||
ref values,
|
||||
} => {
|
||||
let mapped_values: Vec<_> = values
|
||||
.iter()
|
||||
.map(|v| self.visit_node(v.clone(), sub, path))
|
||||
.collect();
|
||||
BoundKind::Record { layout, values } => {
|
||||
let mut mapped_values = Vec::with_capacity(values.len());
|
||||
let mut changed = false;
|
||||
for v in values {
|
||||
let opt = self.visit_node(v.clone(), sub, path);
|
||||
if !Rc::ptr_eq(&opt, v) {
|
||||
changed = true;
|
||||
}
|
||||
mapped_values.push(opt);
|
||||
}
|
||||
|
||||
if self.enabled
|
||||
&& let Some(folded) = folder.try_fold_record(layout, &mapped_values, &node)
|
||||
{
|
||||
return folded;
|
||||
return Rc::new(folded);
|
||||
}
|
||||
|
||||
if !changed {
|
||||
return node_rc;
|
||||
}
|
||||
|
||||
(
|
||||
@@ -589,36 +661,40 @@ impl Optimizer {
|
||||
)
|
||||
}
|
||||
BoundKind::Expansion {
|
||||
ref original_call,
|
||||
ref bound_expanded,
|
||||
original_call,
|
||||
bound_expanded,
|
||||
} => {
|
||||
path.inlining_depth += 1;
|
||||
let bound_expanded =
|
||||
Box::new(self.visit_node((**bound_expanded).clone(), sub, path));
|
||||
let expanded_opt = self.visit_node(bound_expanded.clone(), sub, path);
|
||||
path.inlining_depth -= 1;
|
||||
|
||||
if Rc::ptr_eq(&expanded_opt, bound_expanded) {
|
||||
return node_rc;
|
||||
}
|
||||
|
||||
(
|
||||
BoundKind::Expansion {
|
||||
original_call: original_call.clone(),
|
||||
bound_expanded,
|
||||
bound_expanded: expanded_opt,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
k => (k, node.ty.clone()),
|
||||
k => (k.clone(), node.ty.clone()),
|
||||
};
|
||||
|
||||
Node {
|
||||
identity: node.identity,
|
||||
Rc::new(Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: new_kind,
|
||||
ty: metrics,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn flatten_tuple(&self, node: AnalyzedNode, into: &mut Vec<AnalyzedNode>) {
|
||||
match node.kind {
|
||||
fn flatten_tuple(&self, node: Rc<AnalyzedNode>, into: &mut Vec<Rc<AnalyzedNode>>) {
|
||||
match &node.kind {
|
||||
BoundKind::Tuple { elements } => {
|
||||
for el in elements {
|
||||
self.flatten_tuple(el, into);
|
||||
self.flatten_tuple(el.clone(), into);
|
||||
}
|
||||
}
|
||||
BoundKind::Nop => {}
|
||||
|
||||
@@ -51,7 +51,7 @@ impl<'a> Folder<'a> {
|
||||
pub fn try_fold_record(
|
||||
&self,
|
||||
layout: &std::sync::Arc<RecordLayout>,
|
||||
values: &[AnalyzedNode],
|
||||
values: &[Rc<AnalyzedNode>],
|
||||
template: &AnalyzedNode,
|
||||
) -> Option<AnalyzedNode> {
|
||||
let mut constant_values = Vec::with_capacity(values.len());
|
||||
@@ -71,7 +71,7 @@ impl<'a> Folder<'a> {
|
||||
pub fn try_fold_pure(
|
||||
&self,
|
||||
callee: &AnalyzedNode,
|
||||
arg_nodes: &[AnalyzedNode],
|
||||
arg_nodes: &[Rc<AnalyzedNode>],
|
||||
) -> Option<AnalyzedNode> {
|
||||
if callee.ty.purity < Purity::Pure {
|
||||
return None;
|
||||
|
||||
@@ -65,7 +65,7 @@ impl<'a> Inliner<'a> {
|
||||
pub fn prepare_beta_reduction(
|
||||
&self,
|
||||
params: &AnalyzedNode,
|
||||
arg_vals: &[AnalyzedNode],
|
||||
arg_vals: &[Rc<AnalyzedNode>],
|
||||
body: &AnalyzedNode,
|
||||
sub: &mut SubstitutionMap,
|
||||
) -> Option<()> {
|
||||
@@ -102,7 +102,7 @@ impl<'a> Inliner<'a> {
|
||||
pub fn map_params_to_args(
|
||||
&self,
|
||||
pattern: &AnalyzedNode,
|
||||
args: &[AnalyzedNode],
|
||||
args: &[Rc<AnalyzedNode>],
|
||||
offset: &mut usize,
|
||||
sub: &mut SubstitutionMap,
|
||||
body_usage: &UsageInfo,
|
||||
@@ -117,13 +117,13 @@ impl<'a> Inliner<'a> {
|
||||
} else if let BoundKind::Lambda { upvalues, .. } = &arg.kind
|
||||
&& upvalues.is_empty()
|
||||
{
|
||||
sub.add_ast_substitution(*addr, arg.clone());
|
||||
sub.add_ast_substitution(*addr, (**arg).clone());
|
||||
} else if let BoundKind::Get {
|
||||
addr: Address::Global(_),
|
||||
..
|
||||
} = &arg.kind
|
||||
{
|
||||
sub.add_ast_substitution(*addr, arg.clone());
|
||||
sub.add_ast_substitution(*addr, (**arg).clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@ use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalS
|
||||
use crate::ast::nodes::Node;
|
||||
use crate::ast::types::Value;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SubstitutionMap {
|
||||
pub values: HashMap<Address, Value>,
|
||||
pub ast_substitutions: HashMap<Address, AnalyzedNode>,
|
||||
pub ast_substitutions: HashMap<Address, Rc<AnalyzedNode>>,
|
||||
pub slot_mapping: HashMap<LocalSlot, LocalSlot>,
|
||||
pub assigned: HashSet<Address>,
|
||||
pub next_slot: u32,
|
||||
@@ -38,7 +39,7 @@ impl SubstitutionMap {
|
||||
}
|
||||
|
||||
pub fn add_ast_substitution(&mut self, addr: Address, node: AnalyzedNode) {
|
||||
self.ast_substitutions.insert(addr, node);
|
||||
self.ast_substitutions.insert(addr, Rc::new(node));
|
||||
}
|
||||
|
||||
pub fn map_slot(&mut self, old_slot: LocalSlot) -> LocalSlot {
|
||||
@@ -81,12 +82,13 @@ impl SubstitutionMap {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reindex_upvalues(&self, node: AnalyzedNode, mapping: &[Option<u32>]) -> AnalyzedNode {
|
||||
let (new_kind, metrics) = match node.kind {
|
||||
pub fn reindex_upvalues(&self, node_rc: Rc<AnalyzedNode>, mapping: &[Option<u32>]) -> Rc<AnalyzedNode> {
|
||||
let node = &*node_rc;
|
||||
let (new_kind, metrics) = match &node.kind {
|
||||
BoundKind::Get { addr, name } => (
|
||||
BoundKind::Get {
|
||||
addr: self.reindex_addr(addr, mapping),
|
||||
name,
|
||||
addr: self.reindex_addr(*addr, mapping),
|
||||
name: name.clone(),
|
||||
},
|
||||
node.ty.clone(),
|
||||
),
|
||||
@@ -98,14 +100,14 @@ impl SubstitutionMap {
|
||||
} => {
|
||||
let mut next_upvalues = Vec::new();
|
||||
for addr in upvalues {
|
||||
next_upvalues.push(self.reindex_addr(addr, mapping));
|
||||
next_upvalues.push(self.reindex_addr(*addr, mapping));
|
||||
}
|
||||
(
|
||||
BoundKind::Lambda {
|
||||
params,
|
||||
params: params.clone(),
|
||||
upvalues: next_upvalues,
|
||||
body,
|
||||
positional_count,
|
||||
body: body.clone(),
|
||||
positional_count: *positional_count,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
@@ -115,9 +117,9 @@ impl SubstitutionMap {
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
let cond = Box::new(self.reindex_upvalues(*cond, mapping));
|
||||
let then_br = Box::new(self.reindex_upvalues(*then_br, mapping));
|
||||
let else_br = else_br.map(|e| Box::new(self.reindex_upvalues(*e, mapping)));
|
||||
let cond = self.reindex_upvalues(cond.clone(), mapping);
|
||||
let then_br = self.reindex_upvalues(then_br.clone(), mapping);
|
||||
let else_br = else_br.as_ref().map(|e| self.reindex_upvalues(e.clone(), mapping));
|
||||
(
|
||||
BoundKind::If {
|
||||
cond,
|
||||
@@ -129,14 +131,14 @@ impl SubstitutionMap {
|
||||
}
|
||||
BoundKind::Block { exprs } => {
|
||||
let exprs = exprs
|
||||
.into_iter()
|
||||
.map(|e| self.reindex_upvalues(e, mapping))
|
||||
.iter()
|
||||
.map(|e| self.reindex_upvalues(e.clone(), mapping))
|
||||
.collect();
|
||||
(BoundKind::Block { exprs }, node.ty.clone())
|
||||
}
|
||||
BoundKind::Call { callee, args } => {
|
||||
let callee = Box::new(self.reindex_upvalues(*callee, mapping));
|
||||
let args = Box::new(self.reindex_upvalues(*args, mapping));
|
||||
let callee = self.reindex_upvalues(callee.clone(), mapping);
|
||||
let args = self.reindex_upvalues(args.clone(), mapping);
|
||||
(BoundKind::Call { callee, args }, node.ty.clone())
|
||||
}
|
||||
BoundKind::Define {
|
||||
@@ -146,42 +148,58 @@ impl SubstitutionMap {
|
||||
value,
|
||||
captured_by,
|
||||
} => {
|
||||
let value = Box::new(self.reindex_upvalues(*value, mapping));
|
||||
let value = self.reindex_upvalues(value.clone(), mapping);
|
||||
(
|
||||
BoundKind::Define {
|
||||
name,
|
||||
addr,
|
||||
kind,
|
||||
name: name.clone(),
|
||||
addr: *addr,
|
||||
kind: *kind,
|
||||
value,
|
||||
captured_by,
|
||||
captured_by: captured_by.clone(),
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
BoundKind::Set { addr, value } => {
|
||||
let value = Box::new(self.reindex_upvalues(*value, mapping));
|
||||
(BoundKind::Set { addr, value }, node.ty.clone())
|
||||
let value = self.reindex_upvalues(value.clone(), mapping);
|
||||
(
|
||||
BoundKind::Set {
|
||||
addr: *addr,
|
||||
value,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
let elements = elements
|
||||
.into_iter()
|
||||
.map(|e| self.reindex_upvalues(e, mapping))
|
||||
.iter()
|
||||
.map(|e| self.reindex_upvalues(e.clone(), mapping))
|
||||
.collect();
|
||||
(BoundKind::Tuple { elements }, node.ty.clone())
|
||||
}
|
||||
BoundKind::Record { layout, values } => {
|
||||
let values = values
|
||||
.into_iter()
|
||||
.map(|v| self.reindex_upvalues(v, mapping))
|
||||
.iter()
|
||||
.map(|v| self.reindex_upvalues(v.clone(), mapping))
|
||||
.collect();
|
||||
(BoundKind::Record { layout, values }, node.ty.clone())
|
||||
(BoundKind::Record { layout: layout.clone(), values }, node.ty.clone())
|
||||
}
|
||||
k => (k, node.ty.clone()),
|
||||
BoundKind::Expansion { original_call, bound_expanded } => {
|
||||
let bound_expanded = self.reindex_upvalues(bound_expanded.clone(), mapping);
|
||||
(
|
||||
BoundKind::Expansion {
|
||||
original_call: original_call.clone(),
|
||||
bound_expanded,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
k => (k.clone(), node.ty.clone()),
|
||||
};
|
||||
Node {
|
||||
Rc::new(Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: new_kind,
|
||||
ty: metrics,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user