cc1f139b38002d0e1f7d65d1980170c77868da3f
[platform/upstream/coreclr.git] / src / vm / ngenhash.inl
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 // Abstract base class implementation of a hash table suitable for efficient serialization into an ngen image.
8 // See NgenHash.h for a more detailed description.
9 //
10
11 // Our implementation embeds entry data supplied by the hash sub-class into a larger entry structure
12 // containing NgenHash metadata. We often end up returning pointers to the inner entry to sub-class code and
13 // doing this in a DAC-friendly fashion involves some DAC gymnastics. The following couple of macros factor
14 // those complexities out.
15 #define VALUE_FROM_VOLATILE_ENTRY(_ptr) dac_cast<DPTR(VALUE)>(PTR_TO_MEMBER_TADDR(VolatileEntry, (_ptr), m_sValue))
16 #define VALUE_FROM_PERSISTED_ENTRY(_ptr) dac_cast<DPTR(VALUE)>(PTR_TO_MEMBER_TADDR(PersistedEntry, (_ptr), m_sValue))
17
18 // We provide a mechanism for the sub-class to extend per-entry operations via a callback mechanism where the
19 // sub-class implements methods with a certain name and signature (details in the module header for
20 // NgenHash.h). We could have used virtual methods, but this adds a needless indirection since all the details
21 // are known statically. In order to have a base class call a method defined only in a sub-class however we
22 // need a little pointer trickery. The following macro hides that.
23 #define DOWNCALL(_method) ((FINAL_CLASS*)this)->_method
24
25 #ifndef DACCESS_COMPILE
26
27 // Base constructor. Call this from your derived constructor to provide the owning module, loader heap and
28 // initial number of buckets (which must be non-zero). Module must be provided if this hash is to be
29 // serialized into an ngen image. It is exposed to the derived hash class (many need it) but otherwise is only
30 // used to locate a loader heap for allocating bucket lists and entries unless an alternative heap is
31 // provided. Note that the heap provided is not serialized (so you'll allocate from that heap at ngen-time,
32 // but revert to allocating from the module's heap at runtime). If no Module pointer is supplied (non-ngen'd
33 // hash table) you must provide a direct heap pointer.
34 template <NGEN_HASH_PARAMS>
35 NgenHashTable<NGEN_HASH_ARGS>::NgenHashTable(Module *pModule, LoaderHeap *pHeap, DWORD cInitialBuckets)
36 {
37     CONTRACTL
38     {
39         THROWS;
40         GC_NOTRIGGER;
41         MODE_ANY;
42     }
43     CONTRACTL_END;
44
45     // An invariant in the code is that we always have a non-zero number of warm buckets.
46     _ASSERTE(cInitialBuckets > 0);
47
48     // At least one of module or heap must have been specified or we won't know how to allocate entries and
49     // buckets.
50     _ASSERTE(pModule || pHeap);
51     m_pModule.SetValueMaybeNull(pModule);
52     m_pHeap = pHeap;
53
54     S_SIZE_T cbBuckets = S_SIZE_T(sizeof(VolatileEntry*)) * S_SIZE_T(cInitialBuckets);
55
56     m_cWarmEntries = 0;
57     m_cWarmBuckets = cInitialBuckets;
58     m_pWarmBuckets.SetValue((PTR_VolatileEntry*)(void*)GetHeap()->AllocMem(cbBuckets));
59
60     // Note: Memory allocated on loader heap is zero filled
61     // memset(m_pWarmBuckets, 0, sizeof(VolatileEntry*) * cInitialBuckets);
62
63 #ifdef FEATURE_PREJIT
64     memset(&m_sHotEntries, 0, sizeof(PersistedEntries));
65     memset(&m_sColdEntries, 0, sizeof(PersistedEntries));
66     m_cInitialBuckets = cInitialBuckets;
67 #endif // FEATURE_PREJIT
68 }
69
70 // Allocate an uninitialized entry for the hash table (it's not inserted). The AllocMemTracker is optional and
71 // may be specified as NULL for untracked allocations. This is split from the hash insertion logic so that
72 // callers can pre-allocate entries and then perform insertions which cannot fault.
73 template <NGEN_HASH_PARAMS>
74 VALUE *NgenHashTable<NGEN_HASH_ARGS>::BaseAllocateEntry(AllocMemTracker *pamTracker)
75 {
76     CONTRACTL
77     {
78         THROWS;
79         GC_NOTRIGGER;
80         MODE_ANY;
81     }
82     CONTRACTL_END;
83
84     // Faults are forbidden in BaseInsertEntry. Make the table writeable now that the faults are still allowed.
85     EnsureWritablePages(this);
86     EnsureWritablePages(this->GetWarmBuckets(), m_cWarmBuckets * sizeof(PTR_VolatileEntry));
87
88     TaggedMemAllocPtr pMemory = GetHeap()->AllocMem(S_SIZE_T(sizeof(VolatileEntry)));
89
90     VolatileEntry *pEntry;
91     if (pamTracker)
92         pEntry = (VolatileEntry*)pamTracker->Track(pMemory);
93     else
94         pEntry = pMemory.cast<VolatileEntry*>();
95
96 #ifdef _DEBUG
97     // In debug builds try and catch cases where code attempts to use entries not allocated via this method.
98     pEntry->m_pNextEntry = (VolatileEntry*)0x12345678;
99 #endif
100
101     return &pEntry->m_sValue;
102 }
103
104 // Determine loader heap to be used for allocation of entries and bucket lists.
105 template <NGEN_HASH_PARAMS>
106 LoaderHeap *NgenHashTable<NGEN_HASH_ARGS>::GetHeap()
107 {
108     CONTRACTL
109     {
110         NOTHROW;
111         GC_NOTRIGGER;
112         MODE_ANY;
113     }
114     CONTRACTL_END;
115
116     // Explicitly provided heap takes priority.
117     if (m_pHeap)
118         return m_pHeap;
119
120     // If not specified then we fall back to the owning module's heap (a module must have been specified in
121     // this case).
122     _ASSERTE(!m_pModule.IsNull());
123     return GetModule()->GetAssembly()->GetLowFrequencyHeap();
124 }
125
126 // Insert an entry previously allocated via BaseAllocateEntry (you cannot allocated entries in any other
127 // manner) and associated with the given hash value. The entry should have been initialized prior to
128 // insertion.
129 template <NGEN_HASH_PARAMS>
130 void NgenHashTable<NGEN_HASH_ARGS>::BaseInsertEntry(NgenHashValue iHash, VALUE *pEntry)
131 {
132     CONTRACTL
133     {
134         NOTHROW;
135         GC_NOTRIGGER;
136         MODE_ANY;
137     }
138     CONTRACTL_END;
139
140     // We are always guaranteed at least one warm bucket (which is important here: some hash table sub-classes
141     // require entry insertion to be fault free).
142     _ASSERTE(m_cWarmBuckets > 0);
143
144     // Recover the volatile entry pointer from the sub-class entry pointer passed to us. In debug builds
145     // attempt to validate that this transform is really valid and the caller didn't attempt to allocate the
146     // entry via some other means than BaseAllocateEntry().
147     PTR_VolatileEntry pVolatileEntry = (PTR_VolatileEntry)((BYTE*)pEntry - offsetof(VolatileEntry, m_sValue));
148     _ASSERTE(pVolatileEntry->m_pNextEntry == (VolatileEntry*)0x12345678);
149
150     // Remember the entry hash code.
151     pVolatileEntry->m_iHashValue = iHash;
152
153     // Compute which bucket the entry belongs in based on the hash.
154     DWORD dwBucket = iHash % m_cWarmBuckets;
155
156     // Prepare to link the new entry at the head of the bucket chain.
157     pVolatileEntry->m_pNextEntry = (GetWarmBuckets())[dwBucket];
158
159     // Make sure that all writes to the entry are visible before publishing the entry.
160     MemoryBarrier();
161
162     // Publish the entry by pointing the bucket at it.
163     (GetWarmBuckets())[dwBucket] = pVolatileEntry;
164
165     m_cWarmEntries++;
166
167     // If the insertion pushed the table load over our limit then attempt to grow the bucket list. Note that
168     // we ignore any failure (this is a performance operation and is not required for correctness).
169     if (m_cWarmEntries > (2 * m_cWarmBuckets))
170         GrowTable();
171 }
172
173 // Increase the size of the bucket list in order to reduce the size of bucket chains. Does nothing on failure
174 // to allocate (since this impacts perf, not correctness).
175 template <NGEN_HASH_PARAMS>
176 void NgenHashTable<NGEN_HASH_ARGS>::GrowTable()
177 {
178     CONTRACTL
179     {
180         INSTANCE_CHECK;
181         NOTHROW;
182         GC_NOTRIGGER;
183         MODE_ANY;
184     }
185     CONTRACTL_END;
186
187     // If we can't increase the number of buckets, we lose perf but not correctness. So we won't report this
188     // error to our caller.
189     FAULT_NOT_FATAL();
190
191     // Make the new bucket table larger by the scale factor requested by the subclass (but also prime).
192     DWORD cNewBuckets = NextLargestPrime(m_cWarmBuckets * SCALE_FACTOR);
193     S_SIZE_T cbNewBuckets = S_SIZE_T(cNewBuckets) * S_SIZE_T(sizeof(PTR_VolatileEntry));
194     PTR_VolatileEntry *pNewBuckets = (PTR_VolatileEntry*)(void*)GetHeap()->AllocMem_NoThrow(cbNewBuckets);
195     if (!pNewBuckets)
196         return;
197
198     // All buckets are initially empty.
199     // Note: Memory allocated on loader heap is zero filled
200     // memset(pNewBuckets, 0, cNewBuckets * sizeof(PTR_VolatileEntry));
201
202     // Run through the old table and transfer all the entries. Be sure not to mess with the integrity of the
203     // old table while we are doing this, as there can be concurrent readers! Note that it is OK if the
204     // concurrent reader misses out on a match, though - they will have to acquire the lock on a miss & try
205     // again.
206     for (DWORD i = 0; i < m_cWarmBuckets; i++)
207     {
208         PTR_VolatileEntry pEntry = (GetWarmBuckets())[i];
209
210         // Try to lock out readers from scanning this bucket. This is obviously a race which may fail.
211         // However, note that it's OK if somebody is already in the list - it's OK if we mess with the bucket
212         // groups, as long as we don't destroy anything. The lookup function will still do appropriate
213         // comparison even if it wanders aimlessly amongst entries while we are rearranging things. If a
214         // lookup finds a match under those circumstances, great. If not, they will have to acquire the lock &
215         // try again anyway.
216         (GetWarmBuckets())[i] = NULL;
217
218         while (pEntry != NULL)
219         {
220             DWORD dwNewBucket = pEntry->m_iHashValue % cNewBuckets;
221             PTR_VolatileEntry pNextEntry  = pEntry->m_pNextEntry;
222
223             pEntry->m_pNextEntry = pNewBuckets[dwNewBucket];
224             pNewBuckets[dwNewBucket] = pEntry;
225
226             pEntry = pNextEntry;
227         }
228     }
229
230     // Make sure that all writes are visible before publishing the new array.
231     MemoryBarrier();
232     m_pWarmBuckets.SetValue(pNewBuckets);
233
234     // The new number of buckets has to be published last (prior to this readers may miscalculate a bucket
235     // index, but the result will always be in range and they'll simply walk the wrong chain and get a miss,
236     // prompting a retry under the lock). If we let the count become visible unordered wrt to the bucket array
237     // itself a reader could potentially read buckets from beyond the end of the old bucket list).
238     MemoryBarrier();
239     m_cWarmBuckets = cNewBuckets;
240 }
241
242 // Returns the next prime larger (or equal to) than the number given.
243 template <NGEN_HASH_PARAMS>
244 DWORD NgenHashTable<NGEN_HASH_ARGS>::NextLargestPrime(DWORD dwNumber)
245 {
246     for (DWORD i = 0; i < COUNTOF(g_rgPrimes); i++)
247         if (g_rgPrimes[i] >= dwNumber)
248         {
249             dwNumber = g_rgPrimes[i];
250             break;
251         }
252
253     return dwNumber;
254 }
255 #endif // !DACCESS_COMPILE
256
257 // Return the number of entries held in the table (does not include entries allocated but not inserted yet).
258 template <NGEN_HASH_PARAMS>
259 DWORD NgenHashTable<NGEN_HASH_ARGS>::BaseGetElementCount()
260 {
261     LIMITED_METHOD_DAC_CONTRACT;
262
263     return m_cWarmEntries
264 #ifdef FEATURE_PREJIT
265         + m_sHotEntries.m_cEntries + m_sColdEntries.m_cEntries
266 #endif
267         ;
268 }
269
270 // Find first entry matching a given hash value (returns NULL on no match). Call BaseFindNextEntryByHash to
271 // iterate the remaining matches (until it returns NULL). The LookupContext supplied by the caller is
272 // initialized by BaseFindFirstEntryByHash and read/updated by BaseFindNextEntryByHash to keep track of where
273 // we are.
274 template <NGEN_HASH_PARAMS>
275 DPTR(VALUE) NgenHashTable<NGEN_HASH_ARGS>::BaseFindFirstEntryByHash(NgenHashValue iHash, LookupContext *pContext)
276 {
277     CONTRACTL
278     {
279         NOTHROW;
280         GC_NOTRIGGER;
281         MODE_ANY;
282         SUPPORTS_DAC;
283         PRECONDITION(CheckPointer(pContext));
284     }
285     CONTRACTL_END;
286
287     DPTR(VALUE) pEntry;
288
289 #ifdef FEATURE_PREJIT
290     // Look in the hot entries first.
291     pEntry = FindPersistedEntryByHash(&m_sHotEntries, iHash, pContext);
292     if (pEntry)
293         return pEntry;
294 #endif // FEATURE_PREJIT
295
296     // Then the warm entries.
297     pEntry = FindVolatileEntryByHash(iHash, pContext);
298     if (pEntry)
299         return pEntry;
300
301 #ifdef FEATURE_PREJIT
302     // And finally the cold entries.
303     return FindPersistedEntryByHash(&m_sColdEntries, iHash, pContext);
304 #else // FEATURE_PREJIT
305     return NULL;
306 #endif // FEATURE_PREJIT
307 }
308
309 // Find first entry matching a given hash value (returns NULL on no match). Call BaseFindNextEntryByHash to
310 // iterate the remaining matches (until it returns NULL). The LookupContext supplied by the caller is
311 // initialized by BaseFindFirstEntryByHash and read/updated by BaseFindNextEntryByHash to keep track of where
312 // we are.
313 template <NGEN_HASH_PARAMS>
314 DPTR(VALUE) NgenHashTable<NGEN_HASH_ARGS>::BaseFindNextEntryByHash(LookupContext *pContext)
315 {
316     CONTRACTL
317     {
318         NOTHROW;
319         GC_NOTRIGGER;
320         MODE_ANY;
321         SUPPORTS_DAC;
322         PRECONDITION(CheckPointer(pContext));
323     }
324     CONTRACTL_END;
325
326     NgenHashValue iHash;
327
328     switch (pContext->m_eType)
329     {
330 #ifdef FEATURE_PREJIT
331     case Hot:
332     case Cold:
333     {
334         // Fetch the entry we were looking at last from the context and remember the corresponding hash code.
335         PTR_PersistedEntry pPersistedEntry = dac_cast<PTR_PersistedEntry>(pContext->m_pEntry);
336         iHash = pPersistedEntry->m_iHashValue;
337
338         // Iterate while there are still entries left in the bucket chain.
339         while (pContext->m_cRemainingEntries)
340         {
341             // Advance to next entry, reducing the number of entries left to scan.
342             pPersistedEntry++;
343             pContext->m_cRemainingEntries--;
344
345             if (pPersistedEntry->m_iHashValue == iHash)
346             {
347                 // Found a match on hash code. Update our find context to indicate where we got to and return
348                 // a pointer to the sub-class portion of the entry.
349                 pContext->m_pEntry = dac_cast<TADDR>(pPersistedEntry);
350                 return VALUE_FROM_PERSISTED_ENTRY(pPersistedEntry);
351             }
352         }
353
354         // We didn't find a match.
355         if (pContext->m_eType == Hot)
356         {
357             // If we were searching the hot entries then we should try the warm entries next.
358             DPTR(VALUE) pNext = FindVolatileEntryByHash(iHash, pContext);
359             if (pNext)
360                 return pNext;
361
362             // If that didn't work try the cold entries.
363             return FindPersistedEntryByHash(&m_sColdEntries, iHash, pContext);
364         }
365
366         // We were already searching cold entries, a failure here means the entry is not in the table.
367         return NULL;
368     }
369 #endif // FEATURE_PREJIT
370
371     case Warm:
372     {
373         // Fetch the entry we were looking at last from the context and remember the corresponding hash code.
374         PTR_VolatileEntry pVolatileEntry = dac_cast<PTR_VolatileEntry>(pContext->m_pEntry);
375         iHash = pVolatileEntry->m_iHashValue;
376
377         // Iterate over the bucket chain.
378         while (pVolatileEntry->m_pNextEntry)
379         {
380             // Advance to the next entry.
381             pVolatileEntry = pVolatileEntry->m_pNextEntry;
382             if (pVolatileEntry->m_iHashValue == iHash)
383             {
384                 // Found a match on hash code. Update our find context to indicate where we got to and return
385                 // a pointer to the sub-class portion of the entry.
386                 pContext->m_pEntry = dac_cast<TADDR>(pVolatileEntry);
387                 return VALUE_FROM_VOLATILE_ENTRY(pVolatileEntry);
388             }
389         }
390
391         // We didn't find a match, fall through to the cold entries.
392 #ifdef FEATURE_PREJIT
393         return FindPersistedEntryByHash(&m_sColdEntries, iHash, pContext);
394 #else
395         return NULL;
396 #endif
397     }
398
399     default:
400         _ASSERTE(!"Unknown NgenHashTable entry type");
401         return NULL;
402     }
403 }
404
405 #ifdef FEATURE_PREJIT
406
407 // Allocate and initialize a new list with the given count of buckets and configured to hold no more than the
408 // given number of entries or have a bucket chain longer than the specified maximum. These two maximums allow
409 // the implementation to choose an optimal data format for the bucket list at runtime and are enforced by
410 // asserts in the debug build.
411 // static
412 template <NGEN_HASH_PARAMS>
413 typename NgenHashTable<NGEN_HASH_ARGS>::PersistedBucketList *NgenHashTable<NGEN_HASH_ARGS>::PersistedBucketList::CreateList(DWORD cBuckets, DWORD cEntries, DWORD cMaxEntriesInBucket)
414 {
415     CONTRACTL
416     {
417         THROWS;
418         GC_NOTRIGGER;
419         MODE_ANY;
420     }
421     CONTRACTL_END;
422
423     // The size of each bucket depends on the number of entries we need to store and how big a bucket chain
424     // ever gets.
425     DWORD cbBucket = GetBucketSize(cEntries, cMaxEntriesInBucket);
426
427     // Allocate enough memory to store the bucket list header and bucket array.
428     S_SIZE_T cbBuckets = S_SIZE_T(sizeof(PersistedBucketList)) + (S_SIZE_T(cbBucket) * S_SIZE_T(cBuckets));
429     if (cbBuckets.IsOverflow())
430         COMPlusThrowOM();
431     PersistedBucketList *pBucketList = (PersistedBucketList*)(new BYTE[cbBuckets.Value()]);
432
433 #ifdef _DEBUG
434     // In debug builds we store all the input parameters to validate subsequent requests. In retail none of
435     // this data is needed.
436     pBucketList->m_cBuckets = cBuckets;
437     pBucketList->m_cEntries = cEntries;
438     pBucketList->m_cMaxEntriesInBucket = cMaxEntriesInBucket;
439 #endif // _DEBUG
440
441     pBucketList->m_cbBucket = cbBucket;
442     pBucketList->m_dwEntryCountShift = BitsRequired(cEntries);
443     pBucketList->m_dwInitialEntryMask = (1 << pBucketList->m_dwEntryCountShift) - 1;
444
445     // Zero all the buckets (empty all the bucket chains).
446     memset(pBucketList + 1, 0, cBuckets * cbBucket);
447
448     return pBucketList;
449 }
450
451 // Get the size in bytes of this entire bucket list (need to pass in the bucket count since we save space by
452 // not storing it here, but we do validate this in debug mode).
453 template <NGEN_HASH_PARAMS>
454 size_t NgenHashTable<NGEN_HASH_ARGS>::PersistedBucketList::GetSize(DWORD cBuckets)
455 {
456     LIMITED_METHOD_DAC_CONTRACT;
457
458     _ASSERTE(cBuckets == m_cBuckets);
459     return sizeof(PersistedBucketList) + (cBuckets * m_cbBucket);
460 }
461
462 // Get the initial entry index and entry count for the given bucket. Initial entry index value is undefined
463 // when count comes back as zero.
464 template <NGEN_HASH_PARAMS>
465 void NgenHashTable<NGEN_HASH_ARGS>::PersistedBucketList::GetBucket(DWORD dwIndex, DWORD *pdwFirstEntry, DWORD *pdwCount)
466 {
467     CONTRACTL
468     {
469         NOTHROW;
470         GC_NOTRIGGER;
471         MODE_ANY;
472         SUPPORTS_DAC;
473     }
474     CONTRACTL_END;
475
476     _ASSERTE(dwIndex < m_cBuckets);
477
478     // Find the start of the bucket we're interested in based on the index and size chosen for buckets in this
479     // list instance.
480     TADDR pBucket = dac_cast<TADDR>(this) + sizeof(PersistedBucketList) + (dwIndex * m_cbBucket);
481
482     // Handle each format of bucket separately. In all cases read the correct number of bytes to form one
483     // bitfield, extract the low order bits to retrieve the initial entry index and shift down the remaining
484     // bits to obtain the entry count.
485     switch (m_cbBucket)
486     {
487     case 2:
488     {
489         _ASSERTE(m_dwEntryCountShift < 16 && m_dwInitialEntryMask < 0xffff);
490
491         WORD wBucketContents = *dac_cast<PTR_WORD>(pBucket);
492
493         *pdwFirstEntry = wBucketContents & m_dwInitialEntryMask;
494         *pdwCount = wBucketContents >> m_dwEntryCountShift;
495
496         break;
497     }
498
499     case 4:
500     {
501         _ASSERTE(m_dwEntryCountShift < 32 && m_dwInitialEntryMask < 0xffffffff);
502
503         DWORD dwBucketContents = *dac_cast<PTR_DWORD>(pBucket);
504
505         *pdwFirstEntry = dwBucketContents & m_dwInitialEntryMask;
506         *pdwCount = dwBucketContents >> m_dwEntryCountShift;
507
508         break;
509     }
510
511     case 8:
512     {
513         _ASSERTE(m_dwEntryCountShift < 64);
514
515         ULONG64 qwBucketContents = *dac_cast<PTR_ULONG64>(pBucket);
516
517         *pdwFirstEntry = (DWORD)(qwBucketContents & m_dwInitialEntryMask);
518         *pdwCount = (DWORD)(qwBucketContents >> m_dwEntryCountShift);
519
520         break;
521     }
522
523     default:
524 #ifdef DACCESS_COMPILE
525         // Minidumps don't guarantee this will work - memory may not have been dumped, target corrupted, etc.
526         *pdwFirstEntry = 0;
527         *pdwCount = 0;
528 #else
529         _ASSERTE(!"Invalid bucket list bucket size");
530 #endif
531     }
532
533     _ASSERTE((*pdwFirstEntry < m_cEntries) || (*pdwCount == 0));
534     _ASSERTE(*pdwCount <= m_cMaxEntriesInBucket);
535 }
536
537 // Simplified initial entry index when you don't need the count (don't call this for buckets with zero
538 // entries).
539 template <NGEN_HASH_PARAMS>
540 DWORD NgenHashTable<NGEN_HASH_ARGS>::PersistedBucketList::GetInitialEntry(DWORD dwIndex)
541 {
542     CONTRACTL
543     {
544         NOTHROW;
545         GC_NOTRIGGER;
546         MODE_ANY;
547         SUPPORTS_DAC;
548     }
549     CONTRACTL_END;
550
551     DWORD dwInitialEntry, dwEntryCount;
552     GetBucket(dwIndex, &dwInitialEntry, &dwEntryCount);
553
554     _ASSERTE(dwEntryCount > 0);
555
556     return dwInitialEntry;
557 }
558
559 // For the given bucket set the index of the initial entry and the count of entries in the chain. If the count
560 // is zero the initial entry index is meaningless and ignored.
561 template <NGEN_HASH_PARAMS>
562 void NgenHashTable<NGEN_HASH_ARGS>::PersistedBucketList::SetBucket(DWORD dwIndex, DWORD dwFirstEntry, DWORD cEntries)
563 {
564     CONTRACTL
565     {
566         NOTHROW;
567         GC_NOTRIGGER;
568         MODE_ANY;
569     }
570     CONTRACTL_END;
571
572     _ASSERTE(dwIndex < m_cBuckets);
573     _ASSERTE(cEntries <= m_cMaxEntriesInBucket);
574     if (cEntries > 0)
575     {
576         _ASSERTE(dwFirstEntry < m_cEntries);
577         _ASSERTE(dwFirstEntry <= m_dwInitialEntryMask);
578     }
579
580     // Find the start of the bucket we're interested in based on the index and size chosen for buckets in this
581     // list instance.
582     BYTE *pbBucket = (BYTE*)this + sizeof(PersistedBucketList) + (dwIndex * m_cbBucket);
583
584     // Handle each format of bucket separately. In all cases form a single bitfield with low-order bits
585     // specifying the initial entry index and higher bits containing the entry count. Write this into the
586     // bucket entry using the correct number of bytes.
587     ULONG64 qwBucketBits = dwFirstEntry | (cEntries << m_dwEntryCountShift);
588     switch (m_cbBucket)
589     {
590     case 2:
591     {
592         _ASSERTE(m_dwEntryCountShift < 16 && m_dwInitialEntryMask < 0xffff);
593         *(WORD*)pbBucket = (WORD)qwBucketBits;
594         break;
595     }
596
597     case 4:
598     {
599         _ASSERTE(m_dwEntryCountShift < 32 && m_dwInitialEntryMask < 0xffffffff);
600         *(DWORD*)pbBucket = (DWORD)qwBucketBits;
601         break;
602     }
603
604     case 8:
605     {
606         _ASSERTE(m_dwEntryCountShift < 64);
607         *(ULONG64*)pbBucket = qwBucketBits;
608         break;
609     }
610
611     default:
612         _ASSERTE(!"Invalid bucket list bucket size");
613     }
614 }
615
616 // Return the number of bits required to express a unique ID for the number of entities given.
617 //static
618 template <NGEN_HASH_PARAMS>
619 DWORD NgenHashTable<NGEN_HASH_ARGS>::PersistedBucketList::BitsRequired(DWORD cEntities)
620 {
621     LIMITED_METHOD_CONTRACT;
622
623     // Starting with a bit-mask of the most significant bit and iterating over masks for successively less
624     // significant bits, stop as soon as the mask co-incides with a set bit in the value. Simultaneously we're
625     // counting down the bits required to express the range of values implied by seeing the corresponding bit
626     // set in the value (e.g. when we're testing the high bit we know we'd need 32-bits to encode the range of
627     // values that have this bit set). Stop when we get to one bit (we never return 0 bits required, even for
628     // an input value of 0).
629     DWORD dwMask = 0x80000000;
630     DWORD cBits = 32;
631     while (cBits > 1)
632     {
633         if (cEntities & dwMask)
634             return cBits;
635
636         dwMask >>= 1;
637         cBits--;
638     }
639
640     return 1;
641 }
642
643 // Return the minimum size (in bytes) of each bucket list entry that can express all buckets given the max
644 // count of entries and entries in a single bucket chain.
645 // static
646 template <NGEN_HASH_PARAMS>
647 DWORD NgenHashTable<NGEN_HASH_ARGS>::PersistedBucketList::GetBucketSize(DWORD cEntries, DWORD cMaxEntriesInBucket)
648 {
649     CONTRACTL
650     {
651         NOTHROW;
652         GC_NOTRIGGER;
653         MODE_ANY;
654     }
655     CONTRACTL_END;
656
657     // We need enough bits to express a start entry index (related to the total number of entries in the
658     // table) and a chain count (so take the maximum chain length into consideration).
659     DWORD cTotalBits = BitsRequired(cEntries) + BitsRequired(cMaxEntriesInBucket);
660
661     // Rather than support complete flexibility (an arbitrary number of bytes to express the combination of
662     // the two bitfields above) we'll just pull out the most useful selection (which simplifies the access
663     // code and potentially might give us a perf edge over the more generalized algorithm).
664
665     // We want naturally aligned bucket entries for access perf, 1 byte entries aren't all that interesting
666     // (most tables won't be small enough to be expressed this way and those that are won't get much benefit
667     // from the extra compression of the bucket list). We also don't believe we'll ever need more than 64
668     // bits. This leaves us with 2, 4 and 8 byte entries. The tables in the current desktop CLR for mscorlib
669     // will fit in the 2-byte category and will give us substantial space saving over the naive implementation
670     // of a bucket with two DWORDs.
671
672     if (cTotalBits <= 16)
673         return 2;
674
675     if (cTotalBits <= 32)
676         return 4;
677
678     // Invariant guaranteed by BitsRequired above.
679     _ASSERTE(cTotalBits <= 64);
680     return 8;
681 }
682
683 #ifndef DACCESS_COMPILE
684
685 #ifdef _MSC_VER
686 #pragma warning(push)
687 #pragma warning(disable:4723) // Prevent "warning C4723: potential divide by 0"
688 #endif // _MSC_VER
689
690 // Call during ngen to save hash table data structures into the ngen image. Calls derived-class
691 // implementations of ShouldSave to determine which entries should be serialized, IsHotEntry to hot/cold split
692 // the entries and SaveEntry to allow per-entry extension of the saving process.
693 template <NGEN_HASH_PARAMS>
694 void NgenHashTable<NGEN_HASH_ARGS>::BaseSave(DataImage *pImage, CorProfileData *pProfileData)
695 {
696     STANDARD_VM_CONTRACT;
697
698     // This is a fairly long and complex process but at its heart it's fairly linear. We perform multiple
699     // passes over the data in sequence which might seem slow but everything is arranged to avoid any O(N^2)
700     // algorithms.
701
702     // Persisted hashes had better have supplied an owning module at creation time (otherwise we won't know
703     // how to find a loader heap for further allocations at runtime: we don't know how to serialize a loader
704     // heap pointer).
705     _ASSERTE(!m_pModule.IsNull());
706
707     // We can only save once during ngen so the hot and cold sections of the hash cannot have been populated
708     // yet.
709     _ASSERTE(m_sHotEntries.m_cEntries == 0 && m_sColdEntries.m_cEntries == 0);
710
711     DWORD i;
712
713     // As we re-arrange volatile warm entries into hot and cold sets of persisted entries we need to keep lots
714     // of intermediate tracking information. We also need to provide a subset of this mapping information to
715     // the sub-class (so it can fix up cross entry references for example). The temporary structure allocated
716     // below performs that function (it will be destructed automatically at the end of this method).
717     EntryMappingTable sEntryMap;
718     sEntryMap.m_cEntries = m_cWarmEntries;
719 #ifdef _PREFAST_
720 #pragma warning(suppress:6211) // Suppress bogus prefast warning about memory leak (EntryMappingTable acts as a holder)
721 #endif
722
723     // The 'typename' keyword shouldn't be necessary, but g++ gets confused without it.
724     sEntryMap.m_pEntries = new typename EntryMappingTable::Entry[m_cWarmEntries];
725
726     //
727     // PHASE 1
728     //
729     //  Iterate all the current warm entries, ask the sub-class which of them should be saved into the image
730     //  and of those which are hot and which are cold.
731     //
732
733     DWORD cHotEntries = 0;
734     DWORD cColdEntries = 0;
735
736     // Visit each warm bucket.
737     for (i = 0; i < m_cWarmBuckets; i++)
738     {
739         // Iterate through the chain of warm entries for this bucket.
740         VolatileEntry *pOldEntry = (GetWarmBuckets())[i];
741         while (pOldEntry)
742         {
743             // Is the current entry being saved into the image?
744             if (DOWNCALL(ShouldSave)(pImage, &pOldEntry->m_sValue))
745             {
746                 // Yes, so save the details into the next available slot in the entry map. At this stage we
747                 // know the original entry address, the hash value and whether the entry is hot or cold.
748                 DWORD dwCurrentEntry = cHotEntries + cColdEntries;
749                 sEntryMap.m_pEntries[dwCurrentEntry].m_pOldEntry = &pOldEntry->m_sValue;
750                 sEntryMap.m_pEntries[dwCurrentEntry].m_iHashValue = pOldEntry->m_iHashValue;
751
752                 // Is the entry hot? When given no profile data we assume cold.
753                 if (pProfileData != NULL && DOWNCALL(IsHotEntry)(&pOldEntry->m_sValue, pProfileData))
754                 {
755                     cHotEntries++;
756                     sEntryMap.m_pEntries[dwCurrentEntry].m_fHot = true;
757                 }
758                 else
759                 {
760                     cColdEntries++;
761                     sEntryMap.m_pEntries[dwCurrentEntry].m_fHot = false;
762                 }
763             }
764
765             pOldEntry = pOldEntry->m_pNextEntry;
766         }
767     }
768
769     // Set size of the entry map based on the real number of entries we're going to save.
770     _ASSERTE((cHotEntries + cColdEntries) <= m_cWarmEntries);
771     sEntryMap.m_cEntries = cHotEntries + cColdEntries;
772
773     //
774     // PHASE 2
775     //
776     //  Determine the layout of the new hot and cold tables (if applicable). We pick new bucket list sizes
777     //  based on the number of entries to go in each table and from that we can calculate the length of each
778     //  entry chain off each bucket (which is important both to derive a maximum chain length used when
779     //  picking an optimized encoding for the bucket list and allows us to layout the new entries in linear
780     //  time).
781     //
782     // We need a couple of extra arrays to track bucket chain sizes until we have enough info to allocate the
783     // new bucket lists themselves.
784     //
785
786     // We'll allocate half as many buckets as entries (with at least 1 bucket, or zero if there are no entries
787     // in this section of the hash).
788     DWORD cHotBuckets = cHotEntries ? NextLargestPrime(cHotEntries / 2) : 0;
789     DWORD cColdBuckets = cColdEntries ? NextLargestPrime(cColdEntries / 2) : 0;    
790
791     // Allocate arrays to track bucket chain lengths for each hot or cold bucket list (as needed).
792     DWORD *pHotBucketSizes = cHotBuckets ? new DWORD[cHotBuckets] : NULL;
793     memset(pHotBucketSizes, 0, cHotBuckets * sizeof(DWORD));
794
795     DWORD *pColdBucketSizes = cColdBuckets ? new DWORD[cColdBuckets] : NULL;
796     memset(pColdBucketSizes, 0, cColdBuckets * sizeof(DWORD));
797
798     // We'll calculate the maximum bucket chain length separately for hot and cold sections (each has its own
799     // bucket list that might be optimized differently).
800     DWORD cMaxHotChain = 0;
801     DWORD cMaxColdChain = 0;
802
803     // Iterate through all the entries to be saved (linear scan through the entry map we built in phase 1).
804     for (i = 0; i < sEntryMap.m_cEntries; i++)
805     {
806         // The 'typename' keyword shouldn't be necessary, but g++ gets confused without it.
807         typename EntryMappingTable::Entry *pMapEntry = &sEntryMap.m_pEntries[i];
808
809         // For each entry calculate which bucket it will end up in under the revised bucket list. Also record
810         // its order in the bucket chain (first come, first served). Recording this ordinal now is what allows
811         // us to lay out entries into their final order using a linear algorithm in a later phase.
812         if (pMapEntry->m_fHot)
813         {
814             _ASSERTE(cHotBuckets != 0);
815             pMapEntry->m_dwNewBucket = pMapEntry->m_iHashValue % cHotBuckets;
816             pMapEntry->m_dwChainOrdinal = pHotBucketSizes[pMapEntry->m_dwNewBucket]++;
817             if (pHotBucketSizes[pMapEntry->m_dwNewBucket] > cMaxHotChain)
818                 cMaxHotChain = pHotBucketSizes[pMapEntry->m_dwNewBucket];
819         }
820         else
821         {
822             // The C++ compiler is currently complaining that cColdBuckets could be zero in the modulo
823             // operation below. It cannot due to the logic in this method (if we have a cold entry we'll have
824             // at least one cold bucket, see the assignments above) but the flow is far too complex for the
825             // C++ compiler to follow. Unfortunately it won't be told (the warning can't be disabled and even
826             // an __assume won't work) so we take the hit of generating the useless extra if below.
827             if (cColdBuckets > 0)
828             {
829                 pMapEntry->m_dwNewBucket = pMapEntry->m_iHashValue % cColdBuckets;
830                 pMapEntry->m_dwChainOrdinal = pColdBucketSizes[pMapEntry->m_dwNewBucket]++;
831                 if (pColdBucketSizes[pMapEntry->m_dwNewBucket] > cMaxColdChain)
832                     cMaxColdChain = pColdBucketSizes[pMapEntry->m_dwNewBucket];
833             }
834             else
835                 _ASSERTE(!"Should be unreachable, see comment above");
836         }
837     }
838
839     //
840     // PHASE 3
841     //
842     //  Allocate the new hot and cold bucket lists and entry arrays (as needed). The bucket lists have
843     //  optimized layout based on knowledge of the entries they will map (total number of entries and the size
844     //  of the largest single bucket chain).
845     //
846
847     if (cHotEntries)
848     {
849         m_sHotEntries.m_cEntries = cHotEntries;
850         m_sHotEntries.m_cBuckets = cHotBuckets;
851         m_sHotEntries.m_pEntries.SetValue(new PersistedEntry[cHotEntries]);
852         m_sHotEntries.m_pBuckets.SetValue(PersistedBucketList::CreateList(cHotBuckets, cHotEntries, cMaxHotChain));
853         memset(GetPersistedHotEntries(), 0, cHotEntries * sizeof(PersistedEntry)); // NGen determinism
854     }
855
856     if (cColdEntries)
857     {
858         m_sColdEntries.m_cEntries = cColdEntries;
859         m_sColdEntries.m_cBuckets = cColdBuckets;
860         m_sColdEntries.m_pEntries.SetValue(new PersistedEntry[cColdEntries]);
861         m_sColdEntries.m_pBuckets.SetValue(PersistedBucketList::CreateList(cColdBuckets, cColdEntries, cMaxColdChain));
862         memset(GetPersistedColdEntries(), 0, cColdEntries * sizeof(PersistedEntry));    // NGen determinism
863     }
864
865     //
866     // PHASE 4
867     //
868     //  Initialize the bucket lists. We need to set an initial entry index (index into the entry array) and
869     //  entry count for each bucket. The counts we already computed in phase 2 and since we're free to order
870     //  the entry array however we like, we can compute the initial entry index for each bucket in turn
871     //  trivially by laying out all entries for bucket 0 first followed by all entries for bucket 1 etc.
872     //
873     // This also has the nice effect of placing entries in the same bucket chain contiguously (and in the
874     // order that a full hash traversal will take).
875     //
876
877     DWORD dwNextId = 0; // This represents the index of the next entry to start a bucket chain
878     for (i = 0; i < cHotBuckets; i++)
879     {
880         m_sHotEntries.m_pBuckets.GetValue()->SetBucket(i, dwNextId, pHotBucketSizes[i]);
881         dwNextId += pHotBucketSizes[i];
882     }
883     _ASSERTE(dwNextId == m_sHotEntries.m_cEntries);
884
885     dwNextId = 0; // Reset index for the cold entries (remember they have their own table of entries)
886     for (i = 0; i < cColdBuckets; i++)
887     {
888         m_sColdEntries.m_pBuckets.GetValue()->SetBucket(i, dwNextId, pColdBucketSizes[i]);
889         dwNextId += pColdBucketSizes[i];
890     }
891     _ASSERTE(dwNextId == m_sColdEntries.m_cEntries);
892
893     //
894     // PHASE 5
895     //
896     //  Determine new addresses for each entry. This is relatively simple since we know the bucket index, the
897     //  index of the first entry for that bucket and how far into that chain each entry is located.
898     //
899
900     for (i = 0; i < sEntryMap.m_cEntries; i++)
901     {
902         // The 'typename' keyword shouldn't be necessary, but g++ gets confused without it.
903         typename EntryMappingTable::Entry *pMapEntry = &sEntryMap.m_pEntries[i];
904
905         // Entry block depends on whether this entry is hot or cold.
906         APTR_PersistedEntry pPersistedEntries = pMapEntry->m_fHot ? GetPersistedHotEntries() : GetPersistedColdEntries();
907         PTR_PersistedBucketList pPersistedBucketsList = pMapEntry->m_fHot ? GetPersistedHotBuckets() : GetPersistedColdBuckets();
908
909         // We already know the new bucket this entry will go into. Retrieve the index of the first entry in
910         // that bucket chain.
911         DWORD dwBaseChainIndex = pPersistedBucketsList->GetInitialEntry(pMapEntry->m_dwNewBucket);
912
913         // This entry will be located at some offset from the index above (we calculated this ordinal in phase
914         // 2).
915         PersistedEntry *pNewEntry = &pPersistedEntries[dwBaseChainIndex + pMapEntry->m_dwChainOrdinal];
916
917         // Record the address of the embedded sub-class hash entry in the map entry (sub-classes will use this
918         // info to map old entry addresses to their new locations).
919         sEntryMap.m_pEntries[i].m_pNewEntry = &pNewEntry->m_sValue;
920
921         // Initialize the new entry. Note that a simple bit-copy is performed on the sub-classes embedded
922         // entry. If fixups are needed they can be performed in the call to SaveEntry in the next phase.
923         pNewEntry->m_sValue = *pMapEntry->m_pOldEntry;
924         pNewEntry->m_iHashValue = pMapEntry->m_iHashValue;
925     }
926
927     //
928     // PHASE 6
929     //
930     //  For each entry give the hash sub-class a chance to perform any additional saving or fixups. We pass
931     //  both the old and new address of each entry, plus the mapping table so they can map other entry
932     //  addresses (if, for example, they have cross-entry pointer fields in their data).
933     //
934     //  We ask for each entry whether the saved data will be immutable. This is an optimization: if all
935     //  entries turn out to be immutable we will save the entire entry array in a read-only (shareable)
936     //  section.
937     //
938
939     bool fAllEntriesImmutable = true;
940     for (i = 0; i < sEntryMap.m_cEntries; i++)
941         if (!DOWNCALL(SaveEntry)(pImage,
942                                  pProfileData,
943                                  sEntryMap.m_pEntries[i].m_pOldEntry,
944                                  sEntryMap.m_pEntries[i].m_pNewEntry,
945                                  &sEntryMap))
946             fAllEntriesImmutable = false;
947
948     // We're mostly done. Now just some cleanup and the actual DataImage storage operations.
949
950     // We don't need the bucket size tracking arrays any more.
951     delete [] pHotBucketSizes;
952     delete [] pColdBucketSizes;
953
954     // If there are any hot entries store the entry array and bucket list.
955     if (cHotEntries)
956     {
957         pImage->StoreStructure(GetPersistedHotEntries(),
958                                static_cast<ULONG>(sizeof(PersistedEntry) * cHotEntries), 
959                                fAllEntriesImmutable ? DataImage::ITEM_NGEN_HASH_ENTRIES_RO_HOT : DataImage::ITEM_NGEN_HASH_ENTRIES_HOT);
960
961         pImage->StoreStructure(GetPersistedHotBuckets(),
962                                static_cast<ULONG>(m_sHotEntries.m_pBuckets.GetValue()->GetSize(m_sHotEntries.m_cBuckets)),
963                                DataImage::ITEM_NGEN_HASH_BUCKETLIST_HOT);
964     }
965
966     // If there are any cold entries store the entry array and bucket list.
967     if (cColdEntries)
968     {
969         pImage->StoreStructure(GetPersistedColdEntries(),
970                                static_cast<ULONG>(sizeof(PersistedEntry) * cColdEntries), 
971                                fAllEntriesImmutable ? DataImage::ITEM_NGEN_HASH_ENTRIES_RO_COLD : DataImage::ITEM_NGEN_HASH_ENTRIES_COLD);
972
973         pImage->StoreStructure(GetPersistedColdBuckets(),
974                                static_cast<ULONG>(GetPersistedColdBuckets()->GetSize(m_sColdEntries.m_cBuckets)),
975                                DataImage::ITEM_NGEN_HASH_BUCKETLIST_COLD);
976     }
977
978     // Store the root data structure itself.
979     pImage->StoreStructure(this, sizeof(FINAL_CLASS), cHotEntries ?
980                            DataImage::ITEM_NGEN_HASH_HOT : DataImage::ITEM_NGEN_HASH_COLD);
981
982     // We've moved the warm entries to hot and cold sections, so reset the warm section of the table. We only
983     // do this on the copy of the table that's going to be saved into the ngen image. This is important since
984     // (especially in the case of generics) we might continue to access this table throughout the rest of the
985     // save/arrange/fixup process. Leaving two copies of saved entries in the table (hot or cold plus warm)
986     // doesn't have any real impact, but removing the warm entries could be problematic where the entry was
987     // culled from the ngen image. In those cases we'll get a miss on the lookup with the result that the
988     // caller might try to add the type back to the table, something that is prohibited in the debug build
989     // during the ngen save/arrange/fixup phases.
990
991     // Reset the warm buckets to their original size or a fairly restrictive cap. These (empty) buckets will
992     // be saved into the ngen image and form the basis for further entries added at runtime. Thus we have a
993     // trade-off between storing dead space in the ngen image and having to re-size the bucket list at
994     // runtime. Note that we can't save a zero sized bucket list: the invariant we have is that there are
995     // always a non-zero number of buckets available when we come to do an insertion (since insertions cannot
996     // fail). An alternative strategy would be to initialize these buckets at ngen image load time.
997     _ASSERTE(m_cWarmBuckets >= m_cInitialBuckets);
998     DWORD cNewWarmBuckets = min(m_cInitialBuckets, 11);
999
1000     // Create the ngen version of the warm buckets.
1001     pImage->StoreStructure(GetWarmBuckets(),
1002                            cNewWarmBuckets * sizeof(VolatileEntry*),
1003                            DataImage::ITEM_NGEN_HASH_HOT);
1004
1005     // Reset the ngen-version of the table to have no warm entries and the reduced warm bucket count.
1006     NgenHashTable<NGEN_HASH_ARGS> *pNewTable = (NgenHashTable<NGEN_HASH_ARGS>*)pImage->GetImagePointer(this);
1007     pNewTable->m_cWarmEntries = 0;
1008     pNewTable->m_cWarmBuckets = cNewWarmBuckets;
1009
1010     // Zero-out the ngen version of the warm buckets.
1011     VolatileEntry *pNewBuckets = (VolatileEntry*)pImage->GetImagePointer(GetWarmBuckets());
1012     memset(pNewBuckets, 0, cNewWarmBuckets * sizeof(VolatileEntry*));
1013 }
1014
1015 #ifdef _MSC_VER
1016 #pragma warning(pop)
1017 #endif // _MSC_VER:
1018
1019 // Call during ngen to register fixups for hash table data structure fields. Calls derived-class
1020 // implementation of FixupEntry to allow per-entry extension of the fixup process.
1021 template <NGEN_HASH_PARAMS>
1022 void NgenHashTable<NGEN_HASH_ARGS>::BaseFixup(DataImage *pImage)
1023 {
1024     STANDARD_VM_CONTRACT;
1025
1026     DWORD i;
1027
1028     // Fixup the module pointer.
1029     pImage->FixupRelativePointerField(this, offsetof(NgenHashTable<NGEN_HASH_ARGS>, m_pModule));
1030
1031     // Throw away the heap pointer, we can't serialize it into the image. We'll rely on the loader heap
1032     // associated with the module above at runtime.
1033     pImage->ZeroPointerField(this, offsetof(NgenHashTable<NGEN_HASH_ARGS>, m_pHeap));
1034
1035     // Give the hash sub-class a chance to fixup any pointers in its entries. We provide the pointer to the
1036     // hot or cold entry block and the offset into that block for this entry since we don't save individual
1037     // zap nodes for each entry; just a single node covering the entire array. As a result all fixups have to
1038     // be relative to the base of this array.
1039
1040     for (i = 0; i < m_sHotEntries.m_cEntries; i++)
1041         DOWNCALL(FixupEntry)(pImage,
1042                              &(GetPersistedHotEntries())[i].m_sValue,
1043                              GetPersistedHotEntries(),
1044                              i * sizeof(PersistedEntry));
1045
1046     for (i = 0; i < m_sColdEntries.m_cEntries; i++)
1047         DOWNCALL(FixupEntry)(pImage,
1048                              &(GetPersistedColdEntries())[i].m_sValue,
1049                              GetPersistedColdEntries(),
1050                              i * sizeof(PersistedEntry));
1051
1052     // Fixup the warm (empty) bucket list.
1053     pImage->FixupRelativePointerField(this, offsetof(NgenHashTable<NGEN_HASH_ARGS>, m_pWarmBuckets));
1054
1055     // Fixup the hot entry array and bucket list.
1056     pImage->FixupRelativePointerField(this, offsetof(NgenHashTable<NGEN_HASH_ARGS>, m_sHotEntries) + offsetof(PersistedEntries, m_pEntries));
1057     pImage->FixupRelativePointerField(this, offsetof(NgenHashTable<NGEN_HASH_ARGS>, m_sHotEntries) + offsetof(PersistedEntries, m_pBuckets));
1058
1059     // Fixup the cold entry array and bucket list.
1060     pImage->FixupRelativePointerField(this, offsetof(NgenHashTable<NGEN_HASH_ARGS>, m_sColdEntries) + offsetof(PersistedEntries, m_pEntries));
1061     pImage->FixupRelativePointerField(this, offsetof(NgenHashTable<NGEN_HASH_ARGS>, m_sColdEntries) + offsetof(PersistedEntries, m_pBuckets));
1062 }
1063 #endif // !DACCESS_COMPILE
1064 #endif // FEATURE_PREJIT
1065
1066 #ifdef DACCESS_COMPILE
1067
1068 // Call during DAC enumeration of memory regions to save all hash table data structures. Calls derived-class
1069 // implementation of EnumMemoryRegionsForEntry to allow additional per-entry memory to be reported.
1070 template <NGEN_HASH_PARAMS>
1071 void NgenHashTable<NGEN_HASH_ARGS>::BaseEnumMemoryRegions(CLRDataEnumMemoryFlags flags)
1072 {
1073     SUPPORTS_DAC;
1074
1075     // Save the base data structure itself (can't use DAC_ENUM_DTHIS() since the size to save is based on a
1076     // sub-class).
1077     DacEnumMemoryRegion(dac_cast<TADDR>(this), sizeof(FINAL_CLASS));
1078
1079     // Save the warm bucket list.
1080     DacEnumMemoryRegion(dac_cast<TADDR>(GetWarmBuckets()), m_cWarmBuckets * sizeof(VolatileEntry*));
1081
1082     // Save all the warm entries.
1083     if (GetWarmBuckets().IsValid())
1084     {
1085         for (DWORD i = 0; i < m_cWarmBuckets; i++)
1086         {
1087             PTR_VolatileEntry pEntry = (GetWarmBuckets())[i];
1088             while (pEntry.IsValid())
1089             {
1090                 pEntry.EnumMem();
1091
1092                 // Ask the sub-class whether each entry points to further data to be saved.
1093                 DOWNCALL(EnumMemoryRegionsForEntry)(VALUE_FROM_VOLATILE_ENTRY(pEntry), flags);
1094
1095                 pEntry = pEntry->m_pNextEntry;
1096             }
1097         }
1098     }
1099
1100 #ifdef FEATURE_PREJIT
1101     // Save hot buckets and entries.
1102     if (m_sHotEntries.m_cEntries > 0)
1103     {
1104         DacEnumMemoryRegion(dac_cast<TADDR>(GetPersistedHotEntries()),
1105                             m_sHotEntries.m_cEntries * sizeof(PersistedEntry));
1106         DacEnumMemoryRegion(dac_cast<TADDR>(GetPersistedHotBuckets()),
1107                             GetPersistedHotBuckets()->GetSize(m_sHotEntries.m_cBuckets));
1108         for (DWORD i = 0; i < m_sHotEntries.m_cEntries; i++)
1109         {
1110             PTR_PersistedEntry pEntry = dac_cast<PTR_PersistedEntry>(&(GetPersistedHotEntries())[i]);
1111             DOWNCALL(EnumMemoryRegionsForEntry)(VALUE_FROM_PERSISTED_ENTRY(pEntry), flags);
1112         }
1113     }
1114
1115     // Save cold buckets and entries.
1116     if (m_sColdEntries.m_cEntries > 0)
1117     {
1118         DacEnumMemoryRegion(dac_cast<TADDR>(GetPersistedColdEntries()),
1119                             m_sColdEntries.m_cEntries * sizeof(PersistedEntry));
1120         DacEnumMemoryRegion(dac_cast<TADDR>(GetPersistedColdBuckets()),
1121                             GetPersistedColdBuckets()->GetSize(m_sColdEntries.m_cBuckets));
1122         for (DWORD i = 0; i < m_sColdEntries.m_cEntries; i++)
1123         {
1124             PTR_PersistedEntry pEntry = dac_cast<PTR_PersistedEntry>(&(GetPersistedColdEntries())[i]);
1125             DOWNCALL(EnumMemoryRegionsForEntry)(VALUE_FROM_PERSISTED_ENTRY(pEntry), flags);
1126         }
1127     }
1128 #endif // FEATURE_PREJIT
1129
1130     // Save the module if present.
1131     if (GetModule().IsValid())
1132         GetModule()->EnumMemoryRegions(flags, true);
1133 }
1134 #endif // DACCESS_COMPILE
1135
1136 #ifdef FEATURE_PREJIT
1137
1138 // Find the first persisted entry (hot or cold based on pEntries) that matches the given hash. Looks only in
1139 // the persisted block given (i.e. searches only hot *or* cold entries). Returns NULL on failure. Otherwise
1140 // returns pointer to the derived class portion of the entry and initializes the provided LookupContext to
1141 // allow enumeration of any further matches.
1142 template <NGEN_HASH_PARAMS>
1143 DPTR(VALUE) NgenHashTable<NGEN_HASH_ARGS>::FindPersistedEntryByHash(PersistedEntries *pEntries, NgenHashValue iHash, LookupContext *pContext)
1144 {
1145     CONTRACTL
1146     {
1147         NOTHROW;
1148         GC_NOTRIGGER;
1149         MODE_ANY;
1150         SUPPORTS_DAC;
1151         PRECONDITION(CheckPointer(pContext));
1152     }
1153     CONTRACTL_END;
1154
1155     // No point looking if there are no entries.
1156     if (pEntries->m_cEntries == 0)
1157         return NULL;
1158
1159     // Since there is at least one entry there must be at least one bucket.
1160     _ASSERTE(pEntries->m_cBuckets > 0);
1161
1162     DWORD eType = (pEntries == &m_sHotEntries ? Hot : Cold);
1163
1164     // Get the first entry and count of entries for the bucket which contains all entries with the given hash
1165     // code.
1166     DWORD dwEntryIndex, cEntriesLeft;
1167     if (eType == Hot)
1168     {
1169         GetPersistedHotBuckets()->GetBucket(iHash % pEntries->m_cBuckets, &dwEntryIndex, &cEntriesLeft);
1170     }
1171     else
1172     {
1173         GetPersistedColdBuckets()->GetBucket(iHash % pEntries->m_cBuckets, &dwEntryIndex, &cEntriesLeft);
1174     }
1175
1176     // Determine the address of the first entry in the chain by indexing into the entry array.
1177     PTR_PersistedEntry pEntry;
1178
1179     if (eType == Hot)
1180     {
1181        pEntry = dac_cast<PTR_PersistedEntry>(&(GetPersistedHotEntries())[dwEntryIndex]);
1182     }
1183     else
1184     {
1185        pEntry = dac_cast<PTR_PersistedEntry>(&(GetPersistedColdEntries())[dwEntryIndex]);
1186     }
1187
1188     // Iterate while we've still got entries left to check in this chain.
1189     while (cEntriesLeft--)
1190     {
1191         if (pEntry->m_iHashValue == iHash)
1192         {
1193             // We've found our match.
1194
1195             // Record our current search state into the provided context so that a subsequent call to
1196             // BaseFindNextEntryByHash can pick up the search where it left off.
1197             pContext->m_pEntry = dac_cast<TADDR>(pEntry);
1198             pContext->m_eType = eType;
1199             pContext->m_cRemainingEntries = cEntriesLeft;
1200
1201             // Return the address of the sub-classes' embedded entry structure.
1202             return VALUE_FROM_PERSISTED_ENTRY(pEntry);
1203         }
1204
1205         // Move to the next entry in the chain.
1206         pEntry++;
1207     }
1208
1209     // If we get here then none of the entries in the target bucket matched the hash code and we have a miss
1210     // (for this section of the table at least).
1211     return NULL;
1212 }
1213
1214 #ifndef DACCESS_COMPILE
1215 template <NGEN_HASH_PARAMS>
1216 NgenHashTable<NGEN_HASH_ARGS>::EntryMappingTable::~EntryMappingTable()
1217 {
1218     LIMITED_METHOD_CONTRACT;
1219
1220     delete [] m_pEntries;
1221 }
1222
1223 // Given an old entry address (pre-BaseSave) return the address of the entry relocated ready for saving to
1224 // disk. Note that this address is the (ngen) runtime address, not the disk image address you can further
1225 // obtain by calling DataImage::GetImagePointer().
1226 template <NGEN_HASH_PARAMS>
1227 VALUE *NgenHashTable<NGEN_HASH_ARGS>::EntryMappingTable::GetNewEntryAddress(VALUE *pOldEntry)
1228 {
1229     LIMITED_METHOD_CONTRACT;
1230
1231     // Perform a simple linear search. If this proves to be a bottleneck in ngen production (the only scenario
1232     // in which it's called) we can replace this with something faster such as a hash lookup.
1233     for (DWORD i = 0; i < m_cEntries; i++)
1234         if (m_pEntries[i].m_pOldEntry == pOldEntry)
1235             return m_pEntries[i].m_pNewEntry;
1236
1237     _ASSERTE(!"Couldn't map old hash entry to new entry");
1238     return NULL;
1239 }
1240 #endif // !DACCESS_COMPILE
1241 #endif // FEATURE_PREJIT
1242
1243 // Find the first volatile (warm) entry that matches the given hash. Looks only at warm entries. Returns NULL
1244 // on failure. Otherwise returns pointer to the derived class portion of the entry and initializes the
1245 // provided LookupContext to allow enumeration of any further matches.
1246 template <NGEN_HASH_PARAMS>
1247 DPTR(VALUE) NgenHashTable<NGEN_HASH_ARGS>::FindVolatileEntryByHash(NgenHashValue iHash, LookupContext *pContext)
1248 {
1249     CONTRACTL
1250     {
1251         NOTHROW;
1252         GC_NOTRIGGER;
1253         MODE_ANY;
1254         SUPPORTS_DAC;
1255         PRECONDITION(CheckPointer(pContext));
1256     }
1257     CONTRACTL_END;
1258
1259     // No point looking if there are no entries.
1260     if (m_cWarmEntries == 0)
1261         return NULL;
1262
1263     // Since there is at least one entry there must be at least one bucket.
1264     _ASSERTE(m_cWarmBuckets > 0);
1265
1266     // Point at the first entry in the bucket chain which would contain any entries with the given hash code.
1267     PTR_VolatileEntry pEntry = (GetWarmBuckets())[iHash % m_cWarmBuckets];
1268
1269     // Walk the bucket chain one entry at a time.
1270     while (pEntry)
1271     {
1272         if (pEntry->m_iHashValue == iHash)
1273         {
1274             // We've found our match.
1275
1276             // Record our current search state into the provided context so that a subsequent call to
1277             // BaseFindNextEntryByHash can pick up the search where it left off.
1278             pContext->m_pEntry = dac_cast<TADDR>(pEntry);
1279             pContext->m_eType = Warm;
1280
1281             // Return the address of the sub-classes' embedded entry structure.
1282             return VALUE_FROM_VOLATILE_ENTRY(pEntry);
1283         }
1284
1285         // Move to the next entry in the chain.
1286         pEntry = pEntry->m_pNextEntry;
1287     }
1288
1289     // If we get here then none of the entries in the target bucket matched the hash code and we have a miss
1290     // (for this section of the table at least).
1291     return NULL;
1292 }
1293
1294 // Initializes the iterator context passed by the caller to make it ready to walk every entry in the table in
1295 // an arbitrary order. Call pIterator->Next() to retrieve the first entry.
1296 template <NGEN_HASH_PARAMS>
1297 void NgenHashTable<NGEN_HASH_ARGS>::BaseInitIterator(BaseIterator *pIterator)
1298 {
1299     LIMITED_METHOD_DAC_CONTRACT;
1300
1301     pIterator->m_pTable = dac_cast<DPTR(NgenHashTable<NGEN_HASH_ARGS>)>(this);
1302     pIterator->m_pEntry = NULL;
1303 #ifdef FEATURE_PREJIT
1304     pIterator->m_eType = Hot;
1305     pIterator->m_cRemainingEntries = m_sHotEntries.m_cEntries;
1306 #else
1307     pIterator->m_eType = Warm;
1308     pIterator->m_dwBucket = 0;
1309 #endif
1310 }
1311
1312 // Returns a pointer to the next entry in the hash table or NULL once all entries have been enumerated. Once
1313 // NULL has been return the only legal operation is to re-initialize the iterator with BaseInitIterator.
1314 template <NGEN_HASH_PARAMS>
1315 DPTR(VALUE) NgenHashTable<NGEN_HASH_ARGS>::BaseIterator::Next()
1316 {
1317     CONTRACTL
1318     {
1319         NOTHROW;
1320         GC_NOTRIGGER;
1321         MODE_ANY;
1322         SUPPORTS_DAC;
1323     }
1324     CONTRACTL_END;
1325
1326     // We might need to re-iterate our algorithm if we fall off the end of one hash table section (Hot or
1327     // Warm) and need to move onto the next.
1328     while (true)
1329     {
1330         // What type of section are we walking (Hot, Warm or Cold)?
1331         switch (m_eType)
1332         {
1333 #ifdef FEATURE_PREJIT
1334         case Hot:
1335         {
1336             if (m_cRemainingEntries)
1337             {
1338                 // There's at least one more entry in the hot section to report.
1339
1340                 if (m_pEntry == NULL)
1341                 {
1342                     // This is our first lookup in the hot section, return the first entry in the hot array.
1343                     m_pEntry = dac_cast<TADDR>(m_pTable->GetPersistedHotEntries());
1344                 }
1345                 else
1346                 {
1347                     // This is not our first lookup, return the entry immediately after the last one we
1348                     // reported.
1349                     m_pEntry = (TADDR)(m_pEntry + sizeof(PersistedEntry));
1350                 }
1351
1352                 // There's one less entry to report in the future.
1353                 m_cRemainingEntries--;
1354
1355                 // Return the pointer to the embedded sub-class entry in the entry we found.
1356                 return VALUE_FROM_PERSISTED_ENTRY(dac_cast<PTR_PersistedEntry>(m_pEntry));
1357             }
1358
1359             // We ran out of hot entries. Set up to search the warm section next and go round the loop again.
1360             m_eType = Warm;
1361             m_pEntry = NULL;
1362             m_dwBucket = 0;
1363             break;
1364         }
1365 #endif // FEATURE_PREJIT
1366
1367         case Warm:
1368         {
1369             if (m_pEntry == NULL)
1370             {
1371                 // This is our first lookup in the warm section for a particular bucket, return the first
1372                 // entry in that bucket.
1373                 m_pEntry = dac_cast<TADDR>((m_pTable->GetWarmBuckets())[m_dwBucket]);
1374             }
1375             else
1376             {
1377                 // This is not our first lookup, return the entry immediately after the last one we
1378                 // reported.
1379                 m_pEntry = dac_cast<TADDR>(dac_cast<PTR_VolatileEntry>(m_pEntry)->m_pNextEntry);
1380             }
1381
1382             // If we found an entry in the last step return with it.
1383             if (m_pEntry)
1384                 return VALUE_FROM_VOLATILE_ENTRY(dac_cast<PTR_VolatileEntry>(m_pEntry));
1385
1386             // Othwerwise we found the end of a bucket chain. Increment the current bucket and, if there are
1387             // buckets left to scan go back around again.
1388             m_dwBucket++;
1389             if (m_dwBucket < m_pTable->m_cWarmBuckets)
1390                 break;
1391
1392             // Othwerwise we should move onto the cold section (if we have one).
1393
1394 #ifdef FEATURE_PREJIT
1395             m_eType = Cold;
1396             m_pEntry = NULL;
1397             m_cRemainingEntries = m_pTable->m_sColdEntries.m_cEntries;
1398             break;
1399 #else
1400             return NULL;
1401 #endif // FEATURE_PREJIT
1402         }
1403
1404 #ifdef FEATURE_PREJIT
1405         case Cold:
1406         {
1407             if (m_cRemainingEntries)
1408             {
1409                 // There's at least one more entry in the cold section to report.
1410
1411                 if (m_pEntry == NULL)
1412                 {
1413                     // This is our first lookup in the cold section, return the first entry in the cold array.
1414                     m_pEntry = dac_cast<TADDR>(m_pTable->GetPersistedColdEntries());
1415                 }
1416                 else
1417                 {
1418                     // This is not our first lookup, return the entry immediately after the last one we
1419                     // reported.
1420                     m_pEntry = (TADDR)(m_pEntry + sizeof(PersistedEntry));
1421                 }
1422
1423                 // There's one less entry to report in the future.
1424                 m_cRemainingEntries--;
1425
1426                 // Return the pointer to the embedded sub-class entry in the entry we found.
1427                 return VALUE_FROM_PERSISTED_ENTRY(dac_cast<PTR_PersistedEntry>(m_pEntry));
1428             }
1429
1430             // If there are no more entries in the cold section that's it, the whole table has been scanned.
1431             return NULL;
1432         }
1433 #endif // FEATURE_PREJIT
1434
1435         default:
1436             _ASSERTE(!"Invalid hash entry type");
1437         }
1438     }
1439 }
1440
1441 // Get a pointer to the referenced entry.
1442 template <NGEN_HASH_PARAMS>
1443 DPTR(VALUE) NgenHashEntryRef<NGEN_HASH_ARGS>::Get()
1444 {
1445     CONTRACTL
1446     {
1447         NOTHROW;
1448         GC_NOTRIGGER;
1449         MODE_ANY;
1450         SUPPORTS_DAC;
1451     }
1452     CONTRACTL_END;
1453
1454     // Short-cut the NULL case, it's a lot cheaper than the code below when compiling for DAC.
1455     if (m_rpEntryRef.IsNull())
1456         return NULL;
1457
1458     // Note that the following code uses a special DAC lookup for an interior pointer (i.e. "this" isn't a
1459     // host address corresponding to a DAC marshalled instance, it's some host address within such an
1460     // instance). These lookups are a little slower than the regular kind since we have to search for the
1461     // containing instance.
1462
1463     // @todo: The following causes gcc to choke on Mac 10.4 at least (complains that offsetof is being passed
1464     // four arguments instead of two). Expanding the top-level macro manually fixes this.
1465     // TADDR pBase = PTR_HOST_INT_MEMBER_TADDR(NgenHashEntryRef<NGEN_HASH_ARGS>, this, m_rpEntryRef);
1466     TADDR pBase = PTR_HOST_INT_TO_TADDR(this) + (TADDR)offsetof(NgenHashEntryRef<NGEN_HASH_ARGS>, m_rpEntryRef);
1467
1468     return m_rpEntryRef.GetValue(pBase);
1469 }
1470
1471 #ifndef DACCESS_COMPILE
1472
1473 // Set the reference to point to the given entry.
1474 template <NGEN_HASH_PARAMS>
1475 void NgenHashEntryRef<NGEN_HASH_ARGS>::Set(VALUE *pEntry)
1476 {
1477     CONTRACTL
1478     {
1479         NOTHROW;
1480         GC_NOTRIGGER;
1481         MODE_ANY;
1482     }
1483     CONTRACTL_END;
1484
1485     m_rpEntryRef.SetValueMaybeNull(pEntry);
1486 }
1487
1488 #ifdef FEATURE_PREJIT
1489
1490 // Call this during the ngen Fixup phase to adjust the relative pointer to account for ngen image layout.
1491 template <NGEN_HASH_PARAMS>
1492 void NgenHashEntryRef<NGEN_HASH_ARGS>::Fixup(DataImage *pImage, NgenHashTable<NGEN_HASH_ARGS> *pTable)
1493 {
1494     STANDARD_VM_CONTRACT;
1495
1496     // No fixup required for null pointers.
1497     if (m_rpEntryRef.IsNull())
1498         return;
1499
1500     // Location is the field containing the entry reference. We need to determine the ngen zap node that
1501     // contains this field (it'll be part of either the hot or cold entry arrays). Then we can determine the
1502     // offset of the field from the beginning of the node.
1503     BYTE *pLocation = (BYTE*)&m_rpEntryRef;
1504     BYTE *pLocationBase;
1505     DWORD cbLocationOffset;
1506
1507     if (pLocation >= (BYTE*)pTable->GetPersistedHotEntries() &&
1508         pLocation < (BYTE*)(pTable->GetPersistedHotEntries() + pTable->m_sHotEntries.m_cEntries))
1509     {
1510         // The field is in a hot entry.
1511         pLocationBase = (BYTE*)pTable->GetPersistedHotEntries();
1512     }
1513     else if (pLocation >= (BYTE*)pTable->GetPersistedColdEntries() &&
1514              pLocation < (BYTE*)(pTable->GetPersistedColdEntries() + pTable->m_sColdEntries.m_cEntries))
1515     {
1516         // The field is in a cold entry.
1517         pLocationBase = (BYTE*)pTable->GetPersistedColdEntries();
1518     }
1519     else
1520     {
1521         // The field doesn't lie in one of the entry arrays. The caller has passed us an NgenHashEntryRef that
1522         // wasn't embedded as a field in one of this hash's entries.
1523         _ASSERTE(!"NgenHashEntryRef must be a field in an NgenHashTable entry for Fixup to work");
1524         return;
1525     }
1526     cbLocationOffset = static_cast<DWORD>(pLocation - pLocationBase);
1527
1528     // Target is the address of the entry that this reference points to. Go through the same kind of logic to
1529     // determine which section the target entry lives in, hot or cold.
1530     BYTE *pTarget = (BYTE*)m_rpEntryRef.GetValue();
1531     BYTE *pTargetBase;
1532     DWORD cbTargetOffset;
1533
1534     if (pTarget >= (BYTE*)pTable->GetPersistedHotEntries() &&
1535         pTarget < (BYTE*)(pTable->GetPersistedHotEntries() + pTable->m_sHotEntries.m_cEntries))
1536     {
1537         // The target is a hot entry.
1538         pTargetBase = (BYTE*)pTable->GetPersistedHotEntries();
1539     }
1540     else if (pTarget >= (BYTE*)pTable->GetPersistedColdEntries() &&
1541              pTarget < (BYTE*)(pTable->GetPersistedColdEntries() + pTable->m_sColdEntries.m_cEntries))
1542     {
1543         // The target is a cold entry.
1544         pTargetBase = (BYTE*)pTable->GetPersistedColdEntries();
1545     }
1546     else
1547     {
1548         // The target doesn't lie in one of the entry arrays. The caller has passed us an NgenHashEntryRef that
1549         // points to an entry (or other memory) not in our hash table.
1550         _ASSERTE(!"NgenHashEntryRef must refer to an entry in the same hash table");
1551         return;
1552     }
1553     cbTargetOffset = static_cast<DWORD>(pTarget - pTargetBase);
1554
1555     // Now we have enough data to ask for a fixup to be generated for this field. The fixup type
1556     // IMAGE_REL_BASED_RELPTR means we won't actually get a base relocation fixup (an entry in the ngen image
1557     // that causes a load-time fixup to be applied). Instead this record will just adjust the relative value
1558     // in the field once the ngen image layout is finalized and it knows the final locations of the field and
1559     // target zap nodes.
1560     pImage->FixupField(pLocationBase, cbLocationOffset, pTargetBase, cbTargetOffset, IMAGE_REL_BASED_RELPTR);
1561 }
1562 #endif // FEATURE_PREJIT
1563 #endif // !DACCESS_COMPILE