Motivation: to make progress in the new compiler stack, we have
to preserve join points during lambda lifting. Right now, they are
lifted as regular lambdas. So, to keep them, we need some basic
support for them in the old VM. The implementation here is quick and
dirty. This is not an issue since this code will be deleted soon.
This is not a perfect solution yet. I have identified two problems:
1) The heuristic used at `mark_to_reset_fields` is too weak.
Suppose we have
```
let ...
_x := prod.mk field_1 0
in some _x
```
The current implementation will mark `field_1` to be reset since it
occurs in a non tail call `prod.mk field_1 0`. However, we can
assume `prod.mk field_1 0` is part of the return value.
2) The current semantics of the reset instruction may produce
unnecessary memory allocation. Consider the following example
```
let _x_1 := x.1,
_x_2 := x.2,
_x_3 := _reset.2 x
in @bool.cases_on y
(_cnstr.0.0)
(let _x_4 := f _x_2,
in _updt.2 _x_3 _x_4)
```
The memory cell `_x_3` is only used in the second branch of
the `bool.cases_on`. So, if `y` is `ff`, and `x` is a shared
object, we will allocate memory at `_reset.2 x` for nothing.
This commit also add two new instructions to the old VM: `updt` and
`updt_cidx`.
They allow us to test the new new memory reuse technique.
TODO: We need to reset fields after we project. Otherwise, we prevent
destructive updates on nested objects.
Remove transformations such as
```
prod.cases_on M (\fun a b, t)
```
==>
```
let a := M.0 in
let b := M.1 in
t
```
We will perform this kind of transformation in a later stage.
Motivation: explicit control flow graph
TODO: disabled `split_entries` for now.
I believe the new feature exposed a bug at `move_to_entries`.
I will fix this new issue in another commit.
We were getting assertion violations when compiling corelib in debug
mode. There were two problems:
1- We were not capturing free variables occurring in types.
2- A paremeter could depend on a free variable associated with a let-declaration.
This transformation is useful for caching the construction of closures
at runtime. For example, consider the following piece of code
```
λ α a,
@tree.cases_on a visit._main._lambda_1
(λ a_a a_a_1 a_a_2,
let _x_1 := visit._main ◾ a_a,
_x_2 := visit._main._lambda_5 a_a_1 a_a_2
in bind._main ◾◾◾◾ _x_1 _x_2)
```
where `visit._main._lambda_1` is of the form
```
visit._main._lambda_1 :=
λ _x, ...
```
At runtime, we will create a closure object for `visit._main._lambda_1`
since it has arity 1, but no arguments have been provided.
This commit implements a new transformation that creates an auxiliary
declaration with arity 0.
```
visit._main._closed_1 :=
visit._main._lambda_1
```
Its value is cached by the runtime. That is, the closure is created only
once.
@kha This optimizations reduces the number of closures by another 200k
at `parser1.lean`. We are now under 2million :)