1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//
5
6//
7// NgenHash is an abstract base class (actually a templated base class) designed to factor out the
8// functionality common to hashes persisted into ngen images.
9//
10// SEMANTICS
11//
12// * Arbitrary entry payload via payload.
13// * 32-bit hash code for entries.
14// * Separate entry allocation and insertion (allowing reliable insertion if required).
15// * Enumerate all entries or entries matching a particular hash.
16// * No entry deletion.
17// * Base logic to efficiently serialize hash contents at ngen time. Hot/cold splitting of entries is
18// supported (along with the ability to tweak the Save and Fixup stages of each entry if needed).
19// * Base logic to support DAC memory enumeration of the hash (including per-entry tweaks as needed).
20// * Lock free lookup (the caller must follow the protocol laid out below under USER REQUIREMENTS).
21// * Automatic hash expansion (with dialable scale factor).
22// * Hash insertion is supported at runtime even when an ngen image is loaded with previously serialized hash
23// entries.
24// * Base logic to support formatting hashes in the nidump tool (only need to supply code for the unique
25// aspects of your hash).
26//
27// BENEFITS
28//
29// * Removes next pointer from all persisted hash entries:
30// o Reduces data footprint of each entry.
31// o Increases density of entries.
32// o Removes a base relocation entry.
33// o Removes a runtime write to each entry (from the relocation above).
34// * Serializes all hot/cold hash entries contigiuously:
35// o Helps keeps hash entries in the same bucket in the same cache line.
36// * Compresses persisted bucket list and removes the use of pointers:
37// o Reduces working set hit of reading hash table (especially on 64-bit systems).
38// o Allows bucket list to be saved in read-only memory and thus use shared rather than private pages.
39// * Factors out common code:
40// o Less chance of bugs, one place to make fixes.
41// o Less code overall.
42//
43// SUB-CLASSING REQUIREMENTS
44//
45// To author a new NgenHash-based hashtable, the following steps are required:
46// 1) In most cases (where each hash entry will have multiple fields) a structure defining the hash entry
47// should be declared (see EEClassHashEntry in ClassHash.h for an example). This structure need not
48// include a field for the hash code or pointer to the next entry in the hash bucket; these are taken care
49// of automatically by the base class. If the entry must reference another entry in the hash (this should
50// be rare) the NgenHashEntryRef<> template class should be used to abstract the reference (this class
51// hides some of the transformation work that must take place when entries are re-ordered during ngen
52// serialization).
53// 2) Declare your new hash class deriving from NgenHash and providing the following template parameters:
54// FINAL_CLASS : The class you're declaring (this is used by the base class to locate certain helper
55// methods in your class used to tweak hash behavior).
56// VALUE : The type of your hash entries (the class defined in the previous step).
57// SCALE_FACTOR : A multipler on bucket count every time the hash table is grown (currently once the
58// number of hash entries exceeds twice the number of buckets). A value of 2 would double
59// the number of buckets on each grow operation for example.
60// 3) Define a constructor that invokes the base class constructor with various setup parameters (see
61// NgenHash constructor in this header). If your hash table is created via a static method rather than
62// direct construction (common) then call your constructor using an in-place new inside the static method
63// (see EEClassHashTable::Create in ClassHash.cpp for an example).
64// 4) Define your basic hash functionality (creation, insertion, lookup, enumeration, ngen Save/Fixup and DAC
65// memory enumeration) using the Base* methods provided by NgenHash.
66// 5) Tweak the operation of BaseSave, BaseFixup and BaseEnumMemoryRegions by providing definitions of the
67// following methods (note that all methods must be defined though they may be no-ops):
68//
69// bool ShouldSave(DataImage *pImage, VALUE *pEntry);
70// Return true if the given entry should be persisted into the ngen image (otherwise it won't be
71// saved with the rest).
72//
73// bool IsHotEntry(VALUE *pEntry, CorProfileData *pProfileData);
74// Return true is the entry is considered hot given the profiling data.
75//
76// bool SaveEntry(DataImage *pImage, CorProfileData *pProfileData, VALUE *pOldEntry, VALUE *pNewEntry, EntryMappingTable *pMap);
77// Gives your hash class a chance to save any additional data needed into the ngen image during
78// the Save phase or otherwise make entry updates prior to saving. The saving process creates a
79// new copy of each hash entry and this method is passed pointers both to the original entry and
80// the new version along with a mapping class that can translate any old entry address in the
81// table into the corresponding new address. If you have inter-entry pointer fields this is your
82// chance to fix up those fields with the new location of their target entries.
83//
84// void FixupEntry(DataImage *pImage, VALUE *pEntry, void *pFixupBase, DWORD cbFixupOffset);
85// Similar to SaveEntry but called during BaseFixup. This is your chance to register fixups for
86// any pointer type fields in your entry. Due to the way hash entries are packed during ngen
87// serialization individual hash entries are not saved as separate ngen zap nodes. So this method
88// is passed a pointer to the enclosing zapped data structure (pFixupBase) and the offset of the
89// entry from this base (cbFixupOffset). When calling pImage->FixupPointerField(...) for
90// instance, pass pFixupBase as the first parameter and cbFixupOffset + offsetof(YourEntryClass,
91// yourField) as the second parameter.
92//
93// void EnumMemoryRegionsForEntry(EEClassHashEntry_t *pEntry, CLRDataEnumMemoryFlags flags);
94// Called during BaseEnumMemoryRegions for each entry in the hash. Use to enumerate any memory
95// referenced by the entry (but not the entry itself).
96//
97// USER REQUIREMENTS
98//
99// Synchronization: It is permissable to read data from the hash without taking a lock as long as:
100// 1) Any hash modifications are performed under a lock or otherwise serialized.
101// 2) Any miss on a lookup is handled by taking a lock are retry-ing the lookup.
102//
103// OVERALL DESIGN
104//
105// The hash contains up to three groups of hash entries. These consist of two groups of entries persisted to
106// disk at ngen time (split into hot and cold based on profile data) and live entries added at runtime (or
107// during the ngen process itself, prior to the save operation).
108//
109// The persisted entries are tightly packed together and can eliminate some pointers and other metadata since
110// we statically know about every entry at the time we format the hash entries (the save phase of ngen
111// generation).
112//
113// Each persisted entry is assigned to a bucket based on its hash code and all entries that collide on a given
114// bucket are placed contiguously in memory. The bucket list itself therefore consists of an array or pairs,
115// each pair containing the count of entries in the bucket and the location of the first entry in the chain.
116// Since all entries are allocated contiguously entry location can be specified by an index into the array of
117// entries.
118//
119// Separate bucket lists and entry arrays are stored for hot and cold entries.
120//
121// The live entries (referred to here as volatile or warm entries) follow a more traditional hash
122// implementation where entries are allocated individually from a loader heap and are chained together with a
123// singly linked list if they collide. Here the bucket list is a simple array of pointers to the first entry
124// in each chain (if any).
125//
126// Unlike the persisted entris the warm section of the table must cope with entry insertions and growing the
127// bucket list when the table becomes too loaded (too many entries causing excessive bucket collisions). This
128// happens when an entry insertion notes that there are twice as many entries as buckets. The bucket list is
129// then reallocated (from a loader heap, consequently the old one is leaked) and resized based on a scale
130// factor supplied by the hash sub-class.
131//
132// At runtime we lookup or enumerate entries by visiting all three sets of entries in the order Hot, Warm and
133// Cold. This imposes a slight but constant time overhead.
134//
135
136#ifndef __NGEN_HASH_INCLUDED
137#define __NGEN_HASH_INCLUDED
138
139#ifdef FEATURE_PREJIT
140#include "corcompile.h"
141#endif
142
143// The type used to contain an entry hash value. This is not customizable on a per-hash class basis: all
144// NgenHash derived hashes will share the same definition. Note that we only care about the data size, and the
145// fact that it is an unsigned integer value (so we can take a modulus for bucket computation and use bitwise
146// equality checks). The base class does not care about or participate in how these hash values are calculated.
147typedef DWORD NgenHashValue;
148
149// The following code (and code in NgenHash.inl) has to replicate the base class template parameters (and in
150// some cases the arguments) many many times. In the interests of brevity (and to make it a whole lot easier
151// to modify these parameters in the future) we define macro shorthands for them here. Scan through the code
152// to see how these are used.
153#define NGEN_HASH_PARAMS typename FINAL_CLASS, typename VALUE, int SCALE_FACTOR
154#define NGEN_HASH_ARGS FINAL_CLASS, VALUE, SCALE_FACTOR
155
156// Forward definition of NgenHashEntryRef (it takes the same template parameters as NgenHash and simplifies
157// hash entries that need to refer to other hash entries).
158template <NGEN_HASH_PARAMS>
159class NgenHashEntryRef;
160
161// The base hash class itself. It's abstract and exposes its functionality via protected members (nothing is
162// public).
163template <NGEN_HASH_PARAMS>
164class NgenHashTable
165{
166 // NgenHashEntryRef needs access to the base table internal during Fixup in order to compute zap node
167 // bases.
168 friend class NgenHashEntryRef<NGEN_HASH_ARGS>;
169
170#ifdef DACCESS_COMPILE
171 // Nidump knows how to walk this data structure.
172 friend class NativeImageDumper;
173#endif
174
175protected:
176 // This opaque structure provides enumeration context when walking the set of entries which share a common
177 // hash code. Initialized by BaseFindFirstEntryByHash and read/updated by BaseFindNextEntryByHash.
178 class LookupContext
179 {
180 friend class NgenHashTable<NGEN_HASH_ARGS>;
181
182 TADDR m_pEntry; // The entry the caller is currently looking at (or NULL to begin
183 // with). This is a VolatileEntry* or PersistedEntry* (depending on
184 // m_eType below) and should always be a target address not a DAC
185 // PTR_.
186 DWORD m_eType; // The entry types we're currently walking (Hot, Warm, Cold in that order)
187 DWORD m_cRemainingEntries; // The remaining entries in the bucket chain (Hot or Cold entries only)
188 };
189
190 // This opaque structure provides enumeration context when walking all entries in the table. Initialized
191 // by BaseInitIterator and updated via the BaseIterator::Next. Note that this structure is somewhat
192 // similar to LookupContext above (though it requires a bit more state). It's possible we could factor
193 // these two iterators into some common base code but the actual implementations have enough differing
194 // requirements that the resultant code could be less readable (and slightly less performant).
195 class BaseIterator
196 {
197 public:
198 // Returns a pointer to the next entry in the hash table or NULL once all entries have been
199 // enumerated. Once NULL has been return the only legal operation is to re-initialize the iterator
200 // with BaseInitIterator.
201 DPTR(VALUE) Next();
202
203 private:
204 friend class NgenHashTable<NGEN_HASH_ARGS>;
205
206 DPTR(NgenHashTable<NGEN_HASH_ARGS>) m_pTable; // Pointer back to the table being enumerated.
207 TADDR m_pEntry; // The entry the caller is currently looking at (or
208 // NULL to begin with). This is a VolatileEntry* or
209 // PersistedEntry* (depending on m_eType below) and
210 // should always be a target address not a DAC PTR_.
211 DWORD m_eType; // The entry types we're currently walking (Hot, Warm,
212 // Cold in that order).
213 union
214 {
215 DWORD m_dwBucket; // Index of bucket we're currently walking (Warm).
216 DWORD m_cRemainingEntries; // Number of entries remaining in hot/cold section
217 // (Hot, Cold).
218 };
219 };
220
221#ifndef DACCESS_COMPILE
222 // Base constructor. Call this from your derived constructor to provide the owning module, loader heap and
223 // initial number of buckets (which must be non-zero). Module must be provided if this hash is to be
224 // serialized into an ngen image. It is exposed to the derived hash class (many need it) but otherwise is
225 // only used to locate a loader heap for allocating bucket lists and entries unless an alternative heap is
226 // provided. Note that the heap provided is not serialized (so you'll allocate from that heap at
227 // ngen-time, but revert to allocating from the module's heap at runtime). If no Module pointer is
228 // supplied (non-ngen'd hash table) you must provide a direct heap pointer.
229 NgenHashTable(Module *pModule, LoaderHeap *pHeap, DWORD cInitialBuckets);
230
231 // Allocate an uninitialized entry for the hash table (it's not inserted). The AllocMemTracker is optional
232 // and may be specified as NULL for untracked allocations. This is split from the hash insertion logic so
233 // that callers can pre-allocate entries and then perform insertions which cannot fault.
234 VALUE *BaseAllocateEntry(AllocMemTracker *pamTracker);
235
236 // Insert an entry previously allocated via BaseAllocateEntry (you cannot allocated entries in any other
237 // manner) and associated with the given hash value. The entry should have been initialized prior to
238 // insertion.
239 void BaseInsertEntry(NgenHashValue iHash, VALUE *pEntry);
240#endif // !DACCESS_COMPILE
241
242 // Return the number of entries held in the table (does not include entries allocated but not inserted
243 // yet).
244 DWORD BaseGetElementCount();
245
246 // Initializes the iterator context passed by the caller to make it ready to walk every entry in the table
247 // in an arbitrary order. Call pIterator->Next() to retrieve the first entry.
248 void BaseInitIterator(BaseIterator *pIterator);
249
250 // Find first entry matching a given hash value (returns NULL on no match). Call BaseFindNextEntryByHash
251 // to iterate the remaining matches (until it returns NULL). The LookupContext supplied by the caller is
252 // initialized by BaseFindFirstEntryByHash and read/updated by BaseFindNextEntryByHash to keep track of
253 // where we are.
254 DPTR(VALUE) BaseFindFirstEntryByHash(NgenHashValue iHash, LookupContext *pContext);
255 DPTR(VALUE) BaseFindNextEntryByHash(LookupContext *pContext);
256
257#if defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE)
258 // Call during ngen to save hash table data structures into the ngen image. Calls derived-class
259 // implementations of ShouldSave to determine which entries should be serialized, IsHotEntry to hot/cold
260 // split the entries and SaveEntry to allow per-entry extension of the saving process.
261 void BaseSave(DataImage *pImage, CorProfileData *pProfileData);
262
263 // Call during ngen to register fixups for hash table data structure fields. Calls derived-class
264 // implementation of FixupEntry to allow per-entry extension of the fixup process.
265 void BaseFixup(DataImage *pImage);
266
267 // Opaque structure used to store state will BaseSave is re-arranging hash entries and also passed to
268 // sub-classes' SaveEntry method to facilitate mapping old entry addresses to their new locations.
269 class EntryMappingTable
270 {
271 public:
272 ~EntryMappingTable();
273
274 // Given an old entry address (pre-BaseSave) return the address of the entry relocated ready for
275 // saving to disk. Note that this address is the (ngen) runtime address, not the disk image address
276 // you can further obtain by calling DataImage::GetImagePointer().
277 VALUE *GetNewEntryAddress(VALUE *pOldEntry);
278
279 private:
280 friend class NgenHashTable<NGEN_HASH_ARGS>;
281
282 // Each Entry holds the mapping from one old-to-new hash entry.
283 struct Entry
284 {
285 VALUE *m_pOldEntry; // Pointer to the user part of the old entry
286 VALUE *m_pNewEntry; // Pointer to the user part of the new version
287 NgenHashValue m_iHashValue; // The hash code of the entry
288 DWORD m_dwNewBucket; // The new bucket index of the entry
289 DWORD m_dwChainOrdinal; // The 0-based position within the chain of that bucket
290 bool m_fHot; // If true this entry was identified as hot by the sub-class
291 };
292
293 Entry *m_pEntries; // Pointer to array of Entries
294 DWORD m_cEntries; // Count of valid entries in the above (may be smaller than
295 // allocated size)
296 };
297#endif // FEATURE_PREJIT && !DACCESS_COMPILE
298
299#ifdef DACCESS_COMPILE
300 // Call during DAC enumeration of memory regions to save in mini-dump to enumerate all hash table data
301 // structures. Calls derived-class implementation of EnumMemoryRegionsForEntry to allow additional
302 // per-entry memory to be reported.
303 void BaseEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
304#endif // DACCESS_COMPILE
305
306 PTR_Module GetModule()
307 {
308 return ReadPointerMaybeNull(this, &NgenHashTable<NGEN_HASH_ARGS>::m_pModule);
309 }
310
311 // Owning module set at hash creation time (possibly NULL if this hash instance is not to be ngen'd).
312 RelativePointer<PTR_Module> m_pModule;
313
314private:
315 // Internal implementation details. Nothing of interest to sub-classers for here on.
316
317#ifdef FEATURE_PREJIT
318 // This is the format of each hash entry that is persisted to disk (Hot or Cold entries).
319 struct PersistedEntry
320 {
321 VALUE m_sValue; // The sub-class supplied entry layout
322 NgenHashValue m_iHashValue; // The hash code associated with the entry (comes after m_sValue to
323 // minimize chance of pad bytes on a 64-bit system).
324 };
325 typedef DPTR(PersistedEntry) PTR_PersistedEntry;
326 typedef ArrayDPTR(PersistedEntry) APTR_PersistedEntry;
327
328 // This class encapsulates a bucket list identifying chains of related persisted entries. It's compressed
329 // rather than being a simple array, hence the encapsulation.
330 // A bucket list represents a non-zero sequence of buckets, each bucket identified by a zero-based index.
331 // Each bucket holds the index of the entry at the start of the bucket chain and a count of entries in
332 // that chain. (In persisted form hash entries are collated into hot and cold entries which are then
333 // allocated in contiguous blocks: this allows entries to be identified by an index into the entry block).
334 // Buckets with zero entries have an undefined start index (and a zero count obviously).
335 class PersistedBucketList
336 {
337#ifdef DACCESS_COMPILE
338 friend class NativeImageDumper;
339#endif
340
341 public:
342 // Allocate and initialize a new list with the given count of buckets and configured to hold no more
343 // than the given number of entries or have a bucket chain longer than the specified maximum. These
344 // two maximums allow the implementation to choose an optimal data format for the bucket list at
345 // runtime and are enforced by asserts in the debug build.
346 static PersistedBucketList *CreateList(DWORD cBuckets, DWORD cEntries, DWORD cMaxEntriesInBucket);
347
348 // For the given bucket set the index of the initial entry and the count of entries in the chain. If
349 // the count is zero the initial entry index is meaningless and ignored.
350 void SetBucket(DWORD dwIndex, DWORD dwFirstEntry, DWORD cEntries);
351
352 // Get the size in bytes of this entire bucket list (need to pass in the bucket count since we save
353 // space by not storing it here, but we do validate this in debug mode).
354 size_t GetSize(DWORD cBuckets);
355
356 // Get the initial entry index and entry count for the given bucket. Initial entry index value is
357 // undefined when count comes back as zero.
358 void GetBucket(DWORD dwIndex, DWORD *pdwFirstEntry, DWORD *pdwCount);
359
360 // Simplified initial entry index when you don't need the count (don't call this for buckets with zero
361 // entries).
362 DWORD GetInitialEntry(DWORD dwIndex);
363
364 private:
365 // Return the number of bits required to express a unique ID for the number of entities given.
366 static DWORD BitsRequired(DWORD cEntities);
367
368 // Return the minimum size (in bytes) of each bucket list entry that can express all buckets given the
369 // max count of entries and entries in a single bucket chain.
370 static DWORD GetBucketSize(DWORD cEntries, DWORD cMaxEntriesInBucket);
371
372 // Each bucket is represented by a variable sized bitfield (16, 32 or 64 bits) whose low-order bits
373 // contain the index of the first entry in the chain and higher-order (just above the initial entry
374 // bits) contain the count of entries in the chain.
375
376 DWORD m_cbBucket; // The size in bytes of each bucket descriptor (2, 4 or 8)
377 DWORD m_dwInitialEntryMask; // The bitmask used to extract the initial entry index from a bucket
378 DWORD m_dwEntryCountShift; // The bit shift used to extract the entry count from a bucket
379
380#ifdef _DEBUG
381 // In debug mode we remember more initial state to catch common errors.
382 DWORD m_cBuckets; // Number of buckets in the list
383 DWORD m_cEntries; // Total number of entries mapped by the list
384 DWORD m_cMaxEntriesInBucket; // Largest bucket chain in the list
385#endif
386 };
387 typedef DPTR(PersistedBucketList) PTR_PersistedBucketList;
388
389 // Pointers and counters for entries and buckets persisted to disk during ngen. Collected into a structure
390 // because this logic is replicated for Hot and Cold entries so we can factor some common code.
391 struct PersistedEntries
392 {
393 RelativePointer<APTR_PersistedEntry> m_pEntries; // Pointer to a contiguous block of PersistedEntry structures
394 // (NULL if zero entries)
395 RelativePointer<PTR_PersistedBucketList> m_pBuckets; // Pointer to abstracted bucket list mapping above entries
396 // into a hash (NULL if zero buckets, which is iff zero
397 // entries)
398 DWORD m_cEntries; // Count of entries in the above block
399 DWORD m_cBuckets; // Count of buckets in the above bucket list
400 };
401#endif // FEATURE_PREJIT
402
403 // This is the format of a Warm entry, defined for our purposes to be a non-persisted entry (i.e. those
404 // created at runtime or during the creation of the ngen image itself).
405 struct VolatileEntry;
406 typedef DPTR(struct VolatileEntry) PTR_VolatileEntry;
407 struct VolatileEntry
408 {
409 VALUE m_sValue; // The derived-class format of an entry
410 PTR_VolatileEntry m_pNextEntry; // Pointer to the next entry in the bucket chain (or NULL)
411 NgenHashValue m_iHashValue; // The hash value associated with the entry
412 };
413
414 // Types of hash entry.
415 enum EntryType
416 {
417 Cold, // Persisted, profiling suggests this data is not read typically
418 Warm, // Volatile (in-memory)
419 Hot // Persisted, profiling suggests this data is probably read (or no profiling data was available)
420 };
421
422#ifdef FEATURE_PREJIT
423 // Find the first persisted entry (hot or cold based on pEntries) that matches the given hash. Looks only
424 // in the persisted block given (i.e. searches only hot *or* cold entries). Returns NULL on failure.
425 // Otherwise returns pointer to the derived class portion of the entry and initializes the provided
426 // LookupContext to allow enumeration of any further matches.
427 DPTR(VALUE) FindPersistedEntryByHash(PersistedEntries *pEntries, NgenHashValue iHash, LookupContext *pContext);
428#endif // FEATURE_PREJIT
429
430 // Find the first volatile (warm) entry that matches the given hash. Looks only at warm entries. Returns
431 // NULL on failure. Otherwise returns pointer to the derived class portion of the entry and initializes
432 // the provided LookupContext to allow enumeration of any further matches.
433 DPTR(VALUE) FindVolatileEntryByHash(NgenHashValue iHash, LookupContext *pContext);
434
435#ifndef DACCESS_COMPILE
436 // Determine loader heap to be used for allocation of entries and bucket lists.
437 LoaderHeap *GetHeap();
438
439 // Increase the size of the bucket list in order to reduce the size of bucket chains. Does nothing on
440 // failure to allocate (since this impacts perf, not correctness).
441 void GrowTable();
442
443 // Returns the next prime larger (or equal to) than the number given.
444 DWORD NextLargestPrime(DWORD dwNumber);
445#endif // !DACCESS_COMPILE
446
447 DPTR(PTR_VolatileEntry) GetWarmBuckets()
448 {
449 SUPPORTS_DAC;
450
451 return ReadPointer(this, &NgenHashTable<NGEN_HASH_ARGS>::m_pWarmBuckets);
452 }
453
454#ifdef FEATURE_PREJIT
455 APTR_PersistedEntry GetPersistedHotEntries()
456 {
457 SUPPORTS_DAC;
458
459 return ReadPointerMaybeNull(this,
460 &NgenHashTable<NGEN_HASH_ARGS>::m_sHotEntries,
461 &decltype(NgenHashTable<NGEN_HASH_ARGS>::m_sHotEntries)::m_pEntries);
462 }
463
464 PTR_PersistedBucketList GetPersistedHotBuckets()
465 {
466 SUPPORTS_DAC;
467
468 return ReadPointerMaybeNull(this,
469 &NgenHashTable<NGEN_HASH_ARGS>::m_sHotEntries,
470 &decltype(NgenHashTable<NGEN_HASH_ARGS>::m_sHotEntries)::m_pBuckets);
471 }
472
473 APTR_PersistedEntry GetPersistedColdEntries()
474 {
475 SUPPORTS_DAC;
476
477 return ReadPointerMaybeNull(this,
478 &NgenHashTable<NGEN_HASH_ARGS>::m_sColdEntries,
479 &decltype(NgenHashTable<NGEN_HASH_ARGS>::m_sColdEntries)::m_pEntries);
480 }
481
482 PTR_PersistedBucketList GetPersistedColdBuckets()
483 {
484 SUPPORTS_DAC;
485
486 return ReadPointerMaybeNull(this,
487 &NgenHashTable<NGEN_HASH_ARGS>::m_sColdEntries,
488 &decltype(NgenHashTable<NGEN_HASH_ARGS>::m_sColdEntries)::m_pBuckets);
489 }
490
491#ifdef DACCESS_COMPILE
492 APTR_PersistedEntry GetPersistedEntries(DPTR(PersistedEntries) pEntries)
493 {
494 SUPPORTS_DAC;
495
496 TADDR hotEntriesAddr = dac_cast<TADDR>(this) + offsetof(NgenHashTable<NGEN_HASH_ARGS>, m_sHotEntries);
497 TADDR coldEntriesAddr = dac_cast<TADDR>(this) + offsetof(NgenHashTable<NGEN_HASH_ARGS>, m_sColdEntries);
498
499 if (hotEntriesAddr == dac_cast<TADDR>(pEntries))
500 {
501 return GetPersistedHotEntries();
502 }
503 else
504 {
505 _ASSERTE(hotEntriesAddr == dac_cast<TADDR>(pEntries));
506
507 return GetPersistedColdEntries();
508 }
509 }
510
511 PTR_PersistedBucketList GetPersistedBuckets(DPTR(PersistedEntries) pEntries)
512 {
513 SUPPORTS_DAC;
514
515 TADDR hotEntriesAddr = dac_cast<TADDR>(this) + offsetof(NgenHashTable<NGEN_HASH_ARGS>, m_sHotEntries);
516 TADDR coldEntriesAddr = dac_cast<TADDR>(this) + offsetof(NgenHashTable<NGEN_HASH_ARGS>, m_sColdEntries);
517
518 if (hotEntriesAddr == dac_cast<TADDR>(pEntries))
519 {
520 return GetPersistedHotBuckets();
521 }
522 else
523 {
524 _ASSERTE(hotEntriesAddr == dac_cast<TADDR>(pEntries));
525
526 return GetPersistedColdBuckets();
527 }
528 }
529#endif // DACCESS_COMPILE
530#endif // FEATURE_PREJIT
531
532 // Loader heap provided at construction time. May be NULL (in which case m_pModule must *not* be NULL).
533 LoaderHeap *m_pHeap;
534
535 // Fields related to the runtime (volatile or warm) part of the hash.
536 RelativePointer<DPTR(PTR_VolatileEntry)> m_pWarmBuckets; // Pointer to a simple bucket list (array of VolatileEntry pointers)
537 DWORD m_cWarmBuckets; // Count of buckets in the above array (always non-zero)
538 DWORD m_cWarmEntries; // Count of elements in the warm section of the hash
539
540#ifdef FEATURE_PREJIT
541 PersistedEntries m_sHotEntries; // Hot persisted hash entries (if any)
542 PersistedEntries m_sColdEntries; // Cold persisted hash entries (if any)
543
544 DWORD m_cInitialBuckets; // Initial number of warm buckets we started with. Only used
545 // to reset warm bucket count in ngen-persisted table.
546#endif // FEATURE_PREJIT
547};
548
549// Abstraction around cross-hash entry references (e.g. EEClassHashTable, where entries for nested types point
550// to entries for their enclosing types). Under the covers we use a relative pointer which avoids the need to
551// allocate a base relocation fixup and the resulting write into the entry at load time. The abstraction hides
552// some of the complexity needed to achieve this.
553template <NGEN_HASH_PARAMS>
554class NgenHashEntryRef
555{
556public:
557 // Get a pointer to the referenced entry.
558 DPTR(VALUE) Get();
559
560#ifndef DACCESS_COMPILE
561 // Set the reference to point to the given entry.
562 void Set(VALUE *pEntry);
563
564#ifdef FEATURE_PREJIT
565 // Call this during the ngen Fixup phase to adjust the relative pointer to account for ngen image layout.
566 void Fixup(DataImage *pImage, NgenHashTable<NGEN_HASH_ARGS> *pTable);
567#endif // FEATURE_PREJIT
568
569 NgenHashEntryRef<NGEN_HASH_ARGS>& operator = (const NgenHashEntryRef<NGEN_HASH_ARGS> &src)
570 {
571 src.m_rpEntryRef.BitwiseCopyTo(m_rpEntryRef);
572
573 return *this;
574 }
575#endif // !DACCESS_COMPILE
576
577private:
578 RelativePointer<DPTR(VALUE)> m_rpEntryRef; // Entry ref encoded as a delta from this field's location.
579};
580
581#endif // __NGEN_HASH_INCLUDED
582