Rejit support for R2R images
authornoahfalk <noahfalk@microsoft.com>
Mon, 13 Feb 2017 22:41:20 +0000 (14:41 -0800)
committernoahfalk <noahfalk@microsoft.com>
Mon, 13 Feb 2017 22:41:20 +0000 (14:41 -0800)
Two changes:
a) R2R code wasn't being reported to the Rejit Manager when it was used, this is a simple fix in prestub.cpp. This makes the ReJit API work.
b) The bulk of the changes handle adding support for an inlining table to R2R so that ICorProfilerInfo6::EnumNgenMethodsInliningThisMethod can supply that information to profilers.

This was only tested on Windows thus far, but there is no apparent reason this change would be OS specific.

20 files changed:
src/inc/corcompile.h
src/inc/corprof.idl
src/inc/readytorun.h
src/mscorlib/src/System/Runtime/Versioning/NonVersionableAttribute.cs
src/vm/ceeload.cpp
src/vm/ceeload.h
src/vm/compile.cpp
src/vm/compile.h
src/vm/dataimage.cpp
src/vm/inlinetracking.cpp
src/vm/inlinetracking.h
src/vm/jitinterface.cpp
src/vm/prestub.cpp
src/vm/proftoeeinterfaceimpl.cpp
src/vm/readytoruninfo.cpp
src/vm/readytoruninfo.h
src/zap/zapimage.cpp
src/zap/zapimage.h
src/zap/zapinfo.cpp
src/zap/zapreadytorun.cpp

index e2d3c05..f99e27e 100644 (file)
@@ -1304,6 +1304,12 @@ class ICorCompilePreloader
             CORINFO_METHOD_HANDLE method, 
             CORINFO_METHOD_HANDLE duplicateMethod) = 0;
 
+    // Returns a compressed encoding of the inline tracking map 
+    // for this compilation
+    virtual void GetSerializedInlineTrackingMap(
+            IN OUT SBuffer    * pSerializedInlineTrackingMap
+            ) = 0;
+
     //
     // Release frees the preloader
     //
index 9af1cd9..3378431 100644 (file)
@@ -3740,15 +3740,20 @@ interface ICorProfilerInfo6 : ICorProfilerInfo5
 {
     /*
     * Returns an enumerator for all methods that 
-    * - belong to a given NGen module (inlinersModuleId) and 
+    * - belong to a given NGen or R2R module (inlinersModuleId) and 
     * - inlined a body of a given method (inlineeModuleId / inlineeMethodId). 
     *
     * If incompleteData is set to TRUE after function is called, it means that the methods enumerator 
     * doesn't contain all methods inlining a given method. 
     * It can happen when one or more direct or indirect dependencies of inliners module haven't been loaded yet.
-    * If profiler needs accurate data it should retry later when more modules are loaded (preferable on each module load).
+    * If profiler needs accurate data it should retry later when more modules are loaded (preferably on each module load).
     *
     * It can be used to lift limitation on inlining for ReJIT.
+    *
+    * NOTE: If the inlinee method is decorated with the System.Runtime.Versioning.NonVersionable attribute then
+    * then some inliners may not ever be reported. If you need to get a full accounting you can avoid the issue
+    * by disabling the use of all native images.
+    * 
     */
     HRESULT EnumNgenModuleMethodsInliningThisMethod(
         [in] ModuleID    inlinersModuleId,
index ebc557b..9084b92 100644 (file)
@@ -16,7 +16,8 @@
 #define READYTORUN_SIGNATURE 0x00525452 // 'RTR'
 
 #define READYTORUN_MAJOR_VERSION 0x0002
-#define READYTORUN_MINOR_VERSION 0x0000
+#define READYTORUN_MINOR_VERSION 0x0001
+// R2R Version 2.1 adds the READYTORUN_SECTION_INLINING_INFO section
 
 struct READYTORUN_HEADER
 {
@@ -57,6 +58,13 @@ enum ReadyToRunSectionType
     // 107 used by an older format of READYTORUN_SECTION_AVAILABLE_TYPES
     READYTORUN_SECTION_AVAILABLE_TYPES              = 108,
     READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS  = 109,
+    READYTORUN_SECTION_INLINING_INFO                = 110  // Added in V2.1
+
+       // If you add a new section consider whether it is a breaking or non-breaking change.
+       // Usually it is non-breaking, but if it is preferable to have older runtimes fail
+       // to load the image vs. ignoring the new section it could be marked breaking.
+       // Increment the READYTORUN_MINOR_VERSION (non-breaking) or READYTORUN_MAJOR_VERSION
+       // (breaking) as appropriate.
 };
 
 //
index 0a9845d..9fafce4 100644 (file)
 ** is never changed in ReadyToRun native images. Any changes to such members or types would be 
 ** breaking changes for ReadyToRun.
 **
+** Applying this type also has the side effect that the inlining tables in R2R images will not
+** report that inlining of NonVersionable attributed methods occured. These inlining tables are used
+** by profilers to figure out the set of methods that need to be rejited when one method is instrumented,
+** so in effect NonVersionable methods are also non-instrumentable. Generally this is OK for
+** extremely trivial low level methods where NonVersionable gets used, but if there is any plan to 
+** significantly extend its usage or allow 3rd parties to use it please discuss with the diagnostics team.
 ===========================================================*/
 using System;
 using System.Diagnostics;
index 4a03927..aa071bc 100644 (file)
@@ -183,10 +183,32 @@ ARRAY_PTR_COR_IL_MAP InstrumentedILOffsetMapping::GetOffsets() const
     return m_rgMap;
 }
 
-PTR_PersistentInlineTrackingMap Module::GetNgenInlineTrackingMap()
+BOOL Module::HasInlineTrackingMap()
 {
     LIMITED_METHOD_DAC_CONTRACT;
-    return m_persistentInlineTrackingMap;
+#ifdef FEATURE_READYTORUN
+       if (IsReadyToRun() && GetReadyToRunInfo()->GetInlineTrackingMap() != NULL)
+       {
+               return TRUE;
+       }
+#endif
+    return (m_pPersistentInlineTrackingMapNGen != NULL);
+}
+
+COUNT_T Module::GetInliners(PTR_Module inlineeOwnerMod, mdMethodDef inlineeTkn, COUNT_T inlinersSize, MethodInModule inliners[], BOOL *incompleteData)
+{
+    WRAPPER_NO_CONTRACT;
+#ifdef FEATURE_READYTORUN
+    if(IsReadyToRun() && GetReadyToRunInfo()->GetInlineTrackingMap() != NULL)
+    {
+        return GetReadyToRunInfo()->GetInlineTrackingMap()->GetInliners(inlineeOwnerMod, inlineeTkn, inlinersSize, inliners, incompleteData);
+    }
+#endif
+    if(m_pPersistentInlineTrackingMapNGen != NULL)
+    {
+        return m_pPersistentInlineTrackingMapNGen->GetInliners(inlineeOwnerMod, inlineeTkn, inlinersSize, inliners, incompleteData);
+    }
+    return 0;
 }
 
 
@@ -10064,8 +10086,8 @@ void Module::Save(DataImage *image)
     InlineTrackingMap *inlineTrackingMap = image->GetInlineTrackingMap();
     if (inlineTrackingMap) 
     {
-        m_persistentInlineTrackingMap = new (image->GetHeap()) PersistentInlineTrackingMap(this);
-        m_persistentInlineTrackingMap->Save(image, inlineTrackingMap);
+        m_pPersistentInlineTrackingMapNGen = new (image->GetHeap()) PersistentInlineTrackingMapNGen(this);
+        m_pPersistentInlineTrackingMapNGen->Save(image, inlineTrackingMap);
     }
 
     if (m_pNgenStats && g_CorCompileVerboseLevel >= CORCOMPILE_STATS)
@@ -11064,14 +11086,14 @@ void Module::Fixup(DataImage *image)
     }
 
     // Fix up inlining data
-    if(m_persistentInlineTrackingMap)
+    if(m_pPersistentInlineTrackingMapNGen)
     {
-        image->FixupPointerField(this, offsetof(Module, m_persistentInlineTrackingMap));
-        m_persistentInlineTrackingMap->Fixup(image);
+        image->FixupPointerField(this, offsetof(Module, m_pPersistentInlineTrackingMapNGen));
+        m_pPersistentInlineTrackingMapNGen->Fixup(image);
     } 
     else
     {
-        image->ZeroPointerField(this, offsetof(Module, m_persistentInlineTrackingMap));
+        image->ZeroPointerField(this, offsetof(Module, m_pPersistentInlineTrackingMapNGen));
     }
 
     SetIsModuleSaved();
index 42023b3..de92900 100644 (file)
@@ -81,7 +81,8 @@ class TypeHandleList;
 class ProfileEmitter;
 class ReJitManager;
 class TrackingMap;
-class PersistentInlineTrackingMap;
+struct MethodInModule;
+class PersistentInlineTrackingMapNGen;
 
 // Hash table parameter of available classes (name -> module/class) hash
 #define AVAILABLE_CLASSES_HASH_BUCKETS 1024
@@ -104,7 +105,7 @@ class PersistentInlineTrackingMap;
 #define NATIVE_SYMBOL_READER_DLL W("diasymreader.dll")
 #endif
 
-typedef DPTR(PersistentInlineTrackingMap) PTR_PersistentInlineTrackingMap;
+typedef DPTR(PersistentInlineTrackingMapNGen) PTR_PersistentInlineTrackingMapNGen;
 
 extern VerboseLevel g_CorCompileVerboseLevel;
 #endif  // FEATURE_PREJIT
@@ -2667,7 +2668,8 @@ public:
     void NotifyProfilerLoadFinished(HRESULT hr);
 #endif // PROFILING_SUPPORTED
 
-    PTR_PersistentInlineTrackingMap GetNgenInlineTrackingMap();
+    BOOL HasInlineTrackingMap();
+    COUNT_T GetInliners(PTR_Module inlineeOwnerMod, mdMethodDef inlineeTkn, COUNT_T inlinersSize, MethodInModule inliners[], BOOL *incompleteData);
 
 public:
     void NotifyEtwLoadFinished(HRESULT hr);
@@ -3436,7 +3438,7 @@ private:
     DebuggerSpecificData  m_debuggerSpecificData;
 
     // This is a compressed read only copy of m_inlineTrackingMap, which is being saved to NGEN image.
-    PTR_PersistentInlineTrackingMap m_persistentInlineTrackingMap;
+    PTR_PersistentInlineTrackingMapNGen m_pPersistentInlineTrackingMapNGen;
 
 
     LPCSTR               *m_AssemblyRefByNameTable;  // array that maps mdAssemblyRef tokens into their simple name
index 8aca2ad..09925cd 100644 (file)
@@ -68,6 +68,7 @@
 #include "argdestination.h"
 
 #include "versionresilienthashcode.h"
+#include "inlinetracking.h"
 
 #ifdef CROSSGEN_COMPILE
 CompilationDomain * theDomain;
@@ -6841,6 +6842,12 @@ ULONG CEEPreloader::Release()
     return 0;
 }
 
+void CEEPreloader::GetSerializedInlineTrackingMap(SBuffer* pBuffer)
+{
+    InlineTrackingMap * pInlineTrackingMap = m_image->GetInlineTrackingMap();
+    PersistentInlineTrackingMapR2R::Save(m_image->GetHeap(), pBuffer, pInlineTrackingMap);
+}
+
 void CEEPreloader::Error(mdToken token, Exception * pException)
 {
     STANDARD_VM_CONTRACT;
index 7aeef31..8fdd383 100644 (file)
@@ -654,6 +654,8 @@ public:
 
     ULONG Release();
 
+    void GetSerializedInlineTrackingMap(SBuffer* pBuffer);
+
     void Error(mdToken token, Exception * pException);
 };
 
index f6b8337..fc584d7 100644 (file)
@@ -126,8 +126,7 @@ DataImage::DataImage(Module *module, CEEPreloader *preloader)
     m_pZapImage->m_pDataImage = this;
 
     m_pInternedStructures = new InternedStructureHashTable();
-
-    m_inlineTrackingMap = NULL;
+    m_inlineTrackingMap = new InlineTrackingMap();
 }
 
 DataImage::~DataImage()
index 02e2a7c..c4bfe07 100644 (file)
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 // =============================================================================================
-// Code for tracking method inlinings in NGen images.
+// Code for tracking method inlinings in NGen and R2R images.
 // The only information stored is "who" got inlined "where", no offsets or inlining depth tracking. 
 // (No good for debugger yet.)
 // This information is later exposed to profilers and can be useful for ReJIT.
@@ -12,6 +12,8 @@
 #include "inlinetracking.h"
 #include "ceeload.h"
 
+#ifndef DACCESS_COMPILE
+
 bool MethodInModule::operator <(const MethodInModule& other) const
 {
     STANDARD_VM_CONTRACT;
@@ -123,9 +125,86 @@ InlineTrackingEntry & InlineTrackingEntry::operator = (const InlineTrackingEntry
     return *this;
 }
 
+void InlineTrackingEntry::Add(PTR_MethodDesc inliner)
+{
+    STANDARD_VM_CONTRACT;
 
-#ifndef DACCESS_COMPILE
-COUNT_T PersistentInlineTrackingMap::GetInliners(PTR_Module inlineeOwnerMod, mdMethodDef inlineeTkn, COUNT_T inlinersSize, MethodInModule inliners[], BOOL *incompleteData)
+    MethodInModule method(inliner->GetModule(), inliner->GetMemberDef());
+
+    // Going through last 10 inliners to check if a given inliner has recently been registered.
+    // It allows to filter out most duplicates without having to scan through hundreds of inliners 
+    // for methods like Object.ctor or Monitor.Enter. 
+    // We are OK to keep occasional duplicates in m_inliners, we'll get rid of them 
+    // in SortAndDeduplicate() anyway.
+    int count = static_cast<int>(m_inliners.GetCount());
+    int start = max(0, count - 10);
+    for (int i = count - 1; i >= start; i--)
+    {
+        if (m_inliners[i] == method)
+            return;
+    }
+
+    //look like we see this inliner for the first time, add it to the collection
+    m_inliners.Append(method);
+}
+
+InlineTrackingMap::InlineTrackingMap()
+    : m_mapCrst(CrstInlineTrackingMap)
+{
+    STANDARD_VM_CONTRACT;
+}
+
+void InlineTrackingMap::AddInlining(MethodDesc *inliner, MethodDesc *inlinee)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(inliner != NULL);
+    _ASSERTE(inlinee != NULL);
+
+    MethodInModule inlineeMnM(inlinee->GetModule(), inlinee->GetMemberDef());
+
+    if (RidFromToken(inlineeMnM.m_methodDef) == 0 || RidFromToken(inliner->GetMemberDef()) == 0)
+    {
+        // Sometimes we do see methods that don't have valid tokens (stubs etc)
+        // we just ignore them.
+        return;
+    }
+
+    CrstHolder lock(&m_mapCrst);
+    InlineTrackingEntry *existingEntry = const_cast<InlineTrackingEntry *>(LookupPtr(inlineeMnM));
+    if (existingEntry)
+    {
+        // We saw this inlinee before, just add one more inliner
+        existingEntry->Add(inliner);
+    }
+    else
+    {
+        // We haven't seen this inlinee before, create a new record in the hashtable
+        // and add a first inliner to it.
+        InlineTrackingEntry newEntry;
+        newEntry.m_inlinee = inlineeMnM;
+        newEntry.Add(inliner);
+        Add(newEntry);
+    }
+}
+
+#endif //!DACCESS_COMPILE
+
+void ZapInlineeRecord::InitForNGen(RID rid, LPCUTF8 simpleName)
+{
+    LIMITED_METHOD_CONTRACT;
+    //XOR of up to first 24 bytes in module name
+    DWORD hash = 0;
+    for (int i = 0; simpleName[i] && i < 24; i++)
+        hash ^= (BYTE)simpleName[i];
+
+    // This key contains 24 bits of RID and 8 bits from module name.
+    // Since RID can't be longer than 24 bits, we can't have method RID collistions,
+    // that's why PersistentInlineTrackingMap::GetInliners only deals with module collisions.
+    m_key = (hash << 24) | rid;
+}
+
+
+COUNT_T PersistentInlineTrackingMapNGen::GetInliners(PTR_Module inlineeOwnerMod, mdMethodDef inlineeTkn, COUNT_T inlinersSize, MethodInModule inliners[], BOOL *incompleteData)
 {
     CONTRACTL
     {
@@ -149,10 +228,11 @@ COUNT_T PersistentInlineTrackingMap::GetInliners(PTR_Module inlineeOwnerMod, mdM
     }
 
     // Binary search to find all records matching (inlineeTkn/inlineeOwnerMod)
-    InlineeRecord probeRecord(RidFromToken(inlineeTkn), inlineeOwnerMod->GetSimpleName());
-    InlineeRecord *begin = m_inlineeIndex;
-    InlineeRecord *end = m_inlineeIndex + m_inlineeIndexSize;
-    InlineeRecord *foundRecord = util::lower_bound(begin, end, probeRecord);
+    ZapInlineeRecord probeRecord;
+    probeRecord.InitForNGen(RidFromToken(inlineeTkn), inlineeOwnerMod->GetSimpleName());
+    ZapInlineeRecord *begin = m_inlineeIndex;
+    ZapInlineeRecord *end = m_inlineeIndex + m_inlineeIndexSize;
+    ZapInlineeRecord *foundRecord = util::lower_bound(begin, end, probeRecord);
     DWORD result = 0;
     DWORD outputIndex = 0;
 
@@ -204,7 +284,9 @@ COUNT_T PersistentInlineTrackingMap::GetInliners(PTR_Module inlineeOwnerMod, mdM
     return result;
 }
 
-Module *PersistentInlineTrackingMap::GetModuleByIndex(DWORD index)
+
+
+Module *PersistentInlineTrackingMapNGen::GetModuleByIndex(DWORD index)
 {
     CONTRACTL
     {
@@ -222,106 +304,42 @@ Module *PersistentInlineTrackingMap::GetModuleByIndex(DWORD index)
     return m_module->GetModuleFromIndexIfLoaded(index);
 }
 
-PersistentInlineTrackingMap::InlineeRecord::InlineeRecord(RID rid, LPCUTF8 simpleName)
-{
-    LIMITED_METHOD_CONTRACT;
-    //XOR of up to first 24 bytes in module name
-    DWORD hash = 0;
-    for (int i = 0; simpleName[i] && i < 24; i++)
-        hash ^= (BYTE)simpleName[i];
-
-    // This key contains 24 bits of RID and 8 bits from module name.
-    // Since RID can't be longer than 24 bits, we can't have method RID collistions,
-    // that's why PersistentInlineTrackingMap::GetInliners only deals with module collisions.
-    m_key = (hash << 24) | rid;
-}
 
-InlineTrackingMap::InlineTrackingMap()
-    : m_mapCrst(CrstInlineTrackingMap)
-{
-    STANDARD_VM_CONTRACT;
-}
-
-void InlineTrackingMap::AddInlining(MethodDesc *inliner, MethodDesc *inlinee)
-{
-    STANDARD_VM_CONTRACT;
-    _ASSERTE(inliner != NULL);
-    _ASSERTE(inlinee != NULL);
-
-    MethodInModule inlineeMnM(inlinee->GetModule(), inlinee->GetMemberDef());
-
-    if (RidFromToken(inlineeMnM.m_methodDef) == 0 || RidFromToken(inliner->GetMemberDef()) == 0)
-    {
-        // Sometimes we do see methods that don't have valid tokens (stubs etc)
-        // we just ignore them.
-        return;
-    }
-
-    CrstHolder lock(&m_mapCrst);
-    InlineTrackingEntry *existingEntry = const_cast<InlineTrackingEntry *>(LookupPtr(inlineeMnM));
-    if (existingEntry)
-    {
-        // We saw this inlinee before, just add one more inliner
-        existingEntry->Add(inliner);
-    }
-    else
-    {
-        // We haven't seen this inlinee before, create a new record in the hashtable
-        // and add a first inliner to it.
-        InlineTrackingEntry newEntry;
-        newEntry.m_inlinee = inlineeMnM;
-        newEntry.Add(inliner);
-        Add(newEntry);
-    }
-}
-
-void InlineTrackingEntry::Add(PTR_MethodDesc inliner)
-{
-    STANDARD_VM_CONTRACT;
-
-    MethodInModule method(inliner->GetModule(), inliner->GetMemberDef());
-
-    // Going through last 10 inliners to check if a given inliner has recently been registered.
-    // It allows to filter out most duplicates without having to scan through hundreds of inliners 
-    // for methods like Object.ctor or Monitor.Enter. 
-    // We are OK to keep occasional duplicates in m_inliners, we'll get rid of them 
-    // in SortAndDeduplicate() anyway.
-    int count = static_cast<int>(m_inliners.GetCount());
-    int start = max(0, count - 10);
-    for (int i = count - 1; i >= start; i--)
-    {
-        if (m_inliners[i] == method)
-            return;
-    }
-
-    //look like we see this inliner for the first time, add it to the collection
-    m_inliners.Append(method);
-}
 
+#ifndef DACCESS_COMPILE
 #ifdef FEATURE_NATIVE_IMAGE_GENERATION
 
-void PersistentInlineTrackingMap::ProcessInlineTrackingEntry(DataImage *image, SBuffer *inlinersBuffer, SArray<InlineeRecord> *inlineeIndex, InlineTrackingEntry *entry)
+// This is a shared serialization routine used for both NGEN and R2R formats. If image != NULL the NGEN format is generated, otherwise the R2R format
+void SerializeInlineTrackingEntry(DataImage* image, SBuffer *inlinersBuffer, SArray<ZapInlineeRecord> *inlineeIndex, InlineTrackingEntry *entry)
 {
     STANDARD_VM_CONTRACT;
     // This call removes duplicates from inliners and makes sure they are sorted by module
     entry->SortAndDeduplicate();
     MethodInModule inlinee = entry->m_inlinee;
-    DWORD inlineeModuleZapIndex = image->GetModuleImportIndex(inlinee.m_module);
+    DWORD inlineeModuleZapIndex = 0;
+    if (image != NULL)
+    {
+        inlineeModuleZapIndex = image->GetModuleImportIndex(inlinee.m_module);
+    }
     InlineSArray<MethodInModule, 3> &inliners = entry->m_inliners;
-    COUNT_T tatalInlinersCount = inliners.GetCount();
-    _ASSERTE(tatalInlinersCount > 0);
+    COUNT_T totalInlinersCount = inliners.GetCount();
+    _ASSERTE(totalInlinersCount > 0);
 
     COUNT_T sameModuleCount;
     // Going through all inliners and grouping them by their module, for each module we'll create
-    // InlineeRecord and encode inliners as bytes in inlinersBuffer.
-    for (COUNT_T thisModuleBegin = 0; thisModuleBegin < tatalInlinersCount; thisModuleBegin += sameModuleCount)
+    // an ZapInlineeRecord and encode inliners as bytes in inlinersBuffer.
+    for (COUNT_T thisModuleBegin = 0; thisModuleBegin < totalInlinersCount; thisModuleBegin += sameModuleCount)
     {
         Module *lastInlinerModule = inliners[thisModuleBegin].m_module;
-        DWORD lastInlinerModuleZapIndex = image->GetModuleImportIndex(lastInlinerModule);
-        
+        DWORD lastInlinerModuleZapIndex = 0;
+        if (image != NULL)
+        {
+            lastInlinerModuleZapIndex = image->GetModuleImportIndex(lastInlinerModule);
+        }
+
         // Counting how many inliners belong to this module
         sameModuleCount = 1;
-        while (thisModuleBegin + sameModuleCount < tatalInlinersCount &&
+        while (thisModuleBegin + sameModuleCount < totalInlinersCount &&
             inliners[thisModuleBegin + sameModuleCount].m_module == lastInlinerModule)
         {
             sameModuleCount++;
@@ -329,8 +347,11 @@ void PersistentInlineTrackingMap::ProcessInlineTrackingEntry(DataImage *image, S
 
         // Saving module indexes and number of inliners
         NibbleWriter inlinersStream;
-        inlinersStream.WriteEncodedU32(inlineeModuleZapIndex);
-        inlinersStream.WriteEncodedU32(lastInlinerModuleZapIndex);
+        if (image != NULL)
+        {
+            inlinersStream.WriteEncodedU32(inlineeModuleZapIndex);
+            inlinersStream.WriteEncodedU32(lastInlinerModuleZapIndex);
+        }
         inlinersStream.WriteEncodedU32(sameModuleCount);
 
         // Saving inliners RIDs, each new RID is represented as an adjustment (diff) to the previous one
@@ -343,15 +364,22 @@ void PersistentInlineTrackingMap::ProcessInlineTrackingEntry(DataImage *image, S
             prevMethodRid = methodRid;
         }
         inlinersStream.Flush();
-
+        
         // Copy output of NibbleWriter into a big buffer (inlinersBuffer) for inliners from the same module
         // and create an InlineeRecord with correct offset
-        InlineeRecord record(RidFromToken(inlinee.m_methodDef), inlinee.m_module->GetSimpleName());
         DWORD inlinersStreamSize;
         const BYTE *inlinersStreamPtr = (const BYTE *)inlinersStream.GetBlob(&inlinersStreamSize);
+        ZapInlineeRecord record;
+        if (image != NULL)
+        {
+            record.InitForNGen(RidFromToken(inlinee.m_methodDef), inlinee.m_module->GetSimpleName());
+        }
+        else
+        {
+            record.InitForR2R(RidFromToken(inlinee.m_methodDef));
+        }
         record.m_offset = inlinersBuffer->GetSize();
         inlinersBuffer->Insert(inlinersBuffer->End(), SBuffer(SBuffer::Immutable, inlinersStreamPtr, inlinersStreamSize));
-
         inlineeIndex->Append(record);
     }
 }
@@ -361,20 +389,16 @@ bool compare_entry(const InlineTrackingEntry* first, const InlineTrackingEntry*
     return first->m_inlinee < second->m_inlinee;
 }
 
-void PersistentInlineTrackingMap::Save(DataImage *image, InlineTrackingMap* runtimeMap)
+// This is a shared serialization routine used for both NGEN and R2R formats. If image != NULL the NGEN format is generated, otherwise the R2R format
+void SerializeTrackingMapBuffers(ZapHeap* heap, DataImage *image, SBuffer *inlinersBuffer, SArray<ZapInlineeRecord> *inlineeIndex, InlineTrackingMap* runtimeMap)
 {
     STANDARD_VM_CONTRACT;
-    _ASSERTE(image != NULL);
     _ASSERTE(runtimeMap != NULL);
 
-    SArray<InlineeRecord> inlineeIndex;
-    SBuffer inlinersBuffer;
-
     // Sort records from runtimeMap, because we need to make sure 
     // we save everything in deterministic order. Hashtable iteration is not deterministic.
     COUNT_T runtimeMapCount = runtimeMap->GetCount();
-    InlineTrackingEntry **inlinees = new InlineTrackingEntry *[runtimeMapCount];
-    NewArrayHolder<InlineTrackingEntry *>inlineesHolder(inlinees);
+    InlineTrackingEntry **inlinees = new (heap) InlineTrackingEntry *[runtimeMapCount];
     int index = 0;
     for (auto iter = runtimeMap->Begin(), end = runtimeMap->End(); iter != end; ++iter)
     {
@@ -387,8 +411,22 @@ void PersistentInlineTrackingMap::Save(DataImage *image, InlineTrackingMap* runt
     // and write corresponding records into inlineeIndex and inlinersBuffer
     for (COUNT_T i = 0; i < runtimeMapCount; i++)
     {
-        ProcessInlineTrackingEntry(image, &inlinersBuffer, &inlineeIndex, inlinees[i]);
+        SerializeInlineTrackingEntry(image, inlinersBuffer, inlineeIndex, inlinees[i]);
     }
+}
+
+
+
+void PersistentInlineTrackingMapNGen::Save(DataImage *image, InlineTrackingMap* runtimeMap)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(image != NULL);
+    _ASSERTE(runtimeMap != NULL);
+
+    SArray<ZapInlineeRecord> inlineeIndex;
+    SBuffer inlinersBuffer;
+
+    SerializeTrackingMapBuffers(image->GetHeap(), image, &inlinersBuffer, &inlineeIndex, runtimeMap);
 
     m_inlineeIndexSize = inlineeIndex.GetCount();
     m_inlinersBufferSize = inlinersBuffer.GetSize();
@@ -398,7 +436,7 @@ void PersistentInlineTrackingMap::Save(DataImage *image, InlineTrackingMap* runt
     {
         // Copy everything to the class fields, we didn't use the class fields for addition
         // because we want to make sure we don't waste memory for buffer's amortized growth
-        m_inlineeIndex = new (image->GetHeap()) InlineeRecord[m_inlineeIndexSize];
+        m_inlineeIndex = new (image->GetHeap()) ZapInlineeRecord[m_inlineeIndexSize];
         inlineeIndex.Copy(m_inlineeIndex, inlineeIndex.Begin(), m_inlineeIndexSize);
 
         m_inlinersBuffer = new (image->GetHeap()) BYTE[m_inlinersBufferSize];
@@ -418,12 +456,146 @@ void PersistentInlineTrackingMap::Save(DataImage *image, InlineTrackingMap* runt
         m_inlineeIndexSize * sizeof(m_inlineeIndex[0]), m_inlinersBufferSize));
 }
 
-void PersistentInlineTrackingMap::Fixup(DataImage *image)
+void PersistentInlineTrackingMapNGen::Fixup(DataImage *image)
+{
+    STANDARD_VM_CONTRACT;
+    image->FixupPointerField(this, offsetof(PersistentInlineTrackingMapNGen, m_module));
+    image->FixupPointerField(this, offsetof(PersistentInlineTrackingMapNGen, m_inlineeIndex));
+    image->FixupPointerField(this, offsetof(PersistentInlineTrackingMapNGen, m_inlinersBuffer));
+}
+
+#endif //FEATURE_NATIVE_IMAGE_GENERATION
+#endif //!DACCESS_COMPILE
+
+#ifdef FEATURE_READYTORUN
+
+struct InliningHeader
+{
+    int SizeOfInlineeIndex;
+};
+
+#ifndef DACCESS_COMPILE
+#ifdef FEATURE_NATIVE_IMAGE_GENERATION
+
+
+
+void PersistentInlineTrackingMapR2R::Save(ZapHeap* pHeap, SBuffer* pSaveTarget, InlineTrackingMap* runtimeMap)
 {
     STANDARD_VM_CONTRACT;
-    image->FixupPointerField(this, offsetof(PersistentInlineTrackingMap, m_module));
-    image->FixupPointerField(this, offsetof(PersistentInlineTrackingMap, m_inlineeIndex));
-    image->FixupPointerField(this, offsetof(PersistentInlineTrackingMap, m_inlinersBuffer));
+    _ASSERTE(pSaveTarget != NULL);
+    _ASSERTE(runtimeMap != NULL);
+
+    SArray<ZapInlineeRecord> inlineeIndex;
+    SBuffer inlinersBuffer;
+
+    SerializeTrackingMapBuffers(pHeap, NULL, &inlinersBuffer, &inlineeIndex, runtimeMap);
+
+    InliningHeader header;
+    header.SizeOfInlineeIndex = inlineeIndex.GetCount() * sizeof(ZapInlineeRecord);
+    
+    pSaveTarget->Insert(pSaveTarget->End(), SBuffer(SBuffer::Immutable, (const BYTE*) &header, sizeof(header)));
+    DWORD unused = 0;
+    pSaveTarget->Insert(pSaveTarget->End(), SBuffer(SBuffer::Immutable, (const BYTE*) inlineeIndex.GetElements(), header.SizeOfInlineeIndex));
+    pSaveTarget->Insert(pSaveTarget->End(), SBuffer(SBuffer::Immutable, (const BYTE*) inlinersBuffer, inlinersBuffer.GetSize()));
+
+    LOG((LF_ZAP, LL_INFO100000,
+        "PersistentInlineTrackingMap saved. InlineeIndexSize: %d bytes, InlinersBufferSize: %d bytes\n",
+        header.SizeOfInlineeIndex, inlinersBuffer.GetSize()));
 }
+
 #endif //FEATURE_NATIVE_IMAGE_GENERATION
+
+BOOL PersistentInlineTrackingMapR2R::TryLoad(Module* pModule, const BYTE* pBuffer, DWORD cbBuffer, 
+                                                AllocMemTracker *pamTracker, PersistentInlineTrackingMapR2R** ppLoadedMap)
+{
+    InliningHeader* pHeader = (InliningHeader*)pBuffer;
+    if (pHeader->SizeOfInlineeIndex > (int)(cbBuffer - sizeof(InliningHeader)))
+    {
+               //invalid serialized data, the index can't be larger the entire block
+               _ASSERTE(!"R2R image is invalid or there is a bug in the R2R parser");
+        return FALSE;
+    }
+
+       //NOTE: Error checking on the format is very limited at this point.
+       //We trust the image format is valid and this initial check is a cheap
+       //verification that may help catch simple bugs. It does not secure against
+       //a deliberately maliciously formed binary.
+
+       LoaderHeap *pHeap = pModule->GetLoaderAllocator()->GetHighFrequencyHeap();
+       void * pMemory = pamTracker->Track(pHeap->AllocMem((S_SIZE_T)sizeof(PersistentInlineTrackingMapR2R)));
+       PersistentInlineTrackingMapR2R* pMap = new (pMemory) PersistentInlineTrackingMapR2R();
+
+    pMap->m_module = pModule;
+       pMap->m_inlineeIndex = (PTR_ZapInlineeRecord)(pHeader + 1);
+       pMap->m_inlineeIndexSize = pHeader->SizeOfInlineeIndex / sizeof(ZapInlineeRecord);
+       pMap->m_inlinersBuffer = ((PTR_BYTE)(pHeader+1)) + pHeader->SizeOfInlineeIndex;
+       pMap->m_inlinersBufferSize = cbBuffer - sizeof(InliningHeader) - pMap->m_inlineeIndexSize;
+       *ppLoadedMap = pMap;
+    return TRUE;
+}
+
 #endif //!DACCESS_COMPILE
+
+COUNT_T PersistentInlineTrackingMapR2R::GetInliners(PTR_Module inlineeOwnerMod, mdMethodDef inlineeTkn, COUNT_T inlinersSize, MethodInModule inliners[], BOOL *incompleteData)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    _ASSERTE(inlineeOwnerMod);
+    _ASSERTE(inliners);
+
+    if (incompleteData)
+    {
+        *incompleteData = FALSE;
+    }
+    if (m_inlineeIndex == NULL || m_inlinersBuffer == NULL)
+    {
+        //No inlines saved in this image.
+        return 0;
+    }
+    if(inlineeOwnerMod != m_module) 
+    {
+        // no cross module inlining (yet?)
+        return 0;
+    }
+
+    // Binary search to find all records matching (inlineeTkn)
+    ZapInlineeRecord probeRecord;
+    probeRecord.InitForR2R(RidFromToken(inlineeTkn));
+    ZapInlineeRecord *begin = m_inlineeIndex;
+    ZapInlineeRecord *end = m_inlineeIndex + m_inlineeIndexSize;
+    ZapInlineeRecord *foundRecord = util::lower_bound(begin, end, probeRecord);
+    DWORD result = 0;
+    DWORD outputIndex = 0;
+
+    // Go through all matching records
+    for (; foundRecord < end && *foundRecord == probeRecord; foundRecord++)
+    {
+        DWORD offset = foundRecord->m_offset;
+        NibbleReader stream(m_inlinersBuffer + offset, m_inlinersBufferSize - offset);
+        Module *inlinerModule = m_module;
+
+        DWORD inlinersCount = stream.ReadEncodedU32();
+        _ASSERTE(inlinersCount > 0);
+
+        RID inlinerRid = 0;
+        // Reading inliner RIDs one by one, each RID is represented as an adjustment (diff) to the previous one.
+        // Adding inliners module and coping to the output buffer 
+        for (DWORD i = 0; i < inlinersCount && outputIndex < inlinersSize; i++)
+        {
+            inlinerRid += stream.ReadEncodedU32();
+            mdMethodDef inlinerTkn = TokenFromRid(inlinerRid, mdtMethodDef);
+            inliners[outputIndex++] = MethodInModule(inlinerModule, inlinerTkn);
+        }
+        result += inlinersCount;
+    }
+
+    return result;
+}
+
+#endif //FEATURE_READYTORUN
\ No newline at end of file
index cf05027..2cfb35b 100644 (file)
@@ -2,12 +2,26 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 // =============================================================================================
-// Definitions for tracking method inlinings in NGen images.
+// Definitions for tracking method inlinings in NGen and R2R images.
 // The only information stored is "who" got inlined "where", no offsets or inlining depth tracking. 
 // (No good for debugger yet.)
 // This information is later exposed to profilers and can be useful for ReJIT.
 // Runtime inlining is not being tracked because profilers can deduce it via callbacks anyway.
+//
+// This file is made of two major component groups:
+// a) InlineTrackingMap - This is a compilation time datastructure that holds an uncompressed
+//    version of the inline tracking information. It is appended to as methods are compiled.
+//    MethodInModule, InlineTrackingEntry, InlineTrackingMapTraits are all support infratsructure
+//    in this group.
+//
+// b) PersistentInlineTrackingMap[R2R/NGen] - These are the types that understand the image persistence 
+//    formats. At the end of image compilation one of them consumes all the data from an 
+//    InlineTrackingMap to encode it. At runtime an instance will be constructed to read back 
+//    the encoded data on demand. PersistantInlineTrackingMapR2R and PersistantInlineTrackingMapNGen
+//    would nominally use a common base type or interface, but due to ngen binary serialization vtables
+//    were avoided. See farther below for the different format descriptions.
 // =============================================================================================
+
 #ifndef INLINETRACKING_H_
 #define INLINETRACKING_H_
 #include "corhdr.h"
 #include "crsttypes.h"
 #include "daccess.h"
 
+
+
+// ---------------------------------- Compile time support ----------------------------------------------
+
 class MethodDesc;
 typedef DPTR(class MethodDesc)          PTR_MethodDesc;
 
@@ -128,6 +146,17 @@ public:
 
 typedef DPTR(InlineTrackingMap) PTR_InlineTrackingMap;
 
+
+
+
+// ------------------------------------ Persistance support ----------------------------------------------------------
+
+
+
+
+
+// NGEN format
+//
 // This is a persistent map that is stored inside each NGen-ed module image and is used to track 
 // inlines in the NGEN-ed code inside this module. 
 // At runtime this map is used by profiler to track methods that inline a given method, 
@@ -135,25 +164,25 @@ typedef DPTR(InlineTrackingMap) PTR_InlineTrackingMap;
 // It doesn't require any load time unpacking and serves requests directly from NGEN image.
 //
 // It is composed of two arrays:
-// m_inlineeIndex - sorted (by InlineeRecord.key i.e. by module then token) array of InlineeRecords, given an inlinee module name hash (8 bits) 
+// m_inlineeIndex - sorted (by ZapInlineeRecord.key i.e. by module then token) array of ZapInlineeRecords, given an inlinee module name hash (8 bits) 
 //                  and a method token (24 bits) we use binary search to find if this method has ever been inlined in NGen-ed code of this image.
 //                  Each record has m_offset, which is an offset inside m_inlinersBuffer, it has more data on where the method got inlined.
 //
-//                  It is totally possible to have more than one InlineeRecords with the same key, not only due hash collision, but also due to 
+//                  It is totally possible to have more than one ZapInlineeRecords with the same key, not only due hash collision, but also due to 
 //                  the fact that we create one record for each (inlinee module / inliner module) pair. 
 //                  For example: we have MyModule!MyType that uses mscorlib!List<T>. Let's say List<T>.ctor got inlined into
 //                  MyType.GetAllThinds() and into List<MyType>.FindAll. In this case we'll have two InlineeRecords for mscorlib!List<T>.ctor
 //                  one for MyModule and another one for mscorlib.
-//                  PersistentInlineTrackingMap.GetInliners() always reads all InlineeRecords as long as they have the same key, few of them filtered out as hash collisions
-//                  others provide legitimate inlining information for methods from different modules.
+//                  PersistentInlineTrackingMap.GetInliners() always reads all ZapInlineeRecords as long as they have the same key, few of them filtered out 
+//                  as hash collisions others provide legitimate inlining information for methods from different modules.
 //
-// m_inlinersBuffer - byte array compressed by NibbleWriter. At any valid offset taken from InlineeRecord from m_inlineeIndex, there is a compressed chunk 
+// m_inlinersBuffer - byte array compressed by NibbleWriter. At any valid offset taken from ZapInlineeRecord from m_inlineeIndex, there is a compressed chunk 
 //                    of this format: 
 //                    [InlineeModuleZapIndex][InlinerModuleZapIndex] [N - # of following inliners] [#1 inliner method RID] ... [#N inliner method RID]
 //                    [InlineeModuleZapIndex] is used to verify that we actually found a desired inlinee module (not just a name hash collision).
 //                    [InlinerModuleZapIndex] is an index of a module that owns following method tokens (inliners)
 //                    [1..N inliner RID] are the sorted diff compressed method RIDs from the module specified by InlinerModuleZapIndex, 
-//                    those methods directly or indirectly inlined code from inlinee method specified by InlineeRecord.
+//                    those methods directly or indirectly inlined code from inlinee method specified by ZapInlineeRecord.
 //                    Since all the RIDs are sorted we'are actually able to save some space by using diffs instead of values, because NibbleWriter 
 //                    is good at saving small numbers.
 //                    For example for RIDs: 5, 6, 19, 25, 30, we'll write: 5, 1 (=6-5), 13 (=19-6), 6 (=25-19), 5 (=30-25)
@@ -170,39 +199,111 @@ typedef DPTR(InlineTrackingMap) PTR_InlineTrackingMap;
 // |  -     -     -  | InlineeModuleZapIndex | InlinerModuleZapIndex  | SavedInlinersCount (N) | rid1 | rid2 | ...... | ridN |  -   -   -  |
 // +-----------------+-----------------------+------------------------+------------------------+------+------+--------+------+-------------+
 //
-class PersistentInlineTrackingMap
+
+
+
+
+
+
+
+
+
+// R2R encoding variation for the map
+//
+// It has several differences from the NGEN encoding. NGEN refers to methods outside the current assembly via module index + foreign module's token
+// but R2R can't take those fragile dependencies. Instead we refer to all methods via MethodDef tokens in the current assembly's metadata. This
+// is sufficient for everything we need to track now but in the future we may need to upgrade to a more expressive encoding. Currently NonVersionable
+// attributed methods may be inlined but will not be tracked. This shows up as a known limitation in the profiler APIs that expose this data.
+//
+// The format changes from NGEN:
+//  a) The InlineIndex uses a MethodDef RID token as the key.
+//  b) InlineeModuleZapIndex is omitted because the module is always the current one being compiled.
+//  c) InlinerModuleZapIndex is similarly omitted.
+//  d) (a), (b) and (c) together imply there is at most one entry in the inlineeIndex for any given key
+//  e) A trivial header is now explicitly described
+//  
+//
+// The resulting serialized format is a sequence of blobs:
+// 1) Header (4 byte aligned)
+//       short   MajorVersion - currently set to 1, increment on breaking change
+//       short   MinorVersion - currently set to 0, increment on non-breaking format addition
+//       int     SizeOfInlineIndex - size in bytes of the inline index
+// 
+// 2) InlineIndex - Immediately following header. This is a sorted (by ZapInlineeRecord.key) array of ZapInlineeRecords, given a method token (32 bits)
+//                  we use binary search to find if this method has ever been inlined in R2R code of this image. Each record has m_offset, which is
+//                  an offset inside InlinersBuffer, it has more data on where the method got inlined. There is at most one ZapInlineeRecord with the 
+//                  same key.
+//
+// 3) InlinersBuffer - Located immediately following the InlineIndex (Header RVA + sizeof(Header) + header.SizeOfInlineIndex)
+//                  This is a byte array compressed by NibbleWriter. At any valid offset taken from ZapInlineeRecord from InlineeIndex, there is a 
+//                  compressed chunk  of this format: 
+//                  [N - # of following inliners] [#1 inliner method RID] ... [#N inliner method RID]
+//                  [1..N inliner RID] are the sorted diff compressed method RIDs interpreted as MethodDefs in this assembly's metadata, 
+//                  Those methods directly or indirectly inlined code from inlinee method specified by ZapInlineeRecord.
+//                  Since all the RIDs are sorted we'are actually able to save some space by using diffs instead of values, because NibbleWriter 
+//                  is good at saving small numbers.
+//                  For example for RIDs: 5, 6, 19, 25, 30, we'll write: 5, 1 (=6-5), 13 (=19-6), 6 (=25-19), 5 (=30-25)
+//
+// InlineeIndex
+// +-----+-----+---------------------------------------+-----+-----+
+// |  -  |  -  | m_key {MethodDefToken); m_offset      |  -  |  -  |  
+// +-----+-----+---------------------------------|-----+-----+-----+
+//                                               |
+//                    +--------------------------+
+//                    |
+// InlinersBuffer    \-/
+// +-----------------+------------------------+------+------+--------+------+-------------+
+// |  -     -     -  | SavedInlinersCount (N) | rid1 | rid2 | ...... | ridN |  -   -   -  |
+// +-----------------+------------------------+------+------+--------+------+-------------+
+//
+
+
+
+//A common key format for R2R and NGEN. If the formats
+//diverge further this might become irrelevant
+struct ZapInlineeRecord
 {
-private:
-    struct InlineeRecord
+    DWORD m_key;
+    DWORD m_offset;
+
+    ZapInlineeRecord()
+        : m_key(0)
+    {
+        LIMITED_METHOD_CONTRACT;
+    }
+
+    void InitForR2R(RID rid)
+    {
+        LIMITED_METHOD_CONTRACT;
+        m_key = rid;
+    }
+
+    void InitForNGen(RID rid, LPCUTF8 simpleName);
+
+    bool operator <(const ZapInlineeRecord& other) const
     {
-        DWORD m_key;
-        DWORD m_offset;
-
-        InlineeRecord()
-            : m_key(0)
-        {
-            LIMITED_METHOD_CONTRACT;
-        }
-
-        InlineeRecord(RID rid, LPCUTF8 simpleName);
-
-        bool operator <(const InlineeRecord& other) const
-        {
-            LIMITED_METHOD_DAC_CONTRACT;
-            return m_key < other.m_key;
-        }
-
-        bool operator ==(const InlineeRecord& other) const
-        {
-            LIMITED_METHOD_DAC_CONTRACT;
-            return m_key == other.m_key;
-        }
-    };
-    typedef DPTR(InlineeRecord) PTR_InlineeRecord;
+        LIMITED_METHOD_DAC_CONTRACT;
+        return m_key < other.m_key;
+    }
+
+    bool operator ==(const ZapInlineeRecord& other) const
+    {
+        LIMITED_METHOD_DAC_CONTRACT;
+        return m_key == other.m_key;
+    }
+};
 
+typedef DPTR(ZapInlineeRecord) PTR_ZapInlineeRecord;
+
+
+// This type knows how to serialize and deserialize the inline tracking map format within an NGEN image. See
+// above for a description of the format.
+class PersistentInlineTrackingMapNGen
+{
+private:
     PTR_Module m_module;
 
-    PTR_InlineeRecord m_inlineeIndex;
+    PTR_ZapInlineeRecord m_inlineeIndex;
     DWORD m_inlineeIndexSize;
 
     PTR_BYTE m_inlinersBuffer;
@@ -210,23 +311,63 @@ private:
 
 public:
 
-    PersistentInlineTrackingMap(Module *module)
+    PersistentInlineTrackingMapNGen(Module *module)
         : m_module(dac_cast<PTR_Module>(module))
     {
         LIMITED_METHOD_CONTRACT;
         _ASSERTE(module != NULL);
     }
 
+    // runtime deserialization
+    COUNT_T GetInliners(PTR_Module inlineeOwnerMod, mdMethodDef inlineeTkn, COUNT_T inlinersSize, MethodInModule inliners[], BOOL *incompleteData);
+
+    // compile-time serialization
+#ifndef DACCESS_COMPILE
     void Save(DataImage *image, InlineTrackingMap* runtimeMap);
     void Fixup(DataImage *image);
 
-    COUNT_T GetInliners(PTR_Module inlineeOwnerMod, mdMethodDef inlineeTkn, COUNT_T inlinersSize, MethodInModule inliners[], BOOL *incompleteData);
-
 private:
-    void ProcessInlineTrackingEntry(DataImage *image, SBuffer *inlinersBuffer, SArray<InlineeRecord> *inlineeIndex, InlineTrackingEntry *entry);
+#endif
+
     Module *GetModuleByIndex(DWORD index);
+
 };
 
-typedef DPTR(PersistentInlineTrackingMap) PTR_PersistentInlineTrackingMap;
+typedef DPTR(PersistentInlineTrackingMapNGen) PTR_PersistentInlineTrackingMapNGen;
+
+
+// This type knows how to serialize and deserialize the inline tracking map format within an R2R image. See
+// above for a description of the format.
+#ifdef FEATURE_READYTORUN
+class PersistentInlineTrackingMapR2R
+{
+private:
+    PTR_Module m_module;
+
+    PTR_ZapInlineeRecord m_inlineeIndex;
+    DWORD m_inlineeIndexSize;
+
+    PTR_BYTE m_inlinersBuffer;
+    DWORD m_inlinersBufferSize;
+
+public:
+
+    // runtime deserialization
+#ifndef DACCESS_COMPILE
+    static BOOL TryLoad(Module* pModule, const BYTE* pBuffer, DWORD cbBuffer, AllocMemTracker *pamTracker, PersistentInlineTrackingMapR2R** ppLoadedMap);
+#endif
+    COUNT_T GetInliners(PTR_Module inlineeOwnerMod, mdMethodDef inlineeTkn, COUNT_T inlinersSize, MethodInModule inliners[], BOOL *incompleteData);
+
+
+    // compile time serialization
+#ifndef DACCESS_COMPILE
+    static void Save(ZapHeap* pHeap, SBuffer *saveTarget, InlineTrackingMap* runtimeMap);
+#endif
+
+};
+
+typedef DPTR(PersistentInlineTrackingMapR2R) PTR_PersistentInlineTrackingMapR2R;
+#endif //FEATURE_READYTORUN
+
 
 #endif //INLINETRACKING_H_
index 6447124..b05b891 100644 (file)
@@ -725,11 +725,31 @@ BOOL CEEInfo::shouldEnforceCallvirtRestriction(
 // If the need arises (i.e. performance issues) we will define sets of assemblies (e.g. all app assemblies)
 // The main point is that all this logic is concentrated in one place.
 
+// NOTICE: If you change this logic to allow multi-assembly version bubbles you
+// need to consider the impact on diagnostic tools. Currently there is an inlining
+// table which tracks inliner/inlinee relationships in R2R images but it is not
+// yet capable of encoding cross-assembly inlines. The scenario where this
+// may show are instrumenting profilers that want to instrument a given method A
+// using the ReJit APIs. If method A happens to inlined within method B in another 
+// assembly then the profiler needs to know that so it can rejit B too.
+// The recommended approach is to upgrade the inlining table (vm\inlinetracking.h\.cpp)
+// now that presumably R2R images have some way to refer to methods in other
+// assemblies in their version bubble. Chat with the diagnostics team if you need more 
+// details.
+//
+// There already is a case where cross-assembly inlining occurs in an
+// unreported fashion for methods marked NonVersionable. There is a specific 
+// exemption called out for this on ICorProfilerInfo6::EnumNgenModuleMethodsInliningThisMethod
+// and the impact of the cut was vetted with partners. It would not be appropriate 
+// to increase that unreported set without additional review.
+
+
 bool IsInSameVersionBubble(Assembly * current, Assembly * target)
 {
     LIMITED_METHOD_CONTRACT;
 
     // trivial case: current and target are identical
+    // DO NOT change this without reading the notice above
     if (current == target)
         return true;
 
@@ -740,6 +760,7 @@ bool IsInSameVersionBubble(Assembly * current, Assembly * target)
 static bool IsInSameVersionBubble(MethodDesc* pCurMD, MethodDesc *pTargetMD)
 {
     LIMITED_METHOD_CONTRACT;
+    // DO NOT change this without reading the notice above
     if (IsInSameVersionBubble(pCurMD->GetModule()->GetAssembly(),
                               pTargetMD->GetModule()->GetAssembly()))
     {
index 723e6b6..8a2396d 100644 (file)
@@ -1619,6 +1619,7 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT)
             else
 #endif // FEATURE_INTERPRETER
             {
+                ReJitPublishMethodHolder publishWorker(this, pCode);
                 SetStableEntryPointInterlocked(pCode);
             }
         }
index 18702d7..b7a0eb7 100644 (file)
@@ -9397,8 +9397,7 @@ HRESULT ProfToEEInterfaceImpl::EnumNgenModuleMethodsInliningThisMethod(
         return CORPROF_E_DATAINCOMPLETE;
     }
 
-    PersistentInlineTrackingMap *inliningMap = inlinersModule->GetNgenInlineTrackingMap();
-    if (inliningMap == NULL)
+    if (!inlinersModule->HasInlineTrackingMap())
     {
         return CORPROF_E_DATAINCOMPLETE;
     }
@@ -9411,14 +9410,14 @@ HRESULT ProfToEEInterfaceImpl::EnumNgenModuleMethodsInliningThisMethod(
     EX_TRY
     {
         // Trying to use static buffer
-        COUNT_T methodsAvailable = inliningMap->GetInliners(inlineeOwnerModule, inlineeMethodId, staticBufferSize, staticBuffer, incompleteData);
+        COUNT_T methodsAvailable = inlinersModule->GetInliners(inlineeOwnerModule, inlineeMethodId, staticBufferSize, staticBuffer, incompleteData);
 
         // If static buffer is not enough, allocate an array.
         if (methodsAvailable > staticBufferSize)
         {
             DWORD dynamicBufferSize = methodsAvailable;
             dynamicBuffer = methodsBuffer = new MethodInModule[dynamicBufferSize];
-            methodsAvailable = inliningMap->GetInliners(inlineeOwnerModule, inlineeMethodId, dynamicBufferSize, dynamicBuffer, incompleteData);                
+            methodsAvailable = inlinersModule->GetInliners(inlineeOwnerModule, inlineeMethodId, dynamicBufferSize, dynamicBuffer, incompleteData);                
             if (methodsAvailable > dynamicBufferSize)
             {
                 _ASSERTE(!"Ngen image inlining info changed, this shouldn't be possible.");
index b1be026..6a47aa8 100644 (file)
@@ -533,11 +533,11 @@ PTR_ReadyToRunInfo ReadyToRunInfo::Initialize(Module * pModule, AllocMemTracker
 
     DoLog("Ready to Run initialized successfully");
 
-    return new (pMemory) ReadyToRunInfo(pModule, pLayout, pHeader);
+    return new (pMemory) ReadyToRunInfo(pModule, pLayout, pHeader, pamTracker);
 }
 
-ReadyToRunInfo::ReadyToRunInfo(Module * pModule, PEImageLayout * pLayout, READYTORUN_HEADER * pHeader)
-    : m_pModule(pModule), m_pLayout(pLayout), m_pHeader(pHeader), m_Crst(CrstLeafLock)
+ReadyToRunInfo::ReadyToRunInfo(Module * pModule, PEImageLayout * pLayout, READYTORUN_HEADER * pHeader, AllocMemTracker *pamTracker)
+    : m_pModule(pModule), m_pLayout(pLayout), m_pHeader(pHeader), m_Crst(CrstLeafLock), m_pPersistentInlineTrackingMap(NULL)
 {
     STANDARD_VM_CONTRACT;
 
@@ -589,6 +589,18 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, PEImageLayout * pLayout, READYT
         LockOwner lock = {&m_Crst, IsOwnerOfCrst};
         m_entryPointToMethodDescMap.Init(TRUE, &lock);
     }
+
+       //In format version >= 2.1 there is an optional inlining table 
+       if (IsImageVersionAtLeast(2, 1))
+       {
+               IMAGE_DATA_DIRECTORY * pInlineTrackingInfoDir = FindSection(READYTORUN_SECTION_INLINING_INFO);
+               if (pInlineTrackingInfoDir != NULL)
+               {
+                       const BYTE* pInlineTrackingMapData = (const BYTE*)GetImage()->GetDirectoryData(pInlineTrackingInfoDir);
+                       PersistentInlineTrackingMapR2R::TryLoad(pModule, pInlineTrackingMapData, pInlineTrackingInfoDir->Size,
+                               pamTracker, &m_pPersistentInlineTrackingMap);
+               }
+       }
 }
 
 static bool SigMatchesMethodDesc(MethodDesc* pMD, SigPointer &sig, Module * pModule)
@@ -854,4 +866,12 @@ DWORD ReadyToRunInfo::GetFieldBaseOffset(MethodTable * pMT)
     return (DWORD)sizeof(Object) + dwCumulativeInstanceFieldPos - dwOffsetBias;
 }
 
+BOOL ReadyToRunInfo::IsImageVersionAtLeast(int majorVersion, int minorVersion)
+{
+       LIMITED_METHOD_CONTRACT;
+       return (m_pHeader->MajorVersion == majorVersion && m_pHeader->MinorVersion >= minorVersion) ||
+                  (m_pHeader->MajorVersion > majorVersion);
+
+}
+
 #endif // DACCESS_COMPILE
index 28efe01..2266e9c 100644 (file)
@@ -13,6 +13,7 @@
 #define _READYTORUNINFO_H_
 
 #include "nativeformatreader.h"
+#include "inlinetracking.h"
 
 typedef DPTR(struct READYTORUN_SECTION) PTR_READYTORUN_SECTION;
 
@@ -40,7 +41,9 @@ class ReadyToRunInfo
     Crst                            m_Crst;
     PtrHashMap                      m_entryPointToMethodDescMap;
 
-    ReadyToRunInfo(Module * pModule, PEImageLayout * pLayout, READYTORUN_HEADER * pHeader);
+    PTR_PersistentInlineTrackingMapR2R m_pPersistentInlineTrackingMap;
+
+    ReadyToRunInfo(Module * pModule, PEImageLayout * pLayout, READYTORUN_HEADER * pHeader, AllocMemTracker *pamTracker);
 
 public:
     static BOOL IsReadyToRunEnabled();
@@ -118,10 +121,16 @@ public:
 
     static DWORD GetFieldBaseOffset(MethodTable * pMT);
 
+    PTR_PersistentInlineTrackingMapR2R GetInlineTrackingMap()
+    {
+        return m_pPersistentInlineTrackingMap;
+    }
+
 private:
     BOOL GetTypeNameFromToken(IMDInternalImport * pImport, mdToken mdType, LPCUTF8 * ppszName, LPCUTF8 * ppszNameSpace);
     BOOL GetEnclosingToken(IMDInternalImport * pImport, mdToken mdType, mdToken * pEnclosingToken);
     BOOL CompareTypeNameOfTokens(mdToken mdToken1, IMDInternalImport * pImport1, mdToken mdToken2, IMDInternalImport * pImport2);
+       BOOL IsImageVersionAtLeast(int majorVersion, int minorVersion);
 };
 
 class DynamicHelpers
index 24f081a..499a35b 100644 (file)
@@ -1742,6 +1742,7 @@ void ZapImage::Compile()
         OutputEntrypointsTableForReadyToRun();
         OutputDebugInfoForReadyToRun();
         OutputTypesTableForReadyToRun(m_pMDImport);
+        OutputInliningTableForReadyToRun();
     }
     else
 #endif
index 02985f5..65347ff 100644 (file)
@@ -557,6 +557,7 @@ private:
     void OutputEntrypointsTableForReadyToRun();
     void OutputDebugInfoForReadyToRun();
     void OutputTypesTableForReadyToRun(IMDInternalImport * pMDImport);
+    void OutputInliningTableForReadyToRun();
 
     void CopyDebugDirEntry();
     void CopyWin32VersionResource();
index 4b2f39a..2095524 100644 (file)
@@ -3622,8 +3622,13 @@ void ZapInfo::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd,
                                                 CorInfoInline inlineResult,
                                                 const char * reason)
 {
-
-
+    if (!dontInline(inlineResult) && inlineeHnd != NULL)
+    {
+        // We deliberately report  m_currentMethodHandle (not inlinerHnd) as inliner, because
+        // if m_currentMethodHandle != inlinerHnd, it simply means that inlinerHnd is intermediate link 
+        // in inlining into m_currentMethodHandle, and we have no interest to track those intermediate links now.
+        m_pImage->m_pPreloader->ReportInlining(m_currentMethodHandle, inlineeHnd);
+    }
     return m_pEEJitInfo->reportInliningDecision(inlinerHnd, inlineeHnd, inlineResult, reason);
 }
 
index 30ad296..8ed3f55 100644 (file)
@@ -380,6 +380,15 @@ void ZapImage::OutputDebugInfoForReadyToRun()
     GetReadyToRunHeader()->RegisterSection(READYTORUN_SECTION_DEBUG_INFO, pBlob);
 }
 
+void ZapImage::OutputInliningTableForReadyToRun()
+{
+    SBuffer serializedInlineTrackingBuffer;
+    m_pPreloader->GetSerializedInlineTrackingMap(&serializedInlineTrackingBuffer);
+    ZapNode * pBlob = ZapBlob::NewAlignedBlob(this, (PVOID)(const BYTE*) serializedInlineTrackingBuffer, serializedInlineTrackingBuffer.GetSize(), 4);
+    m_pDebugSection->Place(pBlob);
+    GetReadyToRunHeader()->RegisterSection(READYTORUN_SECTION_INLINING_INFO, pBlob);
+}
+
 void ZapImage::OutputTypesTableForReadyToRun(IMDInternalImport * pMDImport)
 {
     NativeWriter writer;