Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,8 @@ enum CorInfoHelpFunc
CORINFO_HELP_DELEGATEPROFILE64, // Update 64-bit method profile for a delegate call site
CORINFO_HELP_VTABLEPROFILE32, // Update 32-bit method profile for a vtable call site
CORINFO_HELP_VTABLEPROFILE64, // Update 64-bit method profile for a vtable call site
CORINFO_HELP_GENERICVIRTUALPROFILE32, // Update 32-bit method profile for a generic virtual call site
CORINFO_HELP_GENERICVIRTUALPROFILE64, // Update 64-bit method profile for a generic virtual call site
CORINFO_HELP_COUNTPROFILE32, // Update 32-bit block or edge count profile
CORINFO_HELP_COUNTPROFILE64, // Update 64-bit block or edge count profile
CORINFO_HELP_VALUEPROFILE32, // Update 32-bit value profile
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 = { /* 0d18a7df-af1f-4481-a72f-aa6cf8aa0a65 */
0x0d18a7df,
0xaf1f,
0x4481,
{0xa7, 0x2f, 0xaa, 0x6c, 0xf8, 0xaa, 0x0a, 0x65}
constexpr GUID JITEEVersionIdentifier = { /* c31fbc28-2e59-4770-b836-61c690978dc2 */
0xc31fbc28,
0x2e59,
0x4770,
{0xb8, 0x36, 0x61, 0xc6, 0x90, 0x97, 0x8d, 0xc2}
};

#endif // JIT_EE_VERSIONING_GUID_H
4 changes: 4 additions & 0 deletions src/coreclr/inc/jithelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@
JITHELPER(CORINFO_HELP_DELEGATEPROFILE64, JIT_DelegateProfile64, METHOD__NIL)
JITHELPER(CORINFO_HELP_VTABLEPROFILE32, JIT_VTableProfile32, METHOD__NIL)
JITHELPER(CORINFO_HELP_VTABLEPROFILE64, JIT_VTableProfile64, METHOD__NIL)
JITHELPER(CORINFO_HELP_GENERICVIRTUALPROFILE32, JIT_GenericVirtualProfile32, METHOD__NIL)
JITHELPER(CORINFO_HELP_GENERICVIRTUALPROFILE64, JIT_GenericVirtualProfile64, METHOD__NIL)
JITHELPER(CORINFO_HELP_COUNTPROFILE32, JIT_CountProfile32, METHOD__NIL)
JITHELPER(CORINFO_HELP_COUNTPROFILE64, JIT_CountProfile64, METHOD__NIL)
JITHELPER(CORINFO_HELP_VALUEPROFILE32, JIT_ValueProfile32, METHOD__NIL)
Expand All @@ -347,6 +349,8 @@
JITHELPER(CORINFO_HELP_DELEGATEPROFILE64, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_VTABLEPROFILE32, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_VTABLEPROFILE64, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_GENERICVIRTUALPROFILE32, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_GENERICVIRTUALPROFILE64, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_COUNTPROFILE32, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_COUNTPROFILE64, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_VALUEPROFILE32, NULL, METHOD__NIL)
Expand Down
85 changes: 62 additions & 23 deletions src/coreclr/jit/fgprofile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1968,7 +1968,9 @@ class BuildHandleHistogramProbeSchemaGen
ICorJitInfo::PgoInstrumentationSchema schemaElem = {};
schemaElem.Count = 1;
schemaElem.Other = isTypeHistogram ? ICorJitInfo::HandleHistogram32::CLASS_FLAG : 0;
if (call->IsVirtualStub())
if (call->IsVirtualStub() ||
(call->IsGenericVirtual(compiler) &&
((call->gtControlExpr->AsCall()->gtCallMoreFlags & GTF_CALL_M_LDVIRTFTN_INTERFACE) != 0)))
{
schemaElem.Other |= ICorJitInfo::HandleHistogram32::INTERFACE_FLAG;
}
Expand Down Expand Up @@ -2115,32 +2117,68 @@ class HandleHistogramProbeInserter

if (methodHistogram != nullptr)
{
GenTree* const tmpNode = compiler->gtNewLclvNode(tmpNum, TYP_REF);
GenTree* const methodProfileNode = compiler->gtNewIconNode((ssize_t)methodHistogram, TYP_I_IMPL);

GenTree* methodProfileCallNode;
if (call->IsDelegateInvoke())
if (call->IsGenericVirtual(compiler))
{
methodProfileCallNode = compiler->gtNewHelperCallNode(is32 ? CORINFO_HELP_DELEGATEPROFILE32
: CORINFO_HELP_DELEGATEPROFILE64,
TYP_VOID, tmpNode, methodProfileNode);
GenTreeCall* const lookupCall = call->gtControlExpr->AsCall();
assert(lookupCall->IsHelperCall(CORINFO_HELP_VIRTUAL_FUNC_PTR));
assert(!lookupCall->gtArgs.AreArgsComplete());

CallArg* const methodHandleArg = lookupCall->gtArgs.FindWellKnownArg(WellKnownArg::RuntimeMethodHandle);
assert(methodHandleArg != nullptr);
assert(methodHandleArg->GetEarlyNode()->TypeIs(TYP_I_IMPL));

// Spill the exact instantiated MethodDesc so the profile probe and dispatch lookup
// consume the same value without evaluating the method handle expression twice.
unsigned const methodTmpNum =
compiler->lvaGrabTemp(true DEBUGARG("generic virtual method profile tmp"));
compiler->lvaTable[methodTmpNum].lvType = TYP_I_IMPL;

GenTree* const storeNode =
compiler->gtNewStoreLclVarNode(methodTmpNum, methodHandleArg->GetEarlyNode());
GenTree* const methodHandleNode = compiler->gtNewLclvNode(methodTmpNum, TYP_I_IMPL);
GenTree* const methodProfileNode = compiler->gtNewIconNode((ssize_t)methodHistogram, TYP_I_IMPL);
GenTree* const profileCallNode =
compiler->gtNewHelperCallNode(is32 ? CORINFO_HELP_GENERICVIRTUALPROFILE32
: CORINFO_HELP_GENERICVIRTUALPROFILE64,
TYP_VOID, methodHandleNode, methodProfileNode);
GenTree* const methodTmpNode = compiler->gtNewLclvNode(methodTmpNum, TYP_I_IMPL);
GenTree* const profileCommaNode =
compiler->gtNewOperNode(GT_COMMA, TYP_I_IMPL, profileCallNode, methodTmpNode);
GenTree* const storeCommaNode =
compiler->gtNewOperNode(GT_COMMA, TYP_I_IMPL, storeNode, profileCommaNode);

methodHandleArg->SetEarlyNode(storeCommaNode);
}
else
{
assert(call->IsVirtualVtable());
GenTree* const baseMethodNode = compiler->gtNewIconEmbMethHndNode(call->gtCallMethHnd);
methodProfileCallNode =
compiler->gtNewHelperCallNode(is32 ? CORINFO_HELP_VTABLEPROFILE32 : CORINFO_HELP_VTABLEPROFILE64,
TYP_VOID, tmpNode, baseMethodNode, methodProfileNode);
}
GenTree* const tmpNode = compiler->gtNewLclvNode(tmpNum, TYP_REF);
GenTree* const methodProfileNode = compiler->gtNewIconNode((ssize_t)methodHistogram, TYP_I_IMPL);

if (helperCallNode == nullptr)
{
helperCallNode = methodProfileCallNode;
}
else
{
helperCallNode = compiler->gtNewOperNode(GT_COMMA, TYP_REF, helperCallNode, methodProfileCallNode);
GenTree* methodProfileCallNode;
if (call->IsDelegateInvoke())
{
methodProfileCallNode = compiler->gtNewHelperCallNode(is32 ? CORINFO_HELP_DELEGATEPROFILE32
: CORINFO_HELP_DELEGATEPROFILE64,
TYP_VOID, tmpNode, methodProfileNode);
}
else
{
assert(call->IsVirtualVtable());
GenTree* const baseMethodNode = compiler->gtNewIconEmbMethHndNode(call->gtCallMethHnd);
methodProfileCallNode =
compiler->gtNewHelperCallNode(is32 ? CORINFO_HELP_VTABLEPROFILE32
: CORINFO_HELP_VTABLEPROFILE64,
TYP_VOID, tmpNode, baseMethodNode, methodProfileNode);
}

if (helperCallNode == nullptr)
{
helperCallNode = methodProfileCallNode;
}
else
{
helperCallNode = compiler->gtNewOperNode(GT_COMMA, TYP_REF, helperCallNode, methodProfileCallNode);
}
}
}

Expand Down Expand Up @@ -2599,7 +2637,8 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod()
const bool useClassProfiles = (JitConfig.JitClassProfiling() > 0);
const bool useDelegateProfiles = (JitConfig.JitDelegateProfiling() > 0);
const bool useVTableProfiles = (JitConfig.JitVTableProfiling() > 0);
if (!prejit && (useClassProfiles || useDelegateProfiles || useVTableProfiles))
const bool useGvmProfiles = (JitConfig.JitGenericVirtualProfiling() > 0);
if (!prejit && (useClassProfiles || useDelegateProfiles || useVTableProfiles || useGvmProfiles))
{
fgHistogramInstrumentor = new (this, CMK_Pgo) HandleHistogramProbeInstrumentor(this);
}
Expand Down
36 changes: 17 additions & 19 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8200,7 +8200,7 @@ void Compiler::pickGDV(GenTreeCall* call,
const int maxLikelyClasses = MAX_GDV_TYPE_CHECKS;
LikelyClassMethodRecord likelyClasses[maxLikelyClasses];
unsigned numberOfClasses = 0;
if (call->IsVirtualStub() || call->IsVirtualVtable() || call->IsHelperCall())
if (call->IsVirtualStub() || call->IsVirtualVtable() || call->IsHelperCall() || call->IsGenericVirtual(this))
{
numberOfClasses = getLikelyClasses(likelyClasses, maxLikelyClasses, pgoInfo.PgoSchema, pgoInfo.PgoSchemaCount,
pgoInfo.PgoData, ilOffset);
Expand All @@ -8217,7 +8217,7 @@ void Compiler::pickGDV(GenTreeCall* call,
// impDevirtualizeCall and what happens in
// GuardedDevirtualizationTransformer::CreateThen for method GDV.
//
if (!IsAot() && (call->IsVirtualVtable() || call->IsDelegateInvoke()))
if (!IsAot() && (call->IsVirtualVtable() || call->IsDelegateInvoke() || call->IsGenericVirtual(this)))
{
assert(!call->IsHelperCall());
numberOfMethods = getLikelyMethods(likelyMethods, maxLikelyMethods, pgoInfo.PgoSchema, pgoInfo.PgoSchemaCount,
Expand Down Expand Up @@ -8295,8 +8295,9 @@ void Compiler::pickGDV(GenTreeCall* call,

if ((verbose || JitConfig.EnableExtraSuperPmiQueries()) && (numberOfMethods > 0))
{
assert(call->gtCallType == CT_USER_FUNC);
const char* baseMethName = eeGetMethodFullName(call->gtCallMethHnd);
assert(call->gtCallType == CT_USER_FUNC || call->IsGenericVirtual(this));
const char* baseMethName =
call->IsGenericVirtual(this) ? "<generic virtual>" : eeGetMethodFullName(call->gtCallMethHnd);
JITDUMP("Likely methods for call [%06u] to method %s\n", dspTreeID(call), baseMethName);

for (UINT32 i = 0; i < numberOfMethods; i++)
Expand Down Expand Up @@ -8663,12 +8664,6 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,
{
JITDUMP("Considering guarded devirtualization at IL offset %u (0x%x)\n", ilOffset, ilOffset);

if (call->IsGenericVirtual(this))
{
JITDUMP("Generic virtual methods are not supported by guarded devirtualization, sorry.\n");
return;
}

bool hasPgoData = true;

CORINFO_CLASS_HANDLE likelyClasses[MAX_GDV_TYPE_CHECKS] = {};
Expand Down Expand Up @@ -8802,6 +8797,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,
{
CORINFO_CLASS_HANDLE likelyClass = likelyClasses[candidateId];
CORINFO_METHOD_HANDLE likelyMethod = likelyMethods[candidateId];
CORINFO_METHOD_HANDLE originalMethod = call->IsGenericVirtual(this) ? likelyMethod : baseMethod;
unsigned likelihood = likelihoods[candidateId];
const CORINFO_LOOKUP* pInstParamLookup = nullptr;
CORINFO_RESOLVED_TOKEN* pResolvedToken = nullptr;
Expand All @@ -8828,7 +8824,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,

// Figure out which method will be called.
//
dvInfo.virtualMethod = baseMethod;
dvInfo.virtualMethod = originalMethod;
dvInfo.objClass = likelyClass;
dvInfo.context = originalContext;
dvInfo.pResolvedTokenVirtualMethod = nullptr;
Expand Down Expand Up @@ -8926,7 +8922,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,
// Add this as a potential candidate.
//
addGuardedDevirtualizationCandidate(call, likelyMethod, likelyClass, likelyContext, likelyMethodAttribs,
likelyClassAttribs, likelihood, pInstParamLookup, baseMethod,
likelyClassAttribs, likelihood, pInstParamLookup, originalMethod,
pResolvedToken, pUnboxedResolvedToken);
}
}
Expand All @@ -8953,7 +8949,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,
// classAttr - attributes of the class
// likelihood - odds that this class is the class seen at runtime
// instParamLookup - lookup to use if the target signature requires an instantiation argument
// originalMethodHandle - method handle of base method (before devirt)
// originalMethodHandle - method handle of base method (before devirt), including the profiled GVM instantiation
// pResolvedToken - resolved token for methodHandle, used to get R2R call info; nullptr when unavailable
// pUnboxedResolvedToken - resolved token for the unboxed entry, paired with pResolvedToken
//
Expand All @@ -8970,7 +8966,7 @@ void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call,
CORINFO_RESOLVED_TOKEN* pUnboxedResolvedToken)
{
// This transformation only makes sense for delegate and virtual calls
assert(call->IsDelegateInvoke() || call->IsVirtual());
assert(call->IsDelegateInvoke() || call->IsVirtual() || call->IsGenericVirtual(this));

// Only mark calls if the feature is enabled.
const bool isEnabled = JitConfig.JitEnableGuardedDevirtualization() > 0;
Expand Down Expand Up @@ -9291,7 +9287,7 @@ void Compiler::impMarkInlineCandidateHelper(GenTreeCall* call,
return;
}

// Ignore indirect calls, unless they are indirect virtual stub calls with profile info.
// Ignore indirect calls, unless they are indirect virtual stub calls or generic virtual calls with profile info.
//
if (call->gtCallType == CT_INDIRECT)
{
Expand All @@ -9302,7 +9298,7 @@ void Compiler::impMarkInlineCandidateHelper(GenTreeCall* call,
}
else
{
assert(call->IsVirtualStub());
assert(call->IsVirtualStub() || call->IsGenericVirtual(this));
}
}

Expand Down Expand Up @@ -10588,7 +10584,7 @@ Compiler::GDVProbeType Compiler::compClassifyGDVProbeType(GenTreeCall* call)
bool createTypeHistogram = false;
if (JitConfig.JitClassProfiling() > 0)
{
createTypeHistogram = call->IsVirtualStub() || call->IsVirtualVtable();
createTypeHistogram = call->IsVirtualStub() || call->IsVirtualVtable() || call->IsGenericVirtual(this);

// Cast helpers may conditionally (depending on whether the class is
// exact or not) have probes. For those helpers we do not use this
Expand All @@ -10598,8 +10594,10 @@ Compiler::GDVProbeType Compiler::compClassifyGDVProbeType(GenTreeCall* call)
(call->gtHandleHistogramProfileCandidateInfo != nullptr));
}

bool createMethodHistogram = ((JitConfig.JitDelegateProfiling() > 0) && call->IsDelegateInvoke()) ||
((JitConfig.JitVTableProfiling() > 0) && call->IsVirtualVtable());
bool createMethodHistogram =
((JitConfig.JitDelegateProfiling() > 0) && call->IsDelegateInvoke()) ||
((JitConfig.JitVTableProfiling() > 0) && call->IsVirtualVtable()) ||
((JitConfig.JitGenericVirtualProfiling() > 0) && createTypeHistogram && call->IsGenericVirtual(this));

if (createTypeHistogram && createMethodHistogram)
{
Expand Down
Loading
Loading