From 6c19051273d0d666a758720bf88ab201be8f2ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sun, 14 Jun 2026 00:52:16 +0200 Subject: [PATCH 1/5] Track return type for shared generics via exact context --- src/coreclr/jit/fginline.cpp | 6 +-- src/coreclr/jit/gentree.cpp | 39 ++++++++++++++----- src/coreclr/jit/gentree.h | 4 +- src/coreclr/jit/importercalls.cpp | 15 +++---- src/coreclr/jit/inline.h | 4 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 7 ++-- .../JitInterface/CorInfoImpl.RyuJit.cs | 9 ++++- src/coreclr/vm/methodtable.cpp | 7 +++- 8 files changed, 61 insertions(+), 30 deletions(-) diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 089f8a4dab62d8..1e9af67b3da000 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -608,8 +608,8 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitorgtLateDevirtualizationInfo->methodHnd; - CORINFO_CONTEXT_HANDLE context = call->gtLateDevirtualizationInfo->exactContextHnd; + CORINFO_METHOD_HANDLE method = call->gtExactContextInfo->methodHnd; + CORINFO_CONTEXT_HANDLE context = call->gtExactContextInfo->exactContextHnd; InlineContext* inlinersContext = call->gtInlineContext; unsigned methodFlags = 0; const bool isLateDevirtualization = true; @@ -645,7 +645,7 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitorgtReturnType != TYP_VOID) { - DebugInfo di(call->gtInlineContext, call->gtLateDevirtualizationInfo->ilLocation); + DebugInfo di(call->gtInlineContext, call->gtExactContextInfo->ilLocation); Statement* stmt = m_compiler->gtNewStmt(call, di); m_compiler->fgInsertStmtBefore(m_compiler->compCurBB, m_curStmt, stmt); if (m_firstNewStmt == nullptr) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 1a9afef97e5f18..3b1f8339e305e6 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -9817,9 +9817,10 @@ GenTreeCall* Compiler::gtNewCallNode(gtCallTypes callType, #endif // UNIX_X86_ABI node->gtCallType = callType; INDEBUG_OR_WASM(node->callSig = nullptr;) - node->tailCallInfo = nullptr; - node->gtRetClsHnd = nullptr; - node->gtCallMoreFlags = GTF_CALL_M_EMPTY; + node->tailCallInfo = nullptr; + node->gtRetClsHnd = nullptr; + node->gtExactContextInfo = nullptr; + node->gtCallMoreFlags = GTF_CALL_M_EMPTY; INDEBUG(node->gtCallDebugFlags = GTF_CALL_MD_EMPTY); node->gtInlineInfoCount = 0; @@ -11431,8 +11432,8 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree) copy->gtInlineCandidateInfo = tree->gtInlineCandidateInfo; } - copy->gtInlineInfoCount = tree->gtInlineInfoCount; - copy->gtLateDevirtualizationInfo = tree->gtLateDevirtualizationInfo; + copy->gtInlineInfoCount = tree->gtInlineInfoCount; + copy->gtExactContextInfo = tree->gtExactContextInfo; copy->gtCallType = tree->gtCallType; copy->gtReturnType = tree->gtReturnType; @@ -20844,12 +20845,30 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b else if (call->gtCallType == CT_USER_FUNC) { // For user calls, we can fetch the approximate return - // type info from the method handle. Unfortunately - // we've lost the exact context, so this is the best - // we can do for now. + // type info from the method handle. CORINFO_METHOD_HANDLE method = call->gtCallMethHnd; - CORINFO_CLASS_HANDLE exactClass = nullptr; - CORINFO_SIG_INFO sig; + CORINFO_CLASS_HANDLE exactClass = NO_CLASS_HANDLE; + + ExactContextInfo* contextInfo = call->gtExactContextInfo; + if (contextInfo != nullptr) + { + SIZE_T contextType = (SIZE_T)contextInfo->exactContextHnd & CORINFO_CONTEXTFLAGS_MASK; + SIZE_T contextValue = (SIZE_T)contextInfo->exactContextHnd & ~CORINFO_CONTEXTFLAGS_MASK; + if (contextValue == 0) + { + // we don't have a valid context saved, ignore + } + else if (contextType == CORINFO_CONTEXTFLAGS_CLASS) + { + exactClass = CORINFO_CLASS_HANDLE(contextValue); + } + else + { + method = CORINFO_METHOD_HANDLE(contextValue); + } + } + + CORINFO_SIG_INFO sig; eeGetMethodSig(method, &sig, exactClass); if (sig.retType == CORINFO_TYPE_VOID) { diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 7f8e3d0108a0a1..468ebb1125c9e5 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -162,7 +162,7 @@ struct BasicBlock; enum BasicBlockFlags : uint64_t; struct InlineCandidateInfo; struct HandleHistogramProfileCandidateInfo; -struct LateDevirtualizationInfo; +struct ExactContextInfo; typedef unsigned short AssertionIndex; @@ -5897,7 +5897,7 @@ struct GenTreeCall final : public GenTree void* gtDirectCallAddress; // Used to pass direct call address between lower and codegen }; - LateDevirtualizationInfo* gtLateDevirtualizationInfo; // Always available for user virtual calls + ExactContextInfo* gtExactContextInfo; // Always available for user virtual/shared generic calls // expression evaluated after args are placed which determines the control target GenTree* gtControlExpr; // Applicable to any call type diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 73ae8326e07cde..43c06bcfbe6479 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -1287,16 +1287,17 @@ var_types Compiler::impImportCall(OPCODE opcode, impMarkInlineCandidate(call, exactContextHnd, exactContextNeedsRuntimeLookup, callInfo, compInlineContext); // If the call is virtual, extra information for possible use during late devirt inlining. - // - if (call->AsCall()->IsDevirtualizationCandidate(this)) + // Also save for shared generic returns so that we can know their types. + if (call->AsCall()->IsDevirtualizationCandidate(this) || + (sig->retTypeSigClass != nullptr && sig->retType != CORINFO_TYPE_BYREF && eeIsSharedInst(sig->retTypeSigClass))) { JITDUMP("\nSaving late devirtualization info for call [%06u]\n", dspTreeID(call->AsCall())); assert(call->AsCall()->gtInlineContext == impCurStmtDI.GetInlineContext()); - LateDevirtualizationInfo* const info = new (this, CMK_Inlining) LateDevirtualizationInfo; - info->methodHnd = callInfo->hMethod; - info->exactContextHnd = exactContextHnd; - info->ilLocation = impCurStmtDI.GetLocation(); - call->AsCall()->gtLateDevirtualizationInfo = info; + ExactContextInfo* const info = new (this, CMK_Inlining) ExactContextInfo; + info->methodHnd = callInfo->hMethod; + info->exactContextHnd = exactContextHnd; + info->ilLocation = impCurStmtDI.GetLocation(); + call->AsCall()->gtExactContextInfo = info; } } diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h index 595acc9ecb174f..77ff29c73b8fb3 100644 --- a/src/coreclr/jit/inline.h +++ b/src/coreclr/jit/inline.h @@ -627,11 +627,11 @@ struct InlineCandidateInfo : public HandleHistogramProfileCandidateInfo InlineContext* inlinersContext; }; -// LateDevirtualizationInfo +// ExactContextInfo // // Used to fill in missing contexts during late devirtualization. // -struct LateDevirtualizationInfo +struct ExactContextInfo { CORINFO_METHOD_HANDLE methodHnd; CORINFO_CONTEXT_HANDLE exactContextHnd; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index f36f5701b54275..aabaa0185cf0c9 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1222,11 +1222,10 @@ private void getMethodSig(CORINFO_METHOD_STRUCT_* ftn, CORINFO_SIG_INFO* sig, CO { method = ((ArrayType)type).GetArrayMethod(((ArrayMethod)method).Kind); } - else + else if (type is InstantiatedType instantiatedType && method.OwningType.HasSameTypeDefinition(instantiatedType)) { - Debug.Assert(type.HasSameTypeDefinition(method.OwningType)); Instantiation methodInst = method.Instantiation; - method = _compilation.TypeSystemContext.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), (InstantiatedType)type); + method = _compilation.TypeSystemContext.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), instantiatedType); if (methodInst.Length > 0) { method = method.MakeInstantiatedMethod(methodInst); @@ -3454,7 +3453,7 @@ private void getEEInfo(ref CORINFO_EE_INFO pEEInfoOut) #if READYTORUN pEEInfoOut.offsetOfDelegateInstance = (uint)pointerSize; // Delegate._target pEEInfoOut.offsetOfDelegateFirstTarget = 3 * (uint)pointerSize; // Delegate._methodPtr -#else +#else pEEInfoOut.offsetOfDelegateInstance = 2 * (uint)pointerSize; // Delegate._firstParameter pEEInfoOut.offsetOfDelegateFirstTarget = 3 * (uint)pointerSize; // Delegate._functionPointer #endif diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 185f3863a3eb8f..214b4ebd4b14cb 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -1482,7 +1482,14 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO // the method with a method the intrinsic expands into. If it's not the special intrinsic, // method stays unchanged. var methodIL = (MethodIL)HandleToObject((void*)pResolvedToken.tokenScope); - targetMethod = _compilation.ExpandIntrinsicForCallsite(targetMethod, methodIL.OwningMethod); + MethodDesc callsiteMethod = _compilation.ExpandIntrinsicForCallsite(targetMethod, methodIL.OwningMethod); + if (targetMethod != callsiteMethod) + { + Debug.Assert(!pResult->exactContextNeedsRuntimeLookup); + Debug.Assert(!callsiteMethod.HasInstantiation && !targetMethod.HasInstantiation); + pResult->contextHandle = contextFromType(callsiteMethod.OwningType); + targetMethod = callsiteMethod; + } // For multidim array Address method, we pretend the method requires a hidden instantiation argument // (even though it doesn't need one). We'll actually swap the method out for a differnt one with diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 935d282119e605..4cda536c4dbe24 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -6461,13 +6461,18 @@ Instantiation MethodTable::GetInstantiationOfParentClass(MethodTable *pWhichPare SUPPORTS_DAC; } CONTRACTL_END; - MethodTable * pMatchingParent = GetMethodTableMatchingParentClass(pWhichParent); if (pMatchingParent != NULL) { return pMatchingParent->GetInstantiation(); } + pMatchingParent = pWhichParent->GetMethodTableMatchingParentClass(this); + if (pMatchingParent != NULL) + { + return pMatchingParent->GetInstantiation(); + } + // The parameter should always be a parent class or the dynamic method // class. Since there is no bit on the dynamicclass methodtable to indicate // that it is the dynamic method methodtable, we simply check the debug name From 93ac82430ed74ca57fea29dbd869e67fedb21fc5 Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 14 Jun 2026 14:11:34 +0900 Subject: [PATCH 2/5] Update the exact context info after late devirtualization --- src/coreclr/jit/fginline.cpp | 7 +++++++ src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 7 ++++--- src/coreclr/vm/methodtable.cpp | 7 +------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 1e9af67b3da000..d6610c39826a76 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -624,6 +624,13 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitormethodHnd = method; + exactContextInfo->exactContextHnd = context; + exactContextInfo->ilLocation = call->gtExactContextInfo->ilLocation; + call->gtExactContextInfo = exactContextInfo; + CORINFO_CALL_INFO callInfo = {}; callInfo.hMethod = method; callInfo.methodFlags = methodFlags; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index aabaa0185cf0c9..f36f5701b54275 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1222,10 +1222,11 @@ private void getMethodSig(CORINFO_METHOD_STRUCT_* ftn, CORINFO_SIG_INFO* sig, CO { method = ((ArrayType)type).GetArrayMethod(((ArrayMethod)method).Kind); } - else if (type is InstantiatedType instantiatedType && method.OwningType.HasSameTypeDefinition(instantiatedType)) + else { + Debug.Assert(type.HasSameTypeDefinition(method.OwningType)); Instantiation methodInst = method.Instantiation; - method = _compilation.TypeSystemContext.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), instantiatedType); + method = _compilation.TypeSystemContext.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), (InstantiatedType)type); if (methodInst.Length > 0) { method = method.MakeInstantiatedMethod(methodInst); @@ -3453,7 +3454,7 @@ private void getEEInfo(ref CORINFO_EE_INFO pEEInfoOut) #if READYTORUN pEEInfoOut.offsetOfDelegateInstance = (uint)pointerSize; // Delegate._target pEEInfoOut.offsetOfDelegateFirstTarget = 3 * (uint)pointerSize; // Delegate._methodPtr -#else +#else pEEInfoOut.offsetOfDelegateInstance = 2 * (uint)pointerSize; // Delegate._firstParameter pEEInfoOut.offsetOfDelegateFirstTarget = 3 * (uint)pointerSize; // Delegate._functionPointer #endif diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 4cda536c4dbe24..935d282119e605 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -6461,13 +6461,8 @@ Instantiation MethodTable::GetInstantiationOfParentClass(MethodTable *pWhichPare SUPPORTS_DAC; } CONTRACTL_END; - MethodTable * pMatchingParent = GetMethodTableMatchingParentClass(pWhichParent); - if (pMatchingParent != NULL) - { - return pMatchingParent->GetInstantiation(); - } - pMatchingParent = pWhichParent->GetMethodTableMatchingParentClass(this); + MethodTable * pMatchingParent = GetMethodTableMatchingParentClass(pWhichParent); if (pMatchingParent != NULL) { return pMatchingParent->GetInstantiation(); From ef5897b94423bf513d435b60cc0158f5f3f40ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Mon, 15 Jun 2026 01:36:56 +0200 Subject: [PATCH 3/5] Format --- src/coreclr/jit/importercalls.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 43c06bcfbe6479..901d545a3c6e21 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -1289,7 +1289,8 @@ var_types Compiler::impImportCall(OPCODE opcode, // If the call is virtual, extra information for possible use during late devirt inlining. // Also save for shared generic returns so that we can know their types. if (call->AsCall()->IsDevirtualizationCandidate(this) || - (sig->retTypeSigClass != nullptr && sig->retType != CORINFO_TYPE_BYREF && eeIsSharedInst(sig->retTypeSigClass))) + (sig->retTypeSigClass != nullptr && sig->retType != CORINFO_TYPE_BYREF && + eeIsSharedInst(sig->retTypeSigClass))) { JITDUMP("\nSaving late devirtualization info for call [%06u]\n", dspTreeID(call->AsCall())); assert(call->AsCall()->gtInlineContext == impCurStmtDI.GetInlineContext()); From ffaa85f69f3dd51530b1355d66b3db0e1403fb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Mon, 15 Jun 2026 02:09:47 +0200 Subject: [PATCH 4/5] Cleanup, address review --- src/coreclr/jit/compiler.h | 13 +++++++------ src/coreclr/jit/ee_il_dll.hpp | 16 +++++++++++++++- src/coreclr/jit/fginline.cpp | 12 +++++++----- src/coreclr/jit/gentree.cpp | 18 +++++------------- src/coreclr/jit/gentree.h | 2 +- src/coreclr/jit/importercalls.cpp | 17 ++++++++++------- src/coreclr/jit/inline.h | 3 ++- 7 files changed, 47 insertions(+), 34 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index ee3c19c20256d5..299710022a5dd9 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9390,12 +9390,13 @@ class Compiler unsigned compMethodHash(CORINFO_METHOD_HANDLE methodHandle); - var_types eeGetArgType(CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig); - var_types eeGetArgType(CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig, bool* isPinned); - CORINFO_CLASS_HANDLE eeGetArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE list); - CORINFO_CLASS_HANDLE eeGetClassFromContext(CORINFO_CONTEXT_HANDLE context); - unsigned eeGetArgSize(CorInfoType corInfoType, CORINFO_CLASS_HANDLE typeHnd); - static unsigned eeGetArgSizeAlignment(var_types type, bool isFloatHfa); + var_types eeGetArgType(CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig); + var_types eeGetArgType(CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig, bool* isPinned); + CORINFO_CLASS_HANDLE eeGetArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE list); + CORINFO_CLASS_HANDLE eeGetClassFromContext(CORINFO_CONTEXT_HANDLE context); + CORINFO_METHOD_HANDLE eeGetMethodFromContext(CORINFO_CONTEXT_HANDLE context); + unsigned eeGetArgSize(CorInfoType corInfoType, CORINFO_CLASS_HANDLE typeHnd); + static unsigned eeGetArgSizeAlignment(var_types type, bool isFloatHfa); // VOM info, method sigs diff --git a/src/coreclr/jit/ee_il_dll.hpp b/src/coreclr/jit/ee_il_dll.hpp index a64c9ad9698acb..52a60427f96ead 100644 --- a/src/coreclr/jit/ee_il_dll.hpp +++ b/src/coreclr/jit/ee_il_dll.hpp @@ -150,16 +150,30 @@ inline CORINFO_CLASS_HANDLE Compiler::eeGetClassFromContext(CORINFO_CONTEXT_HAND return impInlineRoot()->info.compClassHnd; } + assert(context != nullptr); if (((SIZE_T)context & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS) { return CORINFO_CLASS_HANDLE((SIZE_T)context & ~CORINFO_CONTEXTFLAGS_MASK); } else { - return info.compCompHnd->getMethodClass(CORINFO_METHOD_HANDLE((SIZE_T)context & ~CORINFO_CONTEXTFLAGS_MASK)); + return info.compCompHnd->getMethodClass(eeGetMethodFromContext(context)); } } +/*****************************************************************************/ +inline CORINFO_METHOD_HANDLE Compiler::eeGetMethodFromContext(CORINFO_CONTEXT_HANDLE context) +{ + if (context == METHOD_BEING_COMPILED_CONTEXT()) + { + return impInlineRoot()->info.compMethodHnd; + } + + assert(context != nullptr); + assert(((SIZE_T)context & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_METHOD); + return CORINFO_METHOD_HANDLE((SIZE_T)context & ~CORINFO_CONTEXTFLAGS_MASK); +} + /***************************************************************************** * * Native Direct Optimizations diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index d6610c39826a76..6999683867b6d4 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -625,11 +625,13 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitormethodHnd = method; - exactContextInfo->exactContextHnd = context; - exactContextInfo->ilLocation = call->gtExactContextInfo->ilLocation; - call->gtExactContextInfo = exactContextInfo; + ExactContextInfo* const contextInfo = call->gtExactContextInfo != nullptr + ? call->gtExactContextInfo + : new (m_compiler, CMK_Inlining) ExactContextInfo; + contextInfo->methodHnd = method; + contextInfo->exactContextHnd = context; + contextInfo->ilLocation = call->gtExactContextInfo->ilLocation; + call->gtExactContextInfo = contextInfo; CORINFO_CALL_INFO callInfo = {}; callInfo.hMethod = method; diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 3b1f8339e305e6..33f8c33a5e3f61 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -20850,21 +20850,13 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b CORINFO_CLASS_HANDLE exactClass = NO_CLASS_HANDLE; ExactContextInfo* contextInfo = call->gtExactContextInfo; - if (contextInfo != nullptr) + if (contextInfo != nullptr && contextInfo->exactContextHnd != nullptr) { - SIZE_T contextType = (SIZE_T)contextInfo->exactContextHnd & CORINFO_CONTEXTFLAGS_MASK; - SIZE_T contextValue = (SIZE_T)contextInfo->exactContextHnd & ~CORINFO_CONTEXTFLAGS_MASK; - if (contextValue == 0) + exactClass = eeGetClassFromContext(contextInfo->exactContextHnd); + if (((SIZE_T)contextInfo->exactContextHnd & CORINFO_CONTEXTFLAGS_MASK) == + CORINFO_CONTEXTFLAGS_METHOD) { - // we don't have a valid context saved, ignore - } - else if (contextType == CORINFO_CONTEXTFLAGS_CLASS) - { - exactClass = CORINFO_CLASS_HANDLE(contextValue); - } - else - { - method = CORINFO_METHOD_HANDLE(contextValue); + method = eeGetMethodFromContext(contextInfo->exactContextHnd); } } diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 468ebb1125c9e5..cd6f92d28f4826 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -5897,7 +5897,7 @@ struct GenTreeCall final : public GenTree void* gtDirectCallAddress; // Used to pass direct call address between lower and codegen }; - ExactContextInfo* gtExactContextInfo; // Always available for user virtual/shared generic calls + ExactContextInfo* gtExactContextInfo; // Always available for user virtual/shared generic return calls // expression evaluated after args are placed which determines the control target GenTree* gtControlExpr; // Applicable to any call type diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 901d545a3c6e21..1056a3819d3350 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -1286,19 +1286,22 @@ var_types Compiler::impImportCall(OPCODE opcode, // Is it an inline candidate? impMarkInlineCandidate(call, exactContextHnd, exactContextNeedsRuntimeLookup, callInfo, compInlineContext); + assert(call->AsCall()->gtExactContextInfo == nullptr); + // If the call is virtual, extra information for possible use during late devirt inlining. - // Also save for shared generic returns so that we can know their types. + // Also save for shared generic returns so that we know their types. if (call->AsCall()->IsDevirtualizationCandidate(this) || (sig->retTypeSigClass != nullptr && sig->retType != CORINFO_TYPE_BYREF && eeIsSharedInst(sig->retTypeSigClass))) { - JITDUMP("\nSaving late devirtualization info for call [%06u]\n", dspTreeID(call->AsCall())); + JITDUMP("\nSaving exact context info for call [%06u]\n", dspTreeID(call->AsCall())); assert(call->AsCall()->gtInlineContext == impCurStmtDI.GetInlineContext()); - ExactContextInfo* const info = new (this, CMK_Inlining) ExactContextInfo; - info->methodHnd = callInfo->hMethod; - info->exactContextHnd = exactContextHnd; - info->ilLocation = impCurStmtDI.GetLocation(); - call->AsCall()->gtExactContextInfo = info; + + ExactContextInfo* const contextInfo = new (this, CMK_Inlining) ExactContextInfo; + contextInfo->methodHnd = callInfo->hMethod; + contextInfo->exactContextHnd = exactContextHnd; + contextInfo->ilLocation = impCurStmtDI.GetLocation(); + call->AsCall()->gtExactContextInfo = contextInfo; } } diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h index 77ff29c73b8fb3..dd872e685d16aa 100644 --- a/src/coreclr/jit/inline.h +++ b/src/coreclr/jit/inline.h @@ -629,7 +629,8 @@ struct InlineCandidateInfo : public HandleHistogramProfileCandidateInfo // ExactContextInfo // -// Used to fill in missing contexts during late devirtualization. +// Used to fill in missing contexts during late devirtualization and +// for getting the return type with shared generics. // struct ExactContextInfo { From e91c12de49c642b60b9be49c91e8421c1c289596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 15 Jun 2026 08:44:54 +0200 Subject: [PATCH 5/5] Update src/coreclr/jit/fginline.cpp Co-authored-by: Steve --- src/coreclr/jit/fginline.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 6999683867b6d4..66cfcbae4fb2a8 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -625,9 +625,7 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitorgtExactContextInfo != nullptr - ? call->gtExactContextInfo - : new (m_compiler, CMK_Inlining) ExactContextInfo; + ExactContextInfo* const contextInfo = new (m_compiler, CMK_Inlining) ExactContextInfo; contextInfo->methodHnd = method; contextInfo->exactContextHnd = context; contextInfo->ilLocation = call->gtExactContextInfo->ilLocation;