Improve likely class handling for pgo (#51284)
authorDavid Wrighton <davidwr@microsoft.com>
Fri, 16 Apr 2021 18:49:56 +0000 (11:49 -0700)
committerGitHub <noreply@github.com>
Fri, 16 Apr 2021 18:49:56 +0000 (11:49 -0700)
- Move processing of likely class histogram into the JIT
- Fix cases where the devirtualization logic in crossgen2 behaved incorrectly in the presence of likely class histogram data
- Pre-process histogram at crossgen2 time to reduce the size of the pgo data
  - This removes 3/4 of the data from System.Private.CoreLib

33 files changed:
src/coreclr/ToolBox/superpmi/superpmi-shared/agnostic.h
src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h
src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h
src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp
src/coreclr/inc/corjit.h
src/coreclr/inc/icorjitinfoimpl_generated.h
src/coreclr/inc/jiteeversionguid.h
src/coreclr/inc/pgo_formatprocessing.h
src/coreclr/jit/CMakeLists.txt
src/coreclr/jit/ClrJit.PAL.exports
src/coreclr/jit/ClrJit.exports
src/coreclr/jit/ICorJitInfo_API_names.h
src/coreclr/jit/ICorJitInfo_API_wrapper.hpp
src/coreclr/jit/importer.cpp
src/coreclr/jit/jit.h
src/coreclr/jit/likelyclass.cpp [new file with mode: 0644]
src/coreclr/tools/Common/JitInterface/CorInfoBase.cs
src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt
src/coreclr/tools/Common/Pgo/PgoFormat.cs
src/coreclr/tools/Common/Pgo/TypeSystemEntityOrUnknown.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstrumentationDataTableNode.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
src/coreclr/tools/aot/jitinterface/jitinterface.h
src/coreclr/vm/jitinterface.cpp
src/coreclr/vm/jitinterface.h
src/coreclr/vm/pgo.cpp
src/coreclr/vm/pgo.h
src/coreclr/zap/zapinfo.cpp

index 44f3f79..59cc785 100644 (file)
@@ -493,20 +493,6 @@ struct Agnostic_GetPgoInstrumentationResults
     DWORD result;
 };
 
-struct Agnostic_GetLikelyClass
-{
-    DWORDLONG ftnHnd;
-    DWORDLONG baseHnd;
-    DWORD     ilOffset;
-};
-
-struct Agnostic_GetLikelyClassResult
-{
-    DWORDLONG classHnd;
-    DWORD     likelihood;
-    DWORD     numberOfClasses;
-};
-
 struct Agnostic_GetProfilingHandle
 {
     DWORD     bHookFunction;
index 71f9eca..338a4eb 100644 (file)
@@ -99,7 +99,6 @@ LWM(GetJitFlags, DWORD, DD)
 LWM(GetJitTimeLogFilename, DWORD, DWORD)
 LWM(GetJustMyCodeHandle, DWORDLONG, DLDL)
 LWM(GetLazyStringLiteralHelper, DWORDLONG, DWORD)
-LWM(GetLikelyClass, Agnostic_GetLikelyClass, Agnostic_GetLikelyClassResult)
 LWM(GetLocationOfThisType, DWORDLONG, Agnostic_CORINFO_LOOKUP_KIND)
 LWM(IsJitIntrinsic, DWORDLONG, DWORD)
 LWM(GetMethodAttribs, DWORDLONG, DWORD)
index 0654207..a4badbf 100644 (file)
@@ -5558,48 +5558,6 @@ HRESULT MethodContext::repGetPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftn
     return result;
 }
 
-void MethodContext::recGetLikelyClass(CORINFO_METHOD_HANDLE ftnHnd, CORINFO_CLASS_HANDLE baseHnd, UINT32 ilOffset, CORINFO_CLASS_HANDLE result, UINT32* pLikelihood, UINT32* pNumberOfClasses)
-{
-    if (GetLikelyClass == nullptr)
-        GetLikelyClass = new LightWeightMap<Agnostic_GetLikelyClass, Agnostic_GetLikelyClassResult>();
-
-    Agnostic_GetLikelyClass key;
-    ZeroMemory(&key, sizeof(Agnostic_GetLikelyClass));
-
-    key.ftnHnd = CastHandle(ftnHnd);
-    key.baseHnd = CastHandle(baseHnd);
-    key.ilOffset = (DWORD) ilOffset;
-
-    Agnostic_GetLikelyClassResult value;
-    ZeroMemory(&value, sizeof(Agnostic_GetLikelyClassResult));
-    value.classHnd = CastHandle(result);
-    value.likelihood = *pLikelihood;
-    value.numberOfClasses = *pNumberOfClasses;
-
-    GetLikelyClass->Add(key, value);
-    DEBUG_REC(dmpGetLikelyClass(key, value));
-}
-void MethodContext::dmpGetLikelyClass(const Agnostic_GetLikelyClass& key, const Agnostic_GetLikelyClassResult& value)
-{
-    printf("GetLikelyClass key ftn-%016llX base-%016llX il-%u, class-%016llX likelihood-%u numberOfClasses-%u",
-        key.ftnHnd, key.baseHnd, key.ilOffset, value.classHnd, value.likelihood, value.numberOfClasses);
-}
-CORINFO_CLASS_HANDLE MethodContext::repGetLikelyClass(CORINFO_METHOD_HANDLE ftnHnd, CORINFO_CLASS_HANDLE baseHnd, UINT32 ilOffset, UINT32* pLikelihood, UINT32* pNumberOfClasses)
-{
-    Agnostic_GetLikelyClass key;
-    ZeroMemory(&key, sizeof(Agnostic_GetLikelyClass));
-    key.ftnHnd = CastHandle(ftnHnd);
-    key.baseHnd = CastHandle(baseHnd);
-    key.ilOffset = (DWORD) ilOffset;
-
-    Agnostic_GetLikelyClassResult value = GetLikelyClass->Get(key);
-    DEBUG_REP(dmpGetLikelyClass(key, value));
-
-    *pLikelihood = value.likelihood;
-    *pNumberOfClasses = value.numberOfClasses;
-    return (CORINFO_CLASS_HANDLE) value.classHnd;
-}
-
 void MethodContext::recMergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2, CORINFO_CLASS_HANDLE result)
 {
     if (MergeClasses == nullptr)
index 6983706..1fe4c95 100644 (file)
@@ -701,10 +701,6 @@ public:
     void dmpGetPgoInstrumentationResults(DWORDLONG key, const Agnostic_GetPgoInstrumentationResults& value);
     HRESULT repGetPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftnHnd, ICorJitInfo::PgoInstrumentationSchema** pSchema, UINT32* pCountSchemaItems, BYTE** pInstrumentationData);
 
-    void recGetLikelyClass(CORINFO_METHOD_HANDLE ftnHnd, CORINFO_CLASS_HANDLE  baseHnd, UINT32 ilOffset, CORINFO_CLASS_HANDLE classHnd, UINT32* pLikelihood, UINT32* pNumberOfClasses);
-    void dmpGetLikelyClass(const Agnostic_GetLikelyClass& key, const Agnostic_GetLikelyClassResult& value);
-    CORINFO_CLASS_HANDLE repGetLikelyClass(CORINFO_METHOD_HANDLE ftnHnd, CORINFO_CLASS_HANDLE  baseHnd, UINT32 ilOffset, UINT32* pLikelihood, UINT32* pNumberOfClasses);
-
     void recMergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2, CORINFO_CLASS_HANDLE result);
     void dmpMergeClasses(DLDL key, DWORDLONG value);
     CORINFO_CLASS_HANDLE repMergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2);
@@ -1002,7 +998,7 @@ enum mcPackets
     Packet_GetJitFlags                                   = 154, // Added 2/3/2016
     Packet_GetJitTimeLogFilename                         = 67,
     Packet_GetJustMyCodeHandle                           = 68,
-    Packet_GetLikelyClass                                = 182, // Added 9/27/2020
+    Retired10                                            = 182, // Added 9/27/2020 // was Packet_GetLikelyClass
     Packet_GetLocationOfThisType                         = 69,
     Packet_IsJitIntrinsic                                = 192,
     Packet_GetMethodAttribs                              = 70,
index 281533e..9b799a1 100644 (file)
@@ -2084,21 +2084,6 @@ HRESULT interceptor_ICJI::getPgoInstrumentationResults(CORINFO_METHOD_HANDLE
     return temp;
 }
 
-// Get the likely implementing class for a virtual call or interface call made by ftnHnd
-// at the indicated IL offset. baseHnd is the interface class or base class for the method
-// being called.
-CORINFO_CLASS_HANDLE interceptor_ICJI::getLikelyClass(CORINFO_METHOD_HANDLE ftnHnd,
-                                                      CORINFO_CLASS_HANDLE  baseHnd,
-                                                      uint32_t              ilOffset,
-                                                      uint32_t*             pLikelihood,
-                                                      uint32_t*             pNumberOfClasses)
-{
-    mc->cr->AddCall("getLikelyClass");
-    CORINFO_CLASS_HANDLE result = original_ICorJitInfo->getLikelyClass(ftnHnd, baseHnd, ilOffset, pLikelihood, pNumberOfClasses);
-    mc->recGetLikelyClass(ftnHnd, baseHnd, ilOffset, result, pLikelihood, pNumberOfClasses);
-    return result;
-}
-
 // Associates a native call site, identified by its offset in the native code stream, with
 // the signature information and method handle the JIT used to lay out the call site. If
 // the call site has no signature information (e.g. a helper call) or has no method handle
index d5ee05b..918d188 100644 (file)
@@ -1352,17 +1352,6 @@ JITINTERFACE_HRESULT interceptor_ICJI::allocPgoInstrumentationBySchema(
     return original_ICorJitInfo->allocPgoInstrumentationBySchema(ftnHnd, pSchema, countSchemaItems, pInstrumentationData);
 }
 
-CORINFO_CLASS_HANDLE interceptor_ICJI::getLikelyClass(
-          CORINFO_METHOD_HANDLE ftnHnd,
-          CORINFO_CLASS_HANDLE baseHnd,
-          uint32_t ilOffset,
-          uint32_t* pLikelihood,
-          uint32_t* pNumberOfClasses)
-{
-    mcs->AddCall("getLikelyClass");
-    return original_ICorJitInfo->getLikelyClass(ftnHnd, baseHnd, ilOffset, pLikelihood, pNumberOfClasses);
-}
-
 void interceptor_ICJI::recordCallSite(
           uint32_t instrOffset,
           CORINFO_SIG_INFO* callSig,
index 3aed50b..19cf1f8 100644 (file)
@@ -1185,16 +1185,6 @@ JITINTERFACE_HRESULT interceptor_ICJI::allocPgoInstrumentationBySchema(
     return original_ICorJitInfo->allocPgoInstrumentationBySchema(ftnHnd, pSchema, countSchemaItems, pInstrumentationData);
 }
 
-CORINFO_CLASS_HANDLE interceptor_ICJI::getLikelyClass(
-          CORINFO_METHOD_HANDLE ftnHnd,
-          CORINFO_CLASS_HANDLE baseHnd,
-          uint32_t ilOffset,
-          uint32_t* pLikelihood,
-          uint32_t* pNumberOfClasses)
-{
-    return original_ICorJitInfo->getLikelyClass(ftnHnd, baseHnd, ilOffset, pLikelihood, pNumberOfClasses);
-}
-
 void interceptor_ICJI::recordCallSite(
           uint32_t instrOffset,
           CORINFO_SIG_INFO* callSig,
index 4f86025..18dac3b 100644 (file)
@@ -1825,19 +1825,6 @@ HRESULT MyICJI::getPgoInstrumentationResults(CORINFO_METHOD_HANDLE      ftnHnd,
     return jitInstance->mc->repGetPgoInstrumentationResults(ftnHnd, pSchema, pCountSchemaItems, pInstrumentationData);
 }
 
-// Get the likely implementing class for a virtual call or interface call made by ftnHnd
-// at the indicated IL offset. baseHnd is the interface class or base class for the method
-// being called.
-CORINFO_CLASS_HANDLE MyICJI::getLikelyClass(CORINFO_METHOD_HANDLE ftnHnd,
-                                            CORINFO_CLASS_HANDLE  baseHnd,
-                                            uint32_t              ilOffset,
-                                            uint32_t*             pLikelihood,
-                                            uint32_t*             pNumberOfClasses)
-{
-    jitInstance->mc->cr->AddCall("getLikelyClass");
-    return jitInstance->mc->repGetLikelyClass(ftnHnd, baseHnd, ilOffset, pLikelihood, pNumberOfClasses);
-}
-
 // Associates a native call site, identified by its offset in the native code stream, with
 // the signature information and method handle the JIT used to lay out the call site. If
 // the call site has no signature information (e.g. a helper call) or has no method handle
index 85cb80b..2407a40 100644 (file)
@@ -363,6 +363,7 @@ public:
         Version = (DescriptorMin * 4) | None, // Version is encoded in the Other field of the schema
         NumRuns = (DescriptorMin * 5) | None, // Number of runs is encoded in the Other field of the schema
         EdgeIntCount = (DescriptorMin * 6) | FourByte, // 4 byte edge counter, using unsigned 4 byte int
+        GetLikelyClass = (DescriptorMin * 7) | TypeHandle, // Compressed get likely class data
     };
 
     struct PgoInstrumentationSchema
@@ -374,6 +375,15 @@ public:
         int32_t Other;
     };
 
+#define DEFAULT_UNKNOWN_TYPEHANDLE 1
+#define UNKNOWN_TYPEHANDLE_MIN 1
+#define UNKNOWN_TYPEHANDLE_MAX 33
+
+    static inline bool IsUnknownTypeHandle(intptr_t typeHandle)
+    {
+        return ((typeHandle >= UNKNOWN_TYPEHANDLE_MIN) && (typeHandle <= UNKNOWN_TYPEHANDLE_MAX));
+    }
+
     // get profile information to be used for optimizing a current method.  The format
     // of the buffer is the same as the format the JIT passes to allocPgoInstrumentationBySchema.
     virtual JITINTERFACE_HRESULT getPgoInstrumentationResults(
@@ -404,24 +414,6 @@ public:
             uint8_t **                pInstrumentationData         // OUT: `*pInstrumentationData` is set to the address of the instrumentation data.
             ) = 0;
 
-    // Get the likely implementing class for a virtual call or interface call made by ftnHnd
-    // at the indicated IL offset. baseHnd is the interface class or base class for the method
-    // being called. May returns NULL.
-    // 
-    // pLikelihood is the estimated percent chance that the class at runtime is the class
-    // returned by this method. A well-estimated monomorphic call site will return a likelihood
-    // of 100.
-    // 
-    // pNumberOfClasses is the estimated number of different classes seen at the site.
-    // A well-estimated monomorphic call site will return 1.
-    virtual CORINFO_CLASS_HANDLE getLikelyClass(
-            CORINFO_METHOD_HANDLE ftnHnd,
-            CORINFO_CLASS_HANDLE  baseHnd,
-            uint32_t                ilOffset,
-            uint32_t *              pLikelihood,      // OUT, estimated likelihood of the class (0...100)
-            uint32_t *              pNumberOfClasses  // OUT, estimated number of possible classes
-            ) = 0;
-
     // Associates a native call site, identified by its offset in the native code stream, with
     // the signature information and method handle the JIT used to lay out the call site. If
     // the call site has no signature information (e.g. a helper call) or has no method handle
index 922a854..8eef23a 100644 (file)
@@ -693,13 +693,6 @@ JITINTERFACE_HRESULT allocPgoInstrumentationBySchema(
           uint32_t countSchemaItems,
           uint8_t** pInstrumentationData) override;
 
-CORINFO_CLASS_HANDLE getLikelyClass(
-          CORINFO_METHOD_HANDLE ftnHnd,
-          CORINFO_CLASS_HANDLE baseHnd,
-          uint32_t ilOffset,
-          uint32_t* pLikelihood,
-          uint32_t* pNumberOfClasses) override;
-
 void recordCallSite(
           uint32_t instrOffset,
           CORINFO_SIG_INFO* callSig,
index f76ceb7..b32d0ed 100644 (file)
@@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
 #define GUID_DEFINED
 #endif // !GUID_DEFINED
 
-constexpr GUID JITEEVersionIdentifier = { /* a33f2f79-dd8d-49dd-b4c3-ac86f34f6a87 */
-    0xa33f2f79,
-    0xdd8d,
-    0x49dd,
-    {0xb4, 0xc3, 0xac, 0x86, 0xf3, 0x4f, 0x6a, 0x87}
+constexpr GUID JITEEVersionIdentifier = { /* 12234eca-dfc2-48bc-a320-6155cf25ce17 */
+    0x12234eca,
+    0xdfc2,
+    0x48bc,
+    {0xa3, 0x20, 0x61, 0x55, 0xcf, 0x25, 0xce, 0x17}
 };
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////
index 9ef06e5..8d111bf 100644 (file)
@@ -8,10 +8,6 @@
 
 #ifdef FEATURE_PGO
 
-#define DEFAULT_UNKNOWN_TYPEHANDLE 1
-#define UNKNOWN_TYPEHANDLE_MIN 1
-#define UNKNOWN_TYPEHANDLE_MAX 32
-
 inline bool AddTypeHandleToUnknownTypeHandleMask(INT_PTR typeHandle, uint32_t *unknownTypeHandleMask)
 {
     uint32_t bitMask = (uint32_t)(1 << (typeHandle - UNKNOWN_TYPEHANDLE_MIN));
@@ -20,11 +16,6 @@ inline bool AddTypeHandleToUnknownTypeHandleMask(INT_PTR typeHandle, uint32_t *u
     return result;
 }
 
-inline bool IsUnknownTypeHandle(INT_PTR typeHandle)
-{
-    return ((typeHandle >= UNKNOWN_TYPEHANDLE_MIN) && (typeHandle <= UNKNOWN_TYPEHANDLE_MAX));
-}
-
 inline INT_PTR HashToPgoUnknownTypeHandle(uint32_t hash)
 {
     // Map from a 32bit hash to the 32 different unknown type handle values
index ed01751..685dedc 100644 (file)
@@ -112,6 +112,7 @@ set( JIT_SOURCES
   jittelemetry.cpp
   lclmorph.cpp
   lclvars.cpp
+  likelyclass.cpp
   lir.cpp
   liveness.cpp
   loopcloning.cpp
index e62cb2f..5c41cf2 100644 (file)
@@ -4,3 +4,4 @@
 EXPORTS
     getJit
     jitStartup
+    getLikelyClass
index dd00635..5588f23 100644 (file)
@@ -171,7 +171,6 @@ DEF_CLR_API(doAssert)
 DEF_CLR_API(reportFatalError)
 DEF_CLR_API(getPgoInstrumentationResults)
 DEF_CLR_API(allocPgoInstrumentationBySchema)
-DEF_CLR_API(getLikelyClass)
 DEF_CLR_API(recordCallSite)
 DEF_CLR_API(recordRelocation)
 DEF_CLR_API(getRelocTypeHint)
index e06bdb5..cbd302d 100644 (file)
@@ -1644,19 +1644,6 @@ JITINTERFACE_HRESULT WrapICorJitInfo::allocPgoInstrumentationBySchema(
     return temp;
 }
 
-CORINFO_CLASS_HANDLE WrapICorJitInfo::getLikelyClass(
-          CORINFO_METHOD_HANDLE ftnHnd,
-          CORINFO_CLASS_HANDLE baseHnd,
-          uint32_t ilOffset,
-          uint32_t* pLikelihood,
-          uint32_t* pNumberOfClasses)
-{
-    API_ENTER(getLikelyClass);
-    CORINFO_CLASS_HANDLE temp = wrapHnd->getLikelyClass(ftnHnd, baseHnd, ilOffset, pLikelihood, pNumberOfClasses);
-    API_LEAVE(getLikelyClass);
-    return temp;
-}
-
 void WrapICorJitInfo::recordCallSite(
           uint32_t instrOffset,
           CORINFO_SIG_INFO* callSig,
index be58950..02ddcb7 100644 (file)
@@ -21325,11 +21325,12 @@ void Compiler::considerGuardedDevirtualization(
 
     // See if there's a likely guess for the class.
     //
-    const unsigned       likelihoodThreshold = isInterface ? 25 : 30;
-    unsigned             likelihood          = 0;
-    unsigned             numberOfClasses     = 0;
+    const unsigned likelihoodThreshold = isInterface ? 25 : 30;
+    unsigned       likelihood          = 0;
+    unsigned       numberOfClasses     = 0;
+
     CORINFO_CLASS_HANDLE likelyClass =
-        info.compCompHnd->getLikelyClass(info.compMethodHnd, baseClass, ilOffset, &likelihood, &numberOfClasses);
+        getLikelyClass(fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset, &likelihood, &numberOfClasses);
 
     if (likelyClass == NO_CLASS_HANDLE)
     {
index 2e39bf4..1a5c80c 100644 (file)
@@ -891,6 +891,13 @@ T dspOffset(T o)
 
 #endif // !defined(DEBUG)
 
+extern "C" CORINFO_CLASS_HANDLE WINAPI getLikelyClass(ICorJitInfo::PgoInstrumentationSchema* schema,
+                                                      UINT32                                 countSchemaItems,
+                                                      BYTE*                                  pInstrumentationData,
+                                                      int32_t                                ilOffset,
+                                                      UINT32*                                pLikelihood,
+                                                      UINT32*                                pNumberOfClasses);
+
 /*****************************************************************************/
 #endif //_JIT_H_
 /*****************************************************************************/
diff --git a/src/coreclr/jit/likelyclass.cpp b/src/coreclr/jit/likelyclass.cpp
new file mode 100644 (file)
index 0000000..c2d570a
--- /dev/null
@@ -0,0 +1,221 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XX                                                                           XX
+XX                           Likely Class Processing                         XX
+XX                                                                           XX
+XX   Parses Pgo data to find the most likely class in use at a given         XX
+XX   IL offset in a method. Used by both the JIT, and by crossgen            XX
+XX                                                                           XX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+*/
+
+#include "jitpch.h"
+#ifdef _MSC_VER
+#pragma hdrstop
+#endif
+
+#ifndef DLLEXPORT
+#define DLLEXPORT
+#endif // !DLLEXPORT
+
+// Data item in class profile histogram
+//
+struct LikelyClassHistogramEntry
+{
+    // Class that was observed at runtime
+    INT_PTR m_mt; // This may be an "unknown type handle"
+    // Number of observations in the table
+    unsigned m_count;
+};
+
+// Summarizes a ClassProfile table by forming a Histogram
+
+//
+struct LikelyClassHistogram
+{
+    LikelyClassHistogram(uint32_t histogramCount, INT_PTR* histogramEntries, unsigned entryCount);
+
+    // Sum of counts from all entries in the histogram. This includes "unknown" entries which are not captured in
+    // m_histogram
+    unsigned m_totalCount;
+    // Rough guess at count of unknown types
+    unsigned m_unknownTypes;
+    // Histogram entries, in no particular order.
+    LikelyClassHistogramEntry m_histogram[64];
+    UINT32                    countHistogramElements = 0;
+
+    LikelyClassHistogramEntry HistogramEntryAt(unsigned index)
+    {
+        return m_histogram[index];
+    }
+};
+
+LikelyClassHistogram::LikelyClassHistogram(uint32_t histogramCount, INT_PTR* histogramEntries, unsigned entryCount)
+{
+    m_unknownTypes                 = 0;
+    m_totalCount                   = 0;
+    uint32_t unknownTypeHandleMask = 0;
+
+    for (unsigned k = 0; k < entryCount; k++)
+    {
+        if (histogramEntries[k] == 0)
+        {
+            continue;
+        }
+
+        m_totalCount++;
+
+        INT_PTR currentEntry = histogramEntries[k];
+
+        bool     found = false;
+        unsigned h     = 0;
+        for (; h < countHistogramElements; h++)
+        {
+            if (m_histogram[h].m_mt == currentEntry)
+            {
+                m_histogram[h].m_count++;
+                found = true;
+                break;
+            }
+        }
+
+        if (!found)
+        {
+            if (countHistogramElements >= _countof(m_histogram))
+            {
+                continue;
+            }
+            LikelyClassHistogramEntry newEntry;
+            newEntry.m_mt                         = currentEntry;
+            newEntry.m_count                      = 1;
+            m_histogram[countHistogramElements++] = newEntry;
+        }
+    }
+}
+
+// This is used by the devirtualization logic below, and by crossgen2 when producing the R2R image (to reduce the size
+// cost of carrying the type histogram)
+extern "C" DLLEXPORT CORINFO_CLASS_HANDLE WINAPI getLikelyClass(ICorJitInfo::PgoInstrumentationSchema* schema,
+                                                                UINT32                                 countSchemaItems,
+                                                                BYTE*   pInstrumentationData,
+                                                                int32_t ilOffset,
+                                                                UINT32* pLikelihood,
+                                                                UINT32* pNumberOfClasses)
+{
+    *pLikelihood      = 0;
+    *pNumberOfClasses = 0;
+
+    if (schema == NULL)
+        return NULL;
+
+    for (COUNT_T i = 0; i < countSchemaItems; i++)
+    {
+        if (schema[i].ILOffset != (int32_t)ilOffset)
+            continue;
+
+        if ((schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::GetLikelyClass) &&
+            (schema[i].Count == 1))
+        {
+            *pNumberOfClasses = (UINT32)schema[i].Other >> 8;
+            *pLikelihood      = (UINT32)(schema[i].Other && 0xFF);
+            INT_PTR result    = *(INT_PTR*)(pInstrumentationData + schema[i + 1].Offset);
+            if (ICorJitInfo::IsUnknownTypeHandle(result))
+                return NULL;
+            else
+                return (CORINFO_CLASS_HANDLE)result;
+        }
+
+        if ((schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramCount) &&
+            (schema[i].Count == 1) && ((i + 1) < countSchemaItems) &&
+            (schema[i + 1].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramTypeHandle))
+        {
+            // Form a histogram
+            //
+            LikelyClassHistogram h(*(uint32_t*)(pInstrumentationData + schema[i].Offset),
+                                   (INT_PTR*)(pInstrumentationData + schema[i + 1].Offset), schema[i + 1].Count);
+
+            // Use histogram count as number of classes estimate
+            //
+            *pNumberOfClasses = (uint32_t)h.countHistogramElements + h.m_unknownTypes;
+
+            // Report back what we've learned
+            // (perhaps, use count to augment likelihood?)
+            //
+            switch (*pNumberOfClasses)
+            {
+                case 0:
+                {
+                    return NULL;
+                }
+                break;
+
+                case 1:
+                {
+                    if (ICorJitInfo::IsUnknownTypeHandle(h.HistogramEntryAt(0).m_mt))
+                    {
+                        return NULL;
+                    }
+                    *pLikelihood = 100;
+                    return (CORINFO_CLASS_HANDLE)h.HistogramEntryAt(0).m_mt;
+                }
+                break;
+
+                case 2:
+                {
+                    if ((h.HistogramEntryAt(0).m_count >= h.HistogramEntryAt(1).m_count) &&
+                        !ICorJitInfo::IsUnknownTypeHandle(h.HistogramEntryAt(0).m_mt))
+                    {
+                        *pLikelihood = (100 * h.HistogramEntryAt(0).m_count) / h.m_totalCount;
+                        return (CORINFO_CLASS_HANDLE)h.HistogramEntryAt(0).m_mt;
+                    }
+                    else if (!ICorJitInfo::IsUnknownTypeHandle(h.HistogramEntryAt(1).m_mt))
+                    {
+                        *pLikelihood = (100 * h.HistogramEntryAt(1).m_count) / h.m_totalCount;
+                        return (CORINFO_CLASS_HANDLE)h.HistogramEntryAt(1).m_mt;
+                    }
+                    else
+                    {
+                        return NULL;
+                    }
+                }
+                break;
+
+                default:
+                {
+                    // Find maximum entry and return it
+                    //
+                    unsigned maxKnownIndex = 0;
+                    unsigned maxKnownCount = 0;
+
+                    for (unsigned m = 0; m < h.countHistogramElements; m++)
+                    {
+                        if ((h.HistogramEntryAt(m).m_count > maxKnownCount) &&
+                            !ICorJitInfo::IsUnknownTypeHandle(h.HistogramEntryAt(m).m_mt))
+                        {
+                            maxKnownIndex = m;
+                            maxKnownCount = h.HistogramEntryAt(m).m_count;
+                        }
+                    }
+
+                    if (maxKnownCount > 0)
+                    {
+                        *pLikelihood = (100 * maxKnownCount) / h.m_totalCount;
+                        ;
+                        return (CORINFO_CLASS_HANDLE)h.HistogramEntryAt(maxKnownIndex).m_mt;
+                    }
+
+                    return NULL;
+                }
+                break;
+            }
+        }
+    }
+
+    // Failed to find histogram data for this method
+    //
+    return NULL;
+}
index e7224e1..031ee67 100644 (file)
@@ -2476,21 +2476,6 @@ namespace Internal.JitInterface
         }
 
         [UnmanagedCallersOnly]
-        static CORINFO_CLASS_STRUCT_* _getLikelyClass(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* ftnHnd, CORINFO_CLASS_STRUCT_* baseHnd, uint ilOffset, uint* pLikelihood, uint* pNumberOfClasses)
-        {
-            var _this = GetThis(thisHandle);
-            try
-            {
-                return _this.getLikelyClass(ftnHnd, baseHnd, ilOffset, ref *pLikelihood, ref *pNumberOfClasses);
-            }
-            catch (Exception ex)
-            {
-                *ppException = _this.AllocException(ex);
-                return default;
-            }
-        }
-
-        [UnmanagedCallersOnly]
         static void _recordCallSite(IntPtr thisHandle, IntPtr* ppException, uint instrOffset, CORINFO_SIG_INFO* callSig, CORINFO_METHOD_STRUCT_* methodHandle)
         {
             var _this = GetThis(thisHandle);
@@ -2566,7 +2551,7 @@ namespace Internal.JitInterface
 
         static IntPtr GetUnmanagedCallbacks()
         {
-            void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 173);
+            void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 172);
 
             callbacks[0] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte>)&_isJitIntrinsic;
             callbacks[1] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint>)&_getMethodAttribs;
@@ -2735,12 +2720,11 @@ namespace Internal.JitInterface
             callbacks[164] = (delegate* unmanaged<IntPtr, IntPtr*, CorJitResult, void>)&_reportFatalError;
             callbacks[165] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema**, uint*, byte**, HRESULT>)&_getPgoInstrumentationResults;
             callbacks[166] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema*, uint, byte**, HRESULT>)&_allocPgoInstrumentationBySchema;
-            callbacks[167] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CLASS_STRUCT_*, uint, uint*, uint*, CORINFO_CLASS_STRUCT_*>)&_getLikelyClass;
-            callbacks[168] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_SIG_INFO*, CORINFO_METHOD_STRUCT_*, void>)&_recordCallSite;
-            callbacks[169] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, ushort, ushort, int, void>)&_recordRelocation;
-            callbacks[170] = (delegate* unmanaged<IntPtr, IntPtr*, void*, ushort>)&_getRelocTypeHint;
-            callbacks[171] = (delegate* unmanaged<IntPtr, IntPtr*, uint>)&_getExpectedTargetArchitecture;
-            callbacks[172] = (delegate* unmanaged<IntPtr, IntPtr*, CORJIT_FLAGS*, uint, uint>)&_getJitFlags;
+            callbacks[167] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_SIG_INFO*, CORINFO_METHOD_STRUCT_*, void>)&_recordCallSite;
+            callbacks[168] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, ushort, ushort, int, void>)&_recordRelocation;
+            callbacks[169] = (delegate* unmanaged<IntPtr, IntPtr*, void*, ushort>)&_getRelocTypeHint;
+            callbacks[170] = (delegate* unmanaged<IntPtr, IntPtr*, uint>)&_getExpectedTargetArchitecture;
+            callbacks[171] = (delegate* unmanaged<IntPtr, IntPtr*, CORJIT_FLAGS*, uint, uint>)&_getJitFlags;
 
             return (IntPtr)callbacks;
         }
index fa5b01a..396769e 100644 (file)
@@ -77,6 +77,9 @@ namespace Internal.JitInterface
         [DllImport(JitLibrary)]
         private extern static IntPtr getJit();
 
+        [DllImport(JitLibrary)]
+        private extern static IntPtr getLikelyClass(PgoInstrumentationSchema* schema, uint countSchemaItems, byte*pInstrumentationData, int ilOffset, out uint pLikelihood, out uint pNumberOfClasses);
+
         [DllImport(JitSupportLibrary)]
         private extern static IntPtr GetJitHost(IntPtr configProvider);
 
@@ -147,6 +150,96 @@ namespace Internal.JitInterface
 
         private CORINFO_MODULE_STRUCT_* _methodScope; // Needed to resolve CORINFO_EH_CLAUSE tokens
 
+        public static IEnumerable<PgoSchemaElem> ConvertTypeHandleHistogramsToCompactTypeHistogramFormat(PgoSchemaElem[] pgoData, CompilationModuleGroup compilationModuleGroup)
+        {
+            bool hasTypeHistogram = false;
+            foreach (var elem in pgoData)
+            {
+                if (elem.InstrumentationKind == PgoInstrumentationKind.TypeHandleHistogramCount)
+                {
+                    // found histogram
+                    hasTypeHistogram = true;
+                    break;
+                }
+            }
+            if (!hasTypeHistogram)
+            {
+                foreach (var elem in pgoData)
+                {
+                    yield return elem;
+                }
+            }
+            else
+            {
+                int currentObjectIndex = 0x1000000; // This needs to be a somewhat large non-zero number, so that the jit does not confuse it with NULL, or any other special value.
+                Dictionary<object, IntPtr> objectToHandle = new Dictionary<object, IntPtr>();
+                Dictionary<IntPtr, object> handleToObject = new Dictionary<IntPtr, object>();
+
+                ComputeJitPgoInstrumentationSchema(LocalObjectToHandle, pgoData, out var nativeSchema, out var instrumentationData);
+
+                for (int i = 0; i < (pgoData.Length); i++)
+                {
+                    if (pgoData[i].InstrumentationKind == PgoInstrumentationKind.TypeHandleHistogramCount)
+                    {
+                        PgoSchemaElem? newElem = ComputeLikelyClass(i, handleToObject, nativeSchema, instrumentationData, compilationModuleGroup);
+                        if (newElem.HasValue)
+                        {
+                            yield return newElem.Value;
+                        }
+                        i++; // The histogram is two entries long, so skip an extra entry
+                        continue;
+                    }
+                    yield return pgoData[i];
+                }
+
+                IntPtr LocalObjectToHandle(object input)
+                {
+                    if (objectToHandle.TryGetValue(input, out var result))
+                    {
+                        return result;
+                    }
+                    result = new IntPtr(currentObjectIndex++);
+                    objectToHandle.Add(input, result);
+                    handleToObject.Add(result, input);
+                    return result;
+                }
+            }
+        }
+
+        private static PgoSchemaElem? ComputeLikelyClass(int index, Dictionary<IntPtr, object> handleToObject, PgoInstrumentationSchema[] nativeSchema, byte[] instrumentationData, CompilationModuleGroup compilationModuleGroup)
+        {
+            // getLikelyClass will use two entries from the native schema table. There must be at least two present to avoid ovberruning the buffer
+            if (index > (nativeSchema.Length - 2))
+                return null;
+
+            fixed(PgoInstrumentationSchema* pSchema = &nativeSchema[index])
+            {
+                fixed(byte* pInstrumentationData = &instrumentationData[0])
+                {
+                    IntPtr classType = getLikelyClass(pSchema, 2, pInstrumentationData, nativeSchema[index].ILOffset, out uint likelihood, out uint numberOfClasses);
+
+                    if (classType != IntPtr.Zero)
+                    {
+                        TypeDesc type = (TypeDesc)handleToObject[classType];
+#if READYTORUN
+                        if (compilationModuleGroup.VersionsWithType(type))
+#endif
+                        {
+                            PgoSchemaElem likelyClassElem = new PgoSchemaElem();
+                            likelyClassElem.InstrumentationKind = PgoInstrumentationKind.GetLikelyClass;
+                            likelyClassElem.ILOffset = nativeSchema[index].ILOffset;
+                            likelyClassElem.Count = 1;
+                            likelyClassElem.Other = (int)(likelihood | (numberOfClasses << 8));
+                            likelyClassElem.DataObject = new TypeSystemEntityOrUnknown[] { new TypeSystemEntityOrUnknown(type) };
+                            return likelyClassElem;
+                        }
+                    }
+                }
+            }
+
+            return null;
+        }
+
         private void CompileMethodInternal(IMethodNode methodCodeNodeNeedingCode, MethodIL methodIL)
         {
             // methodIL must not be null
@@ -1069,7 +1162,7 @@ namespace Internal.JitInterface
             MethodDesc decl = HandleToObject(info->virtualMethod);
             Debug.Assert(!decl.HasInstantiation);
 
-            if (info->context != null)
+            if ((info->context != null) && decl.OwningType.IsInterface)
             {
                 TypeDesc ownerTypeDesc = typeFromContext(info->context);
                 if (decl.OwningType != ownerTypeDesc)
@@ -2837,8 +2930,6 @@ namespace Internal.JitInterface
 
         private CORINFO_MODULE_STRUCT_* embedModuleHandle(CORINFO_MODULE_STRUCT_* handle, ref void* ppIndirection)
         { throw new NotImplementedException("embedModuleHandle"); }
-        private CORINFO_CLASS_STRUCT_* embedClassHandle(CORINFO_CLASS_STRUCT_* handle, ref void* ppIndirection)
-        { throw new NotImplementedException("embedClassHandle"); }
 
         private CORINFO_FIELD_STRUCT_* embedFieldHandle(CORINFO_FIELD_STRUCT_* handle, ref void* ppIndirection)
         { throw new NotImplementedException("embedFieldHandle"); }
@@ -3383,6 +3474,69 @@ namespace Internal.JitInterface
             return _compilation.ProfileData[method]?.SchemaData;
         }
 
+        public static void ComputeJitPgoInstrumentationSchema(Func<object, IntPtr> objectToHandle, PgoSchemaElem[] pgoResultsSchemas, out PgoInstrumentationSchema[] nativeSchemas, out byte[] instrumentationData)
+        {
+            nativeSchemas = new PgoInstrumentationSchema[pgoResultsSchemas.Length];
+            MemoryStream msInstrumentationData = new MemoryStream();
+            BinaryWriter bwInstrumentationData = new BinaryWriter(msInstrumentationData);
+            for (int i = 0; i < nativeSchemas.Length; i++)
+            {
+                if ((bwInstrumentationData.BaseStream.Position % 8) == 4)
+                {
+                    bwInstrumentationData.Write(0);
+                }
+
+                Debug.Assert((bwInstrumentationData.BaseStream.Position % 8) == 0);
+                nativeSchemas[i].Offset = new IntPtr(checked((int)bwInstrumentationData.BaseStream.Position));
+                nativeSchemas[i].ILOffset = pgoResultsSchemas[i].ILOffset;
+                nativeSchemas[i].Count = pgoResultsSchemas[i].Count;
+                nativeSchemas[i].Other = pgoResultsSchemas[i].Other;
+                nativeSchemas[i].InstrumentationKind = (PgoInstrumentationKind)pgoResultsSchemas[i].InstrumentationKind;
+
+                if (pgoResultsSchemas[i].DataObject == null)
+                {
+                    bwInstrumentationData.Write(pgoResultsSchemas[i].DataLong);
+                }
+                else
+                {
+                    object dataObject = pgoResultsSchemas[i].DataObject;
+                    if (dataObject is int[] intArray)
+                    {
+                        foreach (int intVal in intArray)
+                            bwInstrumentationData.Write(intVal);
+                    }
+                    else if (dataObject is long[] longArray)
+                    {
+                        foreach (long longVal in longArray)
+                            bwInstrumentationData.Write(longVal);
+                    }
+                    else if (dataObject is TypeSystemEntityOrUnknown[] typeArray)
+                    {
+                        foreach (TypeSystemEntityOrUnknown typeVal in typeArray)
+                        {
+                            IntPtr ptrVal = IntPtr.Zero;
+                            if (typeVal.AsType != null)
+                                ptrVal = (IntPtr)objectToHandle(typeVal.AsType);
+                            else
+                            {
+                                // The "Unknown types are the values from 1-33
+                                ptrVal = new IntPtr((typeVal.AsUnknown % 32) + 1);
+                            }
+
+                            if (IntPtr.Size == 4)
+                                bwInstrumentationData.Write((int)ptrVal);
+                            else
+                                bwInstrumentationData.Write((long)ptrVal);
+                        }
+                    }
+                }
+            }
+
+            bwInstrumentationData.Flush();
+
+            instrumentationData = msInstrumentationData.ToArray();
+        }
+
         private HRESULT getPgoInstrumentationResults(CORINFO_METHOD_STRUCT_* ftnHnd, ref PgoInstrumentationSchema* pSchema, ref uint countSchemaItems, byte** pInstrumentationData)
         {
             MethodDesc methodDesc = HandleToObject(ftnHnd);
@@ -3396,64 +3550,9 @@ namespace Internal.JitInterface
                 }
                 else
                 {
-                    PgoInstrumentationSchema[] nativeSchemas = new PgoInstrumentationSchema[pgoResultsSchemas.Length];
-                    MemoryStream msInstrumentationData = new MemoryStream();
-                    BinaryWriter bwInstrumentationData = new BinaryWriter(msInstrumentationData);
-                    for (int i = 0; i < nativeSchemas.Length; i++)
-                    {
-                        if ((bwInstrumentationData.BaseStream.Position % 8) == 4)
-                        {
-                            bwInstrumentationData.Write(0);
-                        }
-
-                        Debug.Assert((bwInstrumentationData.BaseStream.Position % 8) == 0);
-                        nativeSchemas[i].Offset = new IntPtr(checked((int)bwInstrumentationData.BaseStream.Position));
-                        nativeSchemas[i].ILOffset = pgoResultsSchemas[i].ILOffset;
-                        nativeSchemas[i].Count = pgoResultsSchemas[i].Count;
-                        nativeSchemas[i].Other = pgoResultsSchemas[i].Other;
-                        nativeSchemas[i].InstrumentationKind = (PgoInstrumentationKind)pgoResultsSchemas[i].InstrumentationKind;
-
-                        if (pgoResultsSchemas[i].DataObject == null)
-                        {
-                            bwInstrumentationData.Write(pgoResultsSchemas[i].DataLong);
-                        }
-                        else
-                        {
-                            object dataObject = pgoResultsSchemas[i].DataObject;
-                            if (dataObject is int[] intArray)
-                            {
-                                foreach (int intVal in intArray)
-                                    bwInstrumentationData.Write(intVal);
-                            }
-                            else if (dataObject is long[] longArray)
-                            {
-                                foreach (long longVal in longArray)
-                                    bwInstrumentationData.Write(longVal);
-                            }
-                            else if (dataObject is TypeSystemEntityOrUnknown[] typeArray)
-                            {
-                                foreach (TypeSystemEntityOrUnknown typeVal in typeArray)
-                                {
-                                    IntPtr ptrVal = IntPtr.Zero;
-                                    if (typeVal.AsType != null)
-                                        ptrVal = (IntPtr)ObjectToHandle(typeVal.AsType);
-                                    else
-                                    {
-                                        // The "Unknown types are the values from 1-33
-                                        ptrVal = new IntPtr((typeVal.AsUnknown % 32) + 1);
-                                    }
-
-                                    if (IntPtr.Size == 4)
-                                        bwInstrumentationData.Write((int)ptrVal);
-                                    else
-                                        bwInstrumentationData.Write((long)ptrVal);
-                                }
-                            }
-                        }
-                    }
+                    ComputeJitPgoInstrumentationSchema(ObjectToHandle, pgoResultsSchemas, out var nativeSchemas, out var instrumentationData);
 
-                    bwInstrumentationData.Flush();
-                    pgoResults.pInstrumentationData = (byte*)GetPin(msInstrumentationData.ToArray());
+                    pgoResults.pInstrumentationData = (byte*)GetPin(instrumentationData);
                     pgoResults.countSchemaItems = (uint)nativeSchemas.Length;
                     pgoResults.pSchema = (PgoInstrumentationSchema*)GetPin(nativeSchemas);
                     pgoResults.hr = HRESULT.S_OK;
index 03ddae0..a829b56 100644 (file)
@@ -317,7 +317,6 @@ FUNCTIONS
     void reportFatalError(CorJitResult result)
     JITINTERFACE_HRESULT getPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftnHnd, ICorJitInfo::PgoInstrumentationSchema** pSchema, uint32_t* pCountSchemaItems, uint8_t**pInstrumentationData)
     JITINTERFACE_HRESULT allocPgoInstrumentationBySchema(CORINFO_METHOD_HANDLE ftnHnd, ICorJitInfo::PgoInstrumentationSchema* pSchema, uint32_t countSchemaItems, uint8_t** pInstrumentationData)
-    CORINFO_CLASS_HANDLE getLikelyClass(CORINFO_METHOD_HANDLE ftnHnd, CORINFO_CLASS_HANDLE baseHnd, uint32_t ilOffset, uint32_t* pLikelihood, uint32_t* pNumberOfClasses)
     void recordCallSite(uint32_t instrOffset, CORINFO_SIG_INFO* callSig, CORINFO_METHOD_HANDLE methodHandle)
     void recordRelocation(void* location, void* target, uint16_t fRelocType, uint16_t slotNum, int32_t addlDelta)
     uint16_t getRelocTypeHint(void* target)
index c38dcdd..3e0013e 100644 (file)
@@ -42,6 +42,7 @@ namespace Internal.Pgo
         Version = (DescriptorMin * 4) | None, // Version is encoded in the Other field of the schema
         NumRuns = (DescriptorMin * 5) | None, // Number of runs is encoded in the Other field of the schema
         EdgeIntCount = (DescriptorMin * 6) | FourByte, // 4 byte edge counter, using unsigned 4 byte int
+        GetLikelyClass = (DescriptorMin * 7) | TypeHandle, // Compressed get likely class data
     }
 
     public interface IPgoSchemaDataLoader<TType>
index dc5bd8c..dbdf44e 100644 (file)
@@ -40,5 +40,20 @@ namespace Internal.Pgo
         public override int GetHashCode() => _data.GetHashCode();
 
         public override bool Equals(object obj) => obj is TypeSystemEntityOrUnknown other && other.Equals(this);
+
+        public override string ToString()
+        {
+            if (_data == null)
+            {
+                return "null";
+            }
+
+            if ((_data is int))
+            {
+                return $"UnknownType({(int)_data})";
+            }
+
+            return _data.ToString();
+        }
     }
 }
index 78d1cf3..5b4b2f5 100644 (file)
@@ -167,7 +167,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
             foreach (MethodDesc method in _instrumentationDataMethods)
             {
                 pgoEmitter.Clear();
-                PgoProcessor.EncodePgoData(_profileDataManager[method].SchemaData, pgoEmitter, false);
+                PgoProcessor.EncodePgoData(CorInfoImpl.ConvertTypeHandleHistogramsToCompactTypeHistogramFormat(_profileDataManager[method].SchemaData, factory.CompilationModuleGroup), pgoEmitter, false);
 
                 // In composite R2R format, always enforce owning type to let us share generic instantiations among modules
                 EcmaMethod typicalMethod = (EcmaMethod)method.GetTypicalMethodDefinition();
index 60fe868..f655f0f 100644 (file)
@@ -2162,6 +2162,18 @@ namespace Internal.JitInterface
             }
         }
 
+        private CORINFO_CLASS_STRUCT_* embedClassHandle(CORINFO_CLASS_STRUCT_* handle, ref void* ppIndirection)
+        {
+            TypeDesc type = HandleToObject(handle);
+            if (!_compilation.CompilationModuleGroup.VersionsWithType(type))
+                throw new RequiresRuntimeJitException(type.ToString());
+
+            Import typeHandleImport = (Import)_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeHandle, type);
+            Debug.Assert(typeHandleImport.RepresentsIndirectionCell);
+            ppIndirection = (void*)ObjectToHandle(typeHandleImport);
+            return null;
+        }
+
         private void embedGenericHandle(ref CORINFO_RESOLVED_TOKEN pResolvedToken, bool fEmbedParent, ref CORINFO_GENERICHANDLE_RESULT pResult)
         {
             ceeInfoEmbedGenericHandle(ref pResolvedToken, fEmbedParent, ref pResult);
@@ -2453,11 +2465,6 @@ namespace Internal.JitInterface
             return 0;
         }
 
-        private CORINFO_CLASS_STRUCT_* getLikelyClass(CORINFO_METHOD_STRUCT_* ftnHnd, CORINFO_CLASS_STRUCT_* baseHnd, uint IlOffset, ref uint pLikelihood, ref uint pNumberOfClasses)
-        {
-            return null;
-        }
-
         private void getAddressOfPInvokeTarget(CORINFO_METHOD_STRUCT_* method, ref CORINFO_CONST_LOOKUP pLookup)
         {
             MethodDesc methodDesc = HandleToObject(method);
index b39c924..4b9cb54 100644 (file)
@@ -178,7 +178,6 @@ struct JitInterfaceCallbacks
     void (* reportFatalError)(void * thisHandle, CorInfoExceptionClass** ppException, CorJitResult result);
     JITINTERFACE_HRESULT (* getPgoInstrumentationResults)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftnHnd, ICorJitInfo::PgoInstrumentationSchema** pSchema, uint32_t* pCountSchemaItems, uint8_t** pInstrumentationData);
     JITINTERFACE_HRESULT (* allocPgoInstrumentationBySchema)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftnHnd, ICorJitInfo::PgoInstrumentationSchema* pSchema, uint32_t countSchemaItems, uint8_t** pInstrumentationData);
-    CORINFO_CLASS_HANDLE (* getLikelyClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftnHnd, CORINFO_CLASS_HANDLE baseHnd, uint32_t ilOffset, uint32_t* pLikelihood, uint32_t* pNumberOfClasses);
     void (* recordCallSite)(void * thisHandle, CorInfoExceptionClass** ppException, uint32_t instrOffset, CORINFO_SIG_INFO* callSig, CORINFO_METHOD_HANDLE methodHandle);
     void (* recordRelocation)(void * thisHandle, CorInfoExceptionClass** ppException, void* location, void* target, uint16_t fRelocType, uint16_t slotNum, int32_t addlDelta);
     uint16_t (* getRelocTypeHint)(void * thisHandle, CorInfoExceptionClass** ppException, void* target);
@@ -1814,19 +1813,6 @@ public:
     return temp;
 }
 
-    virtual CORINFO_CLASS_HANDLE getLikelyClass(
-          CORINFO_METHOD_HANDLE ftnHnd,
-          CORINFO_CLASS_HANDLE baseHnd,
-          uint32_t ilOffset,
-          uint32_t* pLikelihood,
-          uint32_t* pNumberOfClasses)
-{
-    CorInfoExceptionClass* pException = nullptr;
-    CORINFO_CLASS_HANDLE temp = _callbacks->getLikelyClass(_thisHandle, &pException, ftnHnd, baseHnd, ilOffset, pLikelihood, pNumberOfClasses);
-    if (pException != nullptr) throw pException;
-    return temp;
-}
-
     virtual void recordCallSite(
           uint32_t instrOffset,
           CORINFO_SIG_INFO* callSig,
index 50e44d2..11dfd7f 100644 (file)
@@ -12136,54 +12136,6 @@ HRESULT CEEJitInfo::getPgoInstrumentationResults(
     return hr;
 }
 
-CORINFO_CLASS_HANDLE CEEJitInfo::getLikelyClass(
-                     CORINFO_METHOD_HANDLE ftnHnd,
-                     CORINFO_CLASS_HANDLE  baseHnd,
-                     uint32_t              ilOffset,
-                     uint32_t *            pLikelihood,
-                     uint32_t *            pNumberOfClasses
-)
-{
-    CONTRACTL {
-        THROWS;
-        GC_TRIGGERS;
-        MODE_PREEMPTIVE;
-    } CONTRACTL_END;
-
-    CORINFO_CLASS_HANDLE result = NULL;
-    *pLikelihood = 0;
-    *pNumberOfClasses = 0;
-
-    JIT_TO_EE_TRANSITION();
-
-#ifdef FEATURE_PGO
-
-    // Query the PGO manager's per call site class profile.
-    //
-    MethodDesc* pMD = (MethodDesc*)ftnHnd;
-    unsigned codeSize = 0;
-    if (pMD->IsDynamicMethod())
-    {
-        unsigned stackSize, ehSize;
-        CorInfoOptions options;
-        DynamicResolver * pResolver = m_pMethodBeingCompiled->AsDynamicMethodDesc()->GetResolver();
-        pResolver->GetCodeInfo(&codeSize, &stackSize, &options, &ehSize);
-    }
-    else if (pMD->HasILHeader())
-    {
-        COR_ILMETHOD_DECODER decoder(pMD->GetILHeader());
-        codeSize = decoder.GetCodeSize();
-    }
-
-    result = PgoManager::getLikelyClass(pMD, codeSize, ilOffset, pLikelihood, pNumberOfClasses);
-
-#endif
-
-    EE_TO_JIT_TRANSITION();
-
-    return result;
-}
-
 void CEEJitInfo::allocMem (
     uint32_t            hotCodeSize,    /* IN */
     uint32_t            coldCodeSize,   /* IN */
@@ -14433,18 +14385,6 @@ HRESULT CEEInfo::getPgoInstrumentationResults(
     UNREACHABLE_RET();      // only called on derived class.
 }
 
-CORINFO_CLASS_HANDLE CEEInfo::getLikelyClass(
-                     CORINFO_METHOD_HANDLE ftnHnd,
-                     CORINFO_CLASS_HANDLE  baseHnd,
-                     uint32_t              ilOffset,
-                     uint32_t*             pLikelihood,
-                     uint32_t*             pNumberOfCases
-)
-{
-    LIMITED_METHOD_CONTRACT;
-    UNREACHABLE_RET();      // only called on derived class.
-}
-
 void CEEInfo::recordCallSite(
         uint32_t              instrOffset,  /* IN */
         CORINFO_SIG_INFO *    callSig,      /* IN */
index 719bda0..bddc24f 100644 (file)
@@ -687,14 +687,6 @@ public:
             uint8_t**pInstrumentationData /* OUT */
             ) override final;
 
-    CORINFO_CLASS_HANDLE getLikelyClass(
-            CORINFO_METHOD_HANDLE ftnHnd,
-            CORINFO_CLASS_HANDLE  baseHnd,
-            uint32_t              ilOffset,
-            uint32_t *            pLikelihood,
-            uint32_t *            pNumberOfClasses
-            ) override final;
-
     void recordCallSite(
             uint32_t                     instrOffset,  /* IN */
             CORINFO_SIG_INFO *        callSig,      /* IN */
index 15ff3ab..747b4cb 100644 (file)
@@ -50,69 +50,6 @@ const char* const         PgoManager::s_FourByte = "%u\n";
 const char* const         PgoManager::s_EightByte = "%u %u\n";
 const char* const         PgoManager::s_TypeHandle = "TypeHandle: %s\n";
 
-// Data item in class profile histogram
-//
-struct HistogramEntry
-{
-    // Class that was observed at runtime
-    INT_PTR m_mt; // This may be an "unknown type handle"
-    // Number of observations in the table
-    unsigned             m_count;
-};
-
-// Summarizes a ClassProfile table by forming a Histogram
-//
-struct Histogram
-{
-    Histogram(uint32_t histogramCount, INT_PTR* histogramEntries, unsigned entryCount);
-
-    // Sum of counts from all entries in the histogram. This includes "unknown" entries which are not captured in m_histogram
-    unsigned m_totalCount;
-    // Rough guess at count of unknown types
-    unsigned m_unknownTypes;
-    // Histogram entries, in no particular order.
-    // The first m_count of these will be valid.
-    StackSArray<HistogramEntry> m_histogram;
-};
-
-Histogram::Histogram(uint32_t histogramCount, INT_PTR* histogramEntries, unsigned entryCount)
-{
-    m_unknownTypes = 0;
-    m_totalCount = 0;
-    uint32_t unknownTypeHandleMask = 0;
-
-    for (unsigned k = 0; k < entryCount; k++)
-    {
-        if (histogramEntries[k] == 0)
-        {
-            continue;
-        }
-        
-        m_totalCount++;
-
-        INT_PTR currentEntry = histogramEntries[k];
-        
-        bool found = false;
-        unsigned h = 0;
-        for(; h < m_histogram.GetCount(); h++)
-        {
-            if (m_histogram[h].m_mt == currentEntry)
-            {
-                m_histogram[h].m_count++;
-                found = true;
-                break;
-            }
-        }
-        
-        if (!found)
-        {
-            HistogramEntry newEntry;
-            newEntry.m_mt = currentEntry;
-            newEntry.m_count = 1;
-            m_histogram.Append(newEntry);
-        }
-    }
-}
 
 PtrSHash<PgoManager::Header, PgoManager::CodeAndMethodHash> PgoManager::s_textFormatPgoData;
 CrstStatic PgoManager::s_pgoMgrLock;
@@ -828,7 +765,7 @@ HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, BYTE** pAlloca
                                 {
                                     INT_PTR* typeHandleValueAddress = (INT_PTR*)(found->GetData() + schema->Offset + iEntry * InstrumentationKindToSize(schema->InstrumentationKind));
                                     INT_PTR initialTypeHandleValue = VolatileLoad(typeHandleValueAddress);
-                                    if (((initialTypeHandleValue & 1) == 1) && !IsUnknownTypeHandle(initialTypeHandleValue))
+                                    if (((initialTypeHandleValue & 1) == 1) && !ICorJitInfo::IsUnknownTypeHandle(initialTypeHandleValue))
                                     {
                                         INT_PTR newPtr = 0;
                                         TypeHandle th;
@@ -1082,125 +1019,6 @@ HRESULT PgoManager::getPgoInstrumentationResultsInstance(MethodDesc* pMD, BYTE**
     }
 }
 
-// See if there is a class profile for this method at the indicated il Offset.
-// If so, return the most frequently seen class, along with the likelihood that
-// it was the class seen, and the total number of classes seen.
-//
-// Return NULL if there is no profile data to be found.
-//
-CORINFO_CLASS_HANDLE PgoManager::getLikelyClass(MethodDesc* pMD, unsigned ilSize, unsigned ilOffset, UINT32* pLikelihood, UINT32* pNumberOfClasses)
-{
-    *pLikelihood = 0;
-    *pNumberOfClasses = 0;
-
-    NewArrayHolder<BYTE> allocatedData;
-    ICorJitInfo::PgoInstrumentationSchema* schema;
-    BYTE* pInstrumentationData;
-    UINT32 cSchema;
-    HRESULT hr = getPgoInstrumentationResults(pMD, &allocatedData, &schema, &cSchema, &pInstrumentationData);
-
-    // Failed to find any sort of profile data for this method
-    //
-    if (FAILED(hr))
-    {
-        return NULL;
-    }
-
-    // TODO This logic should be moved to the JIT
-    for (COUNT_T i = 0; i < cSchema; i++)
-    {
-        if (schema[i].ILOffset != (int32_t)ilOffset)
-            continue;
-
-        if ((schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramCount) &&
-            (schema[i].Count == 1) &&
-            ((i + 1) < cSchema) &&
-            (schema[i + 1].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramTypeHandle))
-        {
-            // Form a histogram
-            //
-            Histogram h(*(uint32_t*)(pInstrumentationData + schema[i].Offset), (INT_PTR*)(pInstrumentationData + schema[i + 1].Offset), schema[i + 1].Count);
-
-            // Use histogram count as number of classes estimate
-            //
-            *pNumberOfClasses = h.m_histogram.GetCount() + h.m_unknownTypes;
-
-            // Report back what we've learned
-            // (perhaps, use count to augment likelihood?)
-            // 
-            switch (*pNumberOfClasses)
-            {
-                case 0:
-                {
-                    return NULL;
-                }
-                break;
-
-                case 1:
-                {
-                    if (IsUnknownTypeHandle(h.m_histogram[0].m_mt))
-                    {
-                        return NULL;
-                    }
-                    *pLikelihood = 100;
-                    return (CORINFO_CLASS_HANDLE)h.m_histogram[0].m_mt;
-                }
-                break;
-
-                case 2:
-                {
-                    if ((h.m_histogram[0].m_count >= h.m_histogram[1].m_count) && !IsUnknownTypeHandle(h.m_histogram[0].m_mt))
-                    {
-                        *pLikelihood = (100 * h.m_histogram[0].m_count) / h.m_totalCount;
-                        return (CORINFO_CLASS_HANDLE)h.m_histogram[0].m_mt;
-                    }
-                    else if (!IsUnknownTypeHandle(h.m_histogram[1].m_mt))
-                    {
-                        *pLikelihood = (100 * h.m_histogram[1].m_count) / h.m_totalCount;
-                        return (CORINFO_CLASS_HANDLE)h.m_histogram[1].m_mt;
-                    }
-                    else
-                    {
-                        return NULL;
-                    }
-                }
-                break;
-
-                default:
-                {
-                    // Find maximum entry and return it
-                    //
-                    unsigned maxKnownIndex = 0;
-                    unsigned maxKnownCount = 0;
-
-                    for (unsigned m = 0; m < h.m_histogram.GetCount(); m++)
-                    {
-                        if ((h.m_histogram[m].m_count > maxKnownCount) && !IsUnknownTypeHandle(h.m_histogram[m].m_mt))
-                        {
-                            maxKnownIndex = m;
-                            maxKnownCount = h.m_histogram[m].m_count;
-                        }
-                    }
-
-                    if (maxKnownCount > 0)
-                    {
-                        *pLikelihood = (100 * maxKnownCount) / h.m_totalCount;;
-                        return (CORINFO_CLASS_HANDLE)h.m_histogram[maxKnownIndex].m_mt;
-                    }
-
-                    return NULL;
-                }
-                break;
-            }
-
-        }
-    }
-
-    // Failed to find histogram data for this method
-    //
-    return NULL;
-}
-
 #else
 
 // Stub version for !FEATURE_PGO builds
@@ -1227,13 +1045,6 @@ void PgoManager::VerifyAddress(void* address)
 
 // Stub version for !FEATURE_PGO builds
 //
-CORINFO_CLASS_HANDLE PgoManager::getLikelyClass(MethodDesc* pMD, unsigned ilSize, unsigned ilOffset, UINT32* pLikelihood, UINT32* pNumberOfClasses)
-{
-    *pLikelihood = 0;
-    *pNumberOfClasses = 0;
-    return NULL;
-}
-
 void PgoManager::CreatePgoManager(PgoManager** ppMgr, bool loaderAllocator)
 {
     *ppMgr = NULL;
index f50b966..c7c5e1c 100644 (file)
@@ -36,9 +36,6 @@ public:
 
     static void CreatePgoManager(PgoManager* volatile* ppPgoManager, bool loaderAllocator);
 
-    // Retrieve the most likely class for a particular call
-    static CORINFO_CLASS_HANDLE getLikelyClass(MethodDesc* pMD, unsigned ilSize, unsigned ilOffset, UINT32* pLikelihood, UINT32* pNumberOfClasses);
-
     // Verify address in bounds
     static void VerifyAddress(void* address);
 
index 4f23cc5..c242d79 100644 (file)
@@ -1138,16 +1138,6 @@ HRESULT ZapInfo::getPgoInstrumentationResults(CORINFO_METHOD_HANDLE      ftnHnd,
     return pgoResults->m_hr;
 }
 
-CORINFO_CLASS_HANDLE ZapInfo::getLikelyClass(
-    CORINFO_METHOD_HANDLE ftnHnd,
-    CORINFO_CLASS_HANDLE  baseHnd,
-    UINT32                ilOffset,
-    UINT32*               pLikelihood,
-    UINT32*               pNumberOfClasses)
-{
-    return NULL;
-}
-
 void ZapInfo::allocMem(
     uint32_t            hotCodeSize,    /* IN */
     uint32_t            coldCodeSize,   /* IN */