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: genericdict.h
6//
7
8//
9// Definitions for "dictionaries" used to encapsulate generic instantiations
10// and instantiation-specific information for shared-code generics
11//
12
13//
14// ============================================================================
15
16#ifndef _GENERICDICT_H
17#define _GENERICDICT_H
18
19#ifdef FEATURE_PREJIT
20#include "dataimage.h"
21#endif
22
23// DICTIONARIES
24//
25// A dictionary is a cache of handles associated with particular
26// instantiations of generic classes and generic methods, containing
27// - the instantiation itself (a list of TypeHandles)
28// - handles created on demand at runtime when code shared between
29// multiple instantiations needs to lookup an instantiation-specific
30// handle (for example, in newobj C<!0> and castclass !!0[])
31//
32// DICTIONARY ENTRIES
33//
34// Dictionary entries (abstracted as the type DictionaryEntry) can be:
35// a TypeHandle (for type arguments and entries associated with a TypeSpec token)
36// a MethodDesc* (for entries associated with a method MemberRef or MethodSpec token)
37// a FieldDesc* (for entries associated with a field MemberRef token)
38// a code pointer (e.g. for entries associated with an EntryPointAnnotation annotated token)
39// a dispatch stub address (for entries associated with an StubAddrAnnotation annotated token)
40//
41// DICTIONARY LAYOUTS
42//
43// A dictionary layout describes the layout of all dictionaries that can be
44// accessed from the same shared code. For example, Hashtable<string,int> and
45// Hashtable<object,int> share one layout, and Hashtable<int,string> and Hashtable<int,object>
46// share another layout. For generic types, the dictionary layout is stored in the EEClass
47// that is shared across compatible instantiations. For generic methods, the layout
48// is stored in the InstantiatedMethodDesc associated with the shared generic code itself.
49//
50
51class TypeHandleList;
52class Module;
53class BaseDomain;
54class SigTypeContext;
55class SigBuilder;
56
57enum DictionaryEntryKind
58{
59 EmptySlot = 0,
60 TypeHandleSlot = 1,
61 MethodDescSlot = 2,
62 MethodEntrySlot = 3,
63 ConstrainedMethodEntrySlot = 4,
64 DispatchStubAddrSlot = 5,
65 FieldDescSlot = 6,
66 DeclaringTypeHandleSlot = 7,
67};
68
69enum DictionaryEntrySignatureSource : BYTE
70{
71 FromZapImage = 0,
72 FromReadyToRunImage = 1,
73 FromJIT = 2,
74};
75
76class DictionaryEntryLayout
77{
78public:
79 DictionaryEntryLayout(PTR_VOID signature)
80 { LIMITED_METHOD_CONTRACT; m_signature = signature; }
81
82 DictionaryEntryKind GetKind();
83
84 PTR_VOID m_signature;
85
86 DictionaryEntrySignatureSource m_signatureSource;
87};
88
89typedef DPTR(DictionaryEntryLayout) PTR_DictionaryEntryLayout;
90
91
92class DictionaryLayout;
93typedef DPTR(DictionaryLayout) PTR_DictionaryLayout;
94
95// The type of dictionary layouts. We don't include the number of type
96// arguments as this is obtained elsewhere
97class DictionaryLayout
98{
99 friend class Dictionary;
100#ifdef DACCESS_COMPILE
101 friend class NativeImageDumper;
102#endif
103private:
104 // Next bucket of slots (only used to track entries that won't fit in the dictionary)
105 DictionaryLayout* m_pNext;
106
107 // Number of non-type-argument slots in this bucket
108 WORD m_numSlots;
109
110 // m_numSlots of these
111 DictionaryEntryLayout m_slots[1];
112
113 static BOOL FindTokenWorker(LoaderAllocator *pAllocator,
114 DWORD numGenericArgs,
115 DictionaryLayout *pDictLayout,
116 CORINFO_RUNTIME_LOOKUP *pResult,
117 SigBuilder * pSigBuilder,
118 BYTE * pSig,
119 DWORD cbSig,
120 int nFirstOffset,
121 DictionaryEntrySignatureSource signatureSource,
122 WORD * pSlotOut);
123
124
125public:
126 // Create an initial dictionary layout with a single bucket containing numSlots slots
127 static DictionaryLayout* Allocate(WORD numSlots, LoaderAllocator *pAllocator, AllocMemTracker *pamTracker);
128
129 // Bytes used for the first bucket of this dictionary, which might be stored inline in
130 // another structure (e.g. MethodTable)
131 static DWORD GetFirstDictionaryBucketSize(DWORD numGenericArgs, PTR_DictionaryLayout pDictLayout);
132
133 static BOOL FindToken(LoaderAllocator *pAllocator,
134 DWORD numGenericArgs,
135 DictionaryLayout *pDictLayout,
136 CORINFO_RUNTIME_LOOKUP *pResult,
137 SigBuilder * pSigBuilder,
138 int nFirstOffset,
139 DictionaryEntrySignatureSource signatureSource);
140
141 static BOOL FindToken(LoaderAllocator * pAllocator,
142 DWORD numGenericArgs,
143 DictionaryLayout * pDictLayout,
144 CORINFO_RUNTIME_LOOKUP * pResult,
145 BYTE * signature,
146 int nFirstOffset,
147 DictionaryEntrySignatureSource signatureSource,
148 WORD * pSlotOut);
149
150 DWORD GetMaxSlots();
151 DWORD GetNumUsedSlots();
152
153 PTR_DictionaryEntryLayout GetEntryLayout(DWORD i)
154 {
155 LIMITED_METHOD_CONTRACT;
156 _ASSERTE(i >= 0 && i < GetMaxSlots());
157 return dac_cast<PTR_DictionaryEntryLayout>(
158 dac_cast<TADDR>(this) + offsetof(DictionaryLayout, m_slots) + sizeof(DictionaryEntryLayout) * i);
159 }
160
161 DictionaryLayout* GetNextLayout() { LIMITED_METHOD_CONTRACT; return m_pNext; }
162
163#ifdef FEATURE_PREJIT
164 DWORD GetObjectSize();
165
166 // Trim the canonical dictionary layout to include only those used slots actually used.
167 // WARNING!!!
168 // You should only call this if
169 // (a) you're actually saving this shared instantiation into it's PreferredZapModule,
170 // i.e. you must be both saving the shared instantiation and the module
171 // you're ngen'ing MUST be that the PreferredZapModule.
172 // (b) you're sure you've compiled all the shared code for this type
173 // within the context of this NGEN session.
174 // This is currently the same as saying we can hardbind to the EEClass - if it's in another
175 // module then we will have already trimmed the layout, and if it's being saved into the
176 // current module then we can only hardbind to it if the current module is the PreferredZapModule.
177 //
178 // Note after calling this both GetObjectSize for this layout and the
179 // computed dictionary size for all dictionaries based on this layout may
180 // be reduced. This may in turn affect the size of all non-canonical
181 // method tables, potentially trimming some dictionary words off the end
182 // of the method table.
183 void Trim();
184 void Save(DataImage *image);
185 void Fixup(DataImage *image, BOOL fMethod);
186#endif // FEATURE_PREJIT
187
188};
189
190
191// The type of dictionaries. This is just an abstraction around an open-ended array
192class Dictionary
193{
194#ifdef DACCESS_COMPILE
195 friend class NativeImageDumper;
196#endif
197 private:
198 // First N entries are generic instantiations arguments. They are stored as FixupPointers
199 // in NGen images. It means that the lowest bit is used to mark optional indirection (see code:FixupPointer).
200 // The rest of the open array are normal pointers (no optional indirection).
201 DictionaryEntry m_pEntries[1];
202
203 TADDR EntryAddr(ULONG32 idx)
204 {
205 LIMITED_METHOD_CONTRACT;
206 SUPPORTS_DAC;
207 return PTR_HOST_MEMBER_TADDR(Dictionary, this, m_pEntries) +
208 idx * sizeof(m_pEntries[0]);
209 }
210
211 public:
212 inline DPTR(FixupPointer<TypeHandle>) GetInstantiation()
213 {
214 LIMITED_METHOD_CONTRACT;
215 SUPPORTS_DAC;
216 return dac_cast<DPTR(FixupPointer<TypeHandle>)>(EntryAddr(0));
217 }
218
219#ifndef DACCESS_COMPILE
220 inline void* AsPtr()
221 {
222 LIMITED_METHOD_CONTRACT;
223 return (void*) m_pEntries;
224 }
225#endif // #ifndef DACCESS_COMPILE
226
227 private:
228
229#ifndef DACCESS_COMPILE
230
231 inline TypeHandle GetTypeHandleSlot(DWORD numGenericArgs, DWORD i)
232 {
233 LIMITED_METHOD_CONTRACT;
234 return *GetTypeHandleSlotAddr(numGenericArgs, i);
235 }
236 inline MethodDesc *GetMethodDescSlot(DWORD numGenericArgs, DWORD i)
237 {
238 LIMITED_METHOD_CONTRACT;
239 return *GetMethodDescSlotAddr(numGenericArgs,i);
240 }
241 inline FieldDesc *GetFieldDescSlot(DWORD numGenericArgs, DWORD i)
242 {
243 LIMITED_METHOD_CONTRACT;
244 return *GetFieldDescSlotAddr(numGenericArgs,i);
245 }
246 inline TypeHandle *GetTypeHandleSlotAddr(DWORD numGenericArgs, DWORD i)
247 {
248 LIMITED_METHOD_CONTRACT;
249 return ((TypeHandle *) &m_pEntries[numGenericArgs + i]);
250 }
251 inline MethodDesc **GetMethodDescSlotAddr(DWORD numGenericArgs, DWORD i)
252 {
253 LIMITED_METHOD_CONTRACT;
254 return ((MethodDesc **) &m_pEntries[numGenericArgs + i]);
255 }
256 inline FieldDesc **GetFieldDescSlotAddr(DWORD numGenericArgs, DWORD i)
257 {
258 LIMITED_METHOD_CONTRACT;
259 return ((FieldDesc **) &m_pEntries[numGenericArgs + i]);
260 }
261 inline DictionaryEntry *GetSlotAddr(DWORD numGenericArgs, DWORD i)
262 {
263 LIMITED_METHOD_CONTRACT;
264 return ((void **) &m_pEntries[numGenericArgs + i]);
265 }
266 inline DictionaryEntry GetSlot(DWORD numGenericArgs, DWORD i)
267 {
268 LIMITED_METHOD_CONTRACT;
269 return *GetSlotAddr(numGenericArgs,i);
270 }
271 inline BOOL IsSlotEmpty(DWORD numGenericArgs, DWORD i)
272 {
273 LIMITED_METHOD_CONTRACT;
274 return GetSlot(numGenericArgs,i) == NULL;
275 }
276
277#endif // #ifndef DACCESS_COMPILE
278
279 public:
280
281#ifndef DACCESS_COMPILE
282
283 static DictionaryEntry PopulateEntry(MethodDesc * pMD,
284 MethodTable * pMT,
285 LPVOID signature,
286 BOOL nonExpansive,
287 DictionaryEntry ** ppSlot,
288 DWORD dictionaryIndexAndSlot = -1,
289 Module * pModule = NULL);
290
291 void PrepopulateDictionary(MethodDesc * pMD,
292 MethodTable * pMT,
293 BOOL nonExpansive);
294
295#endif // #ifndef DACCESS_COMPILE
296
297 public:
298
299#ifdef FEATURE_PREJIT
300
301 // Fixup the dictionary entries, including the type arguments
302 //
303 // WARNING!!!
304 // You should only pass "canSaveSlots=TRUE" if you are certain the dictionary layout
305 // matches that which will be used at runtime. This means you must either
306 // be able to hard-bind to the EEClass of the canonical instantiation, or else
307 // you are saving a copy of the canonical instantiation itself.
308 //
309 // If we can't save slots, then we will zero all entries in the dictionary (apart from the
310 // instantiation itself) and load at runtime.
311 void Fixup(DataImage *image,
312 BOOL canSaveInstantiation,
313 BOOL canSaveSlots,
314 DWORD numGenericArgs, // Must be non-zero
315 Module *pModule, // module of the generic code
316 DictionaryLayout *pDictLayout); // If NULL, then only type arguments are present
317
318 BOOL IsWriteable(DataImage *image,
319 BOOL canSaveSlots,
320 DWORD numGenericArgs, // Must be non-zero
321 Module *pModule, // module of the generic code
322 DictionaryLayout *pDictLayout); // If NULL, then only type arguments are present
323
324 BOOL ComputeNeedsRestore(DataImage *image,
325 TypeHandleList *pVisited,
326 DWORD numGenericArgs);
327 void Restore(DWORD numGenericArgs, ClassLoadLevel level);
328#endif // FEATURE_PREJIT
329};
330
331#endif
332