I'm having trouble understanding the Stack manipulation needed in order to implement Tail call in assembly language.
When we have a Tail call to function We basically want to override the current Activation Frame with the Activation Frame of the called function.
I'm Having trouble with understanding the process that is needed to take place in order to accomplish this switch of Activation Frames.
How does the RSP changes in order to be in the right address and how to I keep the RBP of the originial calling function from changing?
You just restore the the point that RSP is pointing at a return address (e.g.
popany registers you saved earlier, such as RBP), until the state of stack memory is exactly like on entry to this function.Therefore a
jmpto another function will work as if your caller had called that function instead.(Except you can put different values in arg-passing registers, and in arg-passing space on the stack if there is any.)
In the simplest case, the entire function body is just
jmp foo, like for https://godbolt.org/z/v57MG6fqWUsing that Godbolt link, you can add stuff like
volatile int a = 1;to see how a C compiler uses stack space in a function that eventually tail-calls. Compile with-mno-red-zoneand/or-fno-omit-frame-pointeras well as-O3to see more of a function epilogue.In general, you run a normal function epilogue just like if you were going to return, and then you replace
call/retwithjmp. The trick is being able to delay acallto this point, after you've cleaned up the function's stack frame. Any args should already be computed and in arg-passing registers or stack locations.Of course, a
call/retwouldn't truly be legal at this point on x86-64 because the stack alignment is wrong:RSP % 16 == 8on function entry, rather than0before acall. Similarly for Windows x64 shadow space.