| 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 |  | 
|---|