about summary refs log tree commit diff
path: root/tvix/eval/src/vm.rs
AgeCommit message (Collapse)AuthorFilesLines
2022-09-22 r/4955 feat(tvix/eval): Support builtins.lessThanWilliam Carroll1-13/+20
Extend and export the `cmp_op`, and this becomes trivial. Change-Id: I9c93fa4db0f5a1fc8b56928ea144676f79247de1 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6557 Autosubmit: wpcarro <wpcarro@gmail.com> Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-20 r/4944 feat(tvix/eval): track other type in NotCallable error kindVincent Ambo1-2/+2
This makes for slightly nicer error messages if something isn't, well, callable. Change-Id: I821c8d7447b93aea9ccaaa52ed329de0cca4b18e Reviewed-on: https://cl.tvl.fyi/c/depot/+/6718 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-20 r/4943 refactor(tvix/eval): add VM::call_value helper methodVincent Ambo1-12/+23
This makes it possible to call a callable value (builtin or closure/lambda) directly, without unwrapping it first. This is needed for pretty much all higher-order functions to work correctly. This is mostly equivalent to the previous code in coerce_to_string for calling `__toString`, except it expects the argument(s) to already be placed on the stack. Note that the span for the `NotCallable` error is not currently guaranteed to make any sense, will experiment with this. Change-Id: I821224368d438a28900858b343defc1817e46a0a Reviewed-on: https://cl.tvl.fyi/c/depot/+/6717 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-18 r/4909 chore(tvix/eval): Pass in VM to nix_eqGriffin Smith1-1/+1
Pass in, but ignore, a mutable reference to the VM to the `nix_eq` functions, in preparation for using that VM to force thunks during comparison. Change-Id: I565435d8dfb33768f930fdb5a6b0fb1365d7e161 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6651 Autosubmit: grfn <grfn@gws.fyi> Tested-by: BuildkiteCI Reviewed-by: tazjin <tazjin@tvl.su>
2022-09-18 r/4908 refactor(tvix/eval): Don't (ab)use PartialEq for Nix equalityGriffin Smith1-1/+2
Using rust's PartialEq trait to implement Nix equality semantics is reasonably fraught with peril, both because the actual laws are different than what nix expects, and (more importantly) because certain things actually require extra context to compare for equality (for example, thunks need to be forced). This converts the manual PartialEq impl for Value (and all its descendants) to a *derived* PartialEq impl (which requires a lot of extra PartialEq derives on miscellanious other types within the codebase), and converts the previous nix-semantics equality comparison into a new `nix_eq` method. This returns an EvalResult, even though it can't currently return an error, to allow it to fail when eg forcing thunks (which it will do soon). Since the PartialEq impls for Value and NixAttrs are now quite boring, this converts the generated proptests for those into handwritten ones that cover `nix_eq` instead Change-Id: If3da7171f88c22eda5b7a60030d8b00c3b76f672 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6650 Autosubmit: grfn <grfn@gws.fyi> Tested-by: BuildkiteCI Reviewed-by: tazjin <tazjin@tvl.su>
2022-09-17 r/4890 refactor(tvix/eval): rename OpAttrsIsSet -> OpHasAttrVincent Ambo1-1/+1
This matches the name of the AST node from which it was compiled. Suggested by sterni in cl/6231 Change-Id: Ia51525158d2f47467c01fce2282005b1a8417a47 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6623 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org> Reviewed-by: grfn <grfn@gws.fyi>
2022-09-15 r/4860 fix(tvix/eval): coerce string interpolation parts to stringsterni1-1/+11
With this puzzle piece of string compilation in place, `compile_str` becomes less redundant, as every part now needs to be compiled the same. The thunking logic becomes a bit trickier, since we need to thunk even in the case of `count == 1` if the single part is interpolating. Splitting the inner (shared) code in a separate function turned out to be easier for making rustc content. Change-Id: I6a554ca599926ae5907d7acffce349c9616f568f Reviewed-on: https://cl.tvl.fyi/c/depot/+/6582 Reviewed-by: tazjin <tazjin@tvl.su> Tested-by: BuildkiteCI
2022-09-15 r/4857 feat(tvix/eval): implement correct toString behaviorsterni1-3/+3
Implement C++ Nix's `EvalState::coerceToString` minus some of the Path / store handling. This is currently only used for `toString` which does all possible coercions, but we've already prepared the weaker coercion variant which is e.g. used for builtins that expect string arguments. `EvalState::coerceToPath` is still missing for builtins that need a path, but it'll be easy to build on top of this. Change-Id: I78d15576b18921791d04b6b1e964b951fdef22c6 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6571 Autosubmit: sterni <sternenseemann@systemli.org> Reviewed-by: tazjin <tazjin@tvl.su> Tested-by: BuildkiteCI
2022-09-13 r/4846 refactor(tvix/eval): cover all Value variants in force_for_outputVincent Ambo1-1/+8
Avoids accidentally dropping one on the floor if we add more, pointed out by sterni in cl/6372 Change-Id: Ib7bb0ce9c8331c8337003d20c4d5240dfae1c32a Reviewed-on: https://cl.tvl.fyi/c/depot/+/6570 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-13 r/4838 refactor(tvix/eval): use CodeIdx wrapper for instruction pointerVincent Ambo1-6/+15
As suggested by sterni in cl/6453. Change-Id: I3cf80d97c11fd7d085ab510f6be4b5f937c791ec Reviewed-on: https://cl.tvl.fyi/c/depot/+/6562 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-11 r/4801 refactor(tvix/eval): capture entire with_stack in upvaluesVincent Ambo1-49/+38
This completely rewrites the handling of "dynamic upvalues" to, instead of resolving them at thunk/closure instantiation time (which forces some values too early), capture the entire with stack of parent contexts if it exists. There are a couple of things in here that could be written more efficiently, but I'm first working through this to get to a bug related to with + recursion and the code complexity of some of the optimisations is distracting. Change-Id: Ia538e06c9146e3bf8decb9adf02dd726d2c651cf Reviewed-on: https://cl.tvl.fyi/c/depot/+/6486 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-11 r/4800 refactor(tvix/eval): introduce Upvalues struct in closures & thunksVincent Ambo1-8/+8
This struct will be responsible for tracking upvalues (and is a convenient place to introduce optimisations for reducing value clones) instead of a plain value vector. The main motivation for this is that the upvalues will have to capture the `with`-stack fully and I want to avoid duplicating the logic for this between the two capturing types. Change-Id: I6654f8739fc2e04ca046e6667d4a015f51724e99 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6485 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-11 r/4793 fix(tvix/eval): avoid forcing with-target until absolutely necessaryVincent Ambo1-4/+11
Change-Id: I00efbbb8b9d3d22f32becf0919c6adf1be8b4b69 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6465 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-10 r/4786 fix(tvix/eval): fix doc comment syntax where applicableVincent Ambo1-11/+11
As pointed out by grfn on cl/6091 Change-Id: I28308577b7cf99dffb4a4fd3cc8783eb9ab4d0d6 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6460 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-10 r/4783 feat(tvix/eval): implement OpTailCallVincent Ambo1-9/+37
If the last operation within a chunk is a function call, the call can be executed in the same call frame without increasing the depth of the call stack. To enable this, a new OpTailCall instruction (similar to OpCall) is introduced, but not yet emitted by the compiler. Change-Id: I9ffbd7da6d2d6a8ec7a724646435dc6ee89712f2 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6457 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-09 r/4782 fix(tvix/eval): force value passed to builtins.toStringVincent Ambo1-1/+1
This introduces a macro to do the forcing, but this solution isn't very nice and also does not work in all cases yet. Change-Id: Icd18862ec47edb82c0efc3af5835a6cb6126f629 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6456 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-09 r/4778 feat(tvix/eval): implement runtime tracing methods for ObserverVincent Ambo1-27/+23
These methods make it possible to trace the runtime execution of the VM through an observer. Change-Id: I90e26853ba2fe44748613e7f761ed5c1c5fc9ff7 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6452 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-09 r/4773 refactor(tvix/eval): index into Chunk with ConstantIdx/CodeIdxVincent Ambo1-4/+4
This is a step towards hiding the internal fields of thunk, and making the interface of the type more predictable. Part of the preparation for implementing observers. Change-Id: I1a88a96419c72eb9e2332b56a2dd94afa47e6f88 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6447 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-09 r/4771 chore(tvix/eval): debug_assert that all jumps are patchedVincent Ambo1-0/+4
Suggestion from sterni in cl/6282 Change-Id: I1adbdda9ff74f55a2f72892ffa524808b305f403 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6445 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-08 r/4768 feat(tvix/eval): print lambda memory adresses in disassemblerVincent Ambo1-3/+4
This makes it easier to track exactly which lambda is which when inspecting e.g. the concrete representation of a thunk. At runtime all lambdas live in an Rc. To make this print the right address, the construction of these Rcs had to be moved up right to the point where the lambda is first emitted (and disassembled). Change-Id: I6070e6c8ac55f0bd697966c4e7c5565c20d19106 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6435 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-08 r/4763 fix(tvix/eval): address current clippy & grfn lintsVincent Ambo1-13/+4
Change-Id: I65c6feb9f817b5b367d37204a1f57acfe4100d97 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6430 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-08 r/4752 refactor(tvix/eval): implement much clearer disassembler outputVincent Ambo1-23/+45
With this change the runtime trace contains much more exact information about the context of the computation (entering/exiting calls etc.) This is in large part due to moving the tracer to be a field on the VM itself, which enables consistent ordering of traces across the execution, and tracing an execution with its *input* instead of *output* stack. Change-Id: Ibe525e6e7d869756501e52bef1a441619ce7332c Reviewed-on: https://cl.tvl.fyi/c/depot/+/6419 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-08 r/4751 chore(tvix/eval): print value when panicking on internalsVincent Ambo1-1/+3
This makes it much easier to figure out what happened while debugging this sort of thing. Change-Id: I2e0e8096709adc647d63c04f213c547c415e5f44 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6418 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-08 r/4748 refactor(tvix/eval): return call frame result from VM::callVincent Ambo1-12/+20
Previously, "calling" (setting up the VM run loop for executing a call frame) and "running" (running this loop to completion) were separate operations. This was basically an attempt to avoid nesting `VM::run` invocations. However, doing things this way introduced some tricky bugs for exiting out of the call frames of thunks vs. builtins & closures. For now, we unify the two operations and always return the value to the caller directly. For now this makes calls a little less effective, but it gives us a chance to nail down some other strange behaviours and then re-optimise this afterwards. To make sure we tackle this again further down I've added it to the list of known possible optimisations. Change-Id: I96828ab6a628136e0bac1bf03555faa4e6b74ece Reviewed-on: https://cl.tvl.fyi/c/depot/+/6415 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-08 r/4745 refactor(tvix/eval): add macros for generating Value castersVincent Ambo1-10/+10
The casting methods of `Value` are pretty verbose, and actually incorrect before this commit as they did not account for inner thunk values. To address this, we first attempt to make them correct by introducing a standard macro to generate them and traverse the inner thunk(s) if necessary. This is likely to be a performance hit as it will now involve more cloning of values. We can do multiple things to alleviate this, but should do some measurements first. Change-Id: If315d6e2afe7b69db727df535bc6cbfb89a691aa Reviewed-on: https://cl.tvl.fyi/c/depot/+/6412 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-08 r/4744 refactor(tvix/eval): pass a VM reference to builtinsVincent Ambo1-1/+1
This makes it possible for builtins to force values on their own, without the VM having to apply a strictness mask to the arguments first. Change-Id: Ib49a94e56ca2a8d515c39647381ab55a727766e3 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6411 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-08 r/4741 feat(tvix/eval): ensure all errors always carry a spanVincent Ambo1-48/+83
Previously error spans were optional because the information about code spans was not available at runtime. Now that this information has been added, the error type will always carry a span. This change is very invasive all throughout the codebase. This is due to the fact that many functions that are called *by* the VM expected to return `EvalResult`, but this no longer works as the span information is not available to those functions - only to the VM itself. To work around this the majority of these functions have been changed to return `Result<T, ErrorKind>` instead and an accompanying macro in the VM constructs the "real" error. Note that this implementatino currently has a bug where errors occuring within thunks will yield the location at which the thunk was forced, not the location at which the error occured within the code. This will be fixed soon, but the commit is large enough as is. Change-Id: Ib1ecb81a4d09d464a95ea7ea9e589f3bd08d5202 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6408 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-07 r/4738 feat(tvix/eval): Support builtins.{add,sub}William Carroll1-0/+1
First pass at supporting `builtins` for tvix. The following tests appear to be WAI: ```shell $ cd tvix/eval $ cargo build $ cargo test ``` Change-Id: I27cce23d503b17a886d1109e285e8b4be4264977 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6405 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-07 r/4706 fix(tvix/eval): address current clippy lintsVincent Ambo1-1/+0
Note that I've allowed `needless_lifetimes` for the attribute set iterator, as I find the type easier to understand with these annotations present. Change-Id: I33abb17837ee4813076cdb9a87f54bac4a37044e Reviewed-on: https://cl.tvl.fyi/c/depot/+/6373 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-07 r/4705 feat(tvix/eval): semi-strictly evaluate output values of the VMVincent Ambo1-1/+35
This essentially makes the VM behave like `nix-instantiate --eval --strict`, i.e. data structures are traversed strictly and thunks are forced. Thunks embedded in closures are not forced. This allows us to re-enable tests that were disabled because they needed to output nested thunk contents, but is overall a behaviour that must be configurable later on, as it is not cmopatible with e.g. an evaluation of nixpkgs. Change-Id: I5303a5c8e4322feab1384fdb7712fecb950afca5 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6372 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-07 r/4700 refactor(tvix/eval): encapsulate all thunk-forcing logic in moduleVincent Ambo1-2/+3
The VM previously took care of repeatedly forcing a thunk until it reached an evaluated state. This logic is now encapsulated inside of the `Thunk::force` implementation. In addition, force no longer returns a reference to the value by default, leaving it up to callers to decide whether they want to borrow the value or not (a helper is provided for this). Change-Id: I2aa7da922058ad1c57fbf8bfc7785aab7971c02b Reviewed-on: https://cl.tvl.fyi/c/depot/+/6365 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-07 r/4688 feat(tvix/eval): implement OpForce in VMVincent Ambo1-1/+11
This operation forces the evaluation of a thunk. There is some potential here for making an implementation that avoids some copies, but the thunk machinery is tricky to get right so the first priority is to make sure it is correct by keeping the implementation simple. Change-Id: Ib381455b02f42ded717faff63f55afed4c8fb7e3 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6352 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-06 r/4681 feat(tvix/eval): implement OpThunk for runtime thunk constructionVincent Ambo1-2/+17
Implements an operation very similar to `OpClosure` which populates a thunk's upvalues and leaves it on the stack. Change-Id: I753b4dfeeaae6919316c7028ec361aaa13d87646 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6350 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-06 r/4679 refactor(tvix/eval): extract VM::populate_upvalues functionVincent Ambo1-30/+42
This function is reusable between thunks & closures. Change-Id: I44d5f9897b087a385c8e75027d2ff39c48a096f0 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6349 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-06 r/4678 refactor(tvix/eval): Carry lambda & upvalues directly in CallFrameVincent Ambo1-13/+25
CallFrame has to work for both thunks & closures (as a thunk is basically a "weird 0-argument closure"). We opt to store the common, relevant fields directly in the frame to avoid having to dereference through the nested structures constantly (which would be especially annoying in the case of thunks). Change-Id: I47781597b84ec5cd55502dba1713e92cf2592af3 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6348 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-06 r/4677 refactor(tvix/eval): introduce UpvalueCarrier traitVincent Ambo1-0/+1
This trait abstracts over the commonalities of upvalue handling between closures and thunks. It allows the VM to simplify the code used for setting up upvalues, without duplicating between the two different types. Note that this does not yet refactor the VM code to optimally make use of this. Change-Id: If8de5181f26ae1fa00d554f1ae6ea473ee4b6070 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6347 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-06 r/4674 feat(tvix/eval): Add Compiler::thunk method for emitting thunksVincent Ambo1-0/+2
The logic in this method is *very* similar to `compile_lambda`. It is intended to be called around any expression that should be thunked (such as function applications, attribute set values, etc.). Change-Id: Idfbb2daa9f4b735095378fb9c39a2fd07c8cff91 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6344 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-06 r/4667 feat(tvix/eval): implement OpFinalise instructionVincent Ambo1-0/+13
This instruction finalises the initialisation of deferred upvalues in closures (and soon, thunks). The compiler does not yet emit this instruction, some more accounting is needed for that. Change-Id: Ic4181b26e19779e206f51e17388559400da5f93a Reviewed-on: https://cl.tvl.fyi/c/depot/+/6337 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-06 r/4666 feat(tvix/eval): set up deferred upvalues at runtimeVincent Ambo1-2/+2
This will leave a sentinel value in the upvalue slot in which the actual value is to be captured after resolution once a scope is fully set up. Change-Id: I12b37b0dc8d32603b03e675c3bd039468e70b354 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6336 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-06 r/4665 feat(tvix/eval): detect deferred upvalue capturingVincent Ambo1-0/+5
Uses the threaded through slot offset to determine whether initialisation of a captured local upvalue must be defered to a later point where all values of a scope are available. This adds a new data representation to the opcode for this situation, but the equivalent runtime handling is not yet implemented. This is in part because there is more compiler machinery needed to find the resolution point. Change-Id: Ifd0c393f76abfe6e2d91483faf0f58947ab1dedc Reviewed-on: https://cl.tvl.fyi/c/depot/+/6329 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
2022-09-06 r/4660 fix(tvix/eval): correct runtime error for missing dynamic upvalueVincent Ambo1-0/+6
Change-Id: I75c351619780fdc5186a54f3df9b244ada984069 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6324 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-06 r/4659 fix(tvix/eval): instantiate *new* closures from blueprints each timeVincent Ambo1-9/+13
The previous closure refactoring introduced a bug in which the same closure object would get mutated constantly for each instance of a closure, which is incorrect behaviour. This commit instead introduces an explicit new Value variant for the internal "blueprint" that the compiler generates (essentially just the lambda) and uses this variant to construct the closure at runtime. If the blueprint ever leaks out to a user somehow that is a critical bug and tvix-eval will panic. As a ~treat~ test for this, the fibonacci function is being used as it is a self-recursive closure (i.e. different instantiations of the same "blueprint") getting called with different values and it's good to have it around. Change-Id: I485de675e9bb0c599ed7d5dc0f001eb34ab4c15f Reviewed-on: https://cl.tvl.fyi/c/depot/+/6323 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-06 r/4658 fix(tvix/eval): correctly thread through dynamic upvaluesVincent Ambo1-7/+64
This puts together the puzzle pieces for threading dynamic upvalues (that is, upvalues resolved from the `with`-stack) all the way through. Reading the test case enclosed in this commit and walking through it is recommended to understand what problem is being tackled here. In short, because the compiler can not statically know *which* with-scope a dynamic argument is resolved from it needs to lay the groundwork for resolving from *all* possible scopes. There are multiple different approaches to doing this. The approach chosen in this commit is that if a dynamic upvalue is detected, the compiler will emit instructions to close over this dynamic value in *all* enclosing lambda contexts. It uses a new instruction for this that will leave around a sentinel value in case an identifier could not be resolved, and wire the location of this found value (or sentinel) up through the upvalues to the next level of nesting. In this tradeoff, tvix potentially closes over more upvalues than are needed (but in practice, how often do people create *really* deep `with`-stacks? and in *this* kind of code situation? maybe we should even warn for this!) but avoids keeping the entire attribute sets themselves around. Looking at the test case, each surrounding closure will close over *all* dynamic identifiers that are referenced later on visible to it, but only the last one for each identifier will actually end up being used. This also covers our bases for an additional edge-case this creates, in which an identifier potentially resolves to a dynamic upvalue *and* to a dynamic value within the function's own scope (again, would anyone really do this?) by introducing a resolution instruction for that particular case. There is likely some potential for cleaning up this code which is quite ugly in some parts, but as this implementation is now carefully calibrated to work I decided it is time to commit it and clean it up in subsequent commits. Change-Id: Ib701e3e6da39bd2c95938d1384036ff4f9fb3749 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6322 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-06 r/4654 fix(tvix/eval): account for stack offset when pushing a `with` scopeVincent Ambo1-1/+4
Change-Id: I4b98eaea3ed5059c29938a117a9d59499a0bb95d Reviewed-on: https://cl.tvl.fyi/c/depot/+/6318 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-06 r/4653 feat(tvix/eval): implement capture of self-recursive upvaluesVincent Ambo1-3/+9
With this change, it becomes possible for functions to call themselves as they are being defined in local bindings. Change-Id: Ib46a39ba17b1452b5673d96fa729d633d237241a Reviewed-on: https://cl.tvl.fyi/c/depot/+/6314 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-06 r/4651 refactor(tvix/eval): encapsulate internal mutability within ClosureVincent Ambo1-16/+16
This is required to efficiently construct the upvalue array at runtime, as there are situations where during Closure construction multiple things already have a reference to the closure (e.g. a self-reference). Change-Id: I35263b845fdc695dc873de489f5168d39b370f6a Reviewed-on: https://cl.tvl.fyi/c/depot/+/6312 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-04 r/4635 feat(tvix/eval): implement upvalue resolution in `with` scopesVincent Ambo1-1/+6
These need to be handled specially by the runtime if the compiler determines that a given local must be resolved via `with`. Note that this implementation has a bug: It currently allows `with` inside of nested lambdas to shadow statically known identifiers. This will be cleaned up in the next commit. Change-Id: If196b99cbd1a0f2dbb4a40a0e88cdb09a009c6b9 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6299 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-04 r/4634 fix(tvix/eval): ensure OpResolveWith can be tracedVincent Ambo1-16/+16
The previous implementation of OpResolveWith manually controlled the loop iteration, which skipped over the disassembler's tracing instruction. Instead, the resolution of dynamic variables has been delegated to a new helper function. This has the additional benefit that the loop labels are no longer required, making things a bit cleaner. Change-Id: If22b74c3d49c74bf3a1ec4497cb761a9ee6cf2a4 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6298 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-03 r/4629 feat(tvix/eval): implement runtime closure construction (OpClosure)Vincent Ambo1-1/+28
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>
2022-09-03 r/4628 feat(tvix/eval): implement OpGetUpvalue in the VMVincent Ambo1-4/+6
This resolves an upvalue at runtime by pushing it on the stack from the closure's upvalue vector. Change-Id: Ic3e7a7ecd9f7032f679114a1995e5bbf83062fcf Reviewed-on: https://cl.tvl.fyi/c/depot/+/6294 Tested-by: BuildkiteCI Reviewed-by: grfn <grfn@gws.fyi>