Skip to content

Commit 34aac93

Browse files
authored
JIT: Refactor around impDevirtualizeCall for GVM devirt (#112610)
* Add IsDevirtualizationCandidate * Use GetMethodHandle instead of gtCallMethHnd * Introduce a well-known arg * Adjust arg order * Nit * Add NAOT helper as well * Add an assertion * JIT format * Oops * Address several regressions * JIT format
1 parent a87245f commit 34aac93

File tree

8 files changed

+123
-11
lines changed

8 files changed

+123
-11
lines changed

src/coreclr/jit/compiler.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3065,6 +3065,9 @@ class Compiler
30653065
GenTreeCall* gtNewHelperCallNode(
30663066
unsigned helper, var_types type, GenTree* arg1 = nullptr, GenTree* arg2 = nullptr, GenTree* arg3 = nullptr, GenTree* arg4 = nullptr);
30673067

3068+
GenTreeCall* gtNewVirtualFunctionLookupHelperCallNode(
3069+
unsigned helper, var_types type, GenTree* thisPtr, GenTree* methHnd, GenTree* clsHnd = nullptr);
3070+
30683071
GenTreeCall* gtNewRuntimeLookupHelperCallNode(CORINFO_RUNTIME_LOOKUP* pRuntimeLookup,
30693072
GenTree* ctxTree,
30703073
void* compileTimeHandle);

src/coreclr/jit/compiler.hpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,6 +1593,60 @@ inline GenTreeCall* Compiler::gtNewHelperCallNode(
15931593
return result;
15941594
}
15951595

1596+
/*****************************************************************************/
1597+
1598+
//------------------------------------------------------------------------------
1599+
// gtNewHelperCallNode : Helper to create a call helper node.
1600+
//
1601+
//
1602+
// Arguments:
1603+
// helper - Call helper
1604+
// type - Type of the node
1605+
// thisPtr - 'this' argument
1606+
// methHnd - Runtime method handle argument
1607+
// clsHnd - Class handle argument
1608+
//
1609+
// Return Value:
1610+
// New CT_HELPER node
1611+
//
1612+
inline GenTreeCall* Compiler::gtNewVirtualFunctionLookupHelperCallNode(
1613+
unsigned helper, var_types type, GenTree* thisPtr, GenTree* methHnd, GenTree* clsHnd)
1614+
{
1615+
GenTreeCall* const result = gtNewCallNode(CT_HELPER, eeFindHelper(helper), type);
1616+
1617+
if (!s_helperCallProperties.NoThrow((CorInfoHelpFunc)helper))
1618+
{
1619+
result->gtFlags |= GTF_EXCEPT;
1620+
1621+
if (s_helperCallProperties.AlwaysThrow((CorInfoHelpFunc)helper))
1622+
{
1623+
setCallDoesNotReturn(result);
1624+
}
1625+
}
1626+
#if DEBUG
1627+
// Helper calls are never candidates.
1628+
1629+
result->gtInlineObservation = InlineObservation::CALLSITE_IS_CALL_TO_HELPER;
1630+
#endif
1631+
1632+
assert(methHnd != nullptr);
1633+
result->gtArgs.PushFront(this, NewCallArg::Primitive(methHnd).WellKnown(WellKnownArg::RuntimeMethodHandle));
1634+
result->gtFlags |= methHnd->gtFlags & GTF_ALL_EFFECT;
1635+
1636+
if (clsHnd != nullptr)
1637+
{
1638+
result->gtArgs.PushFront(this, NewCallArg::Primitive(clsHnd));
1639+
result->gtFlags |= clsHnd->gtFlags & GTF_ALL_EFFECT;
1640+
}
1641+
1642+
assert(thisPtr != nullptr);
1643+
1644+
result->gtArgs.PushFront(this, NewCallArg::Primitive(thisPtr).WellKnown(WellKnownArg::ThisPointer));
1645+
result->gtFlags |= thisPtr->gtFlags & GTF_ALL_EFFECT;
1646+
1647+
return result;
1648+
}
1649+
15961650
//------------------------------------------------------------------------
15971651
// gtNewAllocObjNode: A little helper to create an object allocation node.
15981652
//

src/coreclr/jit/fginline.cpp

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,32 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitor<Substi
544544
}
545545
#endif // FEATURE_MULTIREG_RET
546546

547+
CORINFO_METHOD_HANDLE GetMethodHandle(GenTreeCall* call)
548+
{
549+
assert(call->IsDevirtualizationCandidate(m_compiler));
550+
if (call->IsVirtual())
551+
{
552+
return call->gtCallMethHnd;
553+
}
554+
else
555+
{
556+
GenTree* runtimeMethHndNode =
557+
call->gtCallAddr->AsCall()->gtArgs.FindWellKnownArg(WellKnownArg::RuntimeMethodHandle)->GetNode();
558+
assert(runtimeMethHndNode != nullptr);
559+
switch (runtimeMethHndNode->OperGet())
560+
{
561+
case GT_RUNTIMELOOKUP:
562+
return runtimeMethHndNode->AsRuntimeLookup()->GetMethodHandle();
563+
case GT_CNS_INT:
564+
return CORINFO_METHOD_HANDLE(runtimeMethHndNode->AsIntCon()->IconValue());
565+
default:
566+
assert(!"Unexpected type in RuntimeMethodHandle arg.");
567+
return nullptr;
568+
}
569+
return nullptr;
570+
}
571+
}
572+
547573
//------------------------------------------------------------------------
548574
// LateDevirtualization: re-examine calls after inlining to see if we
549575
// can do more devirtualization
@@ -586,8 +612,9 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitor<Substi
586612

587613
if (tree->OperGet() == GT_CALL)
588614
{
589-
GenTreeCall* call = tree->AsCall();
590-
bool tryLateDevirt = call->IsVirtual() && (call->gtCallType == CT_USER_FUNC);
615+
GenTreeCall* call = tree->AsCall();
616+
// TODO-CQ: Drop `call->gtCallType == CT_USER_FUNC` once we have GVM devirtualization
617+
bool tryLateDevirt = call->IsDevirtualizationCandidate(m_compiler) && (call->gtCallType == CT_USER_FUNC);
591618

592619
#ifdef DEBUG
593620
tryLateDevirt = tryLateDevirt && (JitConfig.JitEnableLateDevirtualization() == 1);
@@ -605,7 +632,7 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitor<Substi
605632

606633
CORINFO_CONTEXT_HANDLE context = call->gtLateDevirtualizationInfo->exactContextHnd;
607634
InlineContext* inlinersContext = call->gtLateDevirtualizationInfo->inlinersContext;
608-
CORINFO_METHOD_HANDLE method = call->gtCallMethHnd;
635+
CORINFO_METHOD_HANDLE method = GetMethodHandle(call);
609636
unsigned methodFlags = 0;
610637
const bool isLateDevirtualization = true;
611638
const bool explicitTailCall = call->IsTailPrefixedCall();
@@ -615,7 +642,7 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitor<Substi
615642
m_compiler->impDevirtualizeCall(call, nullptr, &method, &methodFlags, &contextInput, &context,
616643
isLateDevirtualization, explicitTailCall);
617644

618-
if (!call->IsVirtual())
645+
if (!call->IsDevirtualizationCandidate(m_compiler))
619646
{
620647
assert(context != nullptr);
621648
assert(inlinersContext != nullptr);

src/coreclr/jit/gentree.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2334,6 +2334,24 @@ int GenTreeCall::GetNonStandardAddedArgCount(Compiler* compiler) const
23342334
return 0;
23352335
}
23362336

2337+
//-------------------------------------------------------------------------
2338+
// IsDevirtualizationCandidate: Determine if this GT_CALL node is a devirtualization candidate.
2339+
// A call will be unmarked from devirtualization candidate if it
2340+
// is devirtualized.
2341+
//
2342+
// Arguments:
2343+
// compiler - the compiler instance so that we can call eeFindHelper
2344+
//
2345+
// Return Value:
2346+
// Returns true if this GT_CALL node is a devirtualization candidate.
2347+
//
2348+
bool GenTreeCall::IsDevirtualizationCandidate(Compiler* compiler) const
2349+
{
2350+
return IsVirtual() ||
2351+
(gtCallType == CT_INDIRECT && (gtCallAddr->IsHelperCall(compiler, CORINFO_HELP_VIRTUAL_FUNC_PTR) ||
2352+
gtCallAddr->IsHelperCall(compiler, CORINFO_HELP_GVMLOOKUP_FOR_SLOT)));
2353+
}
2354+
23372355
//-------------------------------------------------------------------------
23382356
// IsHelperCall: Determine if this GT_CALL node is a specific helper call.
23392357
//
@@ -13152,6 +13170,8 @@ const char* Compiler::gtGetWellKnownArgNameForArgMsg(WellKnownArg arg)
1315213170
return "tail call";
1315313171
case WellKnownArg::StackArrayLocal:
1315413172
return "&lcl arr";
13173+
case WellKnownArg::RuntimeMethodHandle:
13174+
return "meth hnd";
1315513175
default:
1315613176
return nullptr;
1315713177
}

src/coreclr/jit/gentree.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4589,6 +4589,7 @@ enum class WellKnownArg : unsigned
45894589
SwiftSelf,
45904590
X86TailCallSpecialArg,
45914591
StackArrayLocal,
4592+
RuntimeMethodHandle,
45924593
};
45934594

45944595
#ifdef DEBUG
@@ -5210,6 +5211,9 @@ struct GenTreeCall final : public GenTree
52105211
{
52115212
return (gtFlags & GTF_CALL_VIRT_KIND_MASK) == GTF_CALL_VIRT_VTABLE;
52125213
}
5214+
5215+
bool IsDevirtualizationCandidate(Compiler* compiler) const;
5216+
52135217
bool IsInlineCandidate() const
52145218
{
52155219
return (gtFlags & GTF_CALL_INLINE_CANDIDATE) != 0;

src/coreclr/jit/importer.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2809,7 +2809,8 @@ GenTree* Compiler::impImportLdvirtftn(GenTree* thisPtr,
28092809
{
28102810
GenTree* runtimeMethodHandle =
28112811
impLookupToTree(pResolvedToken, &pCallInfo->codePointerLookup, GTF_ICON_METHOD_HDL, pCallInfo->hMethod);
2812-
call = gtNewHelperCallNode(CORINFO_HELP_GVMLOOKUP_FOR_SLOT, TYP_I_IMPL, thisPtr, runtimeMethodHandle);
2812+
call = gtNewVirtualFunctionLookupHelperCallNode(CORINFO_HELP_GVMLOOKUP_FOR_SLOT, TYP_I_IMPL, thisPtr,
2813+
runtimeMethodHandle);
28132814
}
28142815

28152816
#ifdef FEATURE_READYTORUN
@@ -2850,7 +2851,8 @@ GenTree* Compiler::impImportLdvirtftn(GenTree* thisPtr,
28502851

28512852
// Call helper function. This gets the target address of the final destination callsite.
28522853
//
2853-
call = gtNewHelperCallNode(CORINFO_HELP_VIRTUAL_FUNC_PTR, TYP_I_IMPL, thisPtr, exactTypeDesc, exactMethodDesc);
2854+
call = gtNewVirtualFunctionLookupHelperCallNode(CORINFO_HELP_VIRTUAL_FUNC_PTR, TYP_I_IMPL, thisPtr,
2855+
exactMethodDesc, exactTypeDesc);
28542856
}
28552857

28562858
assert(call != nullptr);

src/coreclr/jit/importercalls.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
965965
// See if we can devirt if we aren't probing.
966966
if (!probing && opts.OptimizationEnabled())
967967
{
968-
if (call->AsCall()->IsVirtual())
968+
if (call->AsCall()->IsDevirtualizationCandidate(this))
969969
{
970970
// only true object pointers can be virtual
971971
assert(call->AsCall()->gtArgs.HasThisPointer() &&
@@ -981,7 +981,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
981981
// inlinees.
982982
rawILOffset);
983983

984-
const bool wasDevirtualized = !call->AsCall()->IsVirtual();
984+
const bool wasDevirtualized = !call->AsCall()->IsDevirtualizationCandidate(this);
985985

986986
if (wasDevirtualized)
987987
{
@@ -1258,7 +1258,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
12581258
// If the call is virtual, record the inliner's context for possible use during late devirt inlining.
12591259
// Also record the generics context if there is any.
12601260
//
1261-
if (call->AsCall()->IsVirtual() && (call->AsCall()->gtCallType != CT_INDIRECT))
1261+
if (call->AsCall()->IsDevirtualizationCandidate(this))
12621262
{
12631263
JITDUMP("\nSaving generic context %p and inline context %p for call [%06u]\n", dspPtr(exactContextHnd),
12641264
dspPtr(compInlineContext), dspTreeID(call->AsCall()));
@@ -8161,9 +8161,9 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
81618161
assert(methodFlags != nullptr);
81628162
assert(pContextHandle != nullptr);
81638163

8164-
// This should be a virtual vtable or virtual stub call.
8164+
// This should be a devirtualization candidate.
81658165
//
8166-
assert(call->IsVirtual());
8166+
assert(call->IsDevirtualizationCandidate(this));
81678167
assert(opts.OptimizationEnabled());
81688168

81698169
#if defined(DEBUG)

src/coreclr/jit/morph.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,8 @@ const char* getWellKnownArgName(WellKnownArg arg)
711711
return "X86TailCallSpecialArg";
712712
case WellKnownArg::StackArrayLocal:
713713
return "StackArrayLocal";
714+
case WellKnownArg::RuntimeMethodHandle:
715+
return "RuntimeMethodHandle";
714716
}
715717

716718
return "N/A";

0 commit comments

Comments
 (0)