Interface call devirtualization: VM side of changes
authorAndy Ayers <andya@microsoft.com>
Thu, 16 Mar 2017 15:30:17 +0000 (08:30 -0700)
committerAndy Ayers <andya@microsoft.com>
Thu, 16 Mar 2017 20:51:36 +0000 (13:51 -0700)
Extend resolveVirtualMethod to take in the owner type needed to resolve
shared generic interface calls, and implement VM support for interface
call devirtualization. Add a check that the class presented by the jit
implements the interface, to catch cases where the IL is missing type
casts.

Update jit GUID, SPMI and zap to reflect the changed signature.

Commit migrated from https://github.com/dotnet/coreclr/commit/00f13f2fb04c21fc1675ce264ef7291cbcec67a1

13 files changed:
src/coreclr/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
src/coreclr/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
src/coreclr/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
src/coreclr/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
src/coreclr/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
src/coreclr/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
src/coreclr/src/inc/corinfo.h
src/coreclr/src/vm/jitinterface.cpp
src/coreclr/src/vm/jitinterface.h
src/coreclr/src/zap/zapinfo.cpp
src/coreclr/src/zap/zapinfo.h

index 4c1aa3d..1e83343 100644 (file)
@@ -129,7 +129,8 @@ public:
     // Return null if devirtualization is not possible.
     CORINFO_METHOD_HANDLE resolveVirtualMethod(
         CORINFO_METHOD_HANDLE virtualMethod,
-        CORINFO_CLASS_HANDLE implementingClass
+        CORINFO_CLASS_HANDLE implementingClass,
+        CORINFO_CONTEXT_HANDLE ownerType
         );
 
     // If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
index 8e19656..6e5f016 100644 (file)
@@ -141,7 +141,7 @@ LWM(IsWriteBarrierHelperRequired, DWORDLONG, DWORD)
 LWM(MergeClasses, DLDL, DWORDLONG)
 LWM(PInvokeMarshalingRequired, Agnostic_PInvokeMarshalingRequired, DWORD)
 LWM(ResolveToken, Agnostic_CORINFO_RESOLVED_TOKENin, Agnostic_CORINFO_RESOLVED_TOKENout)
-LWM(ResolveVirtualMethod, DLDL, DWORDLONG)
+LWM(ResolveVirtualMethod, Agnostic_ResolveVirtualMethod, DWORDLONG)
 LWM(TryResolveToken, Agnostic_CORINFO_RESOLVED_TOKENin, Agnostic_CORINFO_RESOLVED_TOKENout)
 LWM(SatisfiesClassConstraints, DWORDLONG, DWORD)
 LWM(SatisfiesMethodConstraints, DLDL, DWORD)
index c60b593..39bcefe 100644 (file)
@@ -3296,33 +3296,40 @@ void MethodContext::repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method, unsig
     DEBUG_REP(dmpGetMethodVTableOffset((DWORDLONG)method, value));
 }
 
-void MethodContext::recResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod, CORINFO_CLASS_HANDLE implClass, CORINFO_METHOD_HANDLE result)
+void MethodContext::recResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod, CORINFO_CLASS_HANDLE implClass,
+    CORINFO_CONTEXT_HANDLE ownerType, CORINFO_METHOD_HANDLE result)
 {
     if (ResolveVirtualMethod == nullptr)
     {
-        ResolveVirtualMethod = new LightWeightMap<DLDL, DWORDLONG>();
+        ResolveVirtualMethod = new LightWeightMap<Agnostic_ResolveVirtualMethod, DWORDLONG>();
     }
 
-    DLDL key;
-    key.A = (DWORDLONG)virtMethod;
-    key.B = (DWORDLONG)implClass;
+    Agnostic_ResolveVirtualMethod key;
+    key.virtualMethod = (DWORDLONG)virtMethod;
+    key.implementingClass = (DWORDLONG)implClass;
+    key.ownerType = (DWORDLONG)ownerType;
     ResolveVirtualMethod->Add(key, (DWORDLONG) result);
     DEBUG_REC(dmpResolveVirtualMethod(key, result));
 }
 
-void MethodContext::dmpResolveVirtualMethod(DLDL key, DWORDLONG value)
+void MethodContext::dmpResolveVirtualMethod(const Agnostic_ResolveVirtualMethod& key, DWORDLONG value)
 {
-    printf("ResolveVirtualMethod virtMethod-%016llX, implClass-%016llX, result-%016llX", key.A, key.B, value);
+    printf("ResolveVirtualMethod virtMethod-%016llX, implClass-%016llX, ownerType--%01611X, result-%016llX",
+        key.virtualMethod, key.implementingClass, key.ownerType, value);
 }
 
-CORINFO_METHOD_HANDLE MethodContext::repResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod, CORINFO_CLASS_HANDLE implClass)
+CORINFO_METHOD_HANDLE MethodContext::repResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod, CORINFO_CLASS_HANDLE implClass,
+    CORINFO_CONTEXT_HANDLE ownerType)
 {
-    DLDL key;
-    key.A = (DWORDLONG)virtMethod;
-    key.B = (DWORDLONG)implClass;
+    Agnostic_ResolveVirtualMethod key;
+    key.virtualMethod = (DWORDLONG)virtMethod;
+    key.implementingClass = (DWORDLONG)implClass;
+    key.ownerType = (DWORDLONG)ownerType;
 
-    AssertCodeMsg(ResolveVirtualMethod != nullptr, EXCEPTIONCODE_MC, "No ResolveVirtualMap map for %016llX-%016llX", key.A, key.B);
-    AssertCodeMsg(ResolveVirtualMethod->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX-%016llx", key.A, key.B);
+    AssertCodeMsg(ResolveVirtualMethod != nullptr, EXCEPTIONCODE_MC, "No ResolveVirtualMap map for %016llX-%016llX-%016llX",
+        key.virtualMethod, key.implementingClass, key.ownerType);
+    AssertCodeMsg(ResolveVirtualMethod->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX-%016llx-%016llX",
+        key.virtualMethod, key.implementingClass, key.ownerType);
     DWORDLONG result = ResolveVirtualMethod->Get(key);
 
     DEBUG_REP(dmpResolveVirtualMethod(key, result));
index ee2d4ac..5827fff 100644 (file)
@@ -436,6 +436,13 @@ public:
         DWORD   result;
     };
 
+    struct Agnostic_ResolveVirtualMethod
+    {
+        DWORDLONG virtualMethod;
+        DWORDLONG implementingClass;
+        DWORDLONG ownerType;
+    };
+
 #pragma pack(pop)
 
     MethodContext();
@@ -698,9 +705,11 @@ public:
     void dmpGetMethodVTableOffset(DWORDLONG key, DD value);
     void repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method, unsigned *offsetOfIndirection, unsigned* offsetAfterIndirection);
 
-    void recResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod, CORINFO_CLASS_HANDLE implClass, CORINFO_METHOD_HANDLE result);
-    void dmpResolveVirtualMethod(DLDL key, DWORDLONG value);
-    CORINFO_METHOD_HANDLE repResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod, CORINFO_CLASS_HANDLE implClass);
+    void recResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod, CORINFO_CLASS_HANDLE implClass,
+        CORINFO_CONTEXT_HANDLE ownerType, CORINFO_METHOD_HANDLE result);
+    void dmpResolveVirtualMethod(const Agnostic_ResolveVirtualMethod& key, DWORDLONG value);
+    CORINFO_METHOD_HANDLE repResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod, CORINFO_CLASS_HANDLE implClass,
+        CORINFO_CONTEXT_HANDLE ownerType);
 
     void recGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_CLASS_HANDLE result);
     void dmpGetTokenTypeAsHandle(const Agnostic_CORINFO_RESOLVED_TOKEN& key, DWORDLONG value);
index bb19cab..01e98cd 100644 (file)
@@ -240,12 +240,13 @@ void interceptor_ICJI::getMethodVTableOffset (
 // Return null if devirtualization is not possible.
 CORINFO_METHOD_HANDLE interceptor_ICJI::resolveVirtualMethod(
     CORINFO_METHOD_HANDLE virtualMethod,
-    CORINFO_CLASS_HANDLE implementingClass
+    CORINFO_CLASS_HANDLE implementingClass,
+    CORINFO_CONTEXT_HANDLE ownerType
     )
 {
     mc->cr->AddCall("resolveVirtualMethod");
-    CORINFO_METHOD_HANDLE result = original_ICorJitInfo->resolveVirtualMethod(virtualMethod, implementingClass);
-    mc->recResolveVirtualMethod(virtualMethod, implementingClass, result);
+    CORINFO_METHOD_HANDLE result = original_ICorJitInfo->resolveVirtualMethod(virtualMethod, implementingClass, ownerType);
+    mc->recResolveVirtualMethod(virtualMethod, implementingClass, ownerType, result);
     return result;
 }
 
index beff1f4..705b1e8 100644 (file)
@@ -169,11 +169,12 @@ void interceptor_ICJI::getMethodVTableOffset (
 // Return null if devirtualization is not possible.
 CORINFO_METHOD_HANDLE interceptor_ICJI::resolveVirtualMethod(
     CORINFO_METHOD_HANDLE virtualMethod,
-    CORINFO_CLASS_HANDLE implementingClass
+    CORINFO_CLASS_HANDLE implementingClass,
+    CORINFO_CONTEXT_HANDLE ownerType
     )
 {
     mcs->AddCall("resolveVirtualMethod");
-    return original_ICorJitInfo->resolveVirtualMethod(virtualMethod, implementingClass);
+    return original_ICorJitInfo->resolveVirtualMethod(virtualMethod, implementingClass, ownerType);
 }
 
 // If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
index 9e229ee..9783983 100644 (file)
@@ -157,10 +157,11 @@ void interceptor_ICJI::getMethodVTableOffset (
 // Return null if devirtualization is not possible.
 CORINFO_METHOD_HANDLE interceptor_ICJI::resolveVirtualMethod(
     CORINFO_METHOD_HANDLE virtualMethod,
-    CORINFO_CLASS_HANDLE implementingClass
+    CORINFO_CLASS_HANDLE implementingClass,
+    CORINFO_CONTEXT_HANDLE ownerType
     )
 {
-    return original_ICorJitInfo->resolveVirtualMethod(virtualMethod, implementingClass);
+    return original_ICorJitInfo->resolveVirtualMethod(virtualMethod, implementingClass, ownerType);
 }
 
 // If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
index ae4ba99..91ebb4d 100644 (file)
@@ -189,11 +189,12 @@ void MyICJI::getMethodVTableOffset (
 // Return null if devirtualization is not possible.
 CORINFO_METHOD_HANDLE MyICJI::resolveVirtualMethod(
     CORINFO_METHOD_HANDLE virtualMethod,
-    CORINFO_CLASS_HANDLE implementingClass
+    CORINFO_CLASS_HANDLE implementingClass,
+    CORINFO_CONTEXT_HANDLE ownerType
     )
 {
     jitInstance->mc->cr->AddCall("resolveVirtualMethod");
-    CORINFO_METHOD_HANDLE result = jitInstance->mc->repResolveVirtualMethod(virtualMethod, implementingClass);
+    CORINFO_METHOD_HANDLE result = jitInstance->mc->repResolveVirtualMethod(virtualMethod, implementingClass, ownerType);
     return result;
 }
 
index c094c94..7473de8 100644 (file)
@@ -231,11 +231,11 @@ TODO: Talk about initializing strutures before use
 #if COR_JIT_EE_VERSION > 460
 
 // Update this one
-SELECTANY const GUID JITEEVersionIdentifier = { /* cda334f7-0020-4622-a4a5-8b8ac71ee5cf */
-    0xcda334f7,
-    0x0020,
-    0x4622,
-    {0xa4, 0xa5, 0x8b, 0x8a, 0xc7, 0x1e, 0xe5, 0xcf}
+SELECTANY const GUID JITEEVersionIdentifier = { /* 3d43decb-a611-4413-a0af-a24278a00e2d */
+    0x3d43decb,
+    0xa611,
+    0x4413,
+    {0xa0, 0xaf, 0xa2, 0x42, 0x78, 0xa0, 0x0e, 0x2d}
   };
 
 #else
@@ -2117,12 +2117,17 @@ public:
             ) = 0;
 
 #if COR_JIT_EE_VERSION > 460
-    // Find the virtual method in implementingClass that overrides virtualMethod.
-    // Return null if devirtualization is not possible.
+    // Find the virtual method in implementingClass that overrides virtualMethod,
+    // or the method in implementingClass that implements the interface method
+    // represented by virtualMethod.
+    //
+    // Return null if devirtualization is not possible. Owner type is optional
+    // and provides additional context for shared interface devirtualization.
     virtual CORINFO_METHOD_HANDLE resolveVirtualMethod(
-        CORINFO_METHOD_HANDLE virtualMethod,        /* IN */
-        CORINFO_CLASS_HANDLE implementingClass      /* IN */
-        ) = 0;
+            CORINFO_METHOD_HANDLE       virtualMethod,          /* IN */
+            CORINFO_CLASS_HANDLE        implementingClass,      /* IN */
+            CORINFO_CONTEXT_HANDLE      ownerType = NULL        /* IN */
+            ) = 0;
 #endif
 
     // If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
index bdccdef..1639e2d 100644 (file)
@@ -8716,7 +8716,8 @@ void CEEInfo::getMethodVTableOffset (CORINFO_METHOD_HANDLE methodHnd,
 /*********************************************************************/
 static CORINFO_METHOD_HANDLE resolveVirtualMethodHelper(MethodDesc* callerMethod,
                                                         CORINFO_METHOD_HANDLE baseMethod,
-                                                        CORINFO_CLASS_HANDLE derivedClass)
+                                                        CORINFO_CLASS_HANDLE derivedClass,
+                                                        CORINFO_CONTEXT_HANDLE ownerType)
 {
     STANDARD_VM_CONTRACT;
 
@@ -8729,15 +8730,11 @@ static CORINFO_METHOD_HANDLE resolveVirtualMethodHelper(MethodDesc* callerMethod
     //@GENERICS: shouldn't be doing this for instantiated methods as they live elsewhere
     _ASSERTE(!pBaseMD->HasMethodInstantiation());
 
-    // Interface call devirtualization is not yet supported.
-    if (pBaseMT->IsInterface())
-    {
-        return nullptr;
-    }
-
     // Method better be virtual
     _ASSERTE(pBaseMD->IsVirtual());
 
+    MethodDesc* pDevirtMD = nullptr;
+
     TypeHandle DerivedClsHnd(derivedClass);
     MethodTable* pDerivedMT = DerivedClsHnd.GetMethodTable();
     _ASSERTE(pDerivedMT->IsRestored() && pDerivedMT->IsFullyLoaded());
@@ -8748,33 +8745,65 @@ static CORINFO_METHOD_HANDLE resolveVirtualMethodHelper(MethodDesc* callerMethod
         return nullptr;
     }
 
-    // The derived class should be a subclass of the the base class.
-    MethodTable* pCheckMT = pDerivedMT;
-
-    while (pCheckMT != nullptr)
+    if (pBaseMT->IsInterface())
     {
-        if (pCheckMT->HasSameTypeDefAs(pBaseMT))
+        // Interface call devirtualization.
+        //
+        // We must ensure that pDerivedMT actually implements the
+        // interface corresponding to pBaseMD.
+        if (!pDerivedMT->CanCastToInterface(pBaseMT))
         {
-            break;
+            return nullptr;
         }
 
-        pCheckMT = pCheckMT->GetParentMethodTable();
+        // For generic interface methods we must have an ownerType to
+        // safely devirtualize.
+        if (ownerType != nullptr)
+        {
+            pDevirtMD = pDerivedMT->GetMethodDescForInterfaceMethod(GetTypeFromContext(ownerType), pBaseMD);
+        }
+        else if (!pBaseMD->HasClassOrMethodInstantiation())
+        {
+            pDevirtMD = pDerivedMT->GetMethodDescForInterfaceMethod(pBaseMD);
+        }
+        else
+        {
+            return nullptr;
+        }
     }
-
-    if (pCheckMT == nullptr)
+    else
     {
-        return nullptr;
+        // Virtual call devirtualization.
+        // 
+        // The derived class should be a subclass of the the base class.
+        MethodTable* pCheckMT = pDerivedMT;
+        
+        while (pCheckMT != nullptr)
+        {
+            if (pCheckMT->HasSameTypeDefAs(pBaseMT))
+            {
+                break;
+            }
+            
+            pCheckMT = pCheckMT->GetParentMethodTable();
+        }
+        
+        if (pCheckMT == nullptr)
+        {
+            return nullptr;
+        }
+        
+        // The base method should be in the base vtable
+        WORD slot = pBaseMD->GetSlot();
+        _ASSERTE(slot < pBaseMT->GetNumVirtuals());
+        _ASSERTE(pBaseMD == pBaseMT->GetMethodDescForSlot(slot));
+        
+        // Fetch the method that would be invoked if the class were
+        // exactly derived class. It is up to the jit to determine whether
+        // directly calling this method is correct.
+        pDevirtMD = pDerivedMT->GetMethodDescForSlot(slot);
     }
 
-    // The base method should be in the base vtable
-    WORD slot = pBaseMD->GetSlot();
-    _ASSERTE(slot < pBaseMT->GetNumVirtuals());
-    _ASSERTE(pBaseMD == pBaseMT->GetMethodDescForSlot(slot));
-
-    // Fetch the method that would be invoked if the class were
-    // exactly derived class. It is up to the jit to determine whether
-    // directly calling this method is correct.
-    MethodDesc* pDevirtMD = pDerivedMT->GetMethodDescForSlot(slot);
     _ASSERTE(pDevirtMD->IsRestored());
 
 #ifdef FEATURE_READYTORUN_COMPILER
@@ -8798,7 +8827,8 @@ static CORINFO_METHOD_HANDLE resolveVirtualMethodHelper(MethodDesc* callerMethod
 }
 
 CORINFO_METHOD_HANDLE CEEInfo::resolveVirtualMethod(CORINFO_METHOD_HANDLE methodHnd,
-                                                    CORINFO_CLASS_HANDLE derivedClass)
+                                                    CORINFO_CLASS_HANDLE derivedClass,
+                                                    CORINFO_CONTEXT_HANDLE ownerType)
 {
     STANDARD_VM_CONTRACT;
 
@@ -8806,7 +8836,7 @@ CORINFO_METHOD_HANDLE CEEInfo::resolveVirtualMethod(CORINFO_METHOD_HANDLE method
 
     JIT_TO_EE_TRANSITION();
 
-    result = resolveVirtualMethodHelper(m_pMethodBeingCompiled, methodHnd, derivedClass);
+    result = resolveVirtualMethodHelper(m_pMethodBeingCompiled, methodHnd, derivedClass, ownerType);
 
     EE_TO_JIT_TRANSITION();
 
index c0df0cf..a432b59 100644 (file)
@@ -731,7 +731,8 @@ public:
 
     CORINFO_METHOD_HANDLE resolveVirtualMethod(
         CORINFO_METHOD_HANDLE virtualMethod,
-        CORINFO_CLASS_HANDLE implementingClass
+        CORINFO_CLASS_HANDLE implementingClass,
+        CORINFO_CONTEXT_HANDLE ownerType
         );
 
     CorInfoIntrinsics getIntrinsicID(CORINFO_METHOD_HANDLE method,
index e817e44..4cc7e9f 100644 (file)
@@ -3729,10 +3729,11 @@ void ZapInfo::getMethodVTableOffset(CORINFO_METHOD_HANDLE method,
 
 CORINFO_METHOD_HANDLE ZapInfo::resolveVirtualMethod(
         CORINFO_METHOD_HANDLE virtualMethod,
-        CORINFO_CLASS_HANDLE implementingClass
+        CORINFO_CLASS_HANDLE implementingClass,
+        CORINFO_CONTEXT_HANDLE ownerType
         )
 {
-    return m_pEEJitInfo->resolveVirtualMethod(virtualMethod, implementingClass);
+    return m_pEEJitInfo->resolveVirtualMethod(virtualMethod, implementingClass, ownerType);
 }
 
 CorInfoIntrinsics ZapInfo::getIntrinsicID(CORINFO_METHOD_HANDLE method,
index c0846d1..973170f 100644 (file)
@@ -667,7 +667,8 @@ public:
 
     CORINFO_METHOD_HANDLE resolveVirtualMethod(
         CORINFO_METHOD_HANDLE virtualMethod,
-        CORINFO_CLASS_HANDLE implementingClass
+        CORINFO_CLASS_HANDLE implementingClass,
+        CORINFO_CONTEXT_HANDLE ownerType
         );
 
     CorInfoIntrinsics getIntrinsicID(CORINFO_METHOD_HANDLE method,