Skip to content

Commit

Permalink
JIT: don't optimize calls to CORINFO_HELP_VIRTUAL_FUNC_PTR for interf…
Browse files Browse the repository at this point in the history
…ace class lookups (#82209)

The jit currently replaces calls to `CORINFO_HELP_VIRTUAL_FUNC_PTR`
whose results are unused with null pointer checks.

But for interface classes this helper can throw a variety of exceptions, so this
optimization is incorrect.

Fixes #82127.
  • Loading branch information
AndyAyersMS authored Feb 23, 2023
1 parent b79684a commit cbe05f9
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 23 deletions.
1 change: 1 addition & 0 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -4197,6 +4197,7 @@ enum GenTreeCallFlags : unsigned int
GTF_CALL_M_STRESS_TAILCALL = 0x04000000, // the call is NOT "tail" prefixed but GTF_CALL_M_EXPLICIT_TAILCALL was added because of tail call stress mode
GTF_CALL_M_EXPANDED_EARLY = 0x08000000, // the Virtual Call target address is expanded and placed in gtControlExpr in Morph rather than in Lower
GTF_CALL_M_HAS_LATE_DEVIRT_INFO = 0x10000000, // this call has late devirtualzation info
GTF_CALL_M_LDVIRTFTN_INTERFACE = 0x20000000, // ldvirtftn on an interface type
};

inline constexpr GenTreeCallFlags operator ~(GenTreeCallFlags a)
Expand Down
61 changes: 39 additions & 22 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3116,58 +3116,75 @@ GenTree* Compiler::impImportLdvirtftn(GenTree* thisPtr,
CORINFO_RESOLVED_TOKEN* pResolvedToken,
CORINFO_CALL_INFO* pCallInfo)
{
if ((pCallInfo->methodFlags & CORINFO_FLG_EnC) && !(pCallInfo->classFlags & CORINFO_FLG_INTERFACE))
const bool isInterface = (pCallInfo->classFlags & CORINFO_FLG_INTERFACE) == CORINFO_FLG_INTERFACE;

if ((pCallInfo->methodFlags & CORINFO_FLG_EnC) && !isInterface)
{
NO_WAY("Virtual call to a function added via EnC is not supported");
}

GenTreeCall* call = nullptr;

// NativeAOT generic virtual method
if ((pCallInfo->sig.sigInst.methInstCount != 0) && IsTargetAbi(CORINFO_NATIVEAOT_ABI))
{
GenTree* runtimeMethodHandle =
impLookupToTree(pResolvedToken, &pCallInfo->codePointerLookup, GTF_ICON_METHOD_HDL, pCallInfo->hMethod);
return gtNewHelperCallNode(CORINFO_HELP_GVMLOOKUP_FOR_SLOT, TYP_I_IMPL, thisPtr, runtimeMethodHandle);
call = gtNewHelperCallNode(CORINFO_HELP_GVMLOOKUP_FOR_SLOT, TYP_I_IMPL, thisPtr, runtimeMethodHandle);
}

#ifdef FEATURE_READYTORUN
if (opts.IsReadyToRun())
else if (opts.IsReadyToRun())
{
if (!pCallInfo->exactContextNeedsRuntimeLookup)
{
GenTreeCall* call = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR, TYP_I_IMPL, thisPtr);

call = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR, TYP_I_IMPL, thisPtr);
call->setEntryPoint(pCallInfo->codePointerLookup.constLookup);

return call;
}

// We need a runtime lookup. NativeAOT has a ReadyToRun helper for that too.
if (IsTargetAbi(CORINFO_NATIVEAOT_ABI))
else if (IsTargetAbi(CORINFO_NATIVEAOT_ABI))
{
GenTree* ctxTree = getRuntimeContextTree(pCallInfo->codePointerLookup.lookupKind.runtimeLookupKind);

return impReadyToRunHelperToTree(pResolvedToken, CORINFO_HELP_READYTORUN_GENERIC_HANDLE, TYP_I_IMPL,
call = impReadyToRunHelperToTree(pResolvedToken, CORINFO_HELP_READYTORUN_GENERIC_HANDLE, TYP_I_IMPL,
&pCallInfo->codePointerLookup.lookupKind, ctxTree);
}
}
#endif

// Get the exact descriptor for the static callsite
GenTree* exactTypeDesc = impParentClassTokenToHandle(pResolvedToken);
if (exactTypeDesc == nullptr)
{ // compDonotInline()
return nullptr;
}
if (call == nullptr)
{
// Get the exact descriptor for the static callsite
GenTree* exactTypeDesc = impParentClassTokenToHandle(pResolvedToken);
if (exactTypeDesc == nullptr)
{
assert(compIsForInlining());
return nullptr;
}

GenTree* exactMethodDesc = impTokenToHandle(pResolvedToken);
if (exactMethodDesc == nullptr)
{ // compDonotInline()
return nullptr;
GenTree* exactMethodDesc = impTokenToHandle(pResolvedToken);
if (exactMethodDesc == nullptr)
{
assert(compIsForInlining());
return nullptr;
}

// 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 helper function. This gets the target address of the final destination callsite.
assert(call != nullptr);

if (isInterface)
{
// Annotate helper so later on if helper result is unconsumed we know it is not sound
// to optimize the call into a null check.
//
call->gtCallMoreFlags |= GTF_CALL_M_LDVIRTFTN_INTERFACE;
}

return gtNewHelperCallNode(CORINFO_HELP_VIRTUAL_FUNC_PTR, TYP_I_IMPL, thisPtr, exactTypeDesc, exactMethodDesc);
return call;
}

//------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7802,7 +7802,7 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call)
#endif
}

if ((call->gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC) == 0 &&
if (((call->gtCallMoreFlags & (GTF_CALL_M_SPECIAL_INTRINSIC | GTF_CALL_M_LDVIRTFTN_INTERFACE)) == 0) &&
(call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_VIRTUAL_FUNC_PTR)
#ifdef FEATURE_READYTORUN
|| call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR)
Expand Down

0 comments on commit cbe05f9

Please sign in to comment.