Expand description
A call in tail position, as a jump once the frame is given back.
Design: spec/optimizer/25-tail-calls.md section 25.2, the sibling call.
return f(x) needs nothing of the caller once f is running, so the caller can give its frame
back first and jump to f, and f returns straight to whoever called the caller. That saves a
call and a return, and it makes a chain of calls like that run in the same stack however long
it is, which is what a state machine written as functions calling each other relies on. gcc does
it at -O2 and -Os, and so does this.
§Three places
mark works on the IR, before selection. It turns a direct call whose results are exactly
what the block returns next into a tail_call, which ends the block the way a return did, and
it turns down the whole function when the callee could see something of the caller’s frame. Its
answer is what refusal says, a reason rather than a no.
crate::lower builds a tail_call as the call and the return it stands for, and writes the
call down as a Tail when the convention put every argument in a register. A call that needs
the argument area is left as the call it was: the area is the bottom of this function’s frame,
and the frame is gone by the time the callee would read it.
jumps runs last, on machine code with every register handed out and the epilogue written.
Each Tail whose block goes straight from the call to the epilogue, with nothing in between
touching a register the call reads or writes, loses its call, and the ret at the end becomes a
jmp to the callee. Anything else stays a call and a ret, which is right, just not as short.
§Why the frame check is this strict
The one thing that makes a tail call wrong is a pointer into the frame that is given back, and
the section above asks for “provably not reachable” rather than “not known to be”. The only
addresses into a frame the IR has are the ones an alloca makes, so a function with no alloca
has nothing a pointer could point at, and that is the rule. It turns down functions an escape
analysis would let through, which costs a few calls and nothing else.
Structs§
- Tail
- One call
crate::lowerbuilt for atail_call, and the pseudos that leave its answer where the caller’s answer goes.