summaryrefslogtreecommitdiff
path: root/src/vm/genericdict.h
blob: 1cb172a1742b951f534d64ddd57c91b5e702c319 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// File: genericdict.h
//

//
// Definitions for "dictionaries" used to encapsulate generic instantiations
// and instantiation-specific information for shared-code generics
//

//
// ============================================================================

#ifndef _GENERICDICT_H
#define _GENERICDICT_H

#ifdef FEATURE_PREJIT
#include "dataimage.h"
#endif

// DICTIONARIES 
//
// A dictionary is a cache of handles associated with particular
// instantiations of generic classes and generic methods, containing
// - the instantiation itself (a list of TypeHandles)
// - handles created on demand at runtime when code shared between
//   multiple instantiations needs to lookup an instantiation-specific
//   handle (for example, in newobj C<!0> and castclass !!0[])
//
// DICTIONARY ENTRIES
//
// Dictionary entries (abstracted as the type DictionaryEntry) can be:
//   a TypeHandle (for type arguments and entries associated with a TypeSpec token)
//   a MethodDesc* (for entries associated with a method MemberRef or MethodSpec token)
//   a FieldDesc* (for entries associated with a field MemberRef token)
//   a code pointer (e.g. for entries associated with an EntryPointAnnotation annotated token)
//   a dispatch stub address (for entries associated with an StubAddrAnnotation annotated token)
//
// DICTIONARY LAYOUTS
// 
// A dictionary layout describes the layout of all dictionaries that can be
// accessed from the same shared code. For example, Hashtable<string,int> and 
// Hashtable<object,int> share one layout, and Hashtable<int,string> and Hashtable<int,object>
// share another layout. For generic types, the dictionary layout is stored in the EEClass
// that is shared across compatible instantiations. For generic methods, the layout
// is stored in the InstantiatedMethodDesc associated with the shared generic code itself.
//

class TypeHandleList;
class Module;
class BaseDomain;
class SigTypeContext;
class SigBuilder;

enum DictionaryEntryKind 
{ 
    EmptySlot = 0,
    TypeHandleSlot = 1,
    MethodDescSlot = 2,
    MethodEntrySlot = 3,
    ConstrainedMethodEntrySlot = 4,
    DispatchStubAddrSlot = 5, 
    FieldDescSlot = 6,
    DeclaringTypeHandleSlot = 7,
};

enum DictionaryEntrySignatureSource : BYTE
{
    FromZapImage = 0,
    FromReadyToRunImage = 1,
    FromJIT = 2,
};

class DictionaryEntryLayout
{
public:
    DictionaryEntryLayout(PTR_VOID signature)
    { LIMITED_METHOD_CONTRACT; m_signature = signature; }

    DictionaryEntryKind GetKind();

    PTR_VOID m_signature;

    DictionaryEntrySignatureSource m_signatureSource;
};

typedef DPTR(DictionaryEntryLayout) PTR_DictionaryEntryLayout;


class DictionaryLayout;
typedef DPTR(DictionaryLayout) PTR_DictionaryLayout;

// The type of dictionary layouts. We don't include the number of type
// arguments as this is obtained elsewhere
class DictionaryLayout
{
    friend class Dictionary;
#ifdef DACCESS_COMPILE
    friend class NativeImageDumper;
#endif
private:
    // Next bucket of slots (only used to track entries that won't fit in the dictionary)
    DictionaryLayout* m_pNext;
    
    // Number of non-type-argument slots in this bucket
    WORD m_numSlots;          

    // m_numSlots of these
    DictionaryEntryLayout m_slots[1];

    static BOOL FindTokenWorker(LoaderAllocator *pAllocator,
                                DWORD numGenericArgs,
                                DictionaryLayout *pDictLayout,
                                CORINFO_RUNTIME_LOOKUP *pResult,
                                SigBuilder * pSigBuilder,
                                BYTE * pSig,
                                DWORD cbSig,
                                int nFirstOffset,
                                DictionaryEntrySignatureSource signatureSource,
                                WORD * pSlotOut);

     
public:
    // Create an initial dictionary layout with a single bucket containing numSlots slots
    static DictionaryLayout* Allocate(WORD numSlots, LoaderAllocator *pAllocator, AllocMemTracker *pamTracker);

    // Bytes used for the first bucket of this dictionary, which might be stored inline in
    // another structure (e.g. MethodTable)
    static DWORD GetFirstDictionaryBucketSize(DWORD numGenericArgs, PTR_DictionaryLayout pDictLayout);

    static BOOL FindToken(LoaderAllocator *pAllocator,
                          DWORD numGenericArgs,
                          DictionaryLayout *pDictLayout,
                          CORINFO_RUNTIME_LOOKUP *pResult,
                          SigBuilder * pSigBuilder,
                          int nFirstOffset,
                          DictionaryEntrySignatureSource signatureSource);

    static BOOL FindToken(LoaderAllocator * pAllocator,
                          DWORD numGenericArgs,
                          DictionaryLayout * pDictLayout,
                          CORINFO_RUNTIME_LOOKUP * pResult,
                          BYTE * signature,
                          int nFirstOffset,
                          DictionaryEntrySignatureSource signatureSource,
                          WORD * pSlotOut);

    DWORD GetMaxSlots();
    DWORD GetNumUsedSlots();

    PTR_DictionaryEntryLayout GetEntryLayout(DWORD i)
    {
        LIMITED_METHOD_CONTRACT;
        _ASSERTE(i >= 0 && i < GetMaxSlots());
        return dac_cast<PTR_DictionaryEntryLayout>(
            dac_cast<TADDR>(this) + offsetof(DictionaryLayout, m_slots) + sizeof(DictionaryEntryLayout) * i);
    }

    DictionaryLayout* GetNextLayout() { LIMITED_METHOD_CONTRACT; return m_pNext; }

#ifdef FEATURE_PREJIT
    DWORD GetObjectSize();

    // Trim the canonical dictionary layout to include only those used slots actually used.
    // WARNING!!!
    // You should only call this if 
    //    (a) you're actually saving this shared instantiation into it's PreferredZapModule,
    //        i.e. you must be both saving the shared instantiation and the module
    //        you're ngen'ing MUST be that the PreferredZapModule.
    //    (b) you're sure you've compiled all the shared code for this type
    //        within the context of this NGEN session.
    // This is currently the same as saying we can hardbind to the EEClass - if it's in another
    // module then we will have already trimmed the layout, and if it's being saved into the 
    // current module then we can only hardbind to it if the current module is the PreferredZapModule.
    //
    // Note after calling this both GetObjectSize for this layout and the 
    // computed dictionary size for all dictionaries based on this layout may
    // be reduced.  This may in turn affect the size of all non-canonical
    // method tables, potentially trimming some dictionary words off the end 
    // of the method table.
    void Trim(); 
    void Save(DataImage *image);
    void Fixup(DataImage *image, BOOL fMethod);
#endif // FEATURE_PREJIT

};


// The type of dictionaries. This is just an abstraction around an open-ended array
class Dictionary
{  
#ifdef DACCESS_COMPILE
    friend class NativeImageDumper;
#endif
  private:
    // First N entries are generic instantiations arguments. They are stored as FixupPointers 
    // in NGen images. It means that the lowest bit is used to mark optional indirection (see code:FixupPointer).
    // The rest of the open array are normal pointers (no optional indirection).
    DictionaryEntry m_pEntries[1];

    TADDR EntryAddr(ULONG32 idx)
    {
        LIMITED_METHOD_CONTRACT;
        SUPPORTS_DAC;
        return PTR_HOST_MEMBER_TADDR(Dictionary, this, m_pEntries) +
            idx * sizeof(m_pEntries[0]);
    }
    
  public:
    inline DPTR(FixupPointer<TypeHandle>) GetInstantiation() 
    {
        LIMITED_METHOD_CONTRACT;
        SUPPORTS_DAC;
        return dac_cast<DPTR(FixupPointer<TypeHandle>)>(EntryAddr(0));
    }

#ifndef DACCESS_COMPILE
    inline void* AsPtr()
    {
        LIMITED_METHOD_CONTRACT;
        return (void*) m_pEntries;
    }
#endif // #ifndef DACCESS_COMPILE

  private:

#ifndef DACCESS_COMPILE

    inline TypeHandle GetTypeHandleSlot(DWORD numGenericArgs, DWORD i) 
    { 
        LIMITED_METHOD_CONTRACT; 
        return *GetTypeHandleSlotAddr(numGenericArgs, i);
    }
    inline MethodDesc *GetMethodDescSlot(DWORD numGenericArgs, DWORD i) 
    { 
        LIMITED_METHOD_CONTRACT; 
        return *GetMethodDescSlotAddr(numGenericArgs,i);
    }
    inline FieldDesc *GetFieldDescSlot(DWORD numGenericArgs, DWORD i) 
    { 
        LIMITED_METHOD_CONTRACT; 
        return *GetFieldDescSlotAddr(numGenericArgs,i);
    }
    inline TypeHandle *GetTypeHandleSlotAddr(DWORD numGenericArgs, DWORD i) 
    { 
        LIMITED_METHOD_CONTRACT; 
        return ((TypeHandle *) &m_pEntries[numGenericArgs + i]);
    }
    inline MethodDesc **GetMethodDescSlotAddr(DWORD numGenericArgs, DWORD i) 
    { 
        LIMITED_METHOD_CONTRACT; 
        return ((MethodDesc **) &m_pEntries[numGenericArgs + i]);
    }
    inline FieldDesc **GetFieldDescSlotAddr(DWORD numGenericArgs, DWORD i) 
    { 
        LIMITED_METHOD_CONTRACT; 
        return ((FieldDesc **) &m_pEntries[numGenericArgs + i]);
    }
    inline DictionaryEntry *GetSlotAddr(DWORD numGenericArgs, DWORD i) 
    { 
        LIMITED_METHOD_CONTRACT; 
        return ((void **) &m_pEntries[numGenericArgs + i]);
    }
    inline DictionaryEntry GetSlot(DWORD numGenericArgs, DWORD i) 
    { 
        LIMITED_METHOD_CONTRACT; 
        return *GetSlotAddr(numGenericArgs,i);
    }
    inline BOOL IsSlotEmpty(DWORD numGenericArgs, DWORD i) 
    { 
        LIMITED_METHOD_CONTRACT; 
        return GetSlot(numGenericArgs,i) == NULL;
    }

#endif // #ifndef DACCESS_COMPILE

  public:

#ifndef DACCESS_COMPILE

    static DictionaryEntry PopulateEntry(MethodDesc * pMD,
                                         MethodTable * pMT,
                                         LPVOID signature,
                                         BOOL nonExpansive,
                                         DictionaryEntry ** ppSlot,
                                         DWORD dictionaryIndexAndSlot = -1,
                                         Module * pModule = NULL);

    void PrepopulateDictionary(MethodDesc * pMD,
                               MethodTable * pMT,
                               BOOL nonExpansive);

#endif // #ifndef DACCESS_COMPILE

  public:

#ifdef FEATURE_PREJIT

    // Fixup the dictionary entries, including the type arguments
    //
    // WARNING!!!
    // You should only pass "canSaveSlots=TRUE" if you are certain the dictionary layout
    // matches that which will be used at runtime.  This means you must either
    // be able to hard-bind to the EEClass of the canonical instantiation, or else
    // you are saving a copy of the canonical instantiation itself.
    //
    // If we can't save slots, then we will zero all entries in the dictionary (apart from the
    // instantiation itself) and load at runtime.
    void Fixup(DataImage *image,
               BOOL canSaveInstantiation,
               BOOL canSaveSlots,
               DWORD numGenericArgs,            // Must be non-zero
               Module *pModule, // module of the generic code
               DictionaryLayout *pDictLayout);  // If NULL, then only type arguments are present

    BOOL IsWriteable(DataImage *image, 
               BOOL canSaveSlots,
               DWORD numGenericArgs,            // Must be non-zero
               Module *pModule, // module of the generic code
               DictionaryLayout *pDictLayout);  // If NULL, then only type arguments are present

    BOOL ComputeNeedsRestore(DataImage *image,
                             TypeHandleList *pVisited,
                             DWORD numGenericArgs);
    void Restore(DWORD numGenericArgs, ClassLoadLevel level);
#endif // FEATURE_PREJIT
};

#endif