Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT: Refactor around impDevirtualizeCall for GVM devirt #112610

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
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
3 changes: 3 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3219,6 +3219,9 @@ class Compiler

GenTreeCall* gtNewHelperCallNode(
unsigned helper, var_types type, GenTree* arg1 = nullptr, GenTree* arg2 = nullptr, GenTree* arg3 = nullptr);

GenTreeCall* gtNewVirtualFunctionLookupHelperCallNode(
unsigned helper, var_types type, GenTree* thisPtr, GenTree* methHnd, GenTree* clsHnd = nullptr);

GenTreeCall* gtNewRuntimeLookupHelperCallNode(CORINFO_RUNTIME_LOOKUP* pRuntimeLookup,
GenTree* ctxTree,
Expand Down
54 changes: 54 additions & 0 deletions src/coreclr/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1579,6 +1579,60 @@ inline GenTreeCall* Compiler::gtNewHelperCallNode(
return result;
}

/*****************************************************************************/

//------------------------------------------------------------------------------
// gtNewHelperCallNode : Helper to create a call helper node.
//
//
// Arguments:
// helper - Call helper
// type - Type of the node
// thisPtr - 'this' argument
// methHnd - Runtime method handle argument
// clsHnd - Class handle argument
//
// Return Value:
// New CT_HELPER node
//
inline GenTreeCall* Compiler::gtNewVirtualFunctionLookupHelperCallNode(
unsigned helper, var_types type, GenTree* thisPtr, GenTree* methHnd, GenTree* clsHnd)
{
GenTreeCall* const result = gtNewCallNode(CT_HELPER, eeFindHelper(helper), type);

if (!s_helperCallProperties.NoThrow((CorInfoHelpFunc)helper))
{
result->gtFlags |= GTF_EXCEPT;

if (s_helperCallProperties.AlwaysThrow((CorInfoHelpFunc)helper))
{
setCallDoesNotReturn(result);
}
}
#if DEBUG
// Helper calls are never candidates.

result->gtInlineObservation = InlineObservation::CALLSITE_IS_CALL_TO_HELPER;
#endif

assert(methHnd != nullptr);
result->gtArgs.PushFront(this, NewCallArg::Primitive(methHnd).WellKnown(WellKnownArg::RuntimeMethodHandle));
result->gtFlags |= methHnd->gtFlags & GTF_ALL_EFFECT;

if (clsHnd != nullptr)
{
result->gtArgs.PushFront(this, NewCallArg::Primitive(clsHnd));
result->gtFlags |= clsHnd->gtFlags & GTF_ALL_EFFECT;
}

assert(thisPtr != nullptr);

result->gtArgs.PushFront(this, NewCallArg::Primitive(thisPtr).WellKnown(WellKnownArg::ThisPointer));
result->gtFlags |= thisPtr->gtFlags & GTF_ALL_EFFECT;

return result;
}

//------------------------------------------------------------------------
// gtNewAllocObjNode: A little helper to create an object allocation node.
//
Expand Down
34 changes: 31 additions & 3 deletions src/coreclr/jit/fginline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,34 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitor<Substi
}
#endif // FEATURE_MULTIREG_RET

CORINFO_METHOD_HANDLE GetMethodHandle(GenTreeCall* call)
{
assert(call->IsDevirtualizationCandidate(m_compiler));
if (call->IsVirtual())
{
assert(call->gtCallType == CT_USER_FUNC);
return call->gtCallMethHnd;
}
else
{
assert(call->gtCallType == CT_INDIRECT);
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 @@ -573,7 +601,7 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitor<Substi
if (tree->OperGet() == GT_CALL)
{
GenTreeCall* call = tree->AsCall();
bool tryLateDevirt = call->IsVirtual() && (call->gtCallType == CT_USER_FUNC);
bool tryLateDevirt = call->IsDevirtualizationCandidate(m_compiler);

#ifdef DEBUG
tryLateDevirt = tryLateDevirt && (JitConfig.JitEnableLateDevirtualization() == 1);
Expand All @@ -590,7 +618,7 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitor<Substi
#endif // DEBUG

CORINFO_CONTEXT_HANDLE context = nullptr;
CORINFO_METHOD_HANDLE method = call->gtCallMethHnd;
CORINFO_METHOD_HANDLE method = GetMethodHandle(call);
unsigned methodFlags = 0;
const bool isLateDevirtualization = true;
const bool explicitTailCall = call->IsTailPrefixedCall();
Expand All @@ -610,7 +638,7 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitor<Substi
m_compiler->impDevirtualizeCall(call, nullptr, &method, &methodFlags, &contextInput, &context,
isLateDevirtualization, explicitTailCall);

if (!call->IsVirtual())
if (!call->IsDevirtualizationCandidate(m_compiler))
{
assert(context != nullptr);
CORINFO_CALL_INFO callInfo = {};
Expand Down
20 changes: 20 additions & 0 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2417,6 +2417,24 @@ int GenTreeCall::GetNonStandardAddedArgCount(Compiler* compiler) const
return 0;
}

//-------------------------------------------------------------------------
// IsDevirtualizationCandidate: Determine if this GT_CALL node is a devirtualization candidate.
// A call will be unmarked from devirtualization candidate if it
// is devirtualized.
//
// Arguments:
// compiler - the compiler instance so that we can call eeFindHelper
//
// Return Value:
// Returns true if this GT_CALL node is a devirtualization candidate.
//
bool GenTreeCall::IsDevirtualizationCandidate(Compiler* compiler) const
{
return (IsVirtual() && gtCallType == CT_USER_FUNC) ||
(gtCallType == CT_INDIRECT && (gtCallAddr->IsHelperCall(compiler, CORINFO_HELP_VIRTUAL_FUNC_PTR) ||
gtCallAddr->IsHelperCall(compiler, CORINFO_HELP_GVMLOOKUP_FOR_SLOT)));
}

//-------------------------------------------------------------------------
// IsHelperCall: Determine if this GT_CALL node is a specific helper call.
//
Expand Down Expand Up @@ -13198,6 +13216,8 @@ const char* Compiler::gtGetWellKnownArgNameForArgMsg(WellKnownArg arg)
return "tail call";
case WellKnownArg::StackArrayLocal:
return "&lcl arr";
case WellKnownArg::RuntimeMethodHandle:
return "meth hnd";
default:
return nullptr;
}
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -4568,6 +4568,7 @@ enum class WellKnownArg : unsigned
SwiftSelf,
X86TailCallSpecialArg,
StackArrayLocal,
RuntimeMethodHandle,
};

#ifdef DEBUG
Expand Down Expand Up @@ -5331,6 +5332,9 @@ struct GenTreeCall final : public GenTree
{
return (gtFlags & GTF_CALL_VIRT_KIND_MASK) == GTF_CALL_VIRT_VTABLE;
}

bool IsDevirtualizationCandidate(Compiler* compiler) const;

bool IsInlineCandidate() const
{
return (gtFlags & GTF_CALL_INLINE_CANDIDATE) != 0;
Expand Down
6 changes: 4 additions & 2 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2780,7 +2780,8 @@ GenTree* Compiler::impImportLdvirtftn(GenTree* thisPtr,
{
GenTree* runtimeMethodHandle =
impLookupToTree(pResolvedToken, &pCallInfo->codePointerLookup, GTF_ICON_METHOD_HDL, pCallInfo->hMethod);
call = gtNewHelperCallNode(CORINFO_HELP_GVMLOOKUP_FOR_SLOT, TYP_I_IMPL, thisPtr, runtimeMethodHandle);
call = gtNewVirtualFunctionLookupHelperCallNode(CORINFO_HELP_GVMLOOKUP_FOR_SLOT, TYP_I_IMPL, thisPtr,
runtimeMethodHandle);
}

#ifdef FEATURE_READYTORUN
Expand Down Expand Up @@ -2821,7 +2822,8 @@ GenTree* Compiler::impImportLdvirtftn(GenTree* thisPtr,

// Call helper function. This gets the target address of the final destination callsite.
//
call = gtNewHelperCallNode(CORINFO_HELP_VIRTUAL_FUNC_PTR, TYP_I_IMPL, thisPtr, exactTypeDesc, exactMethodDesc);
call = gtNewVirtualFunctionLookupHelperCallNode(CORINFO_HELP_VIRTUAL_FUNC_PTR, TYP_I_IMPL, thisPtr,
exactMethodDesc, exactTypeDesc);
}

assert(call != nullptr);
Expand Down
8 changes: 4 additions & 4 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
// See if we can devirt if we aren't probing.
if (!probing && opts.OptimizationEnabled())
{
if (call->AsCall()->IsVirtual())
if (call->AsCall()->IsDevirtualizationCandidate(this))
{
// only true object pointers can be virtual
assert(call->AsCall()->gtArgs.HasThisPointer() &&
Expand All @@ -965,7 +965,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
// inlinees.
rawILOffset);

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

if (wasDevirtualized)
{
Expand Down Expand Up @@ -8020,9 +8020,9 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
assert(methodFlags != nullptr);
assert(pContextHandle != nullptr);

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

#if defined(DEBUG)
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,8 @@ const char* getWellKnownArgName(WellKnownArg arg)
return "X86TailCallSpecialArg";
case WellKnownArg::StackArrayLocal:
return "StackArrayLocal";
case WellKnownArg::RuntimeMethodHandle:
return "RuntimeMethodHandle";
}

return "N/A";
Expand Down
Loading