Remove tailcall limitations on unix64 and arm64 (dotnet/coreclr#26255)
authorJakob Botsch Nielsen <t-janie@microsoft.com>
Fri, 20 Sep 2019 21:06:59 +0000 (14:06 -0700)
committerJarret Shook <jashoo@microsoft.com>
Fri, 20 Sep 2019 21:06:59 +0000 (14:06 -0700)
commitd89ab9d0e1ec21f6e82ff77c008c7d41502d7dd9
tree7f1d6eb987b389b7c32332d9a0208974821d3025
parenta3b421fd6f7f2fd26f7a50f1d83c3b9a700bdb4b
Remove tailcall limitations on unix64 and arm64 (dotnet/coreclr#26255)

* Remove tailcall limitations on unix64 and arm64

Fast tailcalls have their arguments passed in the incoming argument area
of the caller. This can cause problems when a previous argument might
end up overwriting the stack slot for an incoming argument that is later
used. To resolve this problem, we have logic that detects and introduces
temps in this situation. This logic was originally written for Windows
x64 where it is simple to know what argument is being overwritten, since
every argument always takes up a single slot on the stack. I.e. we know
that outgoing argument 7 can only overwrite incoming argument 7.

On unix x64 and arm64 this assumption does not hold. We previously tried
to workaround this by limiting our fast tailcalls to simple situations
where this assumption held, but this caused many missed fast tailcall
opportunities (for example, when arguments requires two slots or more).

This change removes those limitations. Instead of finding the argument
overwritten using the argument index, it keeps track of which stack
slots are used by each incoming and outgoing argument, allowing us to
more robustly check if an outgoing argument will overwrite an incoming
argument that will be used later.

To do this, we need to set the stack offset during init of args so that
we can use this info to determine whether it is necessary to introduce
temps for fast tailcalls. For arm64 we now define
FEATURE_PUT_STRUCT_ARG_STK to have access to the number of slots in
PUTARG_STK needed for this transformation.

There are also some corner cases we must consider. Since arguments now
consume multiple stack slots we can no longer move them with a single
atomic move instruction. Thus it is possible for us to get into cases
where we need to move an argument that is larger than 8 bytes and where
the move overlaps. This is a problem because codegen cannot handle
partially overlapping struct copies. We see this on unix64 in the
following case (assuming all args are on the stack):
void callee(S16 a, S32 b) { ... }
void caller(S32 a) { callee(default, a); }

Here 'caller' will need to move 'a' 16 bytes ahead in the arg list, and
we thus need a temp because we cannot do this atomically. Fix this by
detecting the partially overlapping case and looking for uses of the arg
from the current PUTARG_STK node's operand (instead of only starting
after).

* Fix formatting

Commit migrated from https://github.com/dotnet/coreclr/commit/908cc72d54cf205107348d0212c9e6d5bd3945bc
src/coreclr/src/jit/codegenlinear.cpp
src/coreclr/src/jit/compiler.h
src/coreclr/src/jit/jit.h
src/coreclr/src/jit/lclvars.cpp
src/coreclr/src/jit/lower.cpp
src/coreclr/src/jit/lower.h
src/coreclr/src/jit/morph.cpp