diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 17d13b362d04cf..bc5920d55f2762 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -717,11 +717,18 @@ Compiler::fgWalkResult Compiler::fgLateDevirtualization(GenTree** pTree, fgWalkD } #endif // DEBUG + CORINFO_CONTEXT_HANDLE context = nullptr; CORINFO_METHOD_HANDLE method = call->gtCallMethHnd; unsigned methodFlags = 0; - CORINFO_CONTEXT_HANDLE context = nullptr; const bool isLateDevirtualization = true; - bool explicitTailCall = (call->AsCall()->gtCallMoreFlags & GTF_CALL_M_EXPLICIT_TAILCALL) != 0; + const bool explicitTailCall = call->IsTailPrefixedCall(); + + if ((call->gtCallMoreFlags & GTF_CALL_M_LATE_DEVIRT) != 0) + { + context = call->gtLateDevirtualizationInfo->exactContextHnd; + call->gtLateDevirtualizationInfo = nullptr; + } + comp->impDevirtualizeCall(call, nullptr, &method, &methodFlags, &context, nullptr, isLateDevirtualization, explicitTailCall); *madeChanges = true; diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 206b326ed6b7b5..e82e5420da5bcf 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -6143,7 +6143,7 @@ GenTree* Compiler::gtNewInlineCandidateReturnExpr(GenTree* inlineCandidate, var_ if (varTypeIsStruct(inlineCandidate) && !inlineCandidate->OperIsBlkOp()) { - node->AsRetExpr()->gtRetClsHnd = gtGetStructHandle(inlineCandidate); + node->gtRetClsHnd = gtGetStructHandle(inlineCandidate); } // GT_RET_EXPR node eventually might be bashed back to GT_CALL (when inlining is aborted for example). @@ -7798,7 +7798,7 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree, else { copy->gtCallMethHnd = tree->gtCallMethHnd; - copy->gtInlineCandidateInfo = nullptr; + copy->gtInlineCandidateInfo = tree->gtInlineCandidateInfo; } copy->gtCallType = tree->gtCallType; diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 19dea944d41154..43551308dd99ac 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -155,6 +155,7 @@ enum BasicBlockFlags : unsigned __int64; struct InlineCandidateInfo; struct GuardedDevirtualizationCandidateInfo; struct ClassProfileCandidateInfo; +struct LateDevirtualizationInfo; typedef unsigned short AssertionIndex; @@ -3760,7 +3761,7 @@ enum GenTreeCallFlags : unsigned int GTF_CALL_M_EXP_RUNTIME_LOOKUP = 0x02000000, // this call needs to be tranformed into CFG for the dynamic dictionary expansion feature. GTF_CALL_M_STRESS_TAILCALL = 0x04000000, // the call is NOT "tail" prefixed but GTF_CALL_M_EXPLICIT_TAILCALL was added because of tail call stress mode GTF_CALL_M_EXPANDED_EARLY = 0x08000000, // the Virtual Call target address is expanded and placed in gtControlExpr in Morph rather than in Lower - + GTF_CALL_M_LATE_DEVIRT = 0x10000000, // this call has late devirtualzation info }; inline constexpr GenTreeCallFlags operator ~(GenTreeCallFlags a) @@ -4742,7 +4743,7 @@ struct GenTreeCall final : public GenTree InlineCandidateInfo* gtInlineCandidateInfo; GuardedDevirtualizationCandidateInfo* gtGuardedDevirtualizationCandidateInfo; ClassProfileCandidateInfo* gtClassProfileCandidateInfo; - + LateDevirtualizationInfo* gtLateDevirtualizationInfo; CORINFO_GENERIC_HANDLE compileTimeHelperArgumentHandle; // Used to track type handle argument of dynamic helpers void* gtDirectCallAddress; // Used to pass direct call address between lower and codegen }; diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 604b5450b0abbc..a5bf33151362ed 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -9950,6 +9950,22 @@ var_types Compiler::impImportCall(OPCODE opcode, } else { + // If the call is virtual, and has a generics context, and is not going to have a class probe, + // record the context for possible use during late devirt. + // + // If we ever want to devirt at Tier0, and/or see issues where OSR methods under PGO lose + // important devirtualizations, we'll want to allow both a class probe and a captured context. + // + if (origCall->IsVirtual() && (origCall->gtCallType != CT_INDIRECT) && (exactContextHnd != nullptr) && + (origCall->gtClassProfileCandidateInfo == nullptr)) + { + JITDUMP("\nSaving context %p for call [%06u]\n", exactContextHnd, dspTreeID(origCall)); + origCall->gtCallMoreFlags |= GTF_CALL_M_LATE_DEVIRT; + LateDevirtualizationInfo* const info = new (this, CMK_Inlining) LateDevirtualizationInfo; + info->exactContextHnd = exactContextHnd; + origCall->gtLateDevirtualizationInfo = info; + } + if (isFatPointerCandidate) { // fatPointer candidates should be in statements of the form call() or var = call(). diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h index dac99bd52f1401..9eacfb34a15138 100644 --- a/src/coreclr/jit/inline.h +++ b/src/coreclr/jit/inline.h @@ -591,6 +591,15 @@ struct InlineCandidateInfo : public GuardedDevirtualizationCandidateInfo InlineContext* inlinersContext; }; +// LateDevirtualizationInfo +// +// Used to fill in missing contexts during late devirtualization. +// +struct LateDevirtualizationInfo +{ + CORINFO_CONTEXT_HANDLE exactContextHnd; +}; + // InlArgInfo describes inline candidate argument properties. struct InlArgInfo