From f90bd197a22cc4921c793d2b3dbfbe5b6b386369 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Sun, 5 Dec 2021 14:58:53 -0800 Subject: [PATCH] Support for devirtualizing array interface methods Update jit and runtime to devirtualize interface calls on arrays over non-shared element types. Shared types are not handled. The resulting enumerator calls would not inline as they are on a different class. This addresses the first two issues noted in #62457. --- src/coreclr/jit/importer.cpp | 18 +++++++++++--- src/coreclr/vm/jitinterface.cpp | 44 ++++++++++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 7860d7742e7c9..20e5e66b315ff 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -21181,8 +21181,18 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, if (derivedMethod != nullptr) { assert(exactContext != nullptr); - assert(((size_t)exactContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS); - derivedClass = (CORINFO_CLASS_HANDLE)((size_t)exactContext & ~CORINFO_CONTEXTFLAGS_MASK); + + if (((size_t)exactContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS) + { + derivedClass = (CORINFO_CLASS_HANDLE)((size_t)exactContext & ~CORINFO_CONTEXTFLAGS_MASK); + } + else + { + // Array interface devirt can return a generic method of the non-generic SZArrayHelper class. + // + assert(((size_t)exactContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_METHOD); + derivedClass = info.compCompHnd->getMethodClass(derivedMethod); + } } DWORD derivedMethodAttribs = 0; @@ -21603,7 +21613,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, // if (pExactContextHandle != nullptr) { - *pExactContextHandle = MAKE_CLASSCONTEXT(derivedClass); + *pExactContextHandle = exactContext; } #ifdef FEATURE_READYTORUN @@ -21933,7 +21943,7 @@ void Compiler::considerGuardedDevirtualization( if (!canResolve) { - JITDUMP("Can't figure out which method would be invoked, sorry\n"); + JITDUMP("Can't figure out which method would be invoked, sorry (reason %d)\n", dvInfo.detail); return; } diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 8b706253186ab..739f21fdca8ed 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8587,7 +8587,27 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) // // We must ensure that pObjMT actually implements the // interface corresponding to pBaseMD. - if (!pObjMT->CanCastToInterface(pBaseMT)) + // + 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. + // + 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 (!pObjMT->CanCastToInterface(pBaseMT)) { info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CAST; return false; @@ -8697,8 +8717,12 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) // We may fail to get an exact context if the method is a default // interface method. If so, we'll use the method's class. // + // For array -> interface devirt, the devirtualized methods + // will be defined by SZArrayHelper. + // MethodTable* pApproxMT = pDevirtMD->GetMethodTable(); MethodTable* pExactMT = pApproxMT; + bool isArray = false; if (pApproxMT->IsInterface()) { @@ -8707,6 +8731,10 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) _ASSERTE(!pDevirtMD->HasClassInstantiation()); } + else if (pBaseMT->IsInterface() && pObjMT->IsArray()) + { + isArray = true; + } else { pExactMT = pDevirtMD->GetExactDeclaringType(pObjMT); @@ -8715,8 +8743,18 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) // Success! Pass back the results. // info->devirtualizedMethod = (CORINFO_METHOD_HANDLE) pDevirtMD; - info->exactContext = MAKE_CLASSCONTEXT((CORINFO_CLASS_HANDLE) pExactMT); - info->requiresInstMethodTableArg = false; + + if (isArray) + { + info->exactContext = MAKE_METHODCONTEXT((CORINFO_METHOD_HANDLE) pDevirtMD); + info->requiresInstMethodTableArg = pDevirtMD->RequiresInstMethodTableArg(); + } + else + { + info->exactContext = MAKE_CLASSCONTEXT((CORINFO_CLASS_HANDLE) pExactMT); + info->requiresInstMethodTableArg = false; + } + info->detail = CORINFO_DEVIRTUALIZATION_SUCCESS; return true;