diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 5a123c8f0a6a6a..7e696d8475eb35 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7555,6 +7555,7 @@ class Compiler unsigned likelihood, bool arrayInterface, bool instantiatingStub, + CORINFO_METHOD_HANDLE originalMethodHandle, CORINFO_CONTEXT_HANDLE originalContextHandle); int getGDVMaxTypeChecks() diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index be1873657fa8dc..dbb524e4d89d5d 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -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; diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 10ac50c6dabbad..2184756da0518c 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -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) @@ -7473,7 +7473,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call, // addGuardedDevirtualizationCandidate(call, likelyMethod, likelyClass, likelyContext, likelyMethodAttribs, likelyClassAttribs, likelihood, arrayInterface, instantiatingStub, - originalContext); + baseMethod, originalContext); } } @@ -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, @@ -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 @@ -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; @@ -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; @@ -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; diff --git a/src/coreclr/jit/indirectcalltransformer.cpp b/src/coreclr/jit/indirectcalltransformer.cpp index 48c66692a42e8c..cfa2a7518074ca 100644 --- a/src/coreclr/jit/indirectcalltransformer.cpp +++ b/src/coreclr/jit/indirectcalltransformer.cpp @@ -973,7 +973,7 @@ class IndirectCallTransformer // if (inlineInfo->arrayInterface) { - methodHnd = call->gtCallMethHnd; + methodHnd = inlineInfo->originalMethodHandle; context = inlineInfo->originalContextHandle; } diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h index 6d869ab3bb8008..a89be81fa68895 100644 --- a/src/coreclr/jit/inline.h +++ b/src/coreclr/jit/inline.h @@ -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. diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 784043ae4b4cb4..d3c9285468b28e 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -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)) @@ -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(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.Enumerators.cs b/src/libraries/System.Private.CoreLib/src/System/Array.Enumerators.cs index c2bbd78c474f2d..0acdb27afa622b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.Enumerators.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.Enumerators.cs @@ -110,6 +110,7 @@ internal sealed class SZGenericArrayEnumerator : SZGenericArrayEnumeratorBase /// internal static readonly SZGenericArrayEnumerator Empty = new SZGenericArrayEnumerator(null, 0); + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal SZGenericArrayEnumerator(T[]? array, int endIndex) : base(endIndex) {