summaryrefslogtreecommitdiff
path: root/src/vm
diff options
context:
space:
mode:
authornoahfalk <noahfalk@microsoft.com>2017-02-13 14:41:20 -0800
committernoahfalk <noahfalk@microsoft.com>2017-02-13 14:41:20 -0800
commitac40eab10f2f65a0fe9c88b762da1719d5c3839c (patch)
tree200f774dd04fb3bf6d318a44d9edc1d9a9623244 /src/vm
parent645f0ad659dda3e356330c36e5c4702040acfb32 (diff)
downloadcoreclr-ac40eab10f2f65a0fe9c88b762da1719d5c3839c.tar.gz
coreclr-ac40eab10f2f65a0fe9c88b762da1719d5c3839c.tar.bz2
coreclr-ac40eab10f2f65a0fe9c88b762da1719d5c3839c.zip
Rejit support for R2R images
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.
Diffstat (limited to 'src/vm')
-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
12 files changed, 566 insertions, 171 deletions
diff --git a/src/vm/ceeload.cpp b/src/vm/ceeload.cpp
index 4a03927ee4..aa071bcea3 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