diff options
author | Vincent Ambo <mail@tazj.in> | 2022-09-04T20·18+0300 |
---|---|---|
committer | tazjin <tazjin@tvl.su> | 2022-09-10T21·57+0000 |
commit | 6bbe7589c5854efb066e9e74c71ad977afaf5d83 (patch) | |
tree | 1460f103a3f0c2a811435eff2d9d4e5bc1a8103a | |
parent | 2e018a50a74040d8db5c0dfeae5f829a5c7c0cf2 (diff) |
feat(tvix/eval): optimise tail calls in emitted chunks r/4784
When the last instruction in a chunk is OpCall, make it an OpTailCall instead. Change-Id: I2c80a06ee85e4abf545887b1a79b6d8b5e6123e9 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6458 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
-rw-r--r-- | tvix/eval/src/compiler/mod.rs | 22 |
1 files changed, 20 insertions, 2 deletions
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs index 448999fb38df..496f0aaf3207 100644 --- a/tvix/eval/src/compiler/mod.rs +++ b/tvix/eval/src/compiler/mod.rs @@ -900,7 +900,11 @@ impl Compiler<'_, '_> { // Pop the lambda context back off, and emit the finished // lambda as a constant. - let compiled = self.contexts.pop().unwrap(); + let mut compiled = self.contexts.pop().unwrap(); + + // Check if tail-call optimisation is possible and perform it. + optimise_tail_call(&mut compiled.lambda.chunk); + let lambda = Rc::new(compiled.lambda); self.observer.observe_compiled_lambda(&lambda); @@ -947,7 +951,8 @@ impl Compiler<'_, '_> { content(self, node, slot); self.end_scope(node); - let thunk = self.contexts.pop().unwrap(); + let mut thunk = self.contexts.pop().unwrap(); + optimise_tail_call(&mut thunk.lambda.chunk); let lambda = Rc::new(thunk.lambda); self.observer.observe_compiled_thunk(&lambda); @@ -1304,6 +1309,19 @@ impl Compiler<'_, '_> { } } +/// Perform tail-call optimisation if the last call within a +/// compiled chunk is another call. +fn optimise_tail_call(chunk: &mut Chunk) { + let last_op = chunk + .code + .last_mut() + .expect("compiler bug: chunk should never be empty"); + + if matches!(last_op, OpCode::OpCall) { + *last_op = OpCode::OpTailCall; + } +} + /// Prepare the full set of globals from additional globals supplied /// by the caller of the compiler, as well as the built-in globals /// that are always part of the language. |