I’m trying to make my way through Implementing Lazy Functional Languages on Stock Hardware: The Spineless Tagless G-machine. I’m stuck at compilation of case expressions.
Suppose I need to generate native code for such STG expression:
case <x> of
Cons y ys -> <z>
Simply speaking I would need to
- Push continuation on the stack.
- Evaluate subexpression x. It will eventually jump to the continuation.
- In the continuation save local environment (y and ys)
- Evaluate subexpression z.
What I can’t figure out is how local environment should be saved. In 9.4.1 the paper suggests that it can be pushed onto the stack. But then it must be cleaned after step 4 and it’s not obvious how. One way I came up with is to push another intermediate continuation before step 4 with sole purpose of cleaning stack of values pushed at step 3 after step 4 and then it would jump to the next “real” continuation. But that seems rather inefficient.
In 9.2.2 the paper explains that the code for applications is supposed to clean the stack before pushing new arguments onto the stack. If <z> is an application then it could clean the stack not only from the arguments but from case-binded variables too. But what if <z> is not an application? Does it mean that all other types of expressions such as literal, constructor application, primitive operation need to clean stack too?
I tried to see what the real compiler does and it seems to be something different from what this paper describes. If I understood C-- correctly it calls functions normally instead of going through continuation mechanism (“tiny interpreter” 6.2.2). The usual stack is used to preserve local environment and it’s cleaned when function returns so there’s no need for manual bookkeeping in this case.