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 | |
51 | class TypeHandleList; |
52 | class Module; |
53 | class BaseDomain; |
54 | class SigTypeContext; |
55 | class SigBuilder; |
56 | |
57 | enum 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 | |
69 | enum DictionaryEntrySignatureSource : BYTE |
70 | { |
71 | FromZapImage = 0, |
72 | FromReadyToRunImage = 1, |
73 | FromJIT = 2, |
74 | }; |
75 | |
76 | class DictionaryEntryLayout |
77 | { |
78 | public: |
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 | |
89 | typedef DPTR(DictionaryEntryLayout) PTR_DictionaryEntryLayout; |
90 | |
91 | |
92 | class DictionaryLayout; |
93 | typedef 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 |
97 | class DictionaryLayout |
98 | { |
99 | friend class Dictionary; |
100 | #ifdef DACCESS_COMPILE |
101 | friend class NativeImageDumper; |
102 | #endif |
103 | private: |
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 | |
125 | public: |
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 |
192 | class 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 | |