Recognition of tail calls.
An
Icall instruction that stores its result in register
rreg
can be turned into a tail call if:
-
its successor is a Ireturn None or Ireturn (Some rreg) instruction;
-
the stack block of the current function is empty: stacksize = 0.
If the current function had a non-empty stack block, it could be that
the called function accesses it, for instance if a pointer into the
stack block is passed as an argument. In this case, it would be
semantically incorrect to deallocate the stack block before the call,
as
Itailcall does. Therefore, the optimization can only be performed if
the stack block of the current function is empty, in which case it makes
no semantic difference to deallocate it before or after the call.
Another complication is that the
Ireturn instruction does not, in general,
follow immediately the
Icall instruction: the RTL code generator
can have inserted moves and no-ops between these instructions.
The general pattern we recognize is therefore:
r1 <- call(....)
nop
r2 <- r1
r3 <- r2
return r3
The
is_return function below recognizes this pattern.
The code transformation is straightforward: call instructions
followed by an appropriate nop/move/return sequence become
tail calls; other instructions are unchanged.
To ensure that the resulting RTL code is well typed, we
restrict the transformation to the cases where a tail call is
allowed by the calling conventions, and where the result signatures
match.
A function is transformed only if its stack block is empty,
as explained above.