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// File: instmethhash.cpp
6//
7
8
9//
10
11//
12// ============================================================================
13
14#include "common.h"
15#include "excep.h"
16#include "instmethhash.h"
17#include "eeconfig.h"
18#include "generics.h"
19#include "typestring.h"
20#ifdef FEATURE_PREJIT
21#include "zapsig.h"
22#include "compile.h"
23#endif
24#include "ngenhash.inl"
25
26PTR_MethodDesc InstMethodHashEntry::GetMethod()
27{
28 LIMITED_METHOD_DAC_CONTRACT;
29
30 return dac_cast<PTR_MethodDesc>(dac_cast<TADDR>(data) & ~0x3);
31}
32
33DWORD InstMethodHashEntry::GetFlags()
34{
35 LIMITED_METHOD_DAC_CONTRACT;
36
37 return (DWORD)(dac_cast<TADDR>(data) & 0x3);
38}
39
40#ifndef DACCESS_COMPILE
41
42void InstMethodHashEntry::SetMethodAndFlags(MethodDesc *pMethod, DWORD dwFlags)
43{
44 LIMITED_METHOD_CONTRACT;
45
46 _ASSERTE(dwFlags <= 0x3);
47 _ASSERTE(((TADDR)pMethod & 0x3) == 0);
48
49 data = (MethodDesc*)((TADDR)pMethod | dwFlags);
50}
51
52// ============================================================================
53// Instantiated method hash table methods
54// ============================================================================
55/* static */ InstMethodHashTable *InstMethodHashTable::Create(LoaderAllocator *pAllocator, Module *pModule, DWORD dwNumBuckets, AllocMemTracker *pamTracker)
56{
57 CONTRACTL
58 {
59 THROWS;
60 GC_NOTRIGGER;
61 INJECT_FAULT(COMPlusThrowOM(););
62 }
63 CONTRACTL_END
64
65 LoaderHeap *pHeap = pAllocator->GetLowFrequencyHeap();
66 InstMethodHashTable *pThis = (InstMethodHashTable*)pamTracker->Track(pHeap->AllocMem((S_SIZE_T)sizeof(InstMethodHashTable)));
67
68 new (pThis) InstMethodHashTable(pModule, pHeap, dwNumBuckets);
69
70#ifdef _DEBUG
71 pThis->InitUnseal();
72#endif
73
74 pThis->m_pLoaderAllocator = pAllocator;
75
76 return pThis;
77}
78
79PTR_LoaderAllocator InstMethodHashTable::GetLoaderAllocator()
80{
81 WRAPPER_NO_CONTRACT;
82
83 if (m_pLoaderAllocator)
84 {
85 return m_pLoaderAllocator;
86 }
87 else
88 {
89 _ASSERTE(!m_pModule.IsNull());
90 return GetModule()->GetLoaderAllocator();
91 }
92}
93
94
95// Calculate a hash value for a method-desc key
96static DWORD Hash(TypeHandle declaringType, mdMethodDef token, Instantiation inst)
97{
98 STATIC_CONTRACT_NOTHROW;
99 STATIC_CONTRACT_GC_NOTRIGGER;
100 STATIC_CONTRACT_FORBID_FAULT;
101
102 DWORD dwHash = 0x87654321;
103#define INST_HASH_ADD(_value) dwHash = ((dwHash << 5) + dwHash) ^ (_value)
104
105 INST_HASH_ADD(declaringType.GetCl());
106 INST_HASH_ADD(token);
107
108 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
109 {
110 TypeHandle thArg = inst[i];
111
112 if (thArg.GetMethodTable())
113 {
114 INST_HASH_ADD(thArg.GetCl());
115
116 Instantiation sArgInst = thArg.GetInstantiation();
117 for (DWORD j = 0; j < sArgInst.GetNumArgs(); j++)
118 {
119 TypeHandle thSubArg = sArgInst[j];
120 if (thSubArg.GetMethodTable())
121 INST_HASH_ADD(thSubArg.GetCl());
122 else
123 INST_HASH_ADD(thSubArg.GetSignatureCorElementType());
124 }
125 }
126 else
127 INST_HASH_ADD(thArg.GetSignatureCorElementType());
128 }
129
130 return dwHash;
131}
132
133MethodDesc* InstMethodHashTable::FindMethodDesc(TypeHandle declaringType,
134 mdMethodDef token,
135 BOOL unboxingStub,
136 Instantiation inst,
137 BOOL getSharedNotStub)
138{
139 CONTRACTL
140 {
141 NOTHROW;
142 GC_NOTRIGGER;
143 FORBID_FAULT;
144 PRECONDITION(CheckPointer(declaringType));
145 }
146 CONTRACTL_END
147
148 // We temporarily disable IBC logging here
149 // because the pMD that we search through may not be restored
150 // and ComputePrefferedZapModule will assert on finding an
151 // encode fixup pointer
152 //
153 IBCLoggingDisabler disableIbcLogging;
154
155 MethodDesc *pMDResult = NULL;
156
157 DWORD dwHash = Hash(declaringType, token, inst);
158 InstMethodHashEntry_t* pSearch;
159 LookupContext sContext;
160
161 for (pSearch = BaseFindFirstEntryByHash(dwHash, &sContext);
162 pSearch != NULL;
163 pSearch = BaseFindNextEntryByHash(&sContext))
164 {
165#ifdef FEATURE_PREJIT
166 // This ensures that GetAssemblyIfLoaded operations that may be triggered by signature walks will succeed if at all possible.
167 ClrFlsThreadTypeSwitch genericInstantionCompareHolder(ThreadType_GenericInstantiationCompare);
168#endif
169
170 MethodDesc *pMD = pSearch->GetMethod();
171
172 if (pMD->GetMemberDef() != token)
173 continue; // Next iteration of the for loop
174
175 if (pMD->GetNumGenericMethodArgs() != inst.GetNumArgs())
176 continue; // Next iteration of the for loop
177
178 DWORD dwKeyFlags = pSearch->GetFlags();
179
180 if ( ((dwKeyFlags & InstMethodHashEntry::RequiresInstArg) == 0) != (getSharedNotStub == 0) )
181 continue;
182
183 if ( ((dwKeyFlags & InstMethodHashEntry::UnboxingStub) == 0) != (unboxingStub == 0) )
184 continue;
185
186 // Note pMD->GetMethodTable() might not be restored at this point.
187
188 RelativeFixupPointer<PTR_MethodTable> * ppMT = pMD->GetMethodTablePtr();
189 TADDR pMT = ppMT->GetValueMaybeTagged((TADDR)ppMT);
190
191 if (!ZapSig::CompareTaggedPointerToTypeHandle(GetModule(), pMT, declaringType))
192 {
193 continue; // Next iteration of the for loop
194 }
195
196 if (!inst.IsEmpty())
197 {
198 Instantiation candidateInst = pMD->GetMethodInstantiation();
199
200 // We have matched the method already, thus the number of arguments in the instantiation should match too.
201 _ASSERTE(inst.GetNumArgs() == candidateInst.GetNumArgs());
202
203 bool match = true; // This is true when all instantiation arguments match
204
205 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
206 {
207 // Fetch the type handle as TADDR. It may be may be encoded fixup - TypeHandle debug-only validation
208 // asserts on encoded fixups.
209 TADDR candidateArg = ((FixupPointer<TADDR> *)candidateInst.GetRawArgs())[i].GetValue();
210
211 if (!ZapSig::CompareTaggedPointerToTypeHandle(GetModule(), candidateArg, inst[i]))
212 {
213 match = false;
214 break;
215 }
216 }
217 if (!match)
218 continue; // Next iteration of the pSearch for loop;
219 }
220 //
221 // Success, we found a pMD that matches
222
223 pMDResult = pMD;
224 break; // Exit the for loop and jump to the return pMDResult
225 }
226
227 return pMDResult;
228}
229
230BOOL InstMethodHashTable::ContainsMethodDesc(MethodDesc* pMD)
231{
232 CONTRACTL
233 {
234 NOTHROW;
235 GC_NOTRIGGER;
236 MODE_ANY;
237 }
238 CONTRACTL_END;
239
240 return FindMethodDesc(
241 pMD->GetMethodTable(), pMD->GetMemberDef(), pMD->IsUnboxingStub(),
242 pMD->GetMethodInstantiation(), pMD->RequiresInstArg()) != NULL;
243}
244
245#endif // #ifndef DACCESS_COMPILE
246
247void InstMethodHashTable::Iterator::Reset()
248{
249 WRAPPER_NO_CONTRACT;
250
251 if (m_pTable)
252 {
253#ifdef _DEBUG
254 m_pTable->Unseal();
255#endif
256 m_pTable = NULL;
257 }
258
259 Init();
260}
261
262void InstMethodHashTable::Iterator::Init()
263{
264 WRAPPER_NO_CONTRACT;
265
266#ifdef _DEBUG
267 if (m_pTable)
268 m_pTable->Seal(); // The table cannot be changing while it is being iterated
269#endif
270
271 m_fIterating = false;
272}
273
274InstMethodHashTable::Iterator::Iterator()
275{
276 WRAPPER_NO_CONTRACT;
277 m_pTable = NULL;
278 Init();
279}
280
281InstMethodHashTable::Iterator::Iterator(InstMethodHashTable * pTable)
282{
283 WRAPPER_NO_CONTRACT;
284 m_pTable = pTable;
285 Init();
286}
287
288InstMethodHashTable::Iterator::~Iterator()
289{
290 WRAPPER_NO_CONTRACT;
291
292#ifdef _DEBUG
293 if (m_pTable)
294 m_pTable->Unseal(); // Done with the iterator so we unseal
295#endif
296}
297
298BOOL InstMethodHashTable::FindNext(Iterator *it, InstMethodHashEntry **ppEntry)
299{
300 LIMITED_METHOD_CONTRACT;
301
302 if (!it->m_fIterating)
303 {
304 BaseInitIterator(&it->m_sIterator);
305 it->m_fIterating = true;
306 }
307
308 *ppEntry = it->m_sIterator.Next();
309 return *ppEntry ? TRUE : FALSE;
310}
311
312DWORD InstMethodHashTable::GetCount()
313{
314 LIMITED_METHOD_CONTRACT;
315
316 return BaseGetElementCount();
317}
318
319#ifndef DACCESS_COMPILE
320
321// Add method desc to the hash table; must not be present already
322void InstMethodHashTable::InsertMethodDesc(MethodDesc *pMD)
323{
324 CONTRACTL
325 {
326 THROWS;
327 GC_NOTRIGGER;
328 INJECT_FAULT(COMPlusThrowOM(););
329 PRECONDITION(IsUnsealed()); // If we are sealed then we should not be adding to this hashtable
330 PRECONDITION(CheckPointer(pMD));
331
332 // Generic method definitions (e.g. D.m<U> or C<int>.m<U>) belong in method tables, not here
333 PRECONDITION(!pMD->IsGenericMethodDefinition());
334 }
335 CONTRACTL_END
336
337 InstMethodHashEntry_t * pNewEntry = (InstMethodHashEntry_t*)BaseAllocateEntry(NULL);
338
339 DWORD dwKeyFlags = 0;
340 if (pMD->RequiresInstArg())
341 dwKeyFlags |= InstMethodHashEntry::RequiresInstArg;
342 if (pMD->IsUnboxingStub())
343 dwKeyFlags |= InstMethodHashEntry::UnboxingStub;
344 pNewEntry->SetMethodAndFlags(pMD, dwKeyFlags);
345
346 DWORD dwHash = Hash(pMD->GetMethodTable(), pMD->GetMemberDef(), pMD->GetMethodInstantiation());
347 BaseInsertEntry(dwHash, pNewEntry);
348}
349
350#ifdef FEATURE_NATIVE_IMAGE_GENERATION
351
352#ifdef _DEBUG
353void InstMethodHashTableSeal(InstMethodHashTable * pTable) { WRAPPER_NO_CONTRACT; pTable->Seal(); }
354void InstMethodHashTableUnseal(InstMethodHashTable * pTable) { WRAPPER_NO_CONTRACT; pTable->Unseal(); }
355typedef Wrapper<InstMethodHashTable *, InstMethodHashTableSeal, InstMethodHashTableUnseal> InstMethodHashTableSealHolder;
356#endif
357
358// Save the hash table and any method descriptors referenced by it
359void InstMethodHashTable::Save(DataImage *image, CorProfileData *pProfileData)
360{
361 CONTRACTL
362 {
363 STANDARD_VM_CHECK;
364 PRECONDITION(image->GetModule() == m_pModule);
365 }
366 CONTRACTL_END;
367
368#ifdef _DEBUG
369 // The table should not change while we are walking the buckets
370 InstMethodHashTableSealHolder h(this);
371#endif
372
373 BaseSave(image, pProfileData);
374}
375
376bool InstMethodHashTable::ShouldSave(DataImage *pImage, InstMethodHashEntry_t *pEntry)
377{
378 STANDARD_VM_CONTRACT;
379
380 return !!pImage->GetPreloader()->IsMethodInTransitiveClosureOfInstantiations(CORINFO_METHOD_HANDLE(pEntry->GetMethod()));
381}
382
383bool InstMethodHashTable::IsHotEntry(InstMethodHashEntry_t *pEntry, CorProfileData *pProfileData)
384{
385 LIMITED_METHOD_CONTRACT;
386
387 return true;
388}
389
390bool InstMethodHashTable::SaveEntry(DataImage *pImage, CorProfileData *pProfileData, InstMethodHashEntry_t *pOldEntry, InstMethodHashEntry_t *pNewEntry, EntryMappingTable *pMap)
391{
392 LIMITED_METHOD_CONTRACT;
393
394 return false;
395}
396
397void InstMethodHashTable::Fixup(DataImage *image)
398{
399 STANDARD_VM_CONTRACT;
400
401 BaseFixup(image);
402
403 image->ZeroPointerField(this, offsetof(InstMethodHashTable, m_pLoaderAllocator));
404
405#ifdef _DEBUG
406 // The persisted table should be unsealed.
407 InstMethodHashTable *pNewTable = (InstMethodHashTable*) image->GetImagePointer(this);
408 pNewTable->InitUnseal();
409#endif
410}
411
412void InstMethodHashTable::FixupEntry(DataImage *pImage, InstMethodHashEntry_t *pEntry, void *pFixupBase, DWORD cbFixupOffset)
413{
414 STANDARD_VM_CONTRACT;
415
416 pImage->FixupField(pFixupBase, cbFixupOffset + offsetof(InstMethodHashEntry_t, data), pEntry->GetMethod(), pEntry->GetFlags());
417
418 pEntry->GetMethod()->Fixup(pImage);
419}
420#endif // FEATURE_PREJIT
421
422#endif // #ifndef DACCESS_COMPILE
423
424#ifdef DACCESS_COMPILE
425void
426InstMethodHashTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
427{
428 SUPPORTS_DAC;
429
430 BaseEnumMemoryRegions(flags);
431}
432
433void InstMethodHashTable::EnumMemoryRegionsForEntry(InstMethodHashEntry_t *pEntry, CLRDataEnumMemoryFlags flags)
434{
435 SUPPORTS_DAC;
436
437 if (pEntry->GetMethod().IsValid())
438 pEntry->GetMethod()->EnumMemoryRegions(flags);
439}
440#endif // #ifdef DACCESS_COMPILE
441