diff options
author | Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com> | 2021-07-05 13:23:00 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-05 13:23:00 +0300 |
commit | 66c339b0be0d544e092fa54047070a25d2d3b4f3 (patch) | |
tree | 284d71a22dcd1a82b5a97f9fbfa9924abd81371e /src/coreclr | |
parent | 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 (diff) |
Allow implicit widenings when tailcalling (#54864)
The managed calling convention dictates that the callee is responsible
for widening up to 4 bytes. We can use this to allow some more
tailcalls.
Fix #51957
Diffstat (limited to 'src/coreclr')
-rw-r--r-- | src/coreclr/jit/importer.cpp | 20 | ||||
-rw-r--r-- | src/coreclr/jit/morph.cpp | 13 |
2 files changed, 17 insertions, 16 deletions
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 66998450dbd..f65eb39fddc 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -7874,14 +7874,24 @@ bool Compiler::impTailCallRetTypeCompatible(var_types callerRetTy CORINFO_CLASS_HANDLE calleeRetTypeClass, CorInfoCallConvExtension calleeCallConv) { - // Note that we can not relax this condition with genActualType() as the - // calling convention dictates that the caller of a function with a small - // typed return value is responsible for normalizing the return val. + // Early out if the types are the same. if (callerRetType == calleeRetType) { return true; } + // For integral types the managed calling convention dictates that callee + // will widen the return value to 4 bytes, so we can allow implicit widening + // in managed to managed tailcalls when dealing with <= 4 bytes. + bool isManaged = + (callerCallConv == CorInfoCallConvExtension::Managed) && (calleeCallConv == CorInfoCallConvExtension::Managed); + + if (isManaged && varTypeIsIntegral(callerRetType) && varTypeIsIntegral(calleeRetType) && + (genTypeSize(callerRetType) <= 4) && (genTypeSize(calleeRetType) <= genTypeSize(callerRetType))) + { + return true; + } + // If the class handles are the same and not null, the return types are compatible. if ((callerRetTypeClass != nullptr) && (callerRetTypeClass == calleeRetTypeClass)) { @@ -8672,7 +8682,9 @@ var_types Compiler::impImportCall(OPCODE opcode, if (call->gtFlags & GTF_CALL_UNMANAGED) { // We set up the unmanaged call by linking the frame, disabling GC, etc - // This needs to be cleaned up on return + // This needs to be cleaned up on return. + // In addition, native calls have different normalization rules than managed code + // (managed calling convention always widens return values in the callee) if (canTailCall) { canTailCall = false; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 4ec1328f878..8f571377489 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -6804,18 +6804,7 @@ void Compiler::fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result) // structs being passed to either the caller or callee. // // Exceptions: -// 1) If the callee has structs which cannot be enregistered it will be -// reported as cannot fast tail call. This is an implementation limitation -// where the callee only is checked for non enregisterable structs. This is -// tracked with https://github.com/dotnet/runtime/issues/8492. -// -// 2) If the caller or callee has stack arguments and the callee has more -// arguments then the caller it will be reported as cannot fast tail call. -// This is due to a bug in LowerFastTailCall which assumes that -// nCalleeArgs <= nCallerArgs, which is always true on Windows Amd64. This -// is tracked with https://github.com/dotnet/runtime/issues/8413. -// -// 3) If the callee has a 9 to 16 byte struct argument and the callee has +// If the callee has a 9 to 16 byte struct argument and the callee has // stack arguments, the decision will be to not fast tail call. This is // because before fgMorphArgs is done, the struct is unknown whether it // will be placed on the stack or enregistered. Therefore, the conservative |