summaryrefslogtreecommitdiff
path: root/src/vm/constrainedexecutionregion.h
blob: 93ceb63010567a5c815ab17d14e30e7d843935da (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
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
// 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.
//
// Methods to support the implementation of Constrained Execution Regions (CERs). This includes logic to walk the IL of methods to
// determine the statically determinable call graph and prepare each submethod (jit, prepopulate generic dictionaries etc.,
// everything needed to ensure that the runtime won't generate implicit failure points during the execution of said call graph).
//

//


#ifndef __CONSTRAINED_EXECUTION_REGION_H
#define __CONSTRAINED_EXECUTION_REGION_H


#include <corhlpr.h>
#include <typestring.h>


// An enumeration that abstracts the interesting information (from our point of view) present in a reliability contract decorating a
// method.
enum ReliabilityContractLevel
{
    RCL_UNKNOWN             = -1,   // The contract attribute hasn't been read yet
    RCL_NO_CONTRACT         = 0,    // No contract (or a fairly useless one) was specified
    RCL_BASIC_CONTRACT      = 1,    // The contract promises enough for the method to be a legal member of a CER call graph
    RCL_PREPARE_CONTRACT    = 2,    // The contract promises enough to be worth preparing the method as part of a CER call graph
};

// Various definitions used to parse reliability contracts. These must be kept synchronized with the managed version in
// BCL\System\Runtime\Reliability\ReliabilityContractAttribute.cs

#define RELIABILITY_CONTRACT_NAME       "System.Runtime.ConstrainedExecution.ReliabilityContractAttribute"
#define RC_CONSISTENCY_PROP_NAME        "ConsistencyGuarantee"
#define RC_CER_PROP_NAME                "Cer"

enum {
    RC_CONSISTENCY_CORRUPT_PROCESS      = 0,
    RC_CONSISTENCY_CORRUPT_APPDOMAIN    = 1,
    RC_CONSISTENCY_CORRUPT_INSTANCE     = 2,
    RC_CONSISTENCY_CORRUPT_NOTHING      = 3,
    RC_CONSISTENCY_UNDEFINED            = 4,
    RC_CER_NONE                         = 0,
    RC_CER_MAYFAIL                      = 1,
    RC_CER_SUCCESS                      = 2,
    RC_CER_UNDEFINED                    = 3
};


// We compact the reliability contract states above into a single DWORD format easy to cache at the assembly and class level
// opaquely. We also encode in this DWORD whether a given part of the state has been defined yet (an assembly might set a
// consistency level without specifying a cer level, for instance, and this information is vital when merging states between
// assembly, class and method levels).
// The macros below handle the encoding so nobody else needs to know the details.

// The base state for an encoded DWORD: neither consistency or cer defined.
#define RC_NULL RC_ENCODE(RC_CONSISTENCY_UNDEFINED, RC_CER_UNDEFINED)

// Extract the raw consistency value from an encoded DWORD.
#define RC_CONSISTENCY(_encoded) ((_encoded) >> 2)

// Extract the raw cer value from an encoded DWORD.
#define RC_CER(_encoded) ((_encoded) & 3)

// Produce an encoded DWORD from a pair of raw consistency and cer values. Values must have been range validated first.
#define RC_ENCODE(_consistency, _cer) (DWORD)(((_consistency) << 2) | (_cer))

// Produce an abstracted ReliabilityContractLevel from an encoded DWORD, see CheckForReliabilityContract for details of the rules.
#define RC_ENCODED_TO_LEVEL(_encoded)                                                   \
    ((RC_CONSISTENCY(_encoded) == RC_CONSISTENCY_UNDEFINED ||                           \
      RC_CONSISTENCY(_encoded) < RC_CONSISTENCY_CORRUPT_INSTANCE) ? RCL_NO_CONTRACT :   \
     (RC_CER(_encoded) == RC_CER_UNDEFINED ||                                           \
      RC_CER(_encoded) == RC_CER_NONE) ? RCL_BASIC_CONTRACT :                           \
     RCL_PREPARE_CONTRACT)

// Given two DWORD encodings presumed to come from different scopes (e.g. method and class) merge them to find the effective
// contract state. It's presumed the first encoding is the most tightly scoped (i.e. method would go first in the example above) and
// therefore takes precedence.
#define RC_MERGE(_old, _new) RC_ENCODE((RC_CONSISTENCY(_old) == RC_CONSISTENCY_UNDEFINED) ? \
                                       RC_CONSISTENCY(_new) : RC_CONSISTENCY(_old),         \
                                       (RC_CER(_old) == RC_CER_UNDEFINED) ?                 \
                                       RC_CER(_new) : RC_CER(_old))                         \

// Return true if either consistency or cer has not been specified in the encoded DWORD given.
#define RC_INCOMPLETE(_encoded) (RC_CONSISTENCY(_encoded) == RC_CONSISTENCY_UNDEFINED || RC_CER(_encoded) == RC_CER_UNDEFINED)

// Look for reliability contracts at the method, class and assembly level and parse them to extract the information we're interested
// in from a runtime preparation viewpoint. This information is abstracted in the form of the ReliabilityContractLevel enumeration.
ReliabilityContractLevel CheckForReliabilityContract(MethodDesc *pMD);


// Structure used to track enough information to identify a method (possibly generic or belonging to a generic class). Includes a
// MethodDesc pointer and a SigTypeContext (values of class and method type parameters to narrow down the exact method being refered
// to). Similar to MethodContext (see ConstrainedExecutionRegion.cpp), but without the unneeded list link field (we expect to embed
// these in arrays, hence the name).
struct MethodContextElement
{
    FixupPointer<PTR_MethodDesc> m_pMethodDesc; // Pointer to a MethodDesc
    FixupPointer<PTR_MethodTable> m_pExactMT;   // Exact type to disambiguate code shared by instantiations

    MethodDesc * GetMethodDesc()
    {
        return m_pMethodDesc.GetValue();
    }

    MethodTable * GetExactMT()
    {
        return m_pExactMT.GetValue();
    }
};


// Base structure used to track which CERs have been prepared so far.
// These structures are looked up via a per-assembly hash table using the root method desc as a key.
// Used to avoid extra work in both the jit and PrepareMethod calls. The latter case is more involved because we support preparing a
// CER with generic type parameters (the instantiation is passed in along with the method in the PrepareMethod call). In that case
// we need to track exactly which instantiations we've prepared for a given method.
struct CerPrepInfo
{
    CerPrepInfo() :
        m_fFullyPrepared(false),
        m_fRequiresInstantiation(false),
        m_fMethodHasCallsWithinExplicitCer(false)
    {
        CONTRACTL {
            THROWS;
            GC_NOTRIGGER;
            MODE_ANY;
        } CONTRACTL_END;

        if (!m_sIsInitAtInstHash.Init(17, NULL, NULL, FALSE))
            COMPlusThrowOM();
    }

    bool                        m_fFullyPrepared;           // True implies we've prep'd this once and there are no shared instantiations
    bool                        m_fRequiresInstantiation;   // True implies that this method is shared amongst multiple instantiations
    bool                        m_fMethodHasCallsWithinExplicitCer; // True if method contains calls out from within an explicit PCER range
    EEInstantiationHashTable    m_sIsInitAtInstHash;        // Hash of instantiations we've prepared this CER for 
};


#ifdef FEATURE_PREJIT

// Structure used to represent a CER by a root method and a list of MethodContextElements that indicate all the methods contained.
// The MethodContextElement list is terminated with a sentinel entry (m_pMethodDesc set to NULL).
// Keep this structure small since we'll access the whole array of them randomly at runtime; density is our best friend.
struct CerRoot
{
    MethodDesc                 *m_pRootMD;                  // Root method (no type context since it never has type params)
    MethodContextElement       *m_pList;                    // List of methods in this CER
};

// Class used to track all the CERs rooted at methods defined within a given module that are discovered at ngen time. This data is
// then used at runtime to determine when and how to perform necessary restoration work so that the CERs don't encounter any
// unexpected failure points during execution.
// During ngen this class keeps a dynamically expanded array of CER roots (both the class and the array are allocated from a win32
// heap). When we save the image to storage (and thus know the final size of the table) we combine the two so that at runtime
// they're adjacent and exactly the right size.
class CerNgenRootTable
{
#ifdef DACCESS_COMPILE
    friend class NativeImageDumper;
#endif

    DWORD                      *m_pRestoreBitmap;           // Pointer to array of restored flag bits
    DWORD                       m_cRoots;                   // Count of root methods represented
    DWORD                       m_cSlots;                   // Extra empty slots at the tail of the array below (ngen time only)
    CerRoot                    *m_pRoots;                   // Pointer to array of CER roots (sorted by RootMD address)
    MethodContextElement      **m_pRootsInCompilationOrder; // Pointer to array of CerRoot::m_pList (in the order AddRoot is called)

public:

    CerNgenRootTable() :
        m_pRestoreBitmap(NULL),
        m_cRoots(0),
        m_cSlots(0),
        m_pRoots(NULL),
        m_pRootsInCompilationOrder(NULL)
    {
    }

    ~CerNgenRootTable()
    {
        CONTRACTL
        {
            NOTHROW;
            GC_NOTRIGGER;
            SO_TOLERANT;
            MODE_ANY;
        }
        CONTRACTL_END;
        delete m_pRestoreBitmap;
        delete m_pRoots;
        delete m_pRootsInCompilationOrder;
    }

    // Add a new root to the table, expanding it as necessary. Note that this method must be called with the CerCrst already held.
    void AddRoot(MethodDesc *pRootMD, MethodContextElement *pList);

    // Retrieve the address of the list of methods for the CER rooted at the given index.
    inline MethodContextElement *GetList(DWORD dwIndex) { LIMITED_METHOD_CONTRACT; _ASSERTE(dwIndex < m_cRoots); return m_pRoots[dwIndex].m_pList; }

    // Retrieve the address of the list of methods for the CER rooted at the given method. (The root must exist).
    inline MethodContextElement *GetList(MethodDesc *pRootMD) { WRAPPER_NO_CONTRACT; return GetList(FindIndex(pRootMD)); }

    // Indicate whether the given method has ngen restoration information associated with it.
    inline bool IsNgenRootMethod(MethodDesc *pRootMD) { WRAPPER_NO_CONTRACT; return FindIndex(pRootMD) != NoSuchRoot; }

    // Prepare the CER rooted at the given method (it's OK to pass a MethodDesc* that doesn't root a CER, in which case the method
    // is a no-op).
    void Restore(MethodDesc *pRootMD);

    // Ngen callouts to help serialize this structure and its children to storage.
    void Save(DataImage *image, CorProfileData *profileData);
    void Fixup(DataImage *image);
    void FixupRVAs(DataImage *image);

    // Calculate (in bytes) the size of bitmap to allocate to record whether each CER has been restored at runtime. Size is
    // rounded up to DWORD alignment.
    inline DWORD SizeOfRestoreBitmap()
    {
        LIMITED_METHOD_CONTRACT;
        return ((m_cRoots + 31) / 32) * sizeof(DWORD);
    }

    inline DWORD GetRootCount() { LIMITED_METHOD_CONTRACT; return m_cRoots; }
    inline CerRoot *GetRoots() { LIMITED_METHOD_CONTRACT; return m_pRoots; }
    inline DWORD *GetRestoreBitmap() { LIMITED_METHOD_CONTRACT; return m_pRestoreBitmap; }

private:
    enum { NoSuchRoot = 0xffffffff };

    // Locate the index of a given CerRoot record in the array given the root method. This is used to access the array and to locate
    // the restored flag for the entry in the restored bitmap. NoSuchRoot is returned if the root cannot be found.
    DWORD FindIndex(MethodDesc *pRootMD);
};

#endif


// Default initial size for hash table used to track CerPrepInfo structures on a per-module basis.
#define CER_DEFAULT_HASH_SIZE   17


// Structure used to track a single exception handling range (catch, finally etc.). We build an array of these and then track which
// ones have become 'activated' by virtue of having their try clause immediately preceded by a call to our preparation marker
// method. This allows us to determine which call sites in the method body should be followed during method preparation.
struct EHClauseRange
{
    DWORD   m_dwTryOffset;
    DWORD   m_dwHandlerOffset;
    DWORD   m_dwHandlerLength;
    bool    m_fActive;
};


// Structure used to track enough information to identify a method (possibly generic or belonging to a generic class). Includes a
// MethodDesc pointer and a SigTypeContext (values of class and method type parameters to narrow down the exact method being refered
// to). The structure also contains a next pointer so that it can be placed in a singly linked list (see MethodContextStack below).
struct MethodContext
{
    MethodContext  *m_pNext;            // Next MethodContext in a MethodContextStack list
    MethodDesc     *m_pMethodDesc;      // Pointer to a MethodDesc
    SigTypeContext  m_sTypeContext;     // Additional type parameter information to qualify the exact method targetted
    bool            m_fRoot;            // Does this method contain a CER root of its own?

    // Allocate and initialize a MethodContext from the per-thread stacking allocator (we assume the checkpoint has already been
    // taken).
    static MethodContext* PerThreadAllocate(MethodDesc *pMD, SigTypeContext *pTypeContext)
    {
        CONTRACTL {
            THROWS;
            GC_TRIGGERS;
            MODE_ANY;
        } CONTRACTL_END;


        MethodContext *pContext = new (&GetThread()->m_MarshalAlloc) MethodContext();
        pContext->m_pMethodDesc = pMD;
        pContext->m_sTypeContext = *pTypeContext;
        pContext->m_fRoot = false;

        return pContext;
    }

    // Determine if two MethodContexts are equivalent (same MethodDesc pointer and identical arrays of TypeHandles in the
    // TypeContext).
    bool Equals(MethodContext *pOther)
    {
        WRAPPER_NO_CONTRACT;

        if (pOther->m_pMethodDesc != m_pMethodDesc)
            return false;

        if (pOther->m_sTypeContext.m_classInst.GetNumArgs() != m_sTypeContext.m_classInst.GetNumArgs())
            return false;

        if (pOther->m_sTypeContext.m_methodInst.GetNumArgs() != m_sTypeContext.m_methodInst.GetNumArgs())
            return false;

        DWORD i;

        for (i = 0; i < m_sTypeContext.m_classInst.GetNumArgs(); i++)
            if (pOther->m_sTypeContext.m_classInst[i] != m_sTypeContext.m_classInst[i])
                return false;

        for (i = 0; i < m_sTypeContext.m_methodInst.GetNumArgs(); i++)
            if (pOther->m_sTypeContext.m_methodInst[i] != m_sTypeContext.m_methodInst[i])
                return false;

        return true;
    }

#ifdef _DEBUG
#define CER_DBG_MAX_OUT 4096
    char *ToString()
    {
        STATIC_CONTRACT_THROWS;
        STATIC_CONTRACT_GC_TRIGGERS;
        STATIC_CONTRACT_MODE_ANY;

        // Support up to two ToString calls before we re-use a buffer and overwrite previous output.
        static char szOut1[CER_DBG_MAX_OUT];
        static char szOut2[CER_DBG_MAX_OUT];
        static char *pszOut = szOut1;

        StackSString ssBuffer;
        StackScratchBuffer ssScratch;

        TypeString::AppendMethod(ssBuffer, m_pMethodDesc, m_sTypeContext.m_classInst, TypeString::FormatNamespace | TypeString::FormatAngleBrackets);
        sprintf_s(&pszOut[0], CER_DBG_MAX_OUT, "%s", ssBuffer.GetUTF8(ssScratch));

        char *pszReturn = pszOut;
        pszOut = pszOut == szOut1 ? szOut2 : szOut1;
        return pszReturn;
    }
#endif
};

// Maintains a stack of MethodContexts (implemented as a singly linked list with insert and remove operations only at the head).
class MethodContextStack
{
    MethodContext  *m_pFirst;       // The head of the linked list
    DWORD           m_cElements;    // Count of elements in the stack

public:

    // Initialize to an empty list.
    MethodContextStack()
    {
        LIMITED_METHOD_CONTRACT;

        m_pFirst = NULL;
        m_cElements = 0;
    }

    // Push a MethodContext pointer on the head of the list.
    void Push(MethodContext *pContext)
    {
        LIMITED_METHOD_CONTRACT;

        pContext->m_pNext = m_pFirst;
        m_pFirst = pContext;
        m_cElements++;
    }

    // Remove and retrieve the most recently pushed MethodContext. Return NULL if no more entries exist.
    MethodContext *Pop()
    {
        LIMITED_METHOD_CONTRACT;

        MethodContext* pContext = m_pFirst;
        if (pContext == NULL)
            return NULL;

        m_pFirst = pContext->m_pNext;
        m_cElements--;

        return pContext;
    }

    // Return true if an MethodContext equivalent to the argument exists in the stack.
    bool IsInStack(MethodContext *pMatchContext)
    {
        WRAPPER_NO_CONTRACT;

        MethodContext* pContext = m_pFirst;
        while (pContext) {
            if (pContext->Equals(pMatchContext))
                return true;
            pContext = pContext->m_pNext;
        }

        return false;
    }

    // Get count of elements in the stack.
    DWORD GetCount()
    {
        LIMITED_METHOD_CONTRACT;

        return m_cElements;
    }
};


class MethodCallGraphPreparer
{
    MethodDesc *m_pRootMD;
    SigTypeContext *m_pRootTypeContext;

    COR_ILMETHOD_DECODER *m_pMethodDecoder; 
    
    MethodContextStack  m_sLeftToProcess;       // MethodContexts we have yet to look at in this call graph
    MethodContextStack  m_sAlreadySeen;         // MethodContexts we've already processed at least once

    EHClauseRange      *m_pEHClauses;           // Array of exception handling clauses in current method (only if !fEntireMethod)
    DWORD               m_cEHClauses;           // Number of elements in above array
    CerPrepInfo        *m_pCerPrepInfo;         // Context recording how much preparation this region has had
    MethodContextStack  m_sPersist;             // MethodContexts we need to keep around past the 'prepare' phase of preparation
#ifdef FEATURE_NATIVE_IMAGE_GENERATION
    bool                m_fNgen;                // True when being called as part of an ngen
    MethodContextStack  m_sRootMethods;         // Methods containing a sub-CER (excludes the real root)
#endif // FEATURE_NATIVE_IMAGE_GENERATION
    Thread             *m_pThread;              // Cached managed thread pointer (for allocations and the like)
    bool                m_fPartialPreparation;  // True if we have unbound type vars at the CER root and can only prep one instantiation at a time

    bool                m_fEntireMethod;        // True if are preparing for the entire method
    bool                m_fExactTypeContext;
    bool                m_fMethodHasCallsWithinExplicitCer; // True if method contains calls out from within an explicit PCER range

    bool                m_fIgnoreVirtualCERCallMDA;  // True if VirtualCER MDA is not desirable to be fired

    MethodCallGraphPreparer * m_pNext;          // Links this instance on a per-thread stack used to detect
                                                // and defeat recursive preparations

  public:
    MethodCallGraphPreparer(MethodDesc *pRootMD, SigTypeContext *pRootTypeContext, bool fEntireMethod, bool fExactTypeContext, bool fIgnoreVirtualCERCallMDA = false);

    // Walk the call graph of the method given by m_pRootMD (and type context in m_pRootTypeContext which provides instantiation information
    // for generic methods/classes).
    //
    // If fEntireMethod is true then the entire body of pRootMD is scanned for callsites, otherwise we assume that one or more CER
    // exception handlers exist in the method and only the finally and catch blocks of such handlers are scanned for graph roots.
    //
    // Each method we come across in the call graph (excluding late bound invocation destinations precipitated by virtual or interface
    // calls) is jitted and has any generic dictionary information we can determine at jit time prepopulated. This includes implicit
    // cctor invocations. If this method is called at ngen time we will attach extra fixup information to the affected method to ensure
    // that fixing up the root method of the graph will cause all methods in the graph to be fixed up at that point also.
    //
    // Some generic dictionary entries may not be prepopulated if unbound type variables exist at the root of the call tree. Such cases
    // will be ignored (as for the virtual/interface dispatch case we assume the caller will use an out-of-band mechanism to pre-prepare
    // these entries explicitly).
    //
    // Returns true if the m_pRootMD contains a CER that calls outside the method. 
    //
    bool Run();
    
    // Methods used to control re-entrancy on the same thread. Essentially we'd like to avoid all re-entrancy
    // (since it can lead to unbounded recursion easily) but unfortunately jitting methods during the
    // preparation phase can cause this both directly (if we spot a sub-root) or indirectly (where implicit
    // jit execution of a cctor causes a possibly unrelated CER graph to be prepared). The algorithm we use to
    // avoid this records a stack of preparations attempts on the current thread (implemented via a singly
    // linked list of the MethodCallGraphPreparer instances). Re-entrant prepare requests become noops if
    // they're for a root method we're already processing (anywhere in the stack) and run to completion
    // otherwise. This prevents infinite recursion since it removes at least one method from the intersection
    // of the CER call graphs on each iteration. Theoretically it might not be the most efficient solution
    // since there might still be a lot of overlap between graphs, but in practice the number of sub-CER roots
    // is likely to be small and we won't recurse very far. This will still allow a re-entrant preparation
    // that is the result of running a cctor to potentially early-out (and thus allow code to run before its
    // CERs have been fully prepped). But this should only happen when a CER causes (directly or indirectly) a
    // cctor to run that depends on that CER having been prepared already, which we really can't do much
    // about.
    //
    BOOL CanPreparationProceed(MethodDesc * pMD, SigTypeContext * pTypeContext);

    static void BeginPrepareCerForHolder(MethodCallGraphPreparer *pPrepState);
    static void EndPrepareCerForHolder(MethodCallGraphPreparer *pPrepState);

    typedef Holder<MethodCallGraphPreparer*, BeginPrepareCerForHolder, EndPrepareCerForHolder> ThreadPreparingCerHolder;

  private:
    void GetEHClauses();
    void MarkEHClauseActivatedByCERCall(MethodContext *pContext, BYTE *pbIL, DWORD cbIL);
    bool CheckIfCallsiteWithinCER(DWORD dwOffset);
    bool ShouldGatherExplicitCERCallInfo();
    void LookForInterestingCallsites(MethodContext *pContext);
    void PrepareMethods();
    bool RecordResults();
};

// Determines whether the given method contains a CER root that can be pre-prepared (i.e. prepared at jit time).
bool ContainsPrePreparableCerRoot(MethodDesc *pMD);

// Prepares the critical finalizer call graph for the given object type (which must derive from CriticalFinalizerObject). This
// involves preparing at least the finalizer method and possibly some others (for SafeHandle and CriticalHandle derivations). If a
// module pointer is supplied then only the critical methods introduced in that module are prepared (this is used at ngen time to
// ensure that we're only generating ngen preparation info for the targetted module).
void PrepareCriticalFinalizerObject(MethodTable *pMT, Module *pModule = NULL);

void PrepareMethodDesc(MethodDesc* pMD, Instantiation classInst = Instantiation(), Instantiation methodInst = Instantiation(), BOOL onlyContractedMethod = FALSE, BOOL fIgnoreVirtualCERCallMDA = FALSE);
// Determine whether the method given as a parameter is the root of a CER.
// @todo: Need an x86 offset as well and logic to determine whether we're actually in a root-CER portion of the method (if the whole
// thing isn't the root).
bool IsCerRootMethod(MethodDesc *pMD);

// Fill the cache of overflowed generic dictionary entries that the jit maintains with all the overflow slots stored so far in the
// dictionary layout.
void PrepopulateGenericHandleCache(DictionaryLayout  *pDictionaryLayout,
                                   MethodDesc        *pMD,
                                   MethodTable       *pMT);
    
DWORD GetReliabilityContract(IMDInternalImport *pImport, mdToken tkParent);

#ifdef FEATURE_PREJIT

// Prepare the class if it is derived from CriticalFinalizerObject. This is used at ngen time since such classes are normally 
// prepared at runtime (at instantiation) and would therefore miss the ngen image.
void PrepareCriticalType(MethodTable *pMT);

// Prepare a method and its statically determinable call graph if a hint attribute has been applied. This is only called at ngen
// time to save additional preparation information into the ngen image that wouldn't normally be there (and thus lower runtime
// overheads).
void PrePrepareMethodIfNecessary(CORINFO_METHOD_HANDLE hMethod);

#endif


// A fixed sized hash table keyed by pointers and storing two bits worth of value for every entry. The value is stored in the low
// order bits of the keys, so the pointers must be at least DWORD aligned. No hash table expansion occurs so new entries will sooner
// or later overwrite old. The implementation uses no locks (all accesses are single aligned pointer sized operations and therefore
// inherently atomic).
// The purpose of this table is to store a smallish number of reliability contract levels for the most recently queried methods,
// mainly for the purpose of speeding up thread abort processing (where we will walk the stack probing methods for contracts,
// sometimes repeatedly). So we use a small fixed sized hash to speed up lookups on average but avoid impacting working set.
#define PHC_BUCKETS     29
#define PHC_CHAIN       5
#define PHC_DATA_MASK   3

class PtrHashCache
{
public:
    PtrHashCache();
    bool Lookup(void *pKey, DWORD *pdwValue);
    void Add(void *pKey, DWORD dwValue);

#ifdef _DEBUG
    void DbgDumpStats();
#endif

private:
    DWORD GetHash(void *pKey);

    UINT_PTR    m_rEntries[PHC_BUCKETS * PHC_CHAIN];

#ifdef _DEBUG
    DWORD       m_dwHits;
    DWORD       m_dwMisses;
#endif
};

#endif