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 | |
26 | PTR_MethodDesc InstMethodHashEntry::GetMethod() |
27 | { |
28 | LIMITED_METHOD_DAC_CONTRACT; |
29 | |
30 | return dac_cast<PTR_MethodDesc>(dac_cast<TADDR>(data) & ~0x3); |
31 | } |
32 | |
33 | DWORD InstMethodHashEntry::GetFlags() |
34 | { |
35 | LIMITED_METHOD_DAC_CONTRACT; |
36 | |
37 | return (DWORD)(dac_cast<TADDR>(data) & 0x3); |
38 | } |
39 | |
40 | #ifndef DACCESS_COMPILE |
41 | |
42 | void 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 | |
79 | PTR_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 |
96 | static 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 | |
133 | MethodDesc* 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 | |
230 | BOOL 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 | |
247 | void 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 | |
262 | void 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 | |
274 | InstMethodHashTable::Iterator::Iterator() |
275 | { |
276 | WRAPPER_NO_CONTRACT; |
277 | m_pTable = NULL; |
278 | Init(); |
279 | } |
280 | |
281 | InstMethodHashTable::Iterator::Iterator(InstMethodHashTable * pTable) |
282 | { |
283 | WRAPPER_NO_CONTRACT; |
284 | m_pTable = pTable; |
285 | Init(); |
286 | } |
287 | |
288 | InstMethodHashTable::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 | |
298 | BOOL 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 | |
312 | DWORD 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 |
322 | void 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 |
353 | void InstMethodHashTableSeal(InstMethodHashTable * pTable) { WRAPPER_NO_CONTRACT; pTable->Seal(); } |
354 | void InstMethodHashTableUnseal(InstMethodHashTable * pTable) { WRAPPER_NO_CONTRACT; pTable->Unseal(); } |
355 | typedef Wrapper<InstMethodHashTable *, InstMethodHashTableSeal, InstMethodHashTableUnseal> InstMethodHashTableSealHolder; |
356 | #endif |
357 | |
358 | // Save the hash table and any method descriptors referenced by it |
359 | void 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 | |
376 | bool 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 | |
383 | bool InstMethodHashTable::IsHotEntry(InstMethodHashEntry_t *pEntry, CorProfileData *pProfileData) |
384 | { |
385 | LIMITED_METHOD_CONTRACT; |
386 | |
387 | return true; |
388 | } |
389 | |
390 | bool 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 | |
397 | void 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 | |
412 | void 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 |
425 | void |
426 | InstMethodHashTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
427 | { |
428 | SUPPORTS_DAC; |
429 | |
430 | BaseEnumMemoryRegions(flags); |
431 | } |
432 | |
433 | void 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 | |