Implement portable tailcall helpers (#341)
authorJakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Tue, 28 Apr 2020 00:06:15 +0000 (02:06 +0200)
committerGitHub <noreply@github.com>
Tue, 28 Apr 2020 00:06:15 +0000 (17:06 -0700)
commite1ffadd6521b29db350e701b11d78a286ecd783a
treee5b44ed888b3f84ca224192ab98098cf95ef5ec4
parentd68342ceeb69adff35d5b3462091d339a700c1a5
Implement portable tailcall helpers (#341)

* Implement portable tailcall helpers

This implements tailcall-via-help support for all platforms supported by
the runtime. In this new mechanism the JIT asks the runtime for help
whenever it realizes it will need a helper to perform a tailcall, i.e.
when it sees an explicit tail. prefixed call that it cannot make into a
fast jump-based tailcall.

The runtime created two important IL stubs to help the JIT in performing
the necessary tailcalls. One IL stub is used to store the args for the
tailcall, while the other is used to dispatch the actual tailcall
itself. The JIT will then transform the call from

return tail. F(a1, ..., an);

to

IL_STUB_StoreTailCallArgs(a1, ..., an);
T result;
IL_STUB_DispatchTailCalls(..., &result);
return result;

The dispatcher is written in such a way that it is able to dispatch
multiple tailcalls in a row when tailcalled functions also perform
tailcalls. To do this, the JIT helps the dispatcher detect if the
caller's caller is also a dispatcher. When this is the case the
dispatcher returns to let the previous dispatcher perform the tailcall
with the currently stored args. This allows the frame to unwind and
ensures that sequences of tailcalls do not grow the stack more than by a
constant factor.

Due to this unwinding the args cannot be stored on the stack and are
instead stored in TLS. The GC is made specially of this buffer as the
args can be anything, including interior pointers.

The control-flow when performing the new tailcalls is nonstandard, so
this also changes the debugger to support proper stepping into/over/out
of tailcalled functions when they go through the new dispatcher.

x86's tailcalling mechanism does not change.

This commit also includes the following changes:

* Update design doc for helper-based tailcalls

* Change lowering of GT_LABEL on arm.

Generate movw/movt instead of adr on arm. adr on arm allows offsets
up to 4k, which may not be enough. In particular,
IL_STUB_CallTailCallTarget uses GT_LABEL before argument setup code
and it can be more than 4k.

* Add COMPlus_FastTailCalls environment variable.

COMPlus_FastTailCalls controls whether fast tail calls are allowed.
If COMPlus_FastTailCalls is 0, fast tail calls are not allowed even for
tail-prefixed calls. Only helper-based calls are allowed. This is useful
for testing helper-based calls.

Co-authored-by: Eugene Rozenfeld <erozen@microsoft.com>
72 files changed:
docs/design/features/tailcalls-with-helpers.md
src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
src/coreclr/src/System.Private.CoreLib/src/System/StubHelpers.cs
src/coreclr/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
src/coreclr/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
src/coreclr/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
src/coreclr/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
src/coreclr/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
src/coreclr/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
src/coreclr/src/debug/ee/controller.cpp
src/coreclr/src/debug/ee/frameinfo.cpp
src/coreclr/src/inc/corinfo.h
src/coreclr/src/inc/gcrefmap.h
src/coreclr/src/jit/assertionprop.cpp
src/coreclr/src/jit/codegenarmarch.cpp
src/coreclr/src/jit/codegencommon.cpp
src/coreclr/src/jit/codegenxarch.cpp
src/coreclr/src/jit/compiler.cpp
src/coreclr/src/jit/compiler.h
src/coreclr/src/jit/compiler.hpp
src/coreclr/src/jit/compmemkind.h
src/coreclr/src/jit/emit.cpp
src/coreclr/src/jit/emitxarch.cpp
src/coreclr/src/jit/gentree.cpp
src/coreclr/src/jit/gentree.h
src/coreclr/src/jit/importer.cpp
src/coreclr/src/jit/inline.def
src/coreclr/src/jit/jitconfigvalues.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
src/coreclr/src/tools/Common/JitInterface/CorInfoBase.cs
src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.Intrinsics.cs
src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs
src/coreclr/src/tools/Common/JitInterface/CorInfoTypes.cs
src/coreclr/src/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt
src/coreclr/src/tools/crossgen2/jitinterface/jitinterface.h
src/coreclr/src/tools/crossgen2/jitinterface/jitwrapper.cpp
src/coreclr/src/vm/CMakeLists.txt
src/coreclr/src/vm/crossgen/CMakeLists.txt
src/coreclr/src/vm/dllimport.h
src/coreclr/src/vm/ecalllist.h
src/coreclr/src/vm/gcenv.ee.cpp
src/coreclr/src/vm/ilstubcache.cpp
src/coreclr/src/vm/ilstubresolver.cpp
src/coreclr/src/vm/ilstubresolver.h
src/coreclr/src/vm/jitinterface.cpp
src/coreclr/src/vm/jitinterface.h
src/coreclr/src/vm/metasig.h
src/coreclr/src/vm/mscorlib.cpp
src/coreclr/src/vm/mscorlib.h
src/coreclr/src/vm/prestub.cpp
src/coreclr/src/vm/stubgen.cpp
src/coreclr/src/vm/stubgen.h
src/coreclr/src/vm/stubhelpers.cpp
src/coreclr/src/vm/stubhelpers.h
src/coreclr/src/vm/stubmgr.cpp
src/coreclr/src/vm/stubmgr.h
src/coreclr/src/vm/tailcallhelp.cpp [new file with mode: 0644]
src/coreclr/src/vm/tailcallhelp.h [new file with mode: 0644]
src/coreclr/src/vm/threads.cpp
src/coreclr/src/vm/threads.h
src/coreclr/src/zap/zapinfo.cpp
src/coreclr/src/zap/zapinfo.h
src/coreclr/tests/issues.targets
src/coreclr/tests/src/JIT/Directed/tailcall/more_tailcalls.cs
src/coreclr/tests/src/JIT/Directed/tailcall/more_tailcalls.il
src/coreclr/tests/src/JIT/Methodical/tailcall_v4/hijacking.il
src/coreclr/tests/src/JIT/opt/Tailcall/TailcallVerifyWithPrefix.il