feat(tvix/eval): implement runtime closure construction (OpClosure)
Implements the final bit of logic remaining for wiring up closures, which is the runtime construction of closure objects. When encountering an OpClosure, the VM walks through the bytecode collecting all the upvalue location operands (see commit introducing the OpCode::Data* variants for details) and stores the runtime values in the new closures upvalue vector. After that, the handling of the closure itself becomes functionally identical to that of lambdas. With this initial implementation of closures there are several large optimisation potentials available, the two most notable ones are: - Distinguish the runtime representation of lambdas and closures explicitly. - Detect and handle multiple-arity functions directly in the compiler. However, for both of these we should wait until we have appropriate benchmarking infrastructure in place. This is because our test implementations have shown that the complexity of either of these changes is quite significant, and we do not yet know if they really pay off. Change-Id: I077e977810fd5cb2b1ecd7f1a119e728025dd786 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6295 Tested-by: BuildkiteCI Reviewed-by: grfn <grfn@gws.fyi>
This commit is contained in:
parent
0f06d0ca33
commit
f88d248f48
1 changed files with 28 additions and 1 deletions
|
@ -366,12 +366,39 @@ impl VM {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
OpCode::OpClosure(_) => todo!("creating closure objects"),
|
|
||||||
OpCode::OpGetUpvalue(UpvalueIdx(upv_idx)) => {
|
OpCode::OpGetUpvalue(UpvalueIdx(upv_idx)) => {
|
||||||
let value = self.frame().closure.upvalues[upv_idx].clone();
|
let value = self.frame().closure.upvalues[upv_idx].clone();
|
||||||
self.push(value);
|
self.push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OpCode::OpClosure(idx) => {
|
||||||
|
let mut closure = self.chunk().constant(idx).clone().to_closure()?;
|
||||||
|
|
||||||
|
debug_assert!(
|
||||||
|
closure.lambda.upvalue_count > 0,
|
||||||
|
"OpClosure should not be called for plain lambdas"
|
||||||
|
);
|
||||||
|
|
||||||
|
for _ in 0..closure.lambda.upvalue_count {
|
||||||
|
match self.inc_ip() {
|
||||||
|
OpCode::DataLocalIdx(StackIdx(local_idx)) => {
|
||||||
|
let idx = self.frame().stack_offset + local_idx;
|
||||||
|
closure.upvalues.push(self.stack[idx].clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCode::DataUpvalueIdx(UpvalueIdx(upv_idx)) => {
|
||||||
|
closure
|
||||||
|
.upvalues
|
||||||
|
.push(self.frame().closure.upvalues[upv_idx].clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => panic!("compiler error: missing closure operand"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.push(Value::Closure(closure));
|
||||||
|
}
|
||||||
|
|
||||||
// Data-carrying operands should never be executed,
|
// Data-carrying operands should never be executed,
|
||||||
// that is a critical error in the VM.
|
// that is a critical error in the VM.
|
||||||
OpCode::DataLocalIdx(_) | OpCode::DataUpvalueIdx(_) => {
|
OpCode::DataLocalIdx(_) | OpCode::DataUpvalueIdx(_) => {
|
||||||
|
|
Loading…
Reference in a new issue