Jit interface support for devirtualization
authorAndy Ayers <andya@microsoft.com>
Mon, 30 Jan 2017 23:45:11 +0000 (15:45 -0800)
committerAndy Ayers <andya@microsoft.com>
Thu, 2 Mar 2017 15:49:33 +0000 (07:49 -0800)
Add new method to jit interface so the jit can determine what derived
method might be called for a given base method, derived class pair.

Implement support in the VM and in other places (zap, spmi).

Commit migrated from https://github.com/dotnet/coreclr/commit/35c23eccfdeaddbd07cd39473fe7544603e67857

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 b847d9b..4c1aa3d 100644 (file)
@@ -125,6 +125,13 @@ public:
             unsigned*                   offsetAfterIndirection  /* OUT */
             );
 
+    // Find the virtual method in implementingClass that overrides virtualMethod.
+    // Return null if devirtualization is not possible.
+    CORINFO_METHOD_HANDLE resolveVirtualMethod(
+        CORINFO_METHOD_HANDLE virtualMethod,
+        CORINFO_CLASS_HANDLE implementingClass
+        );
+
     // If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
     // getIntrinsicID() returns the intrinsic ID.
     // *pMustExpand tells whether or not JIT must expand the intrinsic.
index de0db3a..8e19656 100644 (file)
@@ -141,6 +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(TryResolveToken, Agnostic_CORINFO_RESOLVED_TOKENin, Agnostic_CORINFO_RESOLVED_TOKENout)
 LWM(SatisfiesClassConstraints, DWORDLONG, DWORD)
 LWM(SatisfiesMethodConstraints, DLDL, DWORD)
index 5768d38..c60b593 100644 (file)
@@ -3296,6 +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)
+{
+    if (ResolveVirtualMethod == nullptr)
+    {
+        ResolveVirtualMethod = new LightWeightMap<DLDL, DWORDLONG>();
+    }
+
+    DLDL key;
+    key.A = (DWORDLONG)virtMethod;
+    key.B = (DWORDLONG)implClass;
+    ResolveVirtualMethod->Add(key, (DWORDLONG) result);
+    DEBUG_REC(dmpResolveVirtualMethod(key, result));
+}
+
+void MethodContext::dmpResolveVirtualMethod(DLDL key, DWORDLONG value)
+{
+    printf("ResolveVirtualMethod virtMethod-%016llX, implClass-%016llX, result-%016llX", key.A, key.B, value);
+}
+
+CORINFO_METHOD_HANDLE MethodContext::repResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod, CORINFO_CLASS_HANDLE implClass)
+{
+    DLDL key;
+    key.A = (DWORDLONG)virtMethod;
+    key.B = (DWORDLONG)implClass;
+
+    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);
+    DWORDLONG result = ResolveVirtualMethod->Get(key);
+
+    DEBUG_REP(dmpResolveVirtualMethod(key, result));
+
+    return (CORINFO_METHOD_HANDLE)result;
+}
+
 void MethodContext::recGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_CLASS_HANDLE result)
 {
     if (GetTokenTypeAsHandle == nullptr)
@@ -6165,7 +6199,7 @@ mdMethodDef MethodContext::repGetMethodDefFromMethod(CORINFO_METHOD_HANDLE hMeth
 
     int index = GetMethodDefFromMethod->GetIndex((DWORDLONG)hMethod);
     if (index < 0)
-        return (mdMethodDef)0x06000001;    
+        return (mdMethodDef)0x06000001;
 
     return (mdMethodDef)GetMethodDefFromMethod->Get((DWORDLONG)hMethod);
 }
index 0d49666..ee2d4ac 100644 (file)
@@ -698,6 +698,10 @@ 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 recGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_CLASS_HANDLE result);
     void dmpGetTokenTypeAsHandle(const Agnostic_CORINFO_RESOLVED_TOKEN& key, DWORDLONG value);
     CORINFO_CLASS_HANDLE repGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken);
@@ -1016,7 +1020,7 @@ private:
 
 
 // ********************* Please keep this up-to-date to ease adding more ***************
-// Highest packet number: 159
+// Highest packet number: 160
 // *************************************************************************************
 enum mcPackets
 {
@@ -1151,6 +1155,7 @@ enum mcPackets
     Packet_MergeClasses = 107,
     Packet_PInvokeMarshalingRequired = 108,
     Packet_ResolveToken = 109,
+    Packet_ResolveVirtualMethod = 160, // Added 2/13/17
     Packet_TryResolveToken = 158, //Added 4/26/2016
     Packet_SatisfiesClassConstraints = 110,
     Packet_SatisfiesMethodConstraints = 111,
index 1813ed2..bb19cab 100644 (file)
@@ -236,6 +236,19 @@ void interceptor_ICJI::getMethodVTableOffset (
     mc->recGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
 }
 
+// Find the virtual method in implementingClass that overrides virtualMethod.
+// Return null if devirtualization is not possible.
+CORINFO_METHOD_HANDLE interceptor_ICJI::resolveVirtualMethod(
+    CORINFO_METHOD_HANDLE virtualMethod,
+    CORINFO_CLASS_HANDLE implementingClass
+    )
+{
+    mc->cr->AddCall("resolveVirtualMethod");
+    CORINFO_METHOD_HANDLE result = original_ICorJitInfo->resolveVirtualMethod(virtualMethod, implementingClass);
+    mc->recResolveVirtualMethod(virtualMethod, implementingClass, result);
+    return result;
+}
+
 // If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
 // getIntrinsicID() returns the intrinsic ID.
 CorInfoIntrinsics interceptor_ICJI::getIntrinsicID(
index 448fb1f..beff1f4 100644 (file)
@@ -165,6 +165,17 @@ void interceptor_ICJI::getMethodVTableOffset (
     original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
 }
 
+// Find the virtual method in implementingClass that overrides virtualMethod.
+// Return null if devirtualization is not possible.
+CORINFO_METHOD_HANDLE interceptor_ICJI::resolveVirtualMethod(
+    CORINFO_METHOD_HANDLE virtualMethod,
+    CORINFO_CLASS_HANDLE implementingClass
+    )
+{
+    mcs->AddCall("resolveVirtualMethod");
+    return original_ICorJitInfo->resolveVirtualMethod(virtualMethod, implementingClass);
+}
+
 // If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
 // getIntrinsicID() returns the intrinsic ID.
 CorInfoIntrinsics interceptor_ICJI::getIntrinsicID(
index 4d145f6..9e229ee 100644 (file)
@@ -153,6 +153,16 @@ void interceptor_ICJI::getMethodVTableOffset (
     original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
 }
 
+// Find the virtual method in implementingClass that overrides virtualMethod.
+// Return null if devirtualization is not possible.
+CORINFO_METHOD_HANDLE interceptor_ICJI::resolveVirtualMethod(
+    CORINFO_METHOD_HANDLE virtualMethod,
+    CORINFO_CLASS_HANDLE implementingClass
+    )
+{
+    return original_ICorJitInfo->resolveVirtualMethod(virtualMethod, implementingClass);
+}
+
 // If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
 // getIntrinsicID() returns the intrinsic ID.
 CorInfoIntrinsics interceptor_ICJI::getIntrinsicID(
index b746d3f..ae4ba99 100644 (file)
@@ -185,6 +185,18 @@ void MyICJI::getMethodVTableOffset (
     jitInstance->mc->repGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
 }
 
+// Find the virtual method in implementingClass that overrides virtualMethod.
+// Return null if devirtualization is not possible.
+CORINFO_METHOD_HANDLE MyICJI::resolveVirtualMethod(
+    CORINFO_METHOD_HANDLE virtualMethod,
+    CORINFO_CLASS_HANDLE implementingClass
+    )
+{
+    jitInstance->mc->cr->AddCall("resolveVirtualMethod");
+    CORINFO_METHOD_HANDLE result = jitInstance->mc->repResolveVirtualMethod(virtualMethod, implementingClass);
+    return result;
+}
+
 // If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
 // getIntrinsicID() returns the intrinsic ID.
 CorInfoIntrinsics MyICJI::getIntrinsicID(
index af01631..c094c94 100644 (file)
@@ -231,12 +231,12 @@ TODO: Talk about initializing strutures before use
 #if COR_JIT_EE_VERSION > 460
 
 // Update this one
-SELECTANY const GUID JITEEVersionIdentifier = { /* 4bd06266-8ef7-4172-bec6-d3149fde7859 */
-    0x4bd06266,
-    0x8ef7,
-    0x4172,
-    {0xbe, 0xc6, 0xd3, 0x14, 0x9f, 0xde, 0x78, 0x59}
-};
+SELECTANY const GUID JITEEVersionIdentifier = { /* cda334f7-0020-4622-a4a5-8b8ac71ee5cf */
+    0xcda334f7,
+    0x0020,
+    0x4622,
+    {0xa4, 0xa5, 0x8b, 0x8a, 0xc7, 0x1e, 0xe5, 0xcf}
+  };
 
 #else
 
@@ -2116,6 +2116,15 @@ public:
             unsigned*                   offsetAfterIndirection  /* OUT */
             ) = 0;
 
+#if COR_JIT_EE_VERSION > 460
+    // Find the virtual method in implementingClass that overrides virtualMethod.
+    // Return null if devirtualization is not possible.
+    virtual CORINFO_METHOD_HANDLE resolveVirtualMethod(
+        CORINFO_METHOD_HANDLE virtualMethod,        /* IN */
+        CORINFO_CLASS_HANDLE implementingClass      /* IN */
+        ) = 0;
+#endif
+
     // If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
     // getIntrinsicID() returns the intrinsic ID.
     // *pMustExpand tells whether or not JIT must expand the intrinsic.
index 0c05350..53b0cc0 100644 (file)
@@ -8701,6 +8701,80 @@ void CEEInfo::getMethodVTableOffset (CORINFO_METHOD_HANDLE methodHnd,
 }
 
 /*********************************************************************/
+CORINFO_METHOD_HANDLE CEEInfo::resolveVirtualMethod(CORINFO_METHOD_HANDLE methodHnd,
+                                                    CORINFO_CLASS_HANDLE derivedClass)
+{
+    CONTRACTL {
+        SO_TOLERANT;
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_PREEMPTIVE;
+    } CONTRACTL_END;
+
+    CORINFO_METHOD_HANDLE result = nullptr;
+
+    JIT_TO_EE_TRANSITION();
+
+    MethodDesc* method = GetMethod(methodHnd);
+
+    // Method better be from a fully loaded class
+    _ASSERTE(method->IsRestored() && method->GetMethodTable()->IsFullyLoaded());
+
+    // Method better be virtual
+    _ASSERTE(method->IsVirtual());
+
+    //@GENERICS: shouldn't be doing this for instantiated methods as they live elsewhere
+    _ASSERTE(!method->HasMethodInstantiation());
+
+    // Method's class better not be an interface
+    _ASSERTE(!method->GetMethodTable()->IsInterface());
+
+    // Method better be in the vtable
+    WORD slot = method->GetSlot();
+    _ASSERTE(slot < method->GetMethodTable()->GetNumVirtuals());
+
+    // TODO: Derived class should have base class as parent...
+    TypeHandle DerivedClsHnd(derivedClass);
+
+    // If derived class is _Canon, we can't devirtualize.
+    if (DerivedClsHnd != TypeHandle(g_pCanonMethodTableClass))
+    {
+        MethodTable* pMT = DerivedClsHnd.GetMethodTable();
+
+        // MethodDescs returned to JIT at runtime are always fully loaded. Verify that it is the case.
+        _ASSERTE(pMT->IsRestored() && pMT->IsFullyLoaded());
+
+        MethodDesc* pDevirtMD = pMT->GetMethodDescForSlot(slot);
+
+        _ASSERTE(pDevirtMD->IsRestored());
+
+        // Allow devirtialization if jitting, or if prejitting and the
+        // method being jitted, the devirtualized class, and the
+        // devirtualized method are all in the same versioning bubble.
+        bool allowDevirt = true;
+
+#ifdef FEATURE_READYTORUN_COMPILER
+        if (IsReadyToRunCompilation())
+        {
+            Assembly * pCallerAssembly = m_pMethodBeingCompiled->GetModule()->GetAssembly();
+            allowDevirt =
+                IsInSameVersionBubble(pCallerAssembly , pDevirtMD->GetModule()->GetAssembly())
+                && IsInSameVersionBubble(pCallerAssembly , pMT->GetAssembly());
+        }
+#endif
+
+        if (allowDevirt)
+        {
+            result = (CORINFO_METHOD_HANDLE) pDevirtMD;
+        }
+    }
+
+    EE_TO_JIT_TRANSITION();
+
+    return result;
+}
+
+/*********************************************************************/
 void CEEInfo::getFunctionEntryPoint(CORINFO_METHOD_HANDLE  ftnHnd,
                                     CORINFO_CONST_LOOKUP * pResult,
                                     CORINFO_ACCESS_FLAGS   accessFlags)
index 1b5a9ea..c0df0cf 100644 (file)
@@ -729,6 +729,11 @@ public:
             unsigned * pOffsetAfterIndirection
             );
 
+    CORINFO_METHOD_HANDLE resolveVirtualMethod(
+        CORINFO_METHOD_HANDLE virtualMethod,
+        CORINFO_CLASS_HANDLE implementingClass
+        );
+
     CorInfoIntrinsics getIntrinsicID(CORINFO_METHOD_HANDLE method,
                                      bool * pMustExpand = NULL);
 
index b203c55..e817e44 100644 (file)
@@ -3727,6 +3727,14 @@ void ZapInfo::getMethodVTableOffset(CORINFO_METHOD_HANDLE method,
     m_pEEJitInfo->getMethodVTableOffset(method, pOffsetOfIndirection, pOffsetAfterIndirection);
 }
 
+CORINFO_METHOD_HANDLE ZapInfo::resolveVirtualMethod(
+        CORINFO_METHOD_HANDLE virtualMethod,
+        CORINFO_CLASS_HANDLE implementingClass
+        )
+{
+    return m_pEEJitInfo->resolveVirtualMethod(virtualMethod, implementingClass);
+}
+
 CorInfoIntrinsics ZapInfo::getIntrinsicID(CORINFO_METHOD_HANDLE method,
                                           bool * pMustExpand)
 {
index 97ecfb6..c0846d1 100644 (file)
@@ -665,6 +665,11 @@ public:
                                unsigned * pOffsetOfIndirection,
                                unsigned * pOffsetAfterIndirection);
 
+    CORINFO_METHOD_HANDLE resolveVirtualMethod(
+        CORINFO_METHOD_HANDLE virtualMethod,
+        CORINFO_CLASS_HANDLE implementingClass
+        );
+
     CorInfoIntrinsics getIntrinsicID(CORINFO_METHOD_HANDLE method,
                                      bool * pMustExpand = NULL);
     bool isInSIMDModule(CORINFO_CLASS_HANDLE classHnd);