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: ILStubCache.cpp |
6 | // |
7 | |
8 | // |
9 | |
10 | |
11 | #include "common.h" |
12 | #include "ilstubcache.h" |
13 | #include "dllimport.h" |
14 | #include <formattype.h> |
15 | #include "jitinterface.h" |
16 | #include "sigbuilder.h" |
17 | #include "ngenhash.inl" |
18 | #include "compile.h" |
19 | |
20 | #include "eventtrace.h" |
21 | |
22 | const char* FormatSig(MethodDesc* pMD, LoaderHeap *pHeap, AllocMemTracker *pamTracker); |
23 | |
24 | ILStubCache::ILStubCache(LoaderHeap *pHeap) : |
25 | CClosedHashBase( |
26 | #ifdef _DEBUG |
27 | 3, |
28 | #else |
29 | 17, // CClosedHashTable will grow as necessary |
30 | #endif |
31 | |
32 | sizeof(ILCHASHENTRY), |
33 | FALSE |
34 | ), |
35 | m_crst(CrstStubCache, CRST_UNSAFE_ANYMODE), |
36 | m_heap(pHeap), |
37 | m_pStubMT(NULL) |
38 | { |
39 | WRAPPER_NO_CONTRACT; |
40 | } |
41 | |
42 | void ILStubCache::Init(LoaderHeap* pHeap) |
43 | { |
44 | LIMITED_METHOD_CONTRACT; |
45 | |
46 | CONSISTENCY_CHECK(NULL == m_heap); |
47 | m_heap = pHeap; |
48 | } |
49 | |
50 | |
51 | #ifndef DACCESS_COMPILE |
52 | |
53 | void CreateModuleIndependentSignature(LoaderHeap* pCreationHeap, |
54 | AllocMemTracker* pamTracker, |
55 | Module* pSigModule, |
56 | PCCOR_SIGNATURE pSig, DWORD cbSig, |
57 | SigTypeContext *pTypeContext, |
58 | PCCOR_SIGNATURE* ppNewSig, DWORD* pcbNewSig) |
59 | { |
60 | CONTRACTL |
61 | { |
62 | STANDARD_VM_CHECK; |
63 | PRECONDITION(CheckPointer(pSigModule, NULL_NOT_OK)); |
64 | PRECONDITION(CheckPointer(ppNewSig, NULL_NOT_OK)); |
65 | PRECONDITION(CheckPointer(pcbNewSig, NULL_NOT_OK)); |
66 | } |
67 | CONTRACTL_END; |
68 | |
69 | SigPointer sigPtr(pSig, cbSig); |
70 | |
71 | SigBuilder sigBuilder; |
72 | sigPtr.ConvertToInternalSignature(pSigModule, pTypeContext, &sigBuilder); |
73 | |
74 | DWORD cbNewSig; |
75 | PVOID pConvertedSig = sigBuilder.GetSignature(&cbNewSig); |
76 | |
77 | PVOID pNewSig = pamTracker->Track(pCreationHeap->AllocMem(S_SIZE_T(cbNewSig))); |
78 | memcpy(pNewSig, pConvertedSig, cbNewSig); |
79 | |
80 | *ppNewSig = (PCCOR_SIGNATURE)pNewSig; |
81 | *pcbNewSig = cbNewSig; |
82 | } |
83 | |
84 | // static |
85 | MethodDesc* ILStubCache::CreateAndLinkNewILStubMethodDesc(LoaderAllocator* pAllocator, MethodTable* pMT, DWORD dwStubFlags, |
86 | Module* pSigModule, PCCOR_SIGNATURE pSig, DWORD cbSig, SigTypeContext *pTypeContext, |
87 | ILStubLinker* pStubLinker) |
88 | { |
89 | CONTRACT (MethodDesc*) |
90 | { |
91 | STANDARD_VM_CHECK; |
92 | PRECONDITION(CheckPointer(pMT, NULL_NOT_OK)); |
93 | POSTCONDITION(CheckPointer(RETVAL)); |
94 | } |
95 | CONTRACT_END; |
96 | AllocMemTracker amTracker; |
97 | |
98 | MethodDesc *pStubMD = ILStubCache::CreateNewMethodDesc(pAllocator->GetHighFrequencyHeap(), |
99 | pMT, |
100 | dwStubFlags, |
101 | pSigModule, |
102 | pSig, cbSig, |
103 | pTypeContext, |
104 | &amTracker); |
105 | |
106 | amTracker.SuppressRelease(); |
107 | |
108 | ILStubResolver *pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver(); |
109 | |
110 | pResolver->SetStubMethodDesc(pStubMD); |
111 | |
112 | |
113 | { |
114 | UINT maxStack; |
115 | size_t cbCode; |
116 | DWORD cbSig; |
117 | BYTE * pbBuffer; |
118 | BYTE * pbLocalSig; |
119 | |
120 | cbCode = pStubLinker->Link(&maxStack); |
121 | cbSig = pStubLinker->GetLocalSigSize(); |
122 | |
123 | COR_ILMETHOD_DECODER * pILHeader = pResolver->AllocGeneratedIL(cbCode, cbSig, maxStack); |
124 | pbBuffer = (BYTE *)pILHeader->Code; |
125 | pbLocalSig = (BYTE *)pILHeader->LocalVarSig; |
126 | _ASSERTE(cbSig == pILHeader->cbLocalVarSig); |
127 | |
128 | pStubLinker->GenerateCode(pbBuffer, cbCode); |
129 | pStubLinker->GetLocalSig(pbLocalSig, cbSig); |
130 | |
131 | pResolver->SetJitFlags(CORJIT_FLAGS(CORJIT_FLAGS::CORJIT_FLAG_IL_STUB)); |
132 | } |
133 | |
134 | pResolver->SetTokenLookupMap(pStubLinker->GetTokenLookupMap()); |
135 | |
136 | RETURN pStubMD; |
137 | |
138 | } |
139 | |
140 | // static |
141 | MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTable* pMT, DWORD dwStubFlags, |
142 | Module* pSigModule, PCCOR_SIGNATURE pSig, DWORD cbSig, SigTypeContext *pTypeContext, |
143 | AllocMemTracker* pamTracker) |
144 | { |
145 | CONTRACT (MethodDesc*) |
146 | { |
147 | STANDARD_VM_CHECK; |
148 | PRECONDITION(CheckPointer(pMT, NULL_NOT_OK)); |
149 | POSTCONDITION(CheckPointer(RETVAL)); |
150 | } |
151 | CONTRACT_END; |
152 | |
153 | // @TODO: reuse the same chunk for multiple methods |
154 | MethodDescChunk* pChunk = MethodDescChunk::CreateChunk(pCreationHeap, |
155 | 1, |
156 | mcDynamic, |
157 | TRUE /* fNonVtableSlot */, |
158 | TRUE /* fNativeCodeSlot */, |
159 | FALSE /* fComPlusCallInfo */, |
160 | pMT, |
161 | pamTracker); |
162 | |
163 | // Note: The method desc memory is zero initialized |
164 | |
165 | DynamicMethodDesc* pMD = (DynamicMethodDesc*)pChunk->GetFirstMethodDesc(); |
166 | |
167 | pMD->SetMemberDef(0); |
168 | pMD->SetSlot(MethodTable::NO_SLOT); // we can't ever use the slot for dynamic methods |
169 | // the no metadata part of the method desc |
170 | pMD->m_pszMethodName.SetValue((PTR_CUTF8)"IL_STUB" ); |
171 | pMD->m_dwExtendedFlags = mdPublic | DynamicMethodDesc::nomdILStub; |
172 | |
173 | pMD->SetTemporaryEntryPoint(pMT->GetLoaderAllocator(), pamTracker); |
174 | |
175 | // |
176 | // convert signature to a compatible signature if needed |
177 | // |
178 | PCCOR_SIGNATURE pNewSig; |
179 | DWORD cbNewSig; |
180 | |
181 | // If we are in the same module and don't have any generics, we can use the incoming signature. |
182 | // Note that pTypeContext may be non-empty and the signature can still have no E_T_(M)VAR in it. |
183 | // We could do a more precise check if we cared. |
184 | if (pMT->GetModule() == pSigModule && (pTypeContext == NULL || pTypeContext->IsEmpty())) |
185 | { |
186 | pNewSig = pSig; |
187 | cbNewSig = cbSig; |
188 | } |
189 | else |
190 | { |
191 | CreateModuleIndependentSignature(pCreationHeap, pamTracker, pSigModule, pSig, cbSig, pTypeContext, &pNewSig, &cbNewSig); |
192 | } |
193 | pMD->SetStoredMethodSig(pNewSig, cbNewSig); |
194 | |
195 | SigPointer sigPtr(pNewSig, cbNewSig); |
196 | ULONG callConvInfo; |
197 | IfFailThrow(sigPtr.GetCallingConvInfo(&callConvInfo)); |
198 | |
199 | if (!(callConvInfo & CORINFO_CALLCONV_HASTHIS)) |
200 | { |
201 | pMD->m_dwExtendedFlags |= mdStatic; |
202 | pMD->SetStatic(); |
203 | } |
204 | |
205 | pMD->m_pResolver = (ILStubResolver*)pamTracker->Track(pCreationHeap->AllocMem(S_SIZE_T(sizeof(ILStubResolver)))); |
206 | #ifdef _DEBUG |
207 | // Poison the ILStubResolver storage |
208 | memset((void*)pMD->m_pResolver, 0xCC, sizeof(ILStubResolver)); |
209 | #endif // _DEBUG |
210 | pMD->m_pResolver = new (pMD->m_pResolver) ILStubResolver(); |
211 | |
212 | #ifdef FEATURE_ARRAYSTUB_AS_IL |
213 | if (SF_IsArrayOpStub(dwStubFlags)) |
214 | { |
215 | pMD->GetILStubResolver()->SetStubType(ILStubResolver::ArrayOpStub); |
216 | } |
217 | else |
218 | #endif |
219 | #ifdef FEATURE_MULTICASTSTUB_AS_IL |
220 | if (SF_IsMulticastDelegateStub(dwStubFlags)) |
221 | { |
222 | pMD->m_dwExtendedFlags |= DynamicMethodDesc::nomdMulticastStub; |
223 | pMD->GetILStubResolver()->SetStubType(ILStubResolver::MulticastDelegateStub); |
224 | } |
225 | else |
226 | #endif |
227 | #ifdef FEATURE_STUBS_AS_IL |
228 | if (SF_IsSecureDelegateStub(dwStubFlags)) |
229 | { |
230 | pMD->m_dwExtendedFlags |= DynamicMethodDesc::nomdSecureDelegateStub; |
231 | pMD->GetILStubResolver()->SetStubType(ILStubResolver::SecureDelegateStub); |
232 | } |
233 | else |
234 | if (SF_IsUnboxingILStub(dwStubFlags)) |
235 | { |
236 | pMD->m_dwExtendedFlags |= DynamicMethodDesc::nomdUnboxingILStub; |
237 | pMD->GetILStubResolver()->SetStubType(ILStubResolver::UnboxingILStub); |
238 | } |
239 | else |
240 | if (SF_IsInstantiatingStub(dwStubFlags)) |
241 | { |
242 | pMD->GetILStubResolver()->SetStubType(ILStubResolver::InstantiatingStub); |
243 | } |
244 | else |
245 | #endif |
246 | #ifdef FEATURE_COMINTEROP |
247 | if (SF_IsCOMStub(dwStubFlags)) |
248 | { |
249 | // mark certain types of stub MDs with random flags so ILStubManager recognizes them |
250 | if (SF_IsReverseStub(dwStubFlags)) |
251 | { |
252 | pMD->m_dwExtendedFlags |= DynamicMethodDesc::nomdReverseStub; |
253 | |
254 | ILStubResolver::ILStubType type = (SF_IsWinRTStub(dwStubFlags) ? ILStubResolver::WinRTToCLRInteropStub : ILStubResolver::COMToCLRInteropStub); |
255 | pMD->GetILStubResolver()->SetStubType(type); |
256 | } |
257 | else |
258 | { |
259 | ILStubResolver::ILStubType type = (SF_IsWinRTStub(dwStubFlags) ? ILStubResolver::CLRToWinRTInteropStub : ILStubResolver::CLRToCOMInteropStub); |
260 | pMD->GetILStubResolver()->SetStubType(type); |
261 | } |
262 | |
263 | if (SF_IsWinRTDelegateStub(dwStubFlags)) |
264 | { |
265 | pMD->m_dwExtendedFlags |= DynamicMethodDesc::nomdDelegateCOMStub; |
266 | } |
267 | } |
268 | else |
269 | #endif |
270 | { |
271 | // mark certain types of stub MDs with random flags so ILStubManager recognizes them |
272 | if (SF_IsReverseStub(dwStubFlags)) |
273 | { |
274 | pMD->m_dwExtendedFlags |= DynamicMethodDesc::nomdReverseStub; |
275 | pMD->GetILStubResolver()->SetStubType(ILStubResolver::NativeToCLRInteropStub); |
276 | } |
277 | else |
278 | { |
279 | if (SF_IsDelegateStub(dwStubFlags)) |
280 | { |
281 | pMD->m_dwExtendedFlags |= DynamicMethodDesc::nomdDelegateStub; |
282 | } |
283 | else if (SF_IsCALLIStub(dwStubFlags)) |
284 | { |
285 | pMD->m_dwExtendedFlags |= DynamicMethodDesc::nomdCALLIStub; |
286 | } |
287 | pMD->GetILStubResolver()->SetStubType(ILStubResolver::CLRToNativeInteropStub); |
288 | } |
289 | } |
290 | |
291 | // if we made it this far, we can set a more descriptive stub name |
292 | #ifdef FEATURE_ARRAYSTUB_AS_IL |
293 | if (SF_IsArrayOpStub(dwStubFlags)) |
294 | { |
295 | switch(dwStubFlags) |
296 | { |
297 | case ILSTUB_ARRAYOP_GET: pMD->m_pszMethodName.SetValue((PTR_CUTF8)"IL_STUB_Array_Get" ); |
298 | break; |
299 | case ILSTUB_ARRAYOP_SET: pMD->m_pszMethodName.SetValue((PTR_CUTF8)"IL_STUB_Array_Set" ); |
300 | break; |
301 | case ILSTUB_ARRAYOP_ADDRESS: pMD->m_pszMethodName.SetValue((PTR_CUTF8)"IL_STUB_Array_Address" ); |
302 | break; |
303 | default: _ASSERTE(!"Unknown array il stub" ); |
304 | } |
305 | } |
306 | else |
307 | #endif |
308 | { |
309 | pMD->m_pszMethodName.SetValue(pMD->GetILStubResolver()->GetStubMethodName()); |
310 | } |
311 | |
312 | |
313 | #ifdef _DEBUG |
314 | pMD->m_pszDebugMethodName = RelativePointer<PTR_CUTF8>::GetValueAtPtr(PTR_HOST_MEMBER_TADDR(DynamicMethodDesc, pMD, m_pszMethodName)); |
315 | pMD->m_pszDebugClassName = ILStubResolver::GetStubClassName(pMD); // must be called after type is set |
316 | pMD->m_pszDebugMethodSignature = FormatSig(pMD, pCreationHeap, pamTracker); |
317 | pMD->m_pDebugMethodTable.SetValue(pMT); |
318 | #endif // _DEBUG |
319 | |
320 | RETURN pMD; |
321 | } |
322 | |
323 | // |
324 | // This will get or create a MethodTable in the Module/AppDomain on which |
325 | // we can place a new IL stub MethodDesc. |
326 | // |
327 | MethodTable* ILStubCache::GetOrCreateStubMethodTable(Module* pModule) |
328 | { |
329 | CONTRACT (MethodTable*) |
330 | { |
331 | THROWS; |
332 | GC_TRIGGERS; |
333 | MODE_ANY; |
334 | INJECT_FAULT(COMPlusThrowOM()); |
335 | POSTCONDITION(CheckPointer(RETVAL)); |
336 | } |
337 | CONTRACT_END; |
338 | |
339 | #ifdef _DEBUG |
340 | if (pModule->IsSystem() || pModule->GetDomain()->IsSharedDomain() || pModule->GetDomain()->AsAppDomain()->IsCompilationDomain()) |
341 | { |
342 | // in the shared domain and compilation AD we are associated with the module |
343 | CONSISTENCY_CHECK(pModule->GetILStubCache() == this); |
344 | } |
345 | else |
346 | { |
347 | // otherwise we are associated with the LoaderAllocator |
348 | LoaderAllocator* pStubLoaderAllocator = LoaderAllocator::GetLoaderAllocator(this); |
349 | CONSISTENCY_CHECK(pStubLoaderAllocator == pModule->GetLoaderAllocator()); |
350 | } |
351 | #endif // _DEBUG |
352 | |
353 | if (NULL == m_pStubMT) |
354 | { |
355 | CrstHolder ch(&m_crst); |
356 | |
357 | if (NULL == m_pStubMT) |
358 | { |
359 | AllocMemTracker amt; |
360 | MethodTable* pNewMT = CreateMinimalMethodTable(pModule, m_heap, &amt); |
361 | amt.SuppressRelease(); |
362 | VolatileStore<MethodTable*>(&m_pStubMT, pNewMT); |
363 | } |
364 | } |
365 | |
366 | RETURN m_pStubMT; |
367 | } |
368 | |
369 | #endif // DACCESS_COMPILE |
370 | |
371 | // |
372 | // NGEN'ed IL stubs |
373 | // |
374 | // - We will never NGEN a CALLI pinvoke or vararg pinvoke |
375 | // |
376 | // - We will always place the IL stub MethodDesc on the same MethodTable that the |
377 | // PInvoke or COM Interop call declaration lives on. |
378 | // |
379 | // - We will not pre-populate our runtime ILStubCache with compile-time |
380 | // information (i.e. NGENed stubs are only reachable from the same NGEN image.) |
381 | // |
382 | // JIT'ed IL stubs |
383 | // |
384 | // - The ILStubCache is per-BaseDomain |
385 | // |
386 | // - Each BaseDomain's ILStubCache will lazily create a "minimal MethodTable" to |
387 | // serve as the home for IL stub MethodDescs |
388 | // |
389 | // - The created MethodTables will use the Module belonging to one of the |
390 | // following, based on what type of interop stub we need to create first. |
391 | // |
392 | // - If that stub is for a static-sig-based pinvoke, we will use the |
393 | // Module belonging to that pinvoke's MethodDesc. |
394 | // |
395 | // - If that stub is for a CALLI or vararg pinvoke, we will use the |
396 | // Module belonging to the VASigCookie that the caller supplied to us. |
397 | // |
398 | // It's important to point out that the Module we latch onto here has no knowledge |
399 | // of the MethodTable that we've just "added" to it. There only exists a "back |
400 | // pointer" to the Module from the MethodTable itself. So we're really only using |
401 | // that module to answer the question of what BaseDomain the MethodTable lives in. |
402 | // So as long as the BaseDomain for that module is the same as the BaseDomain the |
403 | // ILStubCache lives in, I think we have a fairly consistent story here. |
404 | // |
405 | // We're relying on the fact that a VASigCookie may only mention types within the |
406 | // corresponding module used to qualify the signature and the fact that interop |
407 | // stubs may only reference mscorlib code or code related to a type mentioned in |
408 | // the signature. Both of these are true unless the sig is allowed to contain |
409 | // ELEMENT_TYPE_INTERNAL, which may refer to any type. |
410 | // |
411 | // We can only access E_T_INTERNAL through LCG, which does not permit referring |
412 | // to types in other BaseDomains. |
413 | // |
414 | // |
415 | // Places for improvement: |
416 | // |
417 | // - allow NGEN'ing of CALLI pinvoke and vararg pinvoke |
418 | // |
419 | // - pre-populate the per-BaseDomain cache with IL stubs from NGEN'ed image |
420 | // |
421 | |
422 | MethodDesc* ILStubCache::GetStubMethodDesc( |
423 | MethodDesc *pTargetMD, |
424 | ILStubHashBlob* pParams, |
425 | DWORD dwStubFlags, |
426 | Module* pSigModule, |
427 | PCCOR_SIGNATURE pSig, |
428 | DWORD cbSig, |
429 | AllocMemTracker* pamTracker, |
430 | bool& bILStubCreator, |
431 | MethodDesc *pLastMD) |
432 | { |
433 | CONTRACT (MethodDesc*) |
434 | { |
435 | STANDARD_VM_CHECK; |
436 | POSTCONDITION(CheckPointer(RETVAL)); |
437 | } |
438 | CONTRACT_END; |
439 | |
440 | MethodDesc* pMD = NULL; |
441 | bool bFireETWCacheHitEvent = true; |
442 | |
443 | #ifndef DACCESS_COMPILE |
444 | ILStubHashBlob* pBlob = NULL; |
445 | |
446 | INDEBUG(LPCSTR pszResult = "[hit cache]" ); |
447 | |
448 | |
449 | if (SF_IsSharedStub(dwStubFlags)) |
450 | { |
451 | CrstHolder ch(&m_crst); |
452 | |
453 | // Try to find the stub |
454 | ILCHASHENTRY* phe = NULL; |
455 | |
456 | phe = (ILCHASHENTRY*)Find((LPVOID)pParams); |
457 | if (phe) |
458 | { |
459 | pMD = phe->m_pMethodDesc; |
460 | if (pMD == pLastMD) |
461 | bFireETWCacheHitEvent = false; |
462 | } |
463 | } |
464 | |
465 | if (!pMD) |
466 | { |
467 | size_t cbSizeOfBlob = pParams->m_cbSizeOfBlob; |
468 | AllocMemHolder<ILStubHashBlob> pBlobHolder( m_heap->AllocMem(S_SIZE_T(cbSizeOfBlob)) ); |
469 | |
470 | |
471 | // |
472 | // Couldn't find it, let's make a new one. |
473 | // |
474 | |
475 | Module *pContainingModule = pSigModule; |
476 | if (pTargetMD != NULL) |
477 | { |
478 | // loader module may be different from signature module for generic targets |
479 | pContainingModule = pTargetMD->GetLoaderModule(); |
480 | } |
481 | |
482 | MethodTable *pStubMT = GetOrCreateStubMethodTable(pContainingModule); |
483 | |
484 | SigTypeContext typeContext; |
485 | if (pTargetMD != NULL) |
486 | { |
487 | SigTypeContext::InitTypeContext(pTargetMD, &typeContext); |
488 | } |
489 | |
490 | pMD = ILStubCache::CreateNewMethodDesc(m_heap, pStubMT, dwStubFlags, pSigModule, pSig, cbSig, &typeContext, pamTracker); |
491 | |
492 | if (SF_IsSharedStub(dwStubFlags)) |
493 | { |
494 | |
495 | CrstHolder ch(&m_crst); |
496 | |
497 | ILCHASHENTRY* phe = NULL; |
498 | |
499 | bool bNew; |
500 | phe = (ILCHASHENTRY*)FindOrAdd((LPVOID)pParams, bNew); |
501 | bILStubCreator |= bNew; |
502 | |
503 | if (NULL != phe) |
504 | { |
505 | if (bNew) |
506 | { |
507 | pBlobHolder.SuppressRelease(); |
508 | |
509 | phe->m_pMethodDesc = pMD; |
510 | pBlob = pBlobHolder; |
511 | phe->m_pBlob = pBlob; |
512 | |
513 | _ASSERTE(pParams->m_cbSizeOfBlob == cbSizeOfBlob); |
514 | memcpy(pBlob, pParams, cbSizeOfBlob); |
515 | |
516 | INDEBUG(pszResult = "[missed cache]" ); |
517 | bFireETWCacheHitEvent = false; |
518 | } |
519 | else |
520 | { |
521 | INDEBUG(pszResult = "[hit cache][wasted new MethodDesc due to race]" ); |
522 | } |
523 | pMD = phe->m_pMethodDesc; |
524 | } |
525 | else |
526 | { |
527 | pMD = NULL; |
528 | } |
529 | } |
530 | else |
531 | { |
532 | INDEBUG(pszResult = "[cache disabled for COM->CLR field access stubs]" ); |
533 | } |
534 | } |
535 | |
536 | |
537 | if (!pMD) |
538 | { |
539 | // Couldn't grow hash table due to lack of memory. |
540 | COMPlusThrowOM(); |
541 | } |
542 | |
543 | #ifdef _DEBUG |
544 | CQuickBytes qbManaged; |
545 | PrettyPrintSig(pSig, cbSig, "*" , &qbManaged, pSigModule->GetMDImport(), NULL); |
546 | LOG((LF_STUBS, LL_INFO1000, "ILSTUBCACHE: ILStubCache::GetStubMethodDesc %s StubMD: %p module: %p blob: %p sig: %s\n" , pszResult, pMD, pSigModule, pBlob, qbManaged.Ptr())); |
547 | #endif // _DEBUG |
548 | #endif // DACCESS_COMPILE |
549 | |
550 | RETURN pMD; |
551 | } |
552 | |
553 | void ILStubCache::DeleteEntry(void* pParams) |
554 | { |
555 | CONTRACTL |
556 | { |
557 | NOTHROW; |
558 | GC_NOTRIGGER; |
559 | MODE_ANY; |
560 | } |
561 | CONTRACTL_END; |
562 | CrstHolder ch(&m_crst); |
563 | |
564 | ILCHASHENTRY* phe = NULL; |
565 | |
566 | phe = (ILCHASHENTRY*)Find((LPVOID)pParams); |
567 | if (phe) |
568 | { |
569 | #ifdef _DEBUG |
570 | LOG((LF_STUBS, LL_INFO1000, "ILSTUBCACHE: ILStubCache::DeleteEntry StubMD: %p\n" , phe->m_pMethodDesc)); |
571 | #endif |
572 | |
573 | Delete(pParams); |
574 | } |
575 | } |
576 | |
577 | void ILStubCache::AddMethodDescChunkWithLockTaken(MethodDesc *pMD) |
578 | { |
579 | CONTRACTL |
580 | { |
581 | STANDARD_VM_CHECK; |
582 | |
583 | PRECONDITION(CheckPointer(pMD)); |
584 | } |
585 | CONTRACTL_END; |
586 | |
587 | #ifndef DACCESS_COMPILE |
588 | CrstHolder ch(&m_crst); |
589 | |
590 | pMD->GetMethodTable()->GetClass()->AddChunkIfItHasNotBeenAdded(pMD->GetMethodDescChunk()); |
591 | #endif // DACCESS_COMPILE |
592 | } |
593 | |
594 | //--------------------------------------------------------- |
595 | // Destructor |
596 | //--------------------------------------------------------- |
597 | ILStubCache::~ILStubCache() |
598 | { |
599 | } |
600 | |
601 | |
602 | //***************************************************************************** |
603 | // Hash is called with a pointer to an element in the table. You must override |
604 | // this method and provide a hash algorithm for your element type. |
605 | //***************************************************************************** |
606 | unsigned int ILStubCache::Hash( // The key value. |
607 | void const* pData) // Raw data to hash. |
608 | { |
609 | CONTRACTL |
610 | { |
611 | NOTHROW; |
612 | GC_NOTRIGGER; |
613 | MODE_ANY; |
614 | } |
615 | CONTRACTL_END; |
616 | |
617 | const ILStubHashBlob* pBlob = (const ILStubHashBlob *)pData; |
618 | |
619 | size_t cb = pBlob->m_cbSizeOfBlob - sizeof(ILStubHashBlobBase); |
620 | int hash = 0; |
621 | |
622 | for (size_t i = 0; i < cb; i++) |
623 | { |
624 | hash = _rotl(hash,1) + pBlob->m_rgbBlobData[i]; |
625 | } |
626 | |
627 | return hash; |
628 | } |
629 | |
630 | //***************************************************************************** |
631 | // Compare is used in the typical memcmp way, 0 is eqaulity, -1/1 indicate |
632 | // direction of miscompare. In this system everything is always equal or not. |
633 | //***************************************************************************** |
634 | unsigned int ILStubCache::Compare( // 0, -1, or 1. |
635 | void const* pData, // Raw key data on lookup. |
636 | BYTE* pElement) // The element to compare data against. |
637 | { |
638 | CONTRACTL |
639 | { |
640 | NOTHROW; |
641 | GC_NOTRIGGER; |
642 | MODE_ANY; |
643 | } |
644 | CONTRACTL_END; |
645 | |
646 | const ILStubHashBlob* pBlob1 = (const ILStubHashBlob*)pData; |
647 | const ILStubHashBlob* pBlob2 = (const ILStubHashBlob*)GetKey(pElement); |
648 | size_t cb1 = pBlob1->m_cbSizeOfBlob - sizeof(ILStubHashBlobBase); |
649 | size_t cb2 = pBlob2->m_cbSizeOfBlob - sizeof(ILStubHashBlobBase); |
650 | |
651 | if (cb1 != cb2) |
652 | { |
653 | return 1; // not equal |
654 | } |
655 | else |
656 | { |
657 | // @TODO: use memcmp |
658 | for (size_t i = 0; i < cb1; i++) |
659 | { |
660 | if (pBlob1->m_rgbBlobData[i] != pBlob2->m_rgbBlobData[i]) |
661 | { |
662 | return 1; // not equal |
663 | } |
664 | } |
665 | return 0; // equal |
666 | } |
667 | } |
668 | |
669 | //***************************************************************************** |
670 | // Return true if the element is free to be used. |
671 | //***************************************************************************** |
672 | CClosedHashBase::ELEMENTSTATUS ILStubCache::Status( // The status of the entry. |
673 | BYTE* pElement) // The element to check. |
674 | { |
675 | CONTRACTL |
676 | { |
677 | NOTHROW; |
678 | GC_NOTRIGGER; |
679 | MODE_ANY; |
680 | } |
681 | CONTRACTL_END; |
682 | |
683 | MethodDesc* pMD = ((ILCHASHENTRY*)pElement)->m_pMethodDesc; |
684 | |
685 | if (pMD == NULL) |
686 | { |
687 | return FREE; |
688 | } |
689 | else if (pMD == (MethodDesc*)(-((INT_PTR)1))) |
690 | { |
691 | return DELETED; |
692 | } |
693 | else |
694 | { |
695 | return USED; |
696 | } |
697 | } |
698 | |
699 | //***************************************************************************** |
700 | // Sets the status of the given element. |
701 | //***************************************************************************** |
702 | void ILStubCache::SetStatus( |
703 | BYTE* pElement, // The element to set status for. |
704 | CClosedHashBase::ELEMENTSTATUS eStatus) // New status. |
705 | { |
706 | CONTRACTL |
707 | { |
708 | NOTHROW; |
709 | GC_NOTRIGGER; |
710 | MODE_ANY; |
711 | } |
712 | CONTRACTL_END; |
713 | |
714 | ILCHASHENTRY* phe = (ILCHASHENTRY*)pElement; |
715 | |
716 | switch (eStatus) |
717 | { |
718 | case FREE: phe->m_pMethodDesc = NULL; break; |
719 | case DELETED: phe->m_pMethodDesc = (MethodDesc*)(-((INT_PTR)1)); break; |
720 | default: |
721 | _ASSERTE(!"MLCacheEntry::SetStatus(): Bad argument." ); |
722 | } |
723 | } |
724 | |
725 | //***************************************************************************** |
726 | // Returns the internal key value for an element. |
727 | //***************************************************************************** |
728 | void* ILStubCache::GetKey( // The data to hash on. |
729 | BYTE* pElement) // The element to return data ptr for. |
730 | { |
731 | CONTRACTL |
732 | { |
733 | NOTHROW; |
734 | GC_NOTRIGGER; |
735 | MODE_ANY; |
736 | } |
737 | CONTRACTL_END; |
738 | |
739 | ILCHASHENTRY* phe = (ILCHASHENTRY*)pElement; |
740 | return (void *)(phe->m_pBlob); |
741 | } |
742 | |
743 | #ifdef FEATURE_PREJIT |
744 | |
745 | // ============================================================================ |
746 | // Stub method hash entry methods |
747 | // ============================================================================ |
748 | PTR_MethodDesc StubMethodHashEntry::GetMethod() |
749 | { |
750 | LIMITED_METHOD_DAC_CONTRACT; |
751 | return pMD; |
752 | } |
753 | |
754 | PTR_MethodDesc StubMethodHashEntry::GetStubMethod() |
755 | { |
756 | LIMITED_METHOD_DAC_CONTRACT; |
757 | return pStubMD; |
758 | } |
759 | |
760 | #ifndef DACCESS_COMPILE |
761 | |
762 | void StubMethodHashEntry::SetMethodAndStub(MethodDesc *pMD, MethodDesc *pStubMD) |
763 | { |
764 | LIMITED_METHOD_CONTRACT; |
765 | this->pMD = pMD; |
766 | this->pStubMD = pStubMD; |
767 | } |
768 | |
769 | // ============================================================================ |
770 | // Stub method hash table methods |
771 | // ============================================================================ |
772 | /* static */ StubMethodHashTable *StubMethodHashTable::Create(LoaderAllocator *pAllocator, Module *pModule, DWORD dwNumBuckets, AllocMemTracker *pamTracker) |
773 | { |
774 | CONTRACTL |
775 | { |
776 | THROWS; |
777 | GC_NOTRIGGER; |
778 | INJECT_FAULT(COMPlusThrowOM();); |
779 | } |
780 | CONTRACTL_END |
781 | |
782 | LoaderHeap *pHeap = pAllocator->GetLowFrequencyHeap(); |
783 | StubMethodHashTable *pThis = (StubMethodHashTable *)pamTracker->Track(pHeap->AllocMem((S_SIZE_T)sizeof(StubMethodHashTable))); |
784 | |
785 | new (pThis) StubMethodHashTable(pModule, pHeap, dwNumBuckets); |
786 | |
787 | return pThis; |
788 | } |
789 | |
790 | // Calculate a hash value for a key |
791 | static DWORD Hash(MethodDesc *pMD) |
792 | { |
793 | LIMITED_METHOD_CONTRACT; |
794 | |
795 | DWORD dwHash = 0x87654321; |
796 | #define INST_HASH_ADD(_value) dwHash = ((dwHash << 5) + dwHash) ^ (_value) |
797 | |
798 | INST_HASH_ADD(pMD->GetMemberDef()); |
799 | |
800 | Instantiation inst = pMD->GetClassInstantiation(); |
801 | for (DWORD i = 0; i < inst.GetNumArgs(); i++) |
802 | { |
803 | TypeHandle thArg = inst[i]; |
804 | |
805 | if (thArg.GetMethodTable()) |
806 | { |
807 | INST_HASH_ADD(thArg.GetCl()); |
808 | |
809 | Instantiation sArgInst = thArg.GetInstantiation(); |
810 | for (DWORD j = 0; j < sArgInst.GetNumArgs(); j++) |
811 | { |
812 | TypeHandle thSubArg = sArgInst[j]; |
813 | if (thSubArg.GetMethodTable()) |
814 | INST_HASH_ADD(thSubArg.GetCl()); |
815 | else |
816 | INST_HASH_ADD(thSubArg.GetSignatureCorElementType()); |
817 | } |
818 | } |
819 | else |
820 | INST_HASH_ADD(thArg.GetSignatureCorElementType()); |
821 | } |
822 | |
823 | return dwHash; |
824 | } |
825 | |
826 | MethodDesc *StubMethodHashTable::FindMethodDesc(MethodDesc *pMD) |
827 | { |
828 | CONTRACTL |
829 | { |
830 | NOTHROW; |
831 | GC_NOTRIGGER; |
832 | FORBID_FAULT; |
833 | } |
834 | CONTRACTL_END |
835 | |
836 | MethodDesc *pMDResult = NULL; |
837 | |
838 | DWORD dwHash = Hash(pMD); |
839 | StubMethodHashEntry_t* pSearch; |
840 | LookupContext sContext; |
841 | |
842 | for (pSearch = BaseFindFirstEntryByHash(dwHash, &sContext); |
843 | pSearch != NULL; |
844 | pSearch = BaseFindNextEntryByHash(&sContext)) |
845 | { |
846 | if (pSearch->GetMethod() == pMD) |
847 | { |
848 | pMDResult = pSearch->GetStubMethod(); |
849 | break; |
850 | } |
851 | } |
852 | |
853 | return pMDResult; |
854 | } |
855 | |
856 | // Add method desc to the hash table; must not be present already |
857 | void StubMethodHashTable::InsertMethodDesc(MethodDesc *pMD, MethodDesc *pStubMD) |
858 | { |
859 | CONTRACTL |
860 | { |
861 | THROWS; |
862 | GC_NOTRIGGER; |
863 | INJECT_FAULT(COMPlusThrowOM();); |
864 | PRECONDITION(CheckPointer(pMD)); |
865 | PRECONDITION(CheckPointer(pStubMD)); |
866 | } |
867 | CONTRACTL_END |
868 | |
869 | StubMethodHashEntry_t *pNewEntry = (StubMethodHashEntry_t *)BaseAllocateEntry(NULL); |
870 | pNewEntry->SetMethodAndStub(pMD, pStubMD); |
871 | |
872 | DWORD dwHash = Hash(pMD); |
873 | BaseInsertEntry(dwHash, pNewEntry); |
874 | } |
875 | |
876 | #ifdef FEATURE_NATIVE_IMAGE_GENERATION |
877 | // Save the hash table and any method descriptors referenced by it |
878 | void StubMethodHashTable::Save(DataImage *image, CorProfileData *pProfileData) |
879 | { |
880 | WRAPPER_NO_CONTRACT; |
881 | BaseSave(image, pProfileData); |
882 | } |
883 | |
884 | void StubMethodHashTable::Fixup(DataImage *image) |
885 | { |
886 | WRAPPER_NO_CONTRACT; |
887 | BaseFixup(image); |
888 | } |
889 | |
890 | void StubMethodHashTable::FixupEntry(DataImage *pImage, StubMethodHashEntry_t *pEntry, void *pFixupBase, DWORD cbFixupOffset) |
891 | { |
892 | WRAPPER_NO_CONTRACT; |
893 | pImage->FixupField(pFixupBase, cbFixupOffset + offsetof(StubMethodHashEntry_t, pMD), pEntry->GetMethod()); |
894 | pImage->FixupField(pFixupBase, cbFixupOffset + offsetof(StubMethodHashEntry_t, pStubMD), pEntry->GetStubMethod()); |
895 | } |
896 | |
897 | bool StubMethodHashTable::ShouldSave(DataImage *pImage, StubMethodHashEntry_t *pEntry) |
898 | { |
899 | STANDARD_VM_CONTRACT; |
900 | |
901 | MethodDesc *pMD = pEntry->GetMethod(); |
902 | if (pMD->GetClassification() == mcInstantiated) |
903 | { |
904 | // save entries only for "accepted" methods |
905 | if (!pImage->GetPreloader()->IsMethodInTransitiveClosureOfInstantiations(CORINFO_METHOD_HANDLE(pMD))) |
906 | return false; |
907 | } |
908 | |
909 | // Save the entry only if the native code was successfully generated for the stub |
910 | if (pImage->GetCodeAddress(pEntry->GetStubMethod()) == NULL) |
911 | return false; |
912 | |
913 | return true; |
914 | } |
915 | #endif // FEATURE_NATIVE_IMAGE_GENERATION |
916 | |
917 | #endif // !DACCESS_COMPILE |
918 | |
919 | #ifdef DACCESS_COMPILE |
920 | |
921 | void StubMethodHashTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
922 | { |
923 | SUPPORTS_DAC; |
924 | BaseEnumMemoryRegions(flags); |
925 | } |
926 | |
927 | void StubMethodHashTable::EnumMemoryRegionsForEntry(StubMethodHashEntry_t *pEntry, CLRDataEnumMemoryFlags flags) |
928 | { |
929 | SUPPORTS_DAC; |
930 | if (pEntry->GetMethod().IsValid()) |
931 | pEntry->GetMethod()->EnumMemoryRegions(flags); |
932 | } |
933 | |
934 | #endif // DACCESS_COMPILE |
935 | |
936 | #endif // FEATURE_PREJIT |
937 | |