It’s not completely clear to me how this works, mainly because I’m not particularly familliar with setjmp/longjmp, so I’m hoping someone can check I understand it correctly.
The first time setjmp is called, it records basically the current state of the CPU (stack pointer and instruction pointer essentially, right?) and returns false because it hasn’t been jumped to using longjmp.
In scm_apply, if the stack depth is too large, the info needed to continue computing is recorded in current_fun and current_args and then longjmp is used to jump back to the place in the stack where the first setjmp was called (what does the 1 in the call to longjmp do?).
Now execution continues in scm_init in the evaluation of the if condition, but this time, setjmp returns true, so the body of the then clause is executed, resetting the stack frame depth (because we’ve jumped back in the stack and can happily overwrite everything that’s happened before because we’re using continuation passing and will never return to a calling function) and execution can continue using the function and args that were saved before the longjmp.
So basically every 100 applications, we reset the stack because we don’t have any real need for its ability to let us return to any calling function. If I understand this correctly… that’s pretty awesome =) The only issue (which is also brought up in the comments) is when is arg_list freed; I think the pieces to explain that are missing in this post, but one way I can see to do it is just to free the args list in every function before calling its continuation.
I’d love some feedback to see if I have understood this correctly and any other interesting uses for setjmp/longjmp.
Author here, you got it!
And yes, the reason why we don’t free arg_list in that function is that we need to toss it to the function we’re calling and since that never returns we can’t free it in scm_apply! The compiler generates code to free the argument list in though so it doesn’t leak.
Aside: I’m thinking that I really should change the lam_t functions to be a vararg function like scm_apply, since it let’s me avoid bouncing every single parameter through the heap.
Thanks for the reply! I’d love to see more detail on how you do this, it’s left me uncomfortable not seeing where the args are freed.
Definitely, I’m currently implementing a simple mark-and-sweep GC for scheme2c but afterwards I plan on writing a couple of posts on how the whole thing works since it is a bit tricky to understand some of the RTS without knowing what sort of code the compiler is generating.