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: Devirtualization and inlining for GVM #112353

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
6 changes: 3 additions & 3 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1512,16 +1512,16 @@ 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
// (in which case the method handle and context will be a generic method)
// - hasGenericMethodHandleContext is set TRUE for cases where the method handle and context will be a generic method
// (for array interface method or generic virtual method devirtualization)
//
CORINFO_METHOD_HANDLE devirtualizedMethod;
CORINFO_CONTEXT_HANDLE exactContext;
CORINFO_DEVIRTUALIZATION_DETAIL detail;
CORINFO_RESOLVED_TOKEN resolvedTokenDevirtualizedMethod;
CORINFO_RESOLVED_TOKEN resolvedTokenDevirtualizedUnboxedMethod;
bool isInstantiatingStub;
bool wasArrayInterfaceDevirt;
bool hasGenericMethodHandleContext;
};

//----------------------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3063,6 +3063,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 @@ -1568,6 +1568,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 @@ -591,7 +619,7 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitor<Substi

CORINFO_CONTEXT_HANDLE context = call->gtLateDevirtualizationInfo->exactContextHnd;
InlineContext* inlinersContext = call->gtLateDevirtualizationInfo->inlinersContext;
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 @@ -601,7 +629,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);
assert(inlinersContext != nullptr);
Expand Down
21 changes: 21 additions & 0 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2304,6 +2304,25 @@ 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) ||
// TODO: Support R2R and NativeAOT (CORINFO_HELP_GVMLOOKUP_FOR_SLOT)
(!compiler->opts.IsReadyToRun() && gtCallType == CT_INDIRECT &&
gtCallAddr->IsHelperCall(compiler, CORINFO_HELP_VIRTUAL_FUNC_PTR));
}

//-------------------------------------------------------------------------
// IsHelperCall: Determine if this GT_CALL node is a specific helper call.
//
Expand Down Expand Up @@ -13093,6 +13112,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 @@ -4564,6 +4564,7 @@ enum class WellKnownArg : unsigned
SwiftSelf,
X86TailCallSpecialArg,
StackArrayLocal,
RuntimeMethodHandle,
};

#ifdef DEBUG
Expand Down Expand Up @@ -5178,6 +5179,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
Loading
Loading