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