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.
5 // File: VirtualCallStub.h
11 // See code:VirtualCallStubManager for details
13 // ============================================================================
15 #ifndef _VIRTUAL_CALL_STUB_H
16 #define _VIRTUAL_CALL_STUB_H
18 #ifndef CROSSGEN_COMPILE
22 #if defined(_TARGET_X86_)
23 // If this is uncommented, leaves a file "StubLog_<pid>.log" with statistics on the behavior
24 // of stub-based interface dispatch.
25 //#define STUB_LOGGING
30 /////////////////////////////////////////////////////////////////////////////////////
31 // Forward class declarations
36 class VirtualCallStubManager;
37 class VirtualCallStubManagerManager;
39 struct DispatchHolder;
42 /////////////////////////////////////////////////////////////////////////////////////
43 // Forward function declarations
44 extern "C" void InContextTPQuickDispatchAsmStub();
46 extern "C" PCODE STDCALL StubDispatchFixupWorker(TransitionBlock * pTransitionBlock,
47 TADDR siteAddrForRegisterIndirect,
51 extern "C" PCODE STDCALL VSD_ResolveWorker(TransitionBlock * pTransitionBlock,
52 TADDR siteAddrForRegisterIndirect,
60 /////////////////////////////////////////////////////////////////////////////////////
61 #if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
65 /////////////////////////////////////////////////////////////////////////////////////
66 // Represents the struct that is added to the resolve cache
67 // NOTE: If you change the layout of this struct, you'll need to update various
68 // ASM helpers in VirtualCallStubCpu that rely on offsets of members.
70 struct ResolveCacheElem
73 size_t token; // DispatchToken
76 // These are used for chaining
77 ResolveCacheElem *pNext;
78 ResolveCacheElem *Next()
79 { LIMITED_METHOD_CONTRACT; return VolatileLoad(&pNext); }
86 BOOL Equals(size_t token, void *pMT)
87 { LIMITED_METHOD_CONTRACT; return (this->pMT == pMT && this->token == token); }
89 BOOL Equals(ResolveCacheElem *pElem)
90 { WRAPPER_NO_CONTRACT; return Equals(pElem->token, pElem->pMT); }
96 e_resolveCacheElem_sizeof_mt = sizeof(void *),
97 e_resolveCacheElem_sizeof_token = sizeof(size_t),
98 e_resolveCacheElem_sizeof_target = sizeof(void *),
99 e_resolveCacheElem_sizeof_next = sizeof(ResolveCacheElem *),
101 e_resolveCacheElem_offset_mt = 0,
102 e_resolveCacheElem_offset_token = e_resolveCacheElem_offset_mt + e_resolveCacheElem_sizeof_mt,
103 e_resolveCacheElem_offset_target = e_resolveCacheElem_offset_token + e_resolveCacheElem_sizeof_token,
104 e_resolveCacheElem_offset_next = e_resolveCacheElem_offset_target + e_resolveCacheElem_sizeof_target,
107 /////////////////////////////////////////////////////////////////////////////////////
108 // A utility class to help manipulate a call site
111 friend class VirtualCallStubManager;
115 // On x86 are four possible kinds of callsites when you take into account all features
116 // Relative: direct call, e.g. "call addr". Not used currently.
117 // RelativeIndirect (JmpRel): indirect call through a relative address, e.g. "call [addr]"
118 // RegisterIndirect: indirect call through a register, e.g. "call [eax]"
119 // DelegateCallSite: anything else, tail called through a register by shuffle thunk, e.g. "jmp [eax]"
121 // On all other platforms we always use an indirect call through an indirection cell
122 // In these cases all calls are made by the platform equivalent of "call [addr]".
124 // DelegateCallSite are particular in that they can come in a variety of forms:
125 // a direct delegate call has a sequence defined by the jit but a multicast or secure delegate
126 // are defined in a stub and have a different shape
128 PTR_PCODE m_siteAddr; // Stores the address of an indirection cell
133 #if defined(_TARGET_X86_)
134 StubCallSite(TADDR siteAddrForRegisterIndirect, PCODE returnAddr);
136 PCODE GetCallerAddress();
137 #else // !defined(_TARGET_X86_)
138 // On platforms where we always use an indirection cell things
139 // are much simpler - the siteAddr always stores a pointer to a
140 // value that in turn points to the indirection cell.
142 StubCallSite(TADDR siteAddr, PCODE returnAddr)
143 { LIMITED_METHOD_CONTRACT; m_siteAddr = dac_cast<PTR_PCODE>(siteAddr); m_returnAddr = returnAddr; }
145 PCODE GetCallerAddress() { LIMITED_METHOD_CONTRACT; return m_returnAddr; }
146 #endif // !defined(_TARGET_X86_)
148 PCODE GetSiteTarget() { WRAPPER_NO_CONTRACT; return *(GetIndirectCell()); }
149 void SetSiteTarget(PCODE newTarget);
150 PTR_PCODE GetIndirectCell() { LIMITED_METHOD_CONTRACT; return dac_cast<PTR_PCODE>(m_siteAddr); }
151 PTR_PCODE * GetIndirectCellAddress() { LIMITED_METHOD_CONTRACT; return &m_siteAddr; }
153 PCODE GetReturnAddress() { LIMITED_METHOD_CONTRACT; return m_returnAddr; }
156 #ifdef FEATURE_PREJIT
157 extern "C" void StubDispatchFixupStub(); // for lazy fixup of ngen call sites
160 // These are the assembly language entry points that the stubs use when they want to go into the EE
162 extern "C" void ResolveWorkerAsmStub(); // resolve a token and transfer control to that method
163 extern "C" void ResolveWorkerChainLookupAsmStub(); // for chaining of entries in the cache
166 extern "C" void BackPatchWorkerAsmStub(); // backpatch a call site to point to a different stub
168 extern "C" void BackPatchWorkerStaticStub(PCODE returnAddr, TADDR siteAddrForRegisterIndirect);
169 #endif // FEATURE_PAL
170 #endif // _TARGET_X86_
173 typedef VPTR(class VirtualCallStubManager) PTR_VirtualCallStubManager;
175 // VirtualCallStubManager is the heart of the stub dispatch logic. See the book of the runtime entry
177 // file:../../doc/BookOfTheRuntime/ClassLoader/VirtualStubDispatchDesign.doc
179 // The basic idea is that a call to an interface (it could also be used for virtual calls in general, but we
180 // do not do this), is simply the code
182 // call [DispatchCell]
184 // Where we make sure 'DispatchCell' points at stubs that will do the right thing. DispatchCell is writable
185 // so we can udpate the code over time. There are three basic types of stubs that the dispatch cell can point
187 // * Lookup: The intial stub that has no 'fast path' and simply pushes a ID for interface being called
188 // and calls into the runtime at code:VirtualCallStubManager.ResolveWorkerStatic.
189 // * Dispatch: Lookup stubs are patched to this stub which has a fast path that checks for a particular
190 // Method Table and if that fails jumps to code that
191 // * Decrements a 'missCount' (starts out as code:STUB_MISS_COUNT_VALUE). If this count goes to zero
192 // code:VirtualCallStubManager.BackPatchWorkerStatic is called, morphs it into a resolve stub
193 // (however since this decrementing logic is SHARED among all dispatch stubs, it may take
194 // multiples of code:STUB_MISS_COUNT_VALUE if mulitple call sites are actively polymorphic (this
196 // * Calls a resolve stub (Whenever a dispatch stub is created, it always has a cooresponding resolve
197 // stub (but the resolve stubs are shared among many dispatch stubs).
198 // * Resolve: see code:ResolveStub. This looks up the Method table in a process wide cache (see
199 // code:ResolveCacheElem, and if found, jumps to it. This code path is about 17 instructions long (so
200 // pretty fast, but certainly much slower than a normal call). If the method table is not found in
201 // the cache, it calls into the runtime code:VirtualCallStubManager.ResolveWorkerStatic, which
203 // So the general progression is call site's cells
204 // * start out life pointing to a lookup stub
205 // * On first call they get updated into a dispatch stub. When this misses, it calls a resolve stub,
206 // which populates a resovle stub's cache, but does not update the call site' cell (thus it is still
207 // pointing at the dispatch cell.
208 // * After code:STUB_MISS_COUNT_VALUE misses, we update the call site's cell to point directly at the
209 // resolve stub (thus avoiding the overhead of the quick check that always seems to be failing and
210 // the miss count update).
212 // QUESTION: What is the lifetimes of the various stubs and hash table entries?
214 // QUESTION: There does not seem to be any logic that will change a call site's cell once it becomes a
215 // Resolve stub. Thus once a particular call site becomes a Resolve stub we live with the Resolve stub's
216 // (in)efficiency forever.
218 // see code:#StubDispatchNotes for more
219 class VirtualCallStubManager : public StubManager
221 friend class ClrDataAccess;
222 friend class VirtualCallStubManagerManager;
223 friend class VirtualCallStubManagerIterator;
225 VPTR_VTABLE_CLASS(VirtualCallStubManager, StubManager)
229 virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "VirtualCallStubManager"; }
232 // The reason for our existence, return a callstub for type id and slot number
233 // where type id = 0 for the class contract (i.e. a virtual call), and type id > 0 for an
234 // interface invoke where the id indicates which interface it is.
236 // The function is idempotent, i.e.
237 // you'll get the same callstub twice if you call it with identical inputs.
238 PCODE GetCallStub(TypeHandle ownerType, MethodDesc *pMD);
239 PCODE GetCallStub(TypeHandle ownerType, DWORD slot);
241 // Generate an fresh indirection cell.
242 BYTE* GenerateStubIndirection(PCODE stub, BOOL fUseRecycledCell = FALSE);
244 // Set up static data structures - called during EEStartup
245 static void InitStatic();
246 static void UninitStatic();
248 // Per instance initialization - called during AppDomain::Init and ::Uninit and for collectible loader allocators
249 void Init(BaseDomain* pDomain, LoaderAllocator *pLoaderAllocator);
252 //@TODO: the logging should be tied into the VMs normal loggin mechanisms,
253 //@TODO: for now we just always write a short log file called "StubLog_<pid>.log"
254 static void StartupLogging();
255 static void LoggingDump();
256 static void FinishLogging();
258 static void ResetCache();
260 // Reclaim/rearrange any structures that can only be done during a gc sync point.
261 // This is the mechanism we are using to avoid synchronization of alot of our
262 // cache and hash table accesses. We are requiring that during a gc sync point we are not
263 // executing any stub code at all, hence at this time we are serialized on a single thread (gc)
264 // and no other thread is accessing the data structures.
265 static void ReclaimAll();
268 #ifndef DACCESS_COMPILE
269 VirtualCallStubManager()
273 dispatch_rangeList(),
274 cache_entry_rangeList(),
276 isCollectible(false),
277 m_initialReservedMemForHeaps(NULL),
278 m_FreeIndCellList(NULL),
279 m_RecycledIndCellList(NULL),
281 cache_entry_heap(NULL),
285 #ifdef _TARGET_AMD64_
286 m_fShouldAllocateLongJumpDispatchStubs(FALSE),
293 m_cur_counter_block(NULL),
294 m_cur_counter_block_for_reclaim(NULL),
295 m_cur_counter_block_for_reclaim_index(NULL),
298 LIMITED_METHOD_CONTRACT;
299 ZeroMemory(&stats, sizeof(stats));
302 ~VirtualCallStubManager();
303 #endif // !DACCESS_COMPILE
308 SK_LOOKUP, // Lookup Stubs are SLOW stubs that simply call into the runtime to do all work.
309 SK_DISPATCH, // Dispatch Stubs have a fast check for one type otherwise jumps to runtime. Works for monomorphic sites
310 SK_RESOLVE, // Resolve Stubs do a hash lookup before fallling back to the runtime. Works for polymorphic sites.
314 // peek at the assembly code and predict which kind of a stub we have
315 StubKind predictStubKind(PCODE stubStartAddress);
317 /* know thine own stubs. It is possible that when multiple
318 virtualcallstub managers are built that these may need to become
319 non-static, and the callers modified accordingly */
320 StubKind getStubKind(PCODE stubStartAddress)
325 // This method can called with stubStartAddress==NULL, e.g. when handling null reference exceptions
326 // caused by IP=0. Early out for this case to avoid confusing handled access violations inside predictStubKind.
327 if (PCODEToPINSTR(stubStartAddress) == NULL)
330 // Rather than calling IsInRange(stubStartAddress) for each possible stub kind
331 // we can peek at the assembly code and predict which kind of a stub we have
332 StubKind predictedKind = predictStubKind(stubStartAddress);
334 if (predictedKind == SK_DISPATCH)
336 if (isDispatchingStub(stubStartAddress))
339 else if (predictedKind == SK_LOOKUP)
341 if (isLookupStub(stubStartAddress))
344 else if (predictedKind == SK_RESOLVE)
346 if (isResolvingStub(stubStartAddress))
350 // This is the slow case. If the predict returned SK_UNKNOWN, SK_BREAKPOINT,
351 // or the predict was found to be incorrect when checked against the RangeLists
352 // (isXXXStub), then we'll check each stub heap in sequence.
353 if (isDispatchingStub(stubStartAddress))
355 else if (isLookupStub(stubStartAddress))
357 else if (isResolvingStub(stubStartAddress))
363 inline BOOL isStub(PCODE stubStartAddress)
368 return (getStubKind(stubStartAddress) != SK_UNKNOWN);
371 BOOL isDispatchingStub(PCODE stubStartAddress)
376 return GetDispatchRangeList()->IsInRange(stubStartAddress);
379 BOOL isResolvingStub(PCODE stubStartAddress)
384 return GetResolveRangeList()->IsInRange(stubStartAddress);
387 BOOL isLookupStub(PCODE stubStartAddress)
392 return GetLookupRangeList()->IsInRange(stubStartAddress);
395 static BOOL isDispatchingStubStatic(PCODE addr)
399 FindStubManager(addr, &stubKind);
400 return stubKind == SK_DISPATCH;
403 static BOOL isResolvingStubStatic(PCODE addr)
407 FindStubManager(addr, &stubKind);
408 return stubKind == SK_RESOLVE;
411 static BOOL isLookupStubStatic(PCODE addr)
415 FindStubManager(addr, &stubKind);
416 return stubKind == SK_LOOKUP;
419 //use range lists to track the chunks of memory that are part of each heap
420 LockedRangeList lookup_rangeList;
421 LockedRangeList resolve_rangeList;
422 LockedRangeList dispatch_rangeList;
423 LockedRangeList cache_entry_rangeList;
425 // Get dac-ized pointers to rangelist.
426 RangeList* GetLookupRangeList()
430 TADDR addr = PTR_HOST_MEMBER_TADDR(VirtualCallStubManager, this, lookup_rangeList);
431 return PTR_RangeList(addr);
433 RangeList* GetResolveRangeList()
437 TADDR addr = PTR_HOST_MEMBER_TADDR(VirtualCallStubManager, this, resolve_rangeList);
438 return PTR_RangeList(addr);
440 RangeList* GetDispatchRangeList()
444 TADDR addr = PTR_HOST_MEMBER_TADDR(VirtualCallStubManager, this, dispatch_rangeList);
445 return PTR_RangeList(addr);
447 RangeList* GetCacheEntryRangeList()
450 TADDR addr = PTR_HOST_MEMBER_TADDR(VirtualCallStubManager, this, cache_entry_rangeList);
451 return PTR_RangeList(addr);
456 //allocate and initialize a stub of the desired kind
457 DispatchHolder *GenerateDispatchStub(PCODE addrOfCode,
460 size_t dispatchToken);
462 #ifdef _TARGET_AMD64_
463 // Used to allocate a long jump dispatch stub. See comment around
464 // m_fShouldAllocateLongJumpDispatchStubs for explaination.
465 DispatchHolder *GenerateDispatchStubLong(PCODE addrOfCode,
468 size_t dispatchToken);
471 ResolveHolder *GenerateResolveStub(PCODE addrOfResolver,
473 size_t dispatchToken);
475 LookupHolder *GenerateLookupStub(PCODE addrOfResolver,
476 size_t dispatchToken);
478 template <typename STUB_HOLDER>
479 void AddToCollectibleVSDRangeList(STUB_HOLDER *holder)
483 parentDomain->GetCollectibleVSDRanges()->AddRange(reinterpret_cast<BYTE *>(holder->stub()),
484 reinterpret_cast<BYTE *>(holder->stub()) + holder->stub()->size(),
489 // The resolve cache is static across all AppDomains
490 ResolveCacheElem *GenerateResolveCacheElem(void *addrOfCode,
494 ResolveCacheElem *GetResolveCacheElem(void *pMT,
498 //Given a dispatch token, an object and a method table, determine the
499 //target address to go to. The return value (BOOL) states whether this address
500 //is cacheable or not.
501 static BOOL Resolver(MethodTable * pMT,
503 OBJECTREF * protectedObj,
506 // This can be used to find a target without needing the ability to throw
507 static BOOL TraceResolver(Object *pObj, DispatchToken token, TraceDestination *trace);
510 // Return the MethodDesc corresponding to this token.
511 static MethodDesc *GetRepresentativeMethodDescFromToken(DispatchToken token, MethodTable *pMT);
512 static MethodDesc *GetInterfaceMethodDescFromToken(DispatchToken token);
513 static MethodTable *GetTypeFromToken(DispatchToken token);
515 //This is used to get the token out of a stub
516 static size_t GetTokenFromStub(PCODE stub);
518 //This is used to get the token out of a stub and we know the stub manager and stub kind
519 static size_t GetTokenFromStubQuick(VirtualCallStubManager * pMgr, PCODE stub, StubKind kind);
521 // General utility functions
522 // Quick lookup in the cache. NOTHROW, GC_NOTRIGGER
523 static PCODE CacheLookup(size_t token, UINT16 tokenHash, MethodTable *pMT);
525 // Full exhaustive lookup. THROWS, GC_TRIGGERS
526 static PCODE GetTarget(DispatchToken token, MethodTable *pMT);
529 // Given a dispatch token, return true if the token represents an interface, false if just a slot.
530 static BOOL IsInterfaceToken(DispatchToken token);
532 // Given a dispatch token, return true if the token represents a slot on the target.
533 static BOOL IsClassToken(DispatchToken token);
536 static ResolveCacheElem* __fastcall PromoteChainEntry(ResolveCacheElem *pElem);
539 // Flags used by the non-x86 versions of VSD_ResolveWorker
541 #define SDF_ResolveBackPatch (0x01)
542 #define SDF_ResolvePromoteChain (0x02)
543 #define SDF_ResolveFlags (0x03)
545 // These method needs to call the instance methods.
546 friend PCODE VSD_ResolveWorker(TransitionBlock * pTransitionBlock,
547 TADDR siteAddrForRegisterIndirect,
554 #if defined(_TARGET_X86_) && defined(FEATURE_PAL)
555 friend void BackPatchWorkerStaticStub(PCODE returnAddr, TADDR siteAddrForRegisterIndirect);
558 //These are the entrypoints that the stubs actually end up calling via the
559 // xxxAsmStub methods above
560 static void STDCALL BackPatchWorkerStatic(PCODE returnAddr, TADDR siteAddrForRegisterIndirect);
563 PCODE ResolveWorker(StubCallSite* pCallSite, OBJECTREF *protectedObj, DispatchToken token, StubKind stubKind);
564 void BackPatchWorker(StubCallSite* pCallSite);
566 //Change the callsite to point to stub
567 void BackPatchSite(StubCallSite* pCallSite, PCODE stub);
570 /* the following two public functions are to support tracing or stepping thru
571 stubs via the debugger. */
572 virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
573 virtual BOOL TraceManager(Thread *thread,
574 TraceDestination *trace,
579 LIMITED_METHOD_CONTRACT;
582 retval+=indcell_heap->GetSize();
584 retval+=cache_entry_heap->GetSize();
586 retval+=lookup_heap->GetSize();
588 retval+=dispatch_heap->GetSize();
590 retval+=resolve_heap->GetSize();
595 /* the following two private functions are to support tracing or stepping thru
596 stubs via the debugger. */
597 virtual BOOL DoTraceStub(PCODE stubStartAddress,
598 TraceDestination *trace);
601 // The parent domain of this manager
602 PTR_BaseDomain parentDomain;
605 BYTE * m_initialReservedMemForHeaps;
607 static const UINT32 INDCELLS_PER_BLOCK = 32; // 32 indirection cells per block.
609 CrstExplicitInit m_indCellLock;
611 // List of free indirection cells. The cells were directly allocated from the loader heap
612 // (code:VirtualCallStubManager::GenerateStubIndirection)
613 BYTE * m_FreeIndCellList;
615 // List of recycled indirection cells. The cells were recycled from finalized dynamic methods
616 // (code:LCGMethodResolver::RecycleIndCells).
617 BYTE * m_RecycledIndCellList;
619 #ifndef DACCESS_COMPILE
620 // This methods returns the a free cell from m_FreeIndCellList. It returns NULL if the list is empty.
621 BYTE * GetOneFreeIndCell()
625 return GetOneIndCell(&m_FreeIndCellList);
628 // This methods returns the a recycled cell from m_RecycledIndCellList. It returns NULL if the list is empty.
629 BYTE * GetOneRecycledIndCell()
633 return GetOneIndCell(&m_RecycledIndCellList);
636 // This methods returns the a cell from ppList. It returns NULL if the list is empty.
637 BYTE * GetOneIndCell(BYTE ** ppList)
643 PRECONDITION(CheckPointer(ppList));
644 PRECONDITION(m_indCellLock.OwnedByCurrentThread());
647 BYTE * temp = *ppList;
651 BYTE * pNext = *((BYTE **)temp);
659 // insert a linked list of indirection cells at the beginning of m_FreeIndCellList
660 void InsertIntoFreeIndCellList(BYTE * head, BYTE * tail)
664 InsertIntoIndCellList(&m_FreeIndCellList, head, tail);
667 // insert a linked list of indirection cells at the beginning of ppList
668 void InsertIntoIndCellList(BYTE ** ppList, BYTE * head, BYTE * tail)
673 PRECONDITION(CheckPointer(ppList));
674 PRECONDITION(CheckPointer(head));
675 PRECONDITION(CheckPointer(tail));
676 PRECONDITION(m_indCellLock.OwnedByCurrentThread());
679 BYTE * temphead = *ppList;
680 *((BYTE**)tail) = temphead;
683 #endif // !DACCESS_COMPILE
685 PTR_LoaderHeap indcell_heap; // indirection cells go here
686 PTR_LoaderHeap cache_entry_heap; // resolve cache elem entries go here
687 PTR_LoaderHeap lookup_heap; // lookup stubs go here
688 PTR_LoaderHeap dispatch_heap; // dispatch stubs go here
689 PTR_LoaderHeap resolve_heap; // resolve stubs go here
691 #ifdef _TARGET_AMD64_
692 // When we layout the stub heaps, we put them close together in a sequential order
693 // so that we maximize performance with respect to branch predictions. On AMD64,
694 // dispatch stubs use a rel32 jump on failure to the resolve stub. This works for
695 // a while because of the ordering, but as soon as we have to start allocating more
696 // memory for either the dispatch or resolve heaps we have a chance that we'll be
697 // further away than a rel32 jump can reach, because we're in a 64-bit address
698 // space. As such, this flag will indicate when we allocate the first dispatch stub
699 // that cannot reach a resolve stub, and when this happens we'll switch over to
700 // allocating the larger version of the dispatch stub which contains an abs64 jump.
701 //@TODO: This is a bit of a workaround, but the limitations of LoaderHeap require that we
702 //@TODO: take this approach. Hopefully in Orcas we'll have a chance to rewrite LoaderHeap.
703 BOOL m_fShouldAllocateLongJumpDispatchStubs; // Defaults to FALSE.
706 BucketTable * lookups; // hash table of lookups keyed by tokens
707 BucketTable * cache_entries; // hash table of dispatch token/target structs for dispatch cache
708 BucketTable * dispatchers; // hash table of dispatching stubs keyed by tokens/actualtype
709 BucketTable * resolvers; // hash table of resolvers keyed by tokens/resolverstub
711 // This structure is used to keep track of the fail counters.
712 // We only need one fail counter per ResolveStub,
713 // and most programs use less than 250 ResolveStubs
714 // We allocate these on the main heap using "new counter block"
717 static const UINT32 MAX_COUNTER_ENTRIES = 256-2; // 254 counters should be enough for most cases.
719 counter_block * next; // the next block
720 UINT32 used; // the index of the next free entry
721 INT32 block[MAX_COUNTER_ENTRIES]; // the counters
724 counter_block *m_counters; // linked list of counter blocks of failure counters
725 counter_block *m_cur_counter_block; // current block for updating counts
726 counter_block *m_cur_counter_block_for_reclaim; // current block for updating
727 UINT32 m_cur_counter_block_for_reclaim_index; // index into the current block for updating
729 // Used to keep track of all the VCSManager objects in the system.
730 PTR_VirtualCallStubManager m_pNext; // Linked list pointer
733 // Given a stub address, find the VCSManager that owns it.
734 static VirtualCallStubManager *FindStubManager(PCODE addr,
735 StubKind* wbStubKind = NULL);
737 #ifndef DACCESS_COMPILE
738 // insert a linked list of indirection cells at the beginning of m_RecycledIndCellList
739 void InsertIntoRecycledIndCellList_Locked(BYTE * head, BYTE * tail)
747 CrstHolder lh(&m_indCellLock);
749 InsertIntoIndCellList(&m_RecycledIndCellList, head, tail);
751 #endif // !DACCESS_COMPILE
753 // These are the counters for keeping statistics
756 UINT32 site_counter; //# of call sites
757 UINT32 stub_lookup_counter; //# of lookup stubs
758 UINT32 stub_poly_counter; //# of resolve stubs
759 UINT32 stub_mono_counter; //# of dispatch stubs
760 UINT32 site_write; //# of call site backpatch writes
761 UINT32 site_write_poly; //# of call site backpatch writes to point to resolve stubs
762 UINT32 site_write_mono; //# of call site backpatch writes to point to dispatch stubs
763 UINT32 worker_call; //# of calls into ResolveWorker
764 UINT32 worker_call_no_patch; //# of times call_worker resulted in no patch
765 UINT32 worker_collide_to_mono; //# of times we converted a poly stub to a mono stub instead of writing the cache entry
766 UINT32 stub_space; //# of bytes of stubs
767 UINT32 cache_entry_counter; //# of cache structs
768 UINT32 cache_entry_space; //# of bytes used by cache lookup structs
773 #ifdef DACCESS_COMPILE
775 virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
776 virtual LPCWSTR GetStubManagerName(PCODE addr)
779 CONSISTENCY_CHECK(isStub(addr));
781 if (isLookupStub(addr))
783 return W("VSD_LookupStub");
785 else if (isDispatchingStub(addr))
787 return W("VSD_DispatchStub");
791 CONSISTENCY_CHECK(isResolvingStub(addr));
792 return W("VSD_ResolveStub");
798 /********************************************************************************************************
799 ********************************************************************************************************/
800 typedef VPTR(class VirtualCallStubManagerManager) PTR_VirtualCallStubManagerManager;
802 class VirtualCallStubManagerIterator;
803 class VirtualCallStubManagerManager : public StubManager
805 VPTR_VTABLE_CLASS(VirtualCallStubManagerManager, StubManager)
807 friend class StubManager;
808 friend class VirtualCallStubManager;
809 friend class VirtualCallStubManagerIterator;
810 friend class StubManagerIterator;
813 virtual BOOL TraceManager(Thread *thread, TraceDestination *trace,
814 T_CONTEXT *pContext, BYTE **pRetAddr);
816 virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
818 virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
820 static MethodDesc *Entry2MethodDesc(PCODE stubStartAddress, MethodTable *pMT);
822 #ifdef DACCESS_COMPILE
823 virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
824 virtual LPCWSTR GetStubManagerName(PCODE addr)
825 { WRAPPER_NO_CONTRACT; return FindVirtualCallStubManager(addr)->GetStubManagerName(addr); }
829 // Used to keep track of all the VCSManager objects in the system.
830 PTR_VirtualCallStubManager m_pManagers; // Head of the linked list
832 #ifndef DACCESS_COMPILE
833 // Ctor. This is only used by StaticInit.
834 VirtualCallStubManagerManager();
837 // A cache element to quickly check the last matched manager.
838 Volatile<VirtualCallStubManager*> m_pCacheElem;
840 // RW lock for reading entries and removing them.
841 SimpleRWLock m_RWLock;
843 // This will look through all the managers in an intelligent fashion to
844 // find the manager that owns the address.
845 VirtualCallStubManager *FindVirtualCallStubManager(PCODE stubAddress);
848 // Add a VCSManager to the linked list.
849 void AddStubManager(VirtualCallStubManager *pMgr);
851 // Remove a VCSManager from the linked list.
852 void RemoveStubManager(VirtualCallStubManager *pMgr);
854 VirtualCallStubManager *FirstManager()
855 { WRAPPER_NO_CONTRACT; return m_pManagers; }
857 #ifndef DACCESS_COMPILE
858 static void InitStatic();
862 SPTR_DECL(VirtualCallStubManagerManager, g_pManager);
864 static VirtualCallStubManagerManager *GlobalManager()
865 { LIMITED_METHOD_DAC_CONTRACT; CONSISTENCY_CHECK(CheckPointer(g_pManager)); return g_pManager; }
867 VirtualCallStubManagerIterator IterateVirtualCallStubManagers();
870 // Debug helper to help identify stub-managers.
871 virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "VirtualCallStubManagerManager"; }
875 /********************************************************************************************************
876 ********************************************************************************************************/
877 class VirtualCallStubManagerIterator
879 friend class VirtualCallStubManagerManager;
883 VirtualCallStubManager *Current();
886 inline VirtualCallStubManagerIterator(const VirtualCallStubManagerIterator &it);
889 inline VirtualCallStubManagerIterator(VirtualCallStubManagerManager *pMgr);
892 VirtualCallStubManager *m_pCurMgr;
895 /////////////////////////////////////////////////////////////////////////////////////////////
897 inline VirtualCallStubManagerIterator::VirtualCallStubManagerIterator(VirtualCallStubManagerManager *pMgr)
898 : m_fIsStart(TRUE), m_pCurMgr(pMgr->m_pManagers)
900 LIMITED_METHOD_DAC_CONTRACT;
901 CONSISTENCY_CHECK(CheckPointer(pMgr));
904 /////////////////////////////////////////////////////////////////////////////////////////////
906 inline VirtualCallStubManagerIterator::VirtualCallStubManagerIterator(const VirtualCallStubManagerIterator &it)
907 : m_fIsStart(it.m_fIsStart), m_pCurMgr(it.m_pCurMgr)
909 LIMITED_METHOD_DAC_CONTRACT;
912 /********************************************************************************************************
915 A note on approach. The cache and hash tables used by the stub and lookup mechanism
916 are designed with an eye to minimizing interlocking and/or syncing and/or locking operations.
917 They are intended to run in a highly concurrent environment. Since there is no magic,
918 some tradeoffs and and some implementation constraints are required. The basic notion
919 is that if all reads and writes are atomic and if all functions and operations operate
920 correctly in the face of commutative reorderings of the visibility of all reads and writes
921 across threads, then we don't have to interlock, sync, or serialize. Our approximation of
924 1. All reads and all writes to tables must be atomic. This effectively limits the actual entry
925 size in a table to be a pointer or a pointer sized thing.
927 2. All functions, like comparisons for equality or computation of hash values must function
928 correctly in the face of concurrent updating of the underlying table. This is accomplished
929 by making the underlying structures/entries effectively immutable, if concurrency is in anyway possible.
930 By effectively immutatable, we mean that the stub or token structure is either immutable or that
931 if it is ever written, all possibley concurrent writes are attempting to write the same value (atomically)
932 or that the competing (atomic) values do not affect correctness, and that the function operates correctly whether
933 or not any of the writes have taken place (is visible yet). The constraint we maintain is that all competeing
934 updates (and their visibility or lack thereof) do not alter the correctness of the program.
936 3. All tables are inexact. The counts they hold (e.g. number of contained entries) may be inaccurrate,
937 but that inaccurracy cannot affect their correctness. Table modifications, such as insertion of
938 an new entry may not succeed, but such failures cannot affect correctness. This implies that just
939 because a stub/entry is not present in a table, e.g. has been removed, that does not mean that
940 it is not in use. It also implies that internal table structures, such as discarded hash table buckets,
941 cannot be freely recycled since another concurrent thread may still be walking thru it.
943 4. Occassionaly it is necessary to pick up the pieces that have been dropped on the floor
944 so to speak, e.g. actually recycle hash buckets that aren't in use. Since we have a natural
945 sync point already in the GC, we use that to provide cleanup points. We need to make sure that code that
946 is walking our structures is not a GC safe point. Hence if the GC calls back into us inside the GC
947 sync point, we know that nobody is inside our stuctures and we can safely rearrange and recycle things.
948 ********************************************************************************************************/
950 //initial and increment value for fail stub counters
952 extern UINT32 STUB_MISS_COUNT_VALUE;
953 extern UINT32 STUB_COLLIDE_WRITE_PCT;
954 extern UINT32 STUB_COLLIDE_MONO_PCT;
955 #else // !STUB_LOGGING
956 #define STUB_MISS_COUNT_VALUE 100
957 #define STUB_COLLIDE_WRITE_PCT 100
958 #define STUB_COLLIDE_MONO_PCT 0
959 #endif // !STUB_LOGGING
961 //size and mask of the cache used by resolve stubs
962 // CALL_STUB_CACHE_SIZE must be equal to 2^CALL_STUB_CACHE_NUM_BITS
963 #define CALL_STUB_CACHE_NUM_BITS 12 //10
964 #define CALL_STUB_CACHE_SIZE 4096 //1024
965 #define CALL_STUB_CACHE_MASK (CALL_STUB_CACHE_SIZE-1)
966 #define CALL_STUB_CACHE_PROBES 5
967 //min sizes for BucketTable and buckets and the growth and hashing constants
968 #define CALL_STUB_MIN_BUCKETS 32
969 #define CALL_STUB_MIN_ENTRIES 4
970 //this is so that the very first growth will jump from 4 to 32 entries, then double from there.
971 #define CALL_STUB_SECONDARY_ENTRIES 8
972 #define CALL_STUB_GROWTH_FACTOR 2
973 #define CALL_STUB_LOAD_FACTOR 90
974 #define CALL_STUB_HASH_CONST1 1327
975 #define CALL_STUB_HASH_CONST2 43627
976 #define LARGE_PRIME 7199369
977 //internal layout of buckets=size-1,count,entries....
978 #define CALL_STUB_MASK_INDEX 0
979 #define CALL_STUB_COUNT_INDEX 1
980 #define CALL_STUB_DEAD_LINK 2
981 #define CALL_STUB_FIRST_INDEX 3
982 //marker entries in cache and hash tables
983 #define CALL_STUB_EMPTY_ENTRY 0
984 // number of successes for a chained element before it gets moved to the front
985 #define CALL_STUB_CACHE_INITIAL_SUCCESS_COUNT (0x100)
987 /*******************************************************************************************************
988 Entry is an abstract class. We will make specific subclasses for each kind of
989 entry. Entries hold references to stubs or tokens. The principle thing they provide
990 is a virtual Equals function that is used by the caching and hashing tables within which
991 the stubs and tokens are stored. Entries are typically stack allocated by the routines
992 that call into the hash and caching functions, and the functions stuff stubs into the entry
993 to do the comparisons. Essentially specific entry subclasses supply a vtable to a stub
994 as and when needed. This means we don't have to have vtables attached to stubs.
996 Summarizing so far, there is a struct for each kind of stub or token of the form XXXXStub.
997 They provide that actual storage layouts.
998 There is a stuct in which each stub which has code is containted of the form XXXXHolder.
999 They provide alignment and anciliary storage for the stub code.
1000 There is a subclass of Entry for each kind of stub or token, of the form XXXXEntry.
1001 They provide the specific implementations of the virtual functions declared in Entry. */
1005 //access and compare the keys of the entry
1006 virtual BOOL Equals(size_t keyA, size_t keyB)=0;
1007 virtual size_t KeyA()=0;
1008 virtual size_t KeyB()=0;
1010 //contents is the struct or token that the entry exposes
1011 virtual void SetContents(size_t contents)=0;
1014 /* define the platform specific Stubs and stub holders */
1016 #include <virtualcallstubcpu.hpp>
1018 #if USES_LOOKUP_STUBS
1019 /**********************************************************************************************
1020 LookupEntry wraps LookupStubs and provide the concrete implementation of the abstract class Entry.
1021 Virtual and interface call sites when they are first jitted point to LookupStubs. The hash table
1022 that contains look up stubs is keyed by token, hence the Equals function uses the embedded token in
1023 the stub for comparison purposes. Since we are willing to allow duplicates in the hash table (as
1024 long as they are relatively rare) we do use direct comparison of the tokens rather than extracting
1025 the fields from within the tokens, for perf reasons. */
1026 class LookupEntry : public Entry
1029 //Creates an entry that wraps lookup stub s
1030 LookupEntry(size_t s)
1032 LIMITED_METHOD_CONTRACT;
1033 _ASSERTE(VirtualCallStubManager::isLookupStubStatic((PCODE)s));
1034 stub = (LookupStub*) s;
1037 //default contructor to allow stack and inline allocation of lookup entries
1038 LookupEntry() {LIMITED_METHOD_CONTRACT; stub = NULL;}
1040 //implementations of abstract class Entry
1041 BOOL Equals(size_t keyA, size_t keyB)
1042 { WRAPPER_NO_CONTRACT; return stub && (keyA == KeyA()) && (keyB == KeyB()); }
1044 size_t KeyA() { WRAPPER_NO_CONTRACT; return Token(); }
1045 size_t KeyB() { WRAPPER_NO_CONTRACT; return (size_t)0; }
1047 void SetContents(size_t contents)
1049 LIMITED_METHOD_CONTRACT;
1050 _ASSERTE(VirtualCallStubManager::isLookupStubStatic((PCODE)contents));
1051 stub = LookupHolder::FromLookupEntry((PCODE)contents)->stub();
1054 //extract the token of the underlying lookup stub
1056 inline size_t Token() { LIMITED_METHOD_CONTRACT; return stub ? stub->token() : 0; }
1059 LookupStub* stub; //the stub the entry wrapping
1061 #endif // USES_LOOKUP_STUBS
1063 /**********************************************************************************************
1064 ResolveCacheEntry wraps a ResolveCacheElem and provides lookup functionality for entries that
1065 were created that may be added to the ResolveCache
1067 class ResolveCacheEntry : public Entry
1070 ResolveCacheEntry(size_t elem)
1072 LIMITED_METHOD_CONTRACT;
1073 _ASSERTE(elem != 0);
1074 pElem = (ResolveCacheElem*) elem;
1077 //default contructor to allow stack and inline allocation of lookup entries
1078 ResolveCacheEntry() { LIMITED_METHOD_CONTRACT; pElem = NULL; }
1080 //access and compare the keys of the entry
1081 virtual BOOL Equals(size_t keyA, size_t keyB)
1082 { WRAPPER_NO_CONTRACT; return pElem && (keyA == KeyA()) && (keyB == KeyB()); }
1083 virtual size_t KeyA()
1084 { LIMITED_METHOD_CONTRACT; return pElem != NULL ? pElem->token : 0; }
1085 virtual size_t KeyB()
1086 { LIMITED_METHOD_CONTRACT; return pElem != NULL ? (size_t) pElem->pMT : 0; }
1088 //contents is the struct or token that the entry exposes
1089 virtual void SetContents(size_t contents)
1091 LIMITED_METHOD_CONTRACT;
1092 pElem = (ResolveCacheElem*) contents;
1095 inline const BYTE *Target()
1097 LIMITED_METHOD_CONTRACT;
1098 return pElem != NULL ? (const BYTE *)pElem->target : NULL;
1102 ResolveCacheElem *pElem;
1105 /**********************************************************************************************
1106 ResolveEntry wraps ResolveStubs and provide the concrete implementation of the abstract class Entry.
1107 Polymorphic call sites and monomorphic calls that fail end up in a ResolveStub. Resolve stubs
1108 are stored in hash tables keyed by token, hence the Equals function uses the embedded token in
1109 the stub for comparison purposes. Since we are willing to allow duplicates in the hash table (as
1110 long as they are relatively rare) we do use direct comparison of the tokens rather than extracting
1111 the fields from within the tokens, for perf reasons. */
1112 class ResolveEntry : public Entry
1115 //Creates an entry that wraps resolve stub s
1116 ResolveEntry (size_t s)
1118 LIMITED_METHOD_CONTRACT;
1119 _ASSERTE(VirtualCallStubManager::isResolvingStubStatic((PCODE)s));
1120 stub = (ResolveStub*) s;
1122 //default contructor to allow stack and inline allocation of resovler entries
1123 ResolveEntry() { LIMITED_METHOD_CONTRACT; stub = CALL_STUB_EMPTY_ENTRY; }
1125 //implementations of abstract class Entry
1126 inline BOOL Equals(size_t keyA, size_t keyB)
1127 { WRAPPER_NO_CONTRACT; return stub && (keyA == KeyA()) && (keyB == KeyB()); }
1128 inline size_t KeyA() { WRAPPER_NO_CONTRACT; return Token(); }
1129 inline size_t KeyB() { WRAPPER_NO_CONTRACT; return (size_t)0; }
1131 void SetContents(size_t contents)
1133 LIMITED_METHOD_CONTRACT;
1134 _ASSERTE(VirtualCallStubManager::isResolvingStubStatic((PCODE)contents));
1135 stub = ResolveHolder::FromResolveEntry((PCODE)contents)->stub();
1137 //extract the token of the underlying resolve stub
1138 inline size_t Token() { WRAPPER_NO_CONTRACT; return stub ? (size_t)(stub->token()) : 0; }
1140 ResolveStub* stub; //the stub the entry is wrapping
1143 /**********************************************************************************************
1144 DispatchEntry wraps DispatchStubs and provide the concrete implementation of the abstract class Entry.
1145 Monomorphic and mostly monomorphic call sites eventually point to DispatchStubs. Dispatch stubs
1146 are placed in hash and cache tables keyed by the expected Method Table and token they are built for.
1147 Since we are willing to allow duplicates in the hash table (as long as they are relatively rare)
1148 we do use direct comparison of the tokens rather than extracting the fields from within the tokens,
1150 class DispatchEntry : public Entry
1153 //Creates an entry that wraps dispatch stub s
1154 DispatchEntry (size_t s)
1156 LIMITED_METHOD_CONTRACT;
1157 _ASSERTE(VirtualCallStubManager::isDispatchingStubStatic((PCODE)s));
1158 stub = (DispatchStub*) s;
1160 //default contructor to allow stack and inline allocation of resovler entries
1161 DispatchEntry() { LIMITED_METHOD_CONTRACT; stub = CALL_STUB_EMPTY_ENTRY; }
1163 //implementations of abstract class Entry
1164 inline BOOL Equals(size_t keyA, size_t keyB)
1165 { WRAPPER_NO_CONTRACT; return stub && (keyA == KeyA()) && (keyB == KeyB()); }
1166 inline size_t KeyA() { WRAPPER_NO_CONTRACT; return Token(); }
1167 inline size_t KeyB() { WRAPPER_NO_CONTRACT; return ExpectedMT();}
1169 void SetContents(size_t contents)
1171 LIMITED_METHOD_CONTRACT;
1172 _ASSERTE(VirtualCallStubManager::isDispatchingStubStatic((PCODE)contents));
1173 stub = DispatchHolder::FromDispatchEntry((PCODE)contents)->stub();
1176 //extract the fields of the underlying dispatch stub
1177 inline size_t ExpectedMT()
1178 { WRAPPER_NO_CONTRACT; return stub ? (size_t)(stub->expectedMT()) : 0; }
1182 WRAPPER_NO_CONTRACT;
1185 ResolveHolder * resolveHolder = ResolveHolder::FromFailEntry(stub->failTarget());
1186 size_t token = resolveHolder->stub()->token();
1187 _ASSERTE(token == VirtualCallStubManager::GetTokenFromStub((PCODE)stub));
1196 inline PCODE Target()
1197 { WRAPPER_NO_CONTRACT; return stub ? stub->implTarget() : 0; }
1203 /*************************************************************************************************
1204 DispatchCache is the cache table that the resolve stubs use for inline polymorphic resolution
1205 of a call. The cache entry is logically a triplet of (method table, token, impl address) where method table
1206 is the type of the calling frame's <this>, token identifies the method being invoked,
1207 i.e. is a (type id,slot #) pair, and impl address is the address of the method implementation.
1212 static const UINT16 INVALID_HASH = (UINT16)(-1);
1216 //read and write the cache keyed by (method table,token) pair.
1217 inline ResolveCacheElem* Lookup(size_t token, void* mt)
1218 { WRAPPER_NO_CONTRACT; return Lookup(token, INVALID_HASH, mt);}
1220 ResolveCacheElem* Lookup(size_t token, UINT16 tokenHash, void* mt);
1222 enum InsertKind {IK_NONE, IK_DISPATCH, IK_RESOLVE, IK_SHARED, IK_EXTERNAL};
1224 BOOL Insert(ResolveCacheElem* elem, InsertKind insertKind);
1226 void PromoteChainEntry(ResolveCacheElem* elem);
1229 // This is the heavyweight hashing algorithm. Use sparingly.
1230 static UINT16 HashToken(size_t token);
1232 inline void GetLoadFactor(size_t *total, size_t *used)
1234 LIMITED_METHOD_CONTRACT;
1236 *total = CALL_STUB_CACHE_SIZE;
1238 for (size_t i = 0; i < CALL_STUB_CACHE_SIZE; i++)
1239 if (cache[i] != empty)
1244 inline void *GetCacheBaseAddr()
1245 { LIMITED_METHOD_CONTRACT; return &cache[0]; }
1246 inline size_t GetCacheCount()
1247 { LIMITED_METHOD_CONTRACT; return CALL_STUB_CACHE_SIZE; }
1248 inline ResolveCacheElem *GetCacheEntry(size_t idx)
1249 { LIMITED_METHOD_CONTRACT; return VolatileLoad(&cache[idx]); }
1250 inline BOOL IsCacheEntryEmpty(size_t idx)
1251 { LIMITED_METHOD_CONTRACT; return cache[idx] == empty; }
1253 inline void SetCacheEntry(size_t idx, ResolveCacheElem *elem)
1255 LIMITED_METHOD_CONTRACT;
1257 cacheData[idx].numWrites++;
1260 CONSISTENCY_CHECK(m_writeLock.OwnedByCurrentThread());
1265 inline void ClearCacheEntry(size_t idx)
1267 LIMITED_METHOD_CONTRACT;
1269 cacheData[idx].numClears++;
1276 UINT32 insert_cache_external; //# of times Insert was called for IK_EXTERNAL
1277 UINT32 insert_cache_shared; //# of times Insert was called for IK_SHARED
1278 UINT32 insert_cache_dispatch; //# of times Insert was called for IK_DISPATCH
1279 UINT32 insert_cache_resolve; //# of times Insert was called for IK_RESOLVE
1280 UINT32 insert_cache_hit; //# of times Insert found an empty cache entry
1281 UINT32 insert_cache_miss; //# of times Insert already had a matching cache entry
1282 UINT32 insert_cache_collide; //# of times Insert found a used cache entry
1283 UINT32 insert_cache_write; //# of times Insert wrote a cache entry
1288 // Unlocked iterator of entries. Use only when read/write access to the cache
1289 // is safe. This would typically be at GC sync points, currently needed during
1290 // appdomain unloading.
1294 Iterator(DispatchCache *pCache);
1295 inline BOOL IsValid()
1296 { WRAPPER_NO_CONTRACT; return (m_curBucket < (INT32)m_pCache->GetCacheCount()); }
1298 // Unlink the current entry.
1299 // **NOTE** Using this method implicitly performs a call to Next to move
1300 // past the unlinked entry. Thus, one could accidentally skip
1301 // entries unless you take this into consideration.
1302 ResolveCacheElem *UnlinkEntry();
1303 inline ResolveCacheElem *Entry()
1304 { LIMITED_METHOD_CONTRACT; CONSISTENCY_CHECK(IsValid()); return *m_ppCurElem; }
1307 void NextValidBucket();
1308 inline void NextBucket()
1309 { LIMITED_METHOD_CONTRACT; m_curBucket++; m_ppCurElem = &m_pCache->cache[m_curBucket]; }
1311 DispatchCache *m_pCache;
1313 ResolveCacheElem **m_ppCurElem;
1321 //the following hash computation is also inlined in the resolve stub in asm (SO NO TOUCHIE)
1322 inline static UINT16 HashMT(UINT16 tokenHash, void* mt)
1324 LIMITED_METHOD_CONTRACT;
1328 size_t mtHash = (size_t) mt;
1329 mtHash = (((mtHash >> CALL_STUB_CACHE_NUM_BITS) + mtHash) >> LOG2_PTRSIZE) & CALL_STUB_CACHE_MASK;
1330 hash = (UINT16) mtHash;
1332 hash ^= (tokenHash & CALL_STUB_CACHE_MASK);
1337 ResolveCacheElem* cache[CALL_STUB_CACHE_SIZE]; //must be first
1338 ResolveCacheElem* empty; //empty entry, initialized to fail all comparisons
1341 struct CacheEntryData {
1345 CacheEntryData cacheData[CALL_STUB_CACHE_SIZE];
1346 #endif // STUB_LOGGING
1349 /**************************************************************************************************
1350 The hash tables are accessed via instances of the Prober. Prober is a probe into a bucket
1351 of the hash table, and therefore has an index which is the current probe position.
1352 It includes a count of the number of probes done in that bucket so far and a stride
1353 to step thru the bucket with. To do comparisons, it has a reference to an entry with which
1354 it can do comparisons (Equals(...)) of the entries (stubs) inside the hash table. It also has
1355 the key pair (keyA, keyB) that it is looking for.
1357 Typically, an entry of the appropriate type is created on the stack and then the prober is created passing
1358 in a reference to the entry. The prober is used for a complete operation, such as look for and find an
1359 entry (stub), creating and inserting it as necessary.
1361 The initial index and the stride are orthogonal hashes of the key pair, i.e. we are doing a varient of
1362 double hashing. When we initialize the prober (see FormHash below) we set the initial probe based on
1363 one hash. The stride (used as a modulo addition of the probe position) is based on a different hash and
1364 is such that it will vist every location in the bucket before repeating. Hence it is imperative that
1365 the bucket size and the stride be relative prime wrt each other. We have chosen to make bucket sizes
1366 a power of 2, so we force stride to be odd.
1368 Note -- it must be assumed that multiple probers are walking the same tables and buckets at the same time.
1369 Additionally, the counts may not be accurate, and there may be duplicates in the tables. Since the tables
1370 do not allow concurrrent deletion, some of the concurrency issues are ameliorated.
1374 friend class FastTable;
1375 friend class BucketTable;
1377 Prober(Entry* e) {LIMITED_METHOD_CONTRACT; comparer = e;}
1378 //find the requested entry, if not there return CALL_STUB_EMPTY_ENTRY
1380 //add the entry into the bucket, if it is not already in the bucket.
1381 //return the entry actually in the bucket (existing or added)
1382 size_t Add(size_t entry);
1384 //return the bucket (FastTable*) that the prober is currently walking
1385 inline size_t* items() {LIMITED_METHOD_CONTRACT; return &base[-CALL_STUB_FIRST_INDEX];}
1386 //are there more probes possible, or have we probed everything in the bucket
1387 inline BOOL NoMore() {LIMITED_METHOD_CONTRACT; return probes>mask;} //both probes and mask are (-1)
1388 //advance the probe to a new place in the bucket
1391 WRAPPER_NO_CONTRACT;
1392 index = (index + stride) & mask;
1396 inline size_t Read()
1398 LIMITED_METHOD_CONTRACT;
1400 return VolatileLoad(&base[index]);
1402 //initialize a prober across a bucket (table) for the specified keys.
1403 void InitProber(size_t key1, size_t key2, size_t* table);
1404 //set up the initial index and stride and probe count
1405 inline void FormHash()
1407 LIMITED_METHOD_CONTRACT;
1410 //these two hash functions have not been formally measured for effectiveness
1411 //but they are at least orthogonal
1413 size_t a = ((keyA>>16) + keyA);
1414 size_t b = ((keyB>>16) ^ keyB);
1415 index = (((a*CALL_STUB_HASH_CONST1)>>4)+((b*CALL_STUB_HASH_CONST2)>>4)+CALL_STUB_HASH_CONST1) & mask;
1416 stride = ((a+(b*CALL_STUB_HASH_CONST1)+CALL_STUB_HASH_CONST2) | 1) & mask;
1418 //atomically grab an empty slot so we can insert a new entry into the bucket
1419 BOOL GrabEntry(size_t entryValue);
1420 size_t keyA; //key pair we are looking for
1422 size_t* base; //we have our own pointer to the bucket, so races don't matter.
1423 // We won't care if we do the lookup in an
1424 // outdated bucket (has grown out from under us).
1425 // All that will happen is possibly dropping an entry
1426 // on the floor or adding a duplicate.
1427 size_t index; //current probe point in the bucket
1428 size_t stride; //amount to step on each successive probe, must be relatively prime wrt the bucket size
1429 size_t mask; //size of bucket - 1
1430 size_t probes; //number probes - 1
1431 Entry* comparer;//used to compare an entry against the sought after key pair
1434 /********************************************************************************************************
1435 FastTable is used to implement the buckets of a BucketTable, a bucketized hash table. A FastTable is
1436 an array of entries (contents). The first two slots of contents store the size-1 and count of entries
1437 actually in the FastTable. Note that the count may be inaccurate and there may be duplicates. Careful
1438 attention must be paid to eliminate the need for interlocked or serialized or locked operations in face
1442 #pragma warning(push)
1443 #pragma warning(disable : 4200) // disable zero-sized array warning
1447 friend class BucketTable;
1450 FastTable() { LIMITED_METHOD_CONTRACT; }
1451 ~FastTable() { LIMITED_METHOD_CONTRACT; }
1453 //initialize a prober for the specified keys.
1454 inline BOOL SetUpProber(size_t keyA, size_t keyB, Prober* probe)
1464 probe->InitProber(keyA, keyB, &contents[0]);
1467 //find the requested entry (keys of prober), if not there return CALL_STUB_EMPTY_ENTRY
1468 size_t Find(Prober* probe);
1469 //add the entry, if it is not already there. Probe is used to search.
1470 //Return the entry actually containted (existing or added)
1471 size_t Add(size_t entry, Prober* probe);
1472 void IncrementCount();
1474 // Create a FastTable with space for numberOfEntries. Please note that this method
1475 // does not throw on OOM. **YOU MUST CHECK FOR NULL RETURN**
1476 static FastTable* MakeTable(size_t numberOfEntries)
1481 INJECT_FAULT(COMPlusThrowOM(););
1484 size_t size = CALL_STUB_MIN_ENTRIES;
1485 while (size < numberOfEntries) {size = size<<1;}
1486 // if (size == CALL_STUB_MIN_ENTRIES)
1488 size_t* bucket = new size_t[(sizeof(FastTable)/sizeof(size_t))+size+CALL_STUB_FIRST_INDEX];
1489 FastTable* table = new (bucket) FastTable();
1490 table->InitializeContents(size);
1493 //Initialize as empty
1494 void InitializeContents(size_t size)
1496 LIMITED_METHOD_CONTRACT;
1497 memset(&contents[0], CALL_STUB_EMPTY_ENTRY, (size+CALL_STUB_FIRST_INDEX)*sizeof(BYTE*));
1498 contents[CALL_STUB_MASK_INDEX] = size-1;
1500 inline size_t tableMask() {LIMITED_METHOD_CONTRACT; return (size_t) (contents[CALL_STUB_MASK_INDEX]);}
1501 inline size_t tableSize() {LIMITED_METHOD_CONTRACT; return tableMask()+1;}
1502 inline size_t tableCount() {LIMITED_METHOD_CONTRACT; return (size_t) (contents[CALL_STUB_COUNT_INDEX]);}
1503 inline BOOL isFull()
1505 LIMITED_METHOD_CONTRACT;
1506 return (tableCount()+1) * 100 / CALL_STUB_LOAD_FACTOR >= tableSize();
1508 //we store (size-1) in bucket[CALL_STUB_MASK_INDEX==0],
1509 //we store the used count in bucket[CALL_STUB_COUNT_INDEX==1],
1510 //we have an unused cell to use as a temp at bucket[CALL_STUB_DEAD_LINK==2],
1511 //and the table starts at bucket[CALL_STUB_FIRST_INDEX==3],
1515 #pragma warning(pop)
1518 /******************************************************************************************************
1519 BucketTable is a bucketized hash table. It uses FastTables for its buckets. The hash tables
1520 used by the VirtualCallStubManager are BucketTables. The number of buckets is fixed at the time
1521 the table is created. The actual buckets are allocated as needed, and grow as necessary. The reason
1522 for using buckets is primarily to reduce the cost of growing, since only a single bucket is actually
1523 grown at any given time. Since the hash tables are accessed infrequently, the load factor that
1524 controls growth is quite high (90%). Since we use hashing to pick the bucket, and we use hashing to
1525 lookup inside the bucket, it is important that the hashing function used here is orthogonal to the ones
1526 used in the buckets themselves (see FastTable::FormHash).
1531 BucketTable(size_t numberOfBuckets)
1533 WRAPPER_NO_CONTRACT;
1534 size_t size = CALL_STUB_MIN_BUCKETS;
1535 while (size < numberOfBuckets) {size = size<<1;}
1536 buckets = AllocateBuckets(size);
1537 // Initialize statistics counters
1538 memset(&stats, 0, sizeof(stats));
1543 LIMITED_METHOD_CONTRACT;
1546 size_t size = bucketCount()+CALL_STUB_FIRST_INDEX;
1547 for(size_t ix = CALL_STUB_FIRST_INDEX; ix < size; ix++) delete (FastTable*)(buckets[ix]);
1552 //initialize a prober for the specified keys.
1553 BOOL SetUpProber(size_t keyA, size_t keyB, Prober *prober);
1554 //find the requested entry (keys of prober), if not there return CALL_STUB_EMPTY_ENTRY
1555 inline size_t Find(Prober* probe) {WRAPPER_NO_CONTRACT; return probe->Find();}
1556 //add the entry, if it is not already there. Probe is used to search.
1557 size_t Add(size_t entry, Prober* probe);
1558 //reclaim abandoned buckets. Buckets are abaondoned when they need to grow.
1559 //needs to be called inside a gc sync point.
1560 static void Reclaim();
1564 UINT32 bucket_space; //# of bytes in caches and tables, not including the stubs themselves
1565 UINT32 bucket_space_dead; //# of bytes of abandoned buckets not yet recycled.
1571 inline size_t bucketMask() {LIMITED_METHOD_CONTRACT; return (size_t) (buckets[CALL_STUB_MASK_INDEX]);}
1572 inline size_t bucketCount() {LIMITED_METHOD_CONTRACT; return bucketMask()+1;}
1573 inline size_t ComputeBucketIndex(size_t keyA, size_t keyB)
1575 LIMITED_METHOD_CONTRACT;
1576 size_t a = ((keyA>>16) + keyA);
1577 size_t b = ((keyB>>16) ^ keyB);
1578 return CALL_STUB_FIRST_INDEX+(((((a*CALL_STUB_HASH_CONST2)>>5)^((b*CALL_STUB_HASH_CONST1)>>5))+CALL_STUB_HASH_CONST2) & bucketMask());
1580 //grows the bucket referenced by probe.
1581 BOOL GetMoreSpace(const Prober* probe);
1582 //creates storage in which to store references to the buckets
1583 static size_t* AllocateBuckets(size_t size)
1585 LIMITED_METHOD_CONTRACT;
1586 size_t* buckets = new size_t[size+CALL_STUB_FIRST_INDEX];
1587 if (buckets != NULL)
1589 memset(&buckets[0], CALL_STUB_EMPTY_ENTRY, (size+CALL_STUB_FIRST_INDEX)*sizeof(void*));
1590 buckets[CALL_STUB_MASK_INDEX] = size-1;
1594 inline size_t Read(size_t index)
1596 LIMITED_METHOD_CONTRACT;
1597 CONSISTENCY_CHECK(index <= bucketMask()+CALL_STUB_FIRST_INDEX);
1598 return VolatileLoad(&buckets[index]);
1602 #pragma warning(disable: 4267) //work-around for the compiler
1604 inline void Write(size_t index, size_t value)
1606 LIMITED_METHOD_CONTRACT;
1607 CONSISTENCY_CHECK(index <= bucketMask()+CALL_STUB_FIRST_INDEX);
1608 VolatileStore(&buckets[index], value);
1611 #pragma warning(default: 4267)
1614 // We store (#buckets-1) in bucket[CALL_STUB_MASK_INDEX ==0]
1615 // We have two unused cells at bucket[CALL_STUB_COUNT_INDEX ==1]
1616 // and bucket[CALL_STUB_DEAD_LINK ==2]
1617 // and the table starts at bucket[CALL_STUB_FIRST_INDEX ==3]
1618 // the number of elements is bucket[CALL_STUB_MASK_INDEX]+CALL_STUB_FIRST_INDEX
1620 static FastTable* dead; //linked list head of to be deleted (abandoned) buckets
1623 #endif // !CROSSGEN_COMPILE
1625 #endif // !_VIRTUAL_CALL_STUB_H