Add positional_count field to Lambda
This field is used for static optimization, determining if parameters are purely positional.
This commit is contained in:
@@ -200,10 +200,30 @@ impl Binder {
|
||||
|
||||
let compiled_fn = self.functions.pop().unwrap();
|
||||
|
||||
// 3. Static optimization: check if parameters are purely positional
|
||||
let positional_count = match ¶ms_bound.kind {
|
||||
BoundKind::Tuple { elements } => {
|
||||
let mut count = 0;
|
||||
let mut all_params = true;
|
||||
for e in elements {
|
||||
if matches!(e.kind, BoundKind::Parameter { .. }) {
|
||||
count += 1;
|
||||
} else {
|
||||
all_params = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if all_params { Some(count) } else { None }
|
||||
}
|
||||
BoundKind::Parameter { .. } => Some(1),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
Ok(self.make_node(identity, BoundKind::Lambda {
|
||||
params: Box::new(params_bound),
|
||||
params: Rc::new(params_bound),
|
||||
upvalues: compiled_fn.upvalues,
|
||||
body: Rc::new(body_bound),
|
||||
positional_count,
|
||||
}))
|
||||
},
|
||||
|
||||
|
||||
@@ -72,10 +72,12 @@ pub enum BoundKind<T = ()> {
|
||||
},
|
||||
|
||||
Lambda {
|
||||
params: Box<BoundNode<T>>,
|
||||
params: Rc<BoundNode<T>>,
|
||||
// The list of variables captured from enclosing scopes
|
||||
upvalues: Vec<Address>,
|
||||
body: Rc<BoundNode<T>>,
|
||||
/// Static optimization: number of positional parameters if the pattern is flat.
|
||||
positional_count: Option<u32>,
|
||||
},
|
||||
|
||||
Call {
|
||||
|
||||
@@ -117,7 +117,7 @@ impl Dumper {
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::Lambda { params, upvalues, body } => {
|
||||
BoundKind::Lambda { params, upvalues, body, .. } => {
|
||||
self.log(&format!("Lambda (Upvalues: {})", upvalues.len()), node);
|
||||
self.indent += 1;
|
||||
|
||||
|
||||
@@ -69,10 +69,10 @@ impl Specializer {
|
||||
let exprs = exprs.into_iter().map(|e| self.visit_node(e)).collect();
|
||||
(BoundKind::Block { exprs }, node.ty)
|
||||
},
|
||||
BoundKind::Lambda { params, upvalues, body } => {
|
||||
let params = Box::new(self.visit_node(*params));
|
||||
BoundKind::Lambda { params, upvalues, body, positional_count } => {
|
||||
let params = Rc::new(self.visit_node(params.as_ref().clone()));
|
||||
let body = Rc::new(self.visit_node((*body).clone()));
|
||||
(BoundKind::Lambda { params, upvalues, body }, node.ty)
|
||||
(BoundKind::Lambda { params, upvalues, body, positional_count }, node.ty)
|
||||
},
|
||||
BoundKind::DefLocal { name, slot, value, captured_by } => {
|
||||
let value = Box::new(self.visit_node(*value));
|
||||
@@ -194,15 +194,25 @@ impl Specializer {
|
||||
// Store in cache
|
||||
self.cache.borrow_mut().insert(key, (res_val.clone(), res_ty.clone()));
|
||||
|
||||
// PERFORMANCE: Flatten the argument tuple to match the specialized signature.
|
||||
// Since we are specializing, we can convert [[1 2] 3] into a flat [1 2 3] Tuple node.
|
||||
let flat_elements = self.flatten_tuple(new_args.clone());
|
||||
let flat_types = flat_elements.iter().map(|e| e.ty.clone()).collect();
|
||||
let flattened_args = Node {
|
||||
identity: new_args.identity.clone(),
|
||||
kind: BoundKind::Tuple { elements: flat_elements },
|
||||
ty: StaticType::Tuple(flat_types),
|
||||
};
|
||||
|
||||
let specialized_callee = Node {
|
||||
identity: new_callee.identity.clone(),
|
||||
kind: BoundKind::Constant(res_val),
|
||||
ty: StaticType::Function(Box::new(Signature {
|
||||
params: StaticType::Tuple(arg_types),
|
||||
params: flattened_args.ty.clone(),
|
||||
ret: res_ty.clone(),
|
||||
})),
|
||||
};
|
||||
return (specialized_callee, new_args, res_ty);
|
||||
return (specialized_callee, flattened_args, res_ty);
|
||||
},
|
||||
Err(_) => {
|
||||
// Fallback on error
|
||||
@@ -214,6 +224,19 @@ impl Specializer {
|
||||
// Fallback: Dynamic Call
|
||||
(new_callee, new_args, original_ty)
|
||||
}
|
||||
|
||||
fn flatten_tuple(&self, node: TypedNode) -> Vec<TypedNode> {
|
||||
match node.kind {
|
||||
BoundKind::Tuple { elements } => {
|
||||
let mut flat = Vec::new();
|
||||
for el in elements {
|
||||
flat.extend(self.flatten_tuple(el));
|
||||
}
|
||||
flat
|
||||
}
|
||||
_ => vec![node],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -268,7 +291,7 @@ mod tests {
|
||||
let func_node = BoundNode {
|
||||
identity: make_identity(),
|
||||
kind: BoundKind::Lambda {
|
||||
params: Box::new(BoundNode {
|
||||
params: Rc::new(BoundNode {
|
||||
identity: make_identity(),
|
||||
kind: BoundKind::Tuple {
|
||||
elements: vec![
|
||||
@@ -282,7 +305,8 @@ mod tests {
|
||||
ty: ()
|
||||
}),
|
||||
upvalues: vec![],
|
||||
body: Rc::new(BoundNode { identity: make_identity(), kind: BoundKind::Nop, ty: () })
|
||||
body: Rc::new(BoundNode { identity: make_identity(), kind: BoundKind::Nop, ty: () }),
|
||||
positional_count: Some(1),
|
||||
},
|
||||
ty: ()
|
||||
};
|
||||
|
||||
@@ -75,15 +75,16 @@ impl TCO {
|
||||
}
|
||||
},
|
||||
|
||||
BoundKind::Lambda { params, upvalues, body } => {
|
||||
BoundKind::Lambda { params, upvalues, body, positional_count } => {
|
||||
// The body of a lambda is implicitly in tail position when the lambda is called.
|
||||
let new_body = Rc::new(Self::transform((*body).clone(), true));
|
||||
|
||||
Node {
|
||||
kind: BoundKind::Lambda {
|
||||
params,
|
||||
params: params.clone(),
|
||||
upvalues,
|
||||
body: new_body,
|
||||
positional_count,
|
||||
},
|
||||
..node
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ impl TypeChecker {
|
||||
|
||||
pub fn check(&self, node: BoundNode, arg_types: &[StaticType]) -> Result<TypedNode, String> {
|
||||
match node.kind {
|
||||
BoundKind::Lambda { params, upvalues, body } => {
|
||||
BoundKind::Lambda { params, upvalues, body, positional_count } => {
|
||||
// 1. Determine types of captured variables (Root lambdas have none)
|
||||
let mut upvalue_types = Vec::with_capacity(upvalues.len());
|
||||
for _ in &upvalues {
|
||||
@@ -66,7 +66,7 @@ impl TypeChecker {
|
||||
StaticType::Tuple(arg_types.to_vec())
|
||||
};
|
||||
|
||||
let params_typed = self.check_params(*params, &arg_tuple_ty, &mut lambda_ctx)?;
|
||||
let params_typed = self.check_params(params.as_ref().clone(), &arg_tuple_ty, &mut lambda_ctx)?;
|
||||
|
||||
// 4. Check body with the new types
|
||||
let body_typed = self.check_node((*body).clone(), &mut lambda_ctx)?;
|
||||
@@ -83,9 +83,10 @@ impl TypeChecker {
|
||||
Ok(Node {
|
||||
identity: node.identity,
|
||||
kind: BoundKind::Lambda {
|
||||
params: Box::new(params_typed),
|
||||
params: Rc::new(params_typed),
|
||||
upvalues,
|
||||
body: Rc::new(body_typed)
|
||||
body: Rc::new(body_typed),
|
||||
positional_count,
|
||||
},
|
||||
ty: fn_ty,
|
||||
})
|
||||
@@ -95,13 +96,14 @@ impl TypeChecker {
|
||||
let virtual_lambda = BoundNode {
|
||||
identity: node.identity.clone(),
|
||||
kind: BoundKind::Lambda {
|
||||
params: Box::new(Node {
|
||||
params: Rc::new(Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: BoundKind::Tuple { elements: vec![] },
|
||||
ty: (),
|
||||
}),
|
||||
upvalues: vec![],
|
||||
body: Rc::new(node)
|
||||
body: Rc::new(node),
|
||||
positional_count: Some(0),
|
||||
},
|
||||
ty: (),
|
||||
};
|
||||
@@ -232,7 +234,7 @@ impl TypeChecker {
|
||||
(BoundKind::Block { exprs: typed_exprs }, last_ty)
|
||||
},
|
||||
|
||||
BoundKind::Lambda { params, upvalues, body } => {
|
||||
BoundKind::Lambda { params, upvalues, body, positional_count } => {
|
||||
// 1. Determine types of captured variables
|
||||
let mut upvalue_types = Vec::with_capacity(upvalues.len());
|
||||
for &addr in &upvalues {
|
||||
@@ -243,7 +245,7 @@ impl TypeChecker {
|
||||
let mut lambda_ctx = TypeContext::new(64, upvalue_types, Some(ctx));
|
||||
|
||||
// 3. Check parameters and body
|
||||
let params_typed = self.check_params(*params, &StaticType::Any, &mut lambda_ctx)?;
|
||||
let params_typed = self.check_params(params.as_ref().clone(), &StaticType::Any, &mut lambda_ctx)?;
|
||||
let body_typed = self.check_node((*body).clone(), &mut lambda_ctx)?;
|
||||
let ret_ty = body_typed.ty.clone();
|
||||
|
||||
@@ -254,9 +256,10 @@ impl TypeChecker {
|
||||
}));
|
||||
|
||||
(BoundKind::Lambda {
|
||||
params: Box::new(params_typed),
|
||||
params: Rc::new(params_typed),
|
||||
upvalues,
|
||||
body: Rc::new(body_typed)
|
||||
body: Rc::new(body_typed),
|
||||
positional_count,
|
||||
}, fn_ty)
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user