Maybe I misunderstand your idea?
Maybe I misunderstand your idea?
The trampoline would replace the {main retaddr, main fp} closure with {trampoline addr, [stack byte count to pop], main retaddr, main fp} and would pop some number of bytes, either hard-coded into the the trampoline function (so you get a bunch of them) or is part of the closure.
Do it right and there should only ever be one trampoline closure on the stack for any chain of tail calls, making the scheme O(1) in space.
So the whole protocol is that when the compiler recognizes that a call is a tail call, and one that can be turned into a jump instead of call, then the compiler must emit code to
a) pop the current continuation (which will either be the original or a trampoline that embeds the original, and from which the original can be recovered),
b) pop all the previous arguments and push all the new ones (possibly some are the same, so there is room for optimization here),
and
c) push a new continuation that is either the same as the previous current continuation or else a new continuation closure that is the correct trampoline corresponding to -and embedding- the original continuation, where the original is recovered from (a).
The trampoline recovers the original continuation, fixes the stack depth to what the caller expects, and executes a return to the original continuation.
For variadic functions it has to be the case that they have used `va_start()`, consumed all variadic arguments with `va_arg()`, then called `va_end()`, leaving no active copy of the `va_list`, then the compiler can arrange to keep a hidden local variable count of stack words used by the variadic arguments that it can use to implement the above protocol correctly.