summaryrefslogtreecommitdiff
path: root/src/vm/inlinetracking.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/inlinetracking.h')
-rw-r--r--src/vm/inlinetracking.h232
1 files changed, 232 insertions, 0 deletions
diff --git a/src/vm/inlinetracking.h b/src/vm/inlinetracking.h
new file mode 100644
index 0000000000..cf05027785
--- /dev/null
+++ b/src/vm/inlinetracking.h
@@ -0,0 +1,232 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.
+// 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.
+// =============================================================================================
+#ifndef INLINETRACKING_H_
+#define INLINETRACKING_H_
+#include "corhdr.h"
+#include "shash.h"
+#include "sarray.h"
+#include "crsttypes.h"
+#include "daccess.h"
+
+class MethodDesc;
+typedef DPTR(class MethodDesc) PTR_MethodDesc;
+
+struct MethodInModule
+{
+ Module *m_module;
+ mdMethodDef m_methodDef;
+
+ bool operator <(const MethodInModule& other) const;
+
+ bool operator ==(const MethodInModule& other) const;
+
+ bool operator !=(const MethodInModule& other) const;
+
+ MethodInModule(Module * module, mdMethodDef methodDef)
+ :m_module(module), m_methodDef(methodDef)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ }
+
+ MethodInModule()
+ :m_module(NULL), m_methodDef(0)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ }
+
+};
+
+struct InlineTrackingEntry
+{
+ MethodInModule m_inlinee;
+
+ //Our research shows that 70% of methods are inlined less than 4 times
+ //so it's probably worth to inline enough storage for 3 inlines.
+ InlineSArray<MethodInModule, 3> m_inliners;
+
+
+ // SArray and SBuffer don't have sane implementations for operator=
+ // but SHash uses operator= for moving values, so we have to provide
+ // implementations that don't corrupt memory.
+ InlineTrackingEntry(const InlineTrackingEntry& other);
+ InlineTrackingEntry &operator=(const InlineTrackingEntry &other);
+
+ InlineTrackingEntry()
+ {
+ WRAPPER_NO_CONTRACT;
+ }
+
+ void Add(PTR_MethodDesc inliner);
+ void SortAndDeduplicate();
+};
+
+class InlineTrackingMapTraits : public NoRemoveSHashTraits <DefaultSHashTraits<InlineTrackingEntry> >
+{
+public:
+ typedef MethodInModule key_t;
+
+ static key_t GetKey(const element_t &e)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return e.m_inlinee;
+ }
+ static BOOL Equals(key_t k1, key_t k2)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return (k1 == k2);
+ }
+ static count_t Hash(key_t k)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return ((count_t)k.m_methodDef ^ (count_t)k.m_module);
+ }
+ static const element_t Null()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ InlineTrackingEntry e;
+ return e;
+ }
+ static bool IsNull(const element_t &e)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return !e.m_inlinee.m_module;
+ }
+
+ static const bool s_NoThrow = false;
+};
+
+// This is a hashtable that is used by each module to track inlines in the code inside this module.
+// For each key (MethodInModule) it stores an array of methods (MethodInModule), each of those methods
+// directly or indirectly inlined code from MethodInModule specified by the key.
+//
+// It is important to understand that even though each module has an its own instance of the map,
+// map can had methods from other modules both as keys and values.
+// - If module has code inlined from other modules we naturally get methods from other modules as keys in the map.
+// - During NGgen process, modules can generate code for generic classes and methods from other modules and
+// embed them into the image (like List<MyStruct>.FindAll() might get embeded into module of MyStruct).
+// In such cases values of the map can belong to other modules.
+//
+// Currently this map is created and updated by modules only during native image generation
+// and later saved as PersistentInlineTrackingMap.
+class InlineTrackingMap : public SHash < InlineTrackingMapTraits >
+{
+private:
+ Crst m_mapCrst;
+
+public:
+ InlineTrackingMap();
+ void AddInlining(MethodDesc *inliner, MethodDesc *inlinee);
+};
+
+typedef DPTR(InlineTrackingMap) PTR_InlineTrackingMap;
+
+// 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,
+// thus answering a question "give me all methods from this native image that has code from this method?"
+// 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)
+// 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
+// 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.
+//
+// m_inlinersBuffer - byte array compressed by NibbleWriter. At any valid offset taken from InlineeRecord 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.
+// 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)
+//
+// m_inlineeIndex
+// +-----+-----+--------------------------------------------------+-----+-----+
+// | - | - | m_key {module name hash, method token); m_offset | - | - |
+// +-----+-----+--------------------------------------------|-----+-----+-----+
+// |
+// +-----------------------------------+
+// |
+// m_inlinersBuffer \-/
+// +-----------------+-----------------------+------------------------+------------------------+------+------+--------+------+-------------+
+// | - - - | InlineeModuleZapIndex | InlinerModuleZapIndex | SavedInlinersCount (N) | rid1 | rid2 | ...... | ridN | - - - |
+// +-----------------+-----------------------+------------------------+------------------------+------+------+--------+------+-------------+
+//
+class PersistentInlineTrackingMap
+{
+private:
+ struct InlineeRecord
+ {
+ 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;
+
+ PTR_Module m_module;
+
+ PTR_InlineeRecord m_inlineeIndex;
+ DWORD m_inlineeIndexSize;
+
+ PTR_BYTE m_inlinersBuffer;
+ DWORD m_inlinersBufferSize;
+
+public:
+
+ PersistentInlineTrackingMap(Module *module)
+ : m_module(dac_cast<PTR_Module>(module))
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(module != NULL);
+ }
+
+ 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);
+ Module *GetModuleByIndex(DWORD index);
+};
+
+typedef DPTR(PersistentInlineTrackingMap) PTR_PersistentInlineTrackingMap;
+
+#endif //INLINETRACKING_H_