Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -7555,6 +7555,7 @@ class Compiler
unsigned likelihood,
bool arrayInterface,
bool instantiatingStub,
CORINFO_METHOD_HANDLE originalMethodHandle,
CORINFO_CONTEXT_HANDLE originalContextHandle);

int getGDVMaxTypeChecks()
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6941,6 +6941,16 @@ void Compiler::impImportBlockCode(BasicBlock* block)
bool isNonNull = false;
CORINFO_CLASS_HANDLE retCls = gtGetClassHandle(call, &isExact, &isNonNull);

if ((retCls == NO_CLASS_HANDLE) && call->IsGuardedDevirtualizationCandidate())
{
// Just check one of the GDV candidates (all should have the same original method handle)
//
InlineCandidateInfo* const inlineInfo = call->GetGDVCandidateInfo(0);
CORINFO_SIG_INFO sig;
info.compCompHnd->getMethodSig(inlineInfo->originalMethodHandle, &sig);
retCls = sig.retTypeClass;
}

if ((retCls != NO_CLASS_HANDLE) && info.compCompHnd->isIntrinsicType(retCls))
{
const char* namespaceName;
Expand Down
17 changes: 15 additions & 2 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7324,7 +7324,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,

addGuardedDevirtualizationCandidate(call, exactMethod, exactCls, exactContext, exactMethodAttrs,
clsAttrs, likelyHood, dvInfo.wasArrayInterfaceDevirt,
dvInfo.isInstantiatingStub, originalContext);
dvInfo.isInstantiatingStub, baseMethod, originalContext);
}

if (call->GetInlineCandidatesCount() == numExactClasses)
Expand Down Expand Up @@ -7473,7 +7473,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,
//
addGuardedDevirtualizationCandidate(call, likelyMethod, likelyClass, likelyContext, likelyMethodAttribs,
likelyClassAttribs, likelihood, arrayInterface, instantiatingStub,
originalContext);
baseMethod, originalContext);
}
}

Expand All @@ -7500,6 +7500,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,
// likelihood - odds that this class is the class seen at runtime
// arrayInterface - devirtualization of an array interface call
// instantiatingStub - devirtualized method in an instantiating stub
// originalMethodHandle - method handle of base method (before devirt)
// originalContextHandle - context for the original call
//
void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call,
Expand All @@ -7511,6 +7512,7 @@ void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call,
unsigned likelihood,
bool arrayInterface,
bool instantiatingStub,
CORINFO_METHOD_HANDLE originalMethodHandle,
CORINFO_CONTEXT_HANDLE originalContextHandle)
{
// This transformation only makes sense for delegate and virtual calls
Expand Down Expand Up @@ -7583,6 +7585,7 @@ void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call,
pInfo->guardedMethodUnboxedEntryHandle = nullptr;
pInfo->guardedMethodInstantiatedEntryHandle = nullptr;
pInfo->guardedClassHandle = classHandle;
pInfo->originalMethodHandle = originalMethodHandle;
pInfo->originalContextHandle = originalContextHandle;
pInfo->likelihood = likelihood;
pInfo->exactContextHandle = contextHandle;
Expand Down Expand Up @@ -8531,6 +8534,15 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
return;
}

// If we don't know the array type exactly we may have the wrong interface type here.
// Bail out.
//
if (!isExact)
{
JITDUMP("Array interface devirt: array type is inexact, sorry.\n");
return;
}

// We want to inline the instantiating stub. Fetch the relevant info.
//
CORINFO_CLASS_HANDLE ignored = NO_CLASS_HANDLE;
Expand Down Expand Up @@ -9503,6 +9515,7 @@ void Compiler::impCheckCanInline(GenTreeCall* call,
pInfo->guardedMethodHandle = nullptr;
pInfo->guardedMethodUnboxedEntryHandle = nullptr;
pInfo->guardedMethodInstantiatedEntryHandle = nullptr;
pInfo->originalMethodHandle = nullptr;
pInfo->originalContextHandle = nullptr;
pInfo->likelihood = 0;
pInfo->arrayInterface = false;
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/indirectcalltransformer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -973,7 +973,7 @@ class IndirectCallTransformer
//
if (inlineInfo->arrayInterface)
{
methodHnd = call->gtCallMethHnd;
methodHnd = inlineInfo->originalMethodHandle;
context = inlineInfo->originalContextHandle;
}

Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/jit/inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -610,9 +610,10 @@ struct InlineCandidateInfo : public HandleHistogramProfileCandidateInfo
//
CORINFO_CONTEXT_HANDLE exactContextHandle;

// Context handle of the call before any
// Method and context handle of the call before any
// GDV/Inlining evaluation
//
CORINFO_METHOD_HANDLE originalMethodHandle;
CORINFO_CONTEXT_HANDLE originalContextHandle;

// The GT_RET_EXPR node linking back to the inline candidate.
Expand Down
46 changes: 33 additions & 13 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8609,23 +8609,29 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info)
// We must ensure that pObjMT actually implements the
// interface corresponding to pBaseMD.
//
bool isArrayImplicitInterface = false;

if (pObjMT->IsArray())
{
// If we're in a shared context we'll devirt to a shared
// generic method and won't be able to inline, so just bail.
// Does the array implicitly implement this interface?
//
if (pBaseMT->IsSharedByGenericInstantiations())
{
info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON;
return false;
}
isArrayImplicitInterface = pBaseMT->HasInstantiation() && IsImplicitInterfaceOfSZArray(pBaseMT);

// Ensure we can cast the array to the interface type
//
if (!TypeHandle(pObjMT).CanCastTo(TypeHandle(pBaseMT)))
if (!isArrayImplicitInterface)
{
info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CAST;
return false;
if (pBaseMT->IsSharedByGenericInstantiations())
{
info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON;
return false;
}

// Ensure we can cast the array to the interface type
//
if (!TypeHandle(pObjMT).CanCastTo(TypeHandle(pBaseMT)))
{
info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CAST;
return false;
}
}
}
else if (!pBaseMT->IsSharedByGenericInstantiations() && !pObjMT->CanCastToInterface(pBaseMT))
Expand All @@ -8640,7 +8646,21 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info)
{
MethodTable* interfaceMT = nullptr;

if (pObjMT->IsSharedByGenericInstantiations() || pBaseMT->IsSharedByGenericInstantiations())
if (isArrayImplicitInterface)
{
_ASSERTE(pObjMT->IsArray());

// We cannot devirtualize unless we know the exact array element type
//
TypeHandle elemType = pObjMT->GetArrayElementTypeHandle();
if (elemType.IsCanonicalSubtype())
{
info->detail = CORINFO_DEVIRTUALIZATION_FAILED_LOOKUP;
return false;
}
pDevirtMD = GetActualImplementationForArrayGenericIListOrIReadOnlyListMethod(pBaseMD, elemType);
}
else if (pObjMT->IsSharedByGenericInstantiations() || pBaseMT->IsSharedByGenericInstantiations())
{
MethodTable* pCanonBaseMT = pBaseMT->GetCanonicalMethodTable();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ internal sealed class SZGenericArrayEnumerator<T> : SZGenericArrayEnumeratorBase
/// </remarks>
internal static readonly SZGenericArrayEnumerator<T> Empty = new SZGenericArrayEnumerator<T>(null, 0);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal SZGenericArrayEnumerator(T[]? array, int endIndex)
: base(endIndex)
{
Expand Down
Loading