summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authornoahfalk <noahfalk@users.noreply.github.com>2017-02-13 17:51:40 -0800
committerGitHub <noreply@github.com>2017-02-13 17:51:40 -0800
commit3cd73fb25a2608b4ef74bbbb8969a5d7ad9da213 (patch)
tree7ddcbc0ab421d192d00b4fc50f00fd505be11bc4 /src
parent4f059553beab82aca5dff28227730ba976afeb99 (diff)
parentac40eab10f2f65a0fe9c88b762da1719d5c3839c (diff)
downloadcoreclr-3cd73fb25a2608b4ef74bbbb8969a5d7ad9da213.tar.gz
coreclr-3cd73fb25a2608b4ef74bbbb8969a5d7ad9da213.tar.bz2
coreclr-3cd73fb25a2608b4ef74bbbb8969a5d7ad9da213.zip
Merge pull request #9298 from noahfalk/r2r
Rejit support for R2R
Diffstat (limited to 'src')
-rw-r--r--src/inc/corcompile.h6
-rw-r--r--src/inc/corprof.idl9
-rw-r--r--src/inc/readytorun.h10
-rw-r--r--src/mscorlib/src/System/Runtime/Versioning/NonVersionableAttribute.cs6
-rw-r--r--src/vm/ceeload.cpp38
-rw-r--r--src/vm/ceeload.h10
-rw-r--r--src/vm/compile.cpp7
-rw-r--r--src/vm/compile.h2
-rw-r--r--src/vm/dataimage.cpp3
-rw-r--r--src/vm/inlinetracking.cpp390
-rw-r--r--src/vm/inlinetracking.h221
-rw-r--r--src/vm/jitinterface.cpp21
-rw-r--r--src/vm/prestub.cpp1
-rw-r--r--src/vm/proftoeeinterfaceimpl.cpp7
-rw-r--r--src/vm/readytoruninfo.cpp26
-rw-r--r--src/vm/readytoruninfo.h11
-rw-r--r--src/zap/zapimage.cpp1
-rw-r--r--src/zap/zapimage.h1
-rw-r--r--src/zap/zapinfo.cpp9
-rw-r--r--src/zap/zapreadytorun.cpp9
20 files changed, 612 insertions, 176 deletions
diff --git a/src/inc/corcompile.h b/src/inc/corcompile.h
index e2d3c05d82..f99e27eec0 100644
--- a/src/inc/corcompile.h
+++ b/src/inc/corcompile.h
@@ -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
//
diff --git a/src/inc/corprof.idl b/src/inc/corprof.idl
index 9af1cd97a8..3378431317 100644
--- a/src/inc/corprof.idl
+++ b/src/inc/corprof.idl
@@ -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,
diff --git a/src/inc/readytorun.h b/src/inc/readytorun.h
index ebc557b6f1..9084b92814 100644
--- a/src/inc/readytorun.h
+++ b/src/inc/readytorun.h
@@ -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.
};
//
diff --git a/src/mscorlib/src/System/Runtime/Versioning/NonVersionableAttribute.cs b/src/mscorlib/src/System/Runtime/Versioning/NonVersionableAttribute.cs
index 0a9845d9c2..9fafce4caa 100644
--- a/src/mscorlib/src/System/Runtime/Versioning/NonVersionableAttribute.cs
+++ b/src/mscorlib/src/System/Runtime/Versioning/NonVersionableAttribute.cs
@@ -13,6 +13,12 @@
** 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;
diff --git a/src/vm/ceeload.cpp b/src/vm/ceeload.cpp
index f6317f03aa..00fa966640 100644
--- a/src/vm/ceeload.cpp
+++ b/src/vm/ceeload.cpp
@@ -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();
diff --git a/src/vm/ceeload.h b/src/vm/ceeload.h
index 42023b3ec1..de92900b2e 100644
--- a/src/vm/ceeload.h
+++ b/src/vm/ceeload.h
@@ -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
diff --git a/src/vm/compile.cpp b/src/vm/compile.cpp
index 8aca2ad6df..09925cd219 100644
--- a/src/vm/compile.cpp
+++ b/src/vm/compile.cpp
@@ -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;
diff --git a/src/vm/compile.h b/src/vm/compile.h
index 7aeef31b6c..8fdd383dfe 100644
--- a/src/vm/compile.h
+++ b/src/vm/compile.h
@@ -654,6 +654,8 @@ public:
ULONG Release();
+ void GetSerializedInlineTrackingMap(SBuffer* pBuffer);
+
void Error(mdToken token, Exception * pException);
};
diff --git a/src/vm/dataimage.cpp b/src/vm/dataimage.cpp
index f6b8337b7c..fc584d7b39 100644
--- a/src/vm/dataimage.cpp
+++ b/src/vm/dataimage.cpp
@@ -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()
diff --git a/src/vm/inlinetracking.cpp b/src/vm/inlinetracking.cpp
index 02e2a7cea6..c4bfe0734c 100644
--- a/src/vm/inlinetracking.cpp
+++ b/src/vm/inlinetracking.cpp
@@ -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
diff --git a/src/vm/inlinetracking.h b/src/vm/inlinetracking.h
index cf05027785..2cfb35bd50 100644
--- a/src/vm/inlinetracking.h
+++ b/src/vm/inlinetracking.h
@@ -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"
@@ -16,6 +30,10 @@
#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_
diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp
index 64471249fc..b05b891951 100644
--- a/src/vm/jitinterface.cpp
+++ b/src/vm/jitinterface.cpp
@@ -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()))
{
diff --git a/src/vm/prestub.cpp b/src/vm/prestub.cpp
index 723e6b68f2..8a2396d88f 100644
--- a/src/vm/prestub.cpp
+++ b/src/vm/prestub.cpp
@@ -1619,6 +1619,7 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT)
else
#endif // FEATURE_INTERPRETER
{
+ ReJitPublishMethodHolder publishWorker(this, pCode);
SetStableEntryPointInterlocked(pCode);
}
}
diff --git a/src/vm/proftoeeinterfaceimpl.cpp b/src/vm/proftoeeinterfaceimpl.cpp
index 18702d7b60..b7a0eb75e6 100644
--- a/src/vm/proftoeeinterfaceimpl.cpp
+++ b/src/vm/proftoeeinterfaceimpl.cpp
@@ -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.");
diff --git a/src/vm/readytoruninfo.cpp b/src/vm/readytoruninfo.cpp
index b1be026a6c..6a47aa8f4c 100644
--- a/src/vm/readytoruninfo.cpp
+++ b/src/vm/readytoruninfo.cpp
@@ -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
diff --git a/src/vm/readytoruninfo.h b/src/vm/readytoruninfo.h
index 28efe01e8d..2266e9c119 100644
--- a/src/vm/readytoruninfo.h
+++ b/src/vm/readytoruninfo.h
@@ -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
diff --git a/src/zap/zapimage.cpp b/src/zap/zapimage.cpp
index 24f081a6f9..499a35b855 100644
--- a/src/zap/zapimage.cpp
+++ b/src/zap/zapimage.cpp
@@ -1742,6 +1742,7 @@ void ZapImage::Compile()
OutputEntrypointsTableForReadyToRun();
OutputDebugInfoForReadyToRun();
OutputTypesTableForReadyToRun(m_pMDImport);
+ OutputInliningTableForReadyToRun();
}
else
#endif
diff --git a/src/zap/zapimage.h b/src/zap/zapimage.h
index 02985f5d12..65347ffcf1 100644
--- a/src/zap/zapimage.h
+++ b/src/zap/zapimage.h
@@ -557,6 +557,7 @@ private:
void OutputEntrypointsTableForReadyToRun();
void OutputDebugInfoForReadyToRun();
void OutputTypesTableForReadyToRun(IMDInternalImport * pMDImport);
+ void OutputInliningTableForReadyToRun();
void CopyDebugDirEntry();
void CopyWin32VersionResource();
diff --git a/src/zap/zapinfo.cpp b/src/zap/zapinfo.cpp
index 4b2f39a264..2095524609 100644
--- a/src/zap/zapinfo.cpp
+++ b/src/zap/zapinfo.cpp
@@ -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);
}
diff --git a/src/zap/zapreadytorun.cpp b/src/zap/zapreadytorun.cpp
index 30ad296f95..8ed3f55159 100644
--- a/src/zap/zapreadytorun.cpp
+++ b/src/zap/zapreadytorun.cpp
@@ -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;