Skip to content
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
fbff8d6
Rename wasArrayInterfaceDevirt to needsMethodContext
hez2010 Nov 27, 2025
729b269
Devirtualize generic virtual methods
hez2010 Nov 27, 2025
5c15b23
Disable GVM devirt for AOT
hez2010 Nov 27, 2025
6e678a5
Unblock late devirt as well
hez2010 Nov 27, 2025
c488b84
Set single-def
hez2010 Nov 27, 2025
0ca8b93
Address feedbacks
hez2010 Nov 27, 2025
b396d26
Merge branch 'main' into gvm-devirt-2
hez2010 Nov 27, 2025
0ac9d6d
Meh
hez2010 Nov 27, 2025
8976d4a
JIT format
hez2010 Nov 27, 2025
8d0845e
JIT format again
hez2010 Nov 27, 2025
1d389f1
Check methHndArg
hez2010 Nov 28, 2025
217c9f2
Rework how inst param being embedded
hez2010 Nov 29, 2025
4b64c2e
Bail out GVM devirt in managed type system instead
hez2010 Nov 29, 2025
e8668a1
Remove the out-dated comments
hez2010 Nov 29, 2025
951c4ea
JIT format
hez2010 Nov 29, 2025
a1d605f
generic context is not always needed
hez2010 Nov 29, 2025
a941aec
Check isInstantiatingStub for the necessity of generic method context
hez2010 Nov 29, 2025
9dd04f0
Minor refactor
hez2010 Nov 30, 2025
e84df69
Use the wrapper entrypoint instead of the instantiating stub
hez2010 Nov 30, 2025
17392c0
JIT format
hez2010 Nov 30, 2025
9847c97
Always use instantiatingStub
hez2010 Nov 30, 2025
7896ef5
Properly handle shared generics on both class and method
hez2010 Nov 30, 2025
905b11d
Update comments
hez2010 Nov 30, 2025
ff217cc
Merge branch 'main' into gvm-devirt-2
hez2010 Dec 1, 2025
521965e
Add tests to cover all kinds of generic virtual methods
hez2010 Dec 4, 2025
51a8026
Merge branch 'main' into gvm-devirt-2
hez2010 Dec 5, 2025
3b0a0fd
Apply suggestions from code review
hez2010 Dec 5, 2025
6815bff
Test improvements
hez2010 Dec 5, 2025
2ad226e
Merge branch 'main' into gvm-devirt-2
hez2010 Dec 10, 2025
c765506
Address review feedbacks
hez2010 Dec 10, 2025
2e1c733
Address another feedback
hez2010 Dec 10, 2025
e52fb65
Bump JIT-EE interface version
hez2010 Dec 10, 2025
9603022
Address an assertion
hez2010 Dec 10, 2025
ac3b74c
Rename to needsMethodContext
hez2010 Dec 11, 2025
ed99ebc
Never return an instantiation stub
hez2010 Dec 11, 2025
babad30
Do not create an instantiating stub
hez2010 Dec 11, 2025
036e446
Merge branch 'main' into gvm-devirt-2
hez2010 Dec 11, 2025
e22692c
Always set needsMethodContext and isInstantiatingStub to false
hez2010 Dec 12, 2025
d2af9f4
Merge branch 'main' into gvm-devirt-2
hez2010 Dec 21, 2025
40d7569
Merge branch 'main' into gvm-devirt-2
hez2010 Jan 6, 2026
7b4c2c8
Address the crossgen2 assertion
hez2010 Jan 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1540,7 +1540,7 @@ enum CORINFO_DEVIRTUALIZATION_DETAIL
{
CORINFO_DEVIRTUALIZATION_UNKNOWN, // no details available
CORINFO_DEVIRTUALIZATION_SUCCESS, // devirtualization was successful
CORINFO_DEVIRTUALIZATION_FAILED_CANON, // object class was canonical
CORINFO_DEVIRTUALIZATION_FAILED_CANON, // object class or method was canonical
CORINFO_DEVIRTUALIZATION_FAILED_COM, // object class was com
CORINFO_DEVIRTUALIZATION_FAILED_CAST, // object class could not be cast to interface class
CORINFO_DEVIRTUALIZATION_FAILED_LOOKUP, // interface method could not be found
Expand All @@ -1556,6 +1556,7 @@ enum CORINFO_DEVIRTUALIZATION_DETAIL
CORINFO_DEVIRTUALIZATION_FAILED_DUPLICATE_INTERFACE, // crossgen2 virtual method algorithm and runtime algorithm differ in the presence of duplicate interface implementations
CORINFO_DEVIRTUALIZATION_FAILED_DECL_NOT_REPRESENTABLE, // Decl method cannot be represented in R2R image
CORINFO_DEVIRTUALIZATION_FAILED_TYPE_EQUIVALENCE, // Support for type equivalence in devirtualization is not yet implemented in crossgen2
CORINFO_DEVIRTUALIZATION_FAILED_GENERIC_VIRTUAL, // Devirtualization of generic virtual methods is not yet implemented in crossgen2
CORINFO_DEVIRTUALIZATION_COUNT, // sentinel for maximum value
};

Expand All @@ -1578,7 +1579,7 @@ struct CORINFO_DEVIRTUALIZATION_INFO
// - If pResolvedTokenDevirtualizedMethod is not set to NULL and targeting an R2R image
// use it as the parameter to getCallInfo
// - isInstantiatingStub is set to TRUE if the devirtualized method is a generic method instantiating stub
// - wasArrayInterfaceDevirt is set TRUE for array interface method devirtualization
// - needsMethodContext is set TRUE if the devirtualized method may require a method context
// (in which case the method handle and context will be a generic method)
//
CORINFO_METHOD_HANDLE devirtualizedMethod;
Expand All @@ -1587,7 +1588,7 @@ struct CORINFO_DEVIRTUALIZATION_INFO
CORINFO_RESOLVED_TOKEN resolvedTokenDevirtualizedMethod;
CORINFO_RESOLVED_TOKEN resolvedTokenDevirtualizedUnboxedMethod;
bool isInstantiatingStub;
bool wasArrayInterfaceDevirt;
bool needsMethodContext;
};

//----------------------------------------------------------------------------
Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/inc/jiteeversionguid.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@

#include <minipal/guid.h>

constexpr GUID JITEEVersionIdentifier = { /* d1188f9b-d81e-487a-b525-cedb10d15c8e */
0xd1188f9b,
0xd81e,
0x487a,
{0xb5, 0x25, 0xce, 0xdb, 0x10, 0xd1, 0x5c, 0x8e}
constexpr GUID JITEEVersionIdentifier = { /* cc48149b-7cb0-404a-9e9a-3d1623bb6194 */
0xcc48149b,
0x7cb0,
0x404a,
{0x9e, 0x9a, 0x3d, 0x16, 0x23, 0xbb, 0x61, 0x94}
};

#endif // JIT_EE_VERSIONING_GUID_H
2 changes: 2 additions & 0 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10590,6 +10590,8 @@ const char* Compiler::devirtualizationDetailToString(CORINFO_DEVIRTUALIZATION_DE
return "Decl method cannot be represented in R2R image";
case CORINFO_DEVIRTUALIZATION_FAILED_TYPE_EQUIVALENCE:
return "Support for type equivalence in devirtualization is not yet implemented in crossgen2";
case CORINFO_DEVIRTUALIZATION_FAILED_GENERIC_VIRTUAL:
return "Devirtualization of generic virtual methods is not yet implemented in crossgen2";
default:
return "undefined";
}
Expand Down
35 changes: 5 additions & 30 deletions src/coreclr/jit/fginline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,32 +544,6 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitor<Substi
}
#endif // FEATURE_MULTIREG_RET

CORINFO_METHOD_HANDLE GetMethodHandle(GenTreeCall* call)
{
assert(call->IsDevirtualizationCandidate(m_compiler));
if (call->IsVirtual())
{
return call->gtCallMethHnd;
}
else
{
GenTree* runtimeMethHndNode =
call->gtCallAddr->AsCall()->gtArgs.FindWellKnownArg(WellKnownArg::RuntimeMethodHandle)->GetNode();
assert(runtimeMethHndNode != nullptr);
switch (runtimeMethHndNode->OperGet())
{
case GT_RUNTIMELOOKUP:
return runtimeMethHndNode->AsRuntimeLookup()->GetMethodHandle();
case GT_CNS_INT:
return CORINFO_METHOD_HANDLE(runtimeMethHndNode->AsIntCon()->IconValue());
default:
assert(!"Unexpected type in RuntimeMethodHandle arg.");
return nullptr;
}
return nullptr;
}
}

//------------------------------------------------------------------------
// LateDevirtualization: re-examine calls after inlining to see if we
// can do more devirtualization
Expand Down Expand Up @@ -612,9 +586,9 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitor<Substi

if (tree->OperIs(GT_CALL))
{
GenTreeCall* call = tree->AsCall();
// TODO-CQ: Drop `call->gtCallType == CT_USER_FUNC` once we have GVM devirtualization
bool tryLateDevirt = call->IsDevirtualizationCandidate(m_compiler) && (call->gtCallType == CT_USER_FUNC);
GenTreeCall* call = tree->AsCall();
CORINFO_METHOD_HANDLE method = NO_METHOD_HANDLE;
bool tryLateDevirt = call->IsDevirtualizationCandidate(m_compiler, &method);

#ifdef DEBUG
tryLateDevirt = tryLateDevirt && (JitConfig.JitEnableLateDevirtualization() == 1);
Expand All @@ -632,11 +606,12 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitor<Substi

CORINFO_CONTEXT_HANDLE context = call->gtLateDevirtualizationInfo->exactContextHnd;
InlineContext* inlinersContext = call->gtLateDevirtualizationInfo->inlinersContext;
CORINFO_METHOD_HANDLE method = GetMethodHandle(call);
unsigned methodFlags = 0;
const bool isLateDevirtualization = true;
const bool explicitTailCall = call->IsTailPrefixedCall();

assert(method != NO_METHOD_HANDLE);

CORINFO_CONTEXT_HANDLE contextInput = context;
context = nullptr;
m_compiler->impDevirtualizeCall(call, nullptr, &method, &methodFlags, &contextInput, &context,
Expand Down
44 changes: 39 additions & 5 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2367,16 +2367,50 @@ int GenTreeCall::GetNonStandardAddedArgCount(Compiler* compiler) const
// is devirtualized.
//
// Arguments:
// compiler - the compiler instance so that we can call eeFindHelper
// compiler - [In] the compiler instance so that we can call eeFindHelper
// pMethHandle - [Out] the method handle if the call is a devirtualization candidate
//
// Return Value:
// Returns true if this GT_CALL node is a devirtualization candidate.
//
bool GenTreeCall::IsDevirtualizationCandidate(Compiler* compiler) const
bool GenTreeCall::IsDevirtualizationCandidate(Compiler* compiler, CORINFO_METHOD_HANDLE* pMethHandle) const
{
return IsVirtual() ||
(gtCallType == CT_INDIRECT && (gtCallAddr->IsHelperCall(compiler, CORINFO_HELP_VIRTUAL_FUNC_PTR) ||
gtCallAddr->IsHelperCall(compiler, CORINFO_HELP_GVMLOOKUP_FOR_SLOT)));
CORINFO_METHOD_HANDLE methHandleToDevirt = NO_METHOD_HANDLE;
bool isDevirtCandidate = false;

if (IsVirtual() && gtCallType == CT_USER_FUNC)
{
methHandleToDevirt = gtCallMethHnd;
isDevirtCandidate = true;
}
else if (IsGenericVirtual(compiler) && (JitConfig.JitEnableGenericVirtualDevirtualization() != 0))
{
GenTree* runtimeMethHndNode =
gtCallAddr->AsCall()->gtArgs.FindWellKnownArg(WellKnownArg::RuntimeMethodHandle)->GetNode();
assert(runtimeMethHndNode != nullptr);
switch (runtimeMethHndNode->OperGet())
{
case GT_RUNTIMELOOKUP:
methHandleToDevirt = runtimeMethHndNode->AsRuntimeLookup()->GetMethodHandle();
isDevirtCandidate = true;
break;
case GT_CNS_INT:
methHandleToDevirt = CORINFO_METHOD_HANDLE(runtimeMethHndNode->AsIntCon()->gtCompileTimeHandle);
isDevirtCandidate = true;
break;
default:
// Unable to get method handle for devirtualization.
// This can happen if the method handle is not an RUNTIMELOOKUP or CNS_INT for generic virtuals,
break;
}
}

if (pMethHandle)
{
*pMethHandle = methHandleToDevirt;
}

return isDevirtCandidate;
}

//-------------------------------------------------------------------------
Expand Down
7 changes: 6 additions & 1 deletion src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -5260,8 +5260,13 @@ struct GenTreeCall final : public GenTree
{
return (gtFlags & GTF_CALL_VIRT_KIND_MASK) == GTF_CALL_VIRT_VTABLE;
}
bool IsGenericVirtual(Compiler* compiler) const
{
return (gtCallType == CT_INDIRECT && (gtCallAddr->IsHelperCall(compiler, CORINFO_HELP_VIRTUAL_FUNC_PTR) ||
gtCallAddr->IsHelperCall(compiler, CORINFO_HELP_GVMLOOKUP_FOR_SLOT)));
}

bool IsDevirtualizationCandidate(Compiler* compiler) const;
bool IsDevirtualizationCandidate(Compiler* compiler, CORINFO_METHOD_HANDLE* pMethHandle = nullptr) const;

bool IsInlineCandidate() const
{
Expand Down
Loading
Loading