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
22const char* FormatSig(MethodDesc* pMD, LoaderHeap *pHeap, AllocMemTracker *pamTracker);
23
24ILStubCache::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
42void 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
53void 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
85MethodDesc* 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
141MethodDesc* 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//
327MethodTable* 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
422MethodDesc* 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
553void 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
577void 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//---------------------------------------------------------
597ILStubCache::~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//*****************************************************************************
606unsigned 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//*****************************************************************************
634unsigned 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//*****************************************************************************
672CClosedHashBase::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//*****************************************************************************
702void 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//*****************************************************************************
728void* 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// ============================================================================
748PTR_MethodDesc StubMethodHashEntry::GetMethod()
749{
750 LIMITED_METHOD_DAC_CONTRACT;
751 return pMD;
752}
753
754PTR_MethodDesc StubMethodHashEntry::GetStubMethod()
755{
756 LIMITED_METHOD_DAC_CONTRACT;
757 return pStubMD;
758}
759
760#ifndef DACCESS_COMPILE
761
762void 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
791static 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
826MethodDesc *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
857void 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
878void StubMethodHashTable::Save(DataImage *image, CorProfileData *pProfileData)
879{
880 WRAPPER_NO_CONTRACT;
881 BaseSave(image, pProfileData);
882}
883
884void StubMethodHashTable::Fixup(DataImage *image)
885{
886 WRAPPER_NO_CONTRACT;
887 BaseFixup(image);
888}
889
890void 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
897bool 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
921void StubMethodHashTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
922{
923 SUPPORTS_DAC;
924 BaseEnumMemoryRegions(flags);
925}
926
927void 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