1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# Mutually-recursive functions
Currently, lambda creation is generally compiled to an AllocClo IR instruction, which stores a list of values in the closure object.
If a set of lambdas f1,...,fN is mutually recursive, each needs a reference to the closures of some of the N functions in its own closure object; which exactly those are, is stored in the TVLambda by analyseValue (provided that all are brought into scope when analysing the lambdas).
The entire set of closures c1,...,cN can be successfully instantiated in memory by first stubbing out the closure entries referring to functions in the set with an RConst 0, and after all N closures have been created that way, replacing the zeros with REFERENCES TO the allocated closures.
1. Replacing an entry in a closure is not supported in the current IR instruction set; a new instruction will need to be added for that.
(Perhaps UpdateClo Ref Int Ref, where UpdateClo c i r updates the i'th entry in the closure referenced by c with the value referred to by r.)
2. The entries in the closure objects referring to other closures in the set need to be actual references, not copies of the closure objects referred to.
This means a new alternative for Ref will be needed, perhaps RPtr Ref, which has the semantics of resolving to a pointer to the value pointed to by the argument Ref.
Currently, the VM leaves memory allocation and GC up to the Haskell runtime, but when we're starting to work with explicit pointers to runtime values, and when we start allowing updating values to which pointers may exist (with UpdateClo in this case), it may be necessary to manually handle allocation and GC of runtime values.
GC of runtime values is hard, though, because with mutually recursive closures, we get cycles that reference counting will fail on.
This is actually also an issue that would be relevant when compiling to an actual native target.
# Singly-recursive functions
Most of the above discussion applies, except that the only reference cycle that will occur is a self-loop on the recursive closure object.
This is easily handled in the GC by not counting the self-reference, since the closure doesn't need to keep itself alive.
|