summaryrefslogtreecommitdiff
path: root/src/vm/rejit.h
blob: 3c8bfd66b2c98c0d516626a11d9d8f1bd98b6c85 (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
564
565
566
567
568
569
// 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: REJIT.H
//

// 
// REJIT.H defines the class and structures used to store info about rejitted
// methods.  See comment at top of rejit.cpp for more information on how
// rejit works.
// 
// ===========================================================================
#ifndef _REJIT_H_
#define _REJIT_H_

#include "common.h"
#include "contractimpl.h"
#include "shash.h"
#include "corprof.h"

struct ReJitInfo;
struct SharedReJitInfo;
class ReJitManager;
class MethodDesc;
class ClrDataAccess;

#ifdef FEATURE_REJIT

//---------------------------------------------------------------------------------------
// The CLR's implementation of ICorProfilerFunctionControl, which is passed
// to the profiler.  The profiler calls methods on this to specify the IL and
// codegen flags for a given rejit request.
// 
class ProfilerFunctionControl : public ICorProfilerFunctionControl
{
public:
    ProfilerFunctionControl(LoaderHeap * pHeap);
    virtual ~ProfilerFunctionControl();

    // IUnknown functions
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void** pInterface);
    virtual ULONG STDMETHODCALLTYPE AddRef();
    virtual ULONG STDMETHODCALLTYPE Release();

    // ICorProfilerFunctionControl functions
    virtual HRESULT STDMETHODCALLTYPE SetCodegenFlags(DWORD flags);
    virtual HRESULT STDMETHODCALLTYPE SetILFunctionBody(ULONG cbNewILMethodHeader, LPCBYTE pbNewILMethodHeader);
    virtual HRESULT STDMETHODCALLTYPE SetILInstrumentedCodeMap(ULONG cILMapEntries, COR_IL_MAP * rgILMapEntries);

    // Accessors
    DWORD GetCodegenFlags();
    LPBYTE GetIL();
    ULONG GetInstrumentedMapEntryCount();
    COR_IL_MAP* GetInstrumentedMapEntries();


protected:
    Volatile<LONG> m_refCount;
    LoaderHeap * m_pHeap;
    DWORD m_dwCodegenFlags;
    ULONG m_cbIL;

    // This pointer will get copied into SharedReJitInfo::m_pbIL and owned there.
    LPBYTE m_pbIL;
    ULONG m_cInstrumentedMapEntries;
    COR_IL_MAP * m_rgInstrumentedMapEntries;
};

//---------------------------------------------------------------------------------------
// Helper base class used by the structures below to enforce that their
// pieces get allocated on the appropriate loader heaps
// 
struct LoaderHeapAllocatedRejitStructure
{
public:
    void * operator new (size_t size, LoaderHeap * pHeap, const NoThrow&);
    void * operator new (size_t size, LoaderHeap * pHeap);
};

//---------------------------------------------------------------------------------------
// One instance of this per rejit request for each mdMethodDef.  Contains IL and
// compilation flags.  This is used primarily as a structure, so most of its
// members are left public.
// 
struct SharedReJitInfo : public LoaderHeapAllocatedRejitStructure
{
private:
    // This determines what to use next as the value of the profiling API's ReJITID.
    static ReJITID s_GlobalReJitId;

public:
    // These represent the various states a SharedReJitInfo can be in.
    enum InternalFlags 
    {
        // The profiler has requested a ReJit, so we've allocated stuff, but we haven't
        // called back to the profiler to get any info or indicate that the ReJit has
        // started. (This Info can be 'reused' for a new ReJit if the
        // profiler calls RequestRejit again before we transition to the next state.)
        kStateRequested = 0x00000000,

        // The CLR has initiated the call to the profiler's GetReJITParameters() callback
        // but it hasn't completed yet. At this point we have to assume the profiler has
        // commited to a specific IL body, even if the CLR doesn't know what it is yet.
        // If the profiler calls RequestRejit we need to allocate a new SharedReJitInfo
        // and call GetReJITParameters() again.
        kStateGettingReJITParameters = 0x00000001,

        // We have asked the profiler about this method via ICorProfilerFunctionControl,
        // and have thus stored the IL and codegen flags the profiler specified. Can only
        // transition to kStateReverted from this state.
        kStateActive    = 0x00000002,

        // The methoddef has been reverted, but not freed yet. It (or its instantiations
        // for generics) *MAY* still be active on the stack someplace or have outstanding
        // memory references.
        kStateReverted  = 0x00000003,


        kStateMask      = 0x0000000F,
    };

    DWORD        m_dwInternalFlags;

    // Data
    LPBYTE       m_pbIL;
    DWORD        m_dwCodegenFlags;
    InstrumentedILOffsetMapping m_instrumentedILMap;

private:
    // This is the value of the profiling API's ReJITID for this particular
    // rejit request.
    const ReJITID m_reJitId;

    // Children
    ReJitInfo *  m_pInfoList;

public:
    // Constructor
    SharedReJitInfo();

    // Intentionally no destructor. SharedReJitInfo and its contents are
    // allocated on a loader heap, so SharedReJitInfo and its contents will be
    // freed when the AD is unloaded.

    // Read-Only Identifcation
    ReJITID GetId() { return m_reJitId; }

    void AddMethod(ReJitInfo * pInfo);

    void RemoveMethod(ReJitInfo * pInfo);

    ReJitInfo * GetMethods() { return m_pInfoList; }

    InternalFlags GetState();
};

//---------------------------------------------------------------------------------------
// One instance of this per rejit request for each MethodDesc*. One SharedReJitInfo
// corresponds to many ReJitInfos, as the SharedReJitInfo tracks the rejit request for
// the methodDef token whereas the ReJitInfo tracks the rejit request for each correspond
// MethodDesc* (instantiation). Points to actual generated code.
// 
// In the case of "pre-rejit" (see comment at top of rejit.cpp), a special "placeholder"
// instance of ReJitInfo is used to "remember" to jmp-stamp a not-yet-jitted-method once
// it finally gets jitted the first time.
// 
// Each ReJitManager contains a hash table of ReJitInfo instances, keyed by
// ReJitManager::m_key.
// 
// This is used primarily as a structure, so most of its members are left public.
//
struct ReJitInfo : public LoaderHeapAllocatedRejitStructure
{
public:
    // The size of the code used to jump stamp the prolog
    static const size_t JumpStubSize =
#if defined(_X86_) || defined(_AMD64_)
        5;
#else
#error "Need to define size of rejit jump-stamp for this platform"
        1;
#endif

    // Used by PtrSHash template as the key for this ReJitInfo.  For regular
    // ReJitInfos, the key is the MethodDesc*.  For placeholder ReJitInfos
    // (to facilitate pre-rejit), the key is (Module*, mdMethodDef).
    struct Key
    {
    public:
        enum
        {
            // The key has not yet had its values initialized
            kUninitialized          = 0x0,
            
            // The key represents a loaded MethodDesc, and is identified by the m_pMD
            // field
            kMethodDesc             = 0x1,
            
            // The key represents a "placeholder" ReJitInfo identified not by loaded
            // MethodDesc, but by the module and metadata token (m_pModule,
            // m_methodDef).
            kMetadataToken          = 0x2,
        };

        // Storage consists of a discriminated union between MethodDesc* or
        // (Module*, mdMethodDef), with the key type as the discriminator.
        union
        {
            TADDR m_pMD;
            TADDR m_pModule;
        };
        ULONG32             m_methodDef : 28;
        ULONG32             m_keyType   : 2;

        Key();
        Key(PTR_MethodDesc pMD);
        Key(PTR_Module pModule, mdMethodDef methodDef);
    };

    static COUNT_T Hash(Key key);

    enum InternalFlags 
    {
        // This ReJitInfo is either a placeholder (identified by module and
        // metadata token, rather than loaded MethodDesc) OR this ReJitInfo is
        // identified by a loaded MethodDesc that has been reverted OR not yet
        // been jump-stamped. In the last case, the time window where this
        // ReJitInfo would stay in kJumpNone is rather small, as
        // RequestReJIT() will immediately cause the originally JITted code to
        // be jump-stamped.
        kJumpNone               = 0x00000000,
        
        // This ReJitInfo is identified by a loaded MethodDesc that has been compiled and
        // jump-stamped, with the target being the prestub. The MethodDesc has not yet
        // been rejitted
        kJumpToPrestub          = 0x00000001,
        
        // This ReJitInfo is identified by a loaded MethodDesc that has been compiled AND
        // rejitted. The top of the originally JITted code has been jump-stamped, with
        // the target being the latest version of the rejitted code.
        kJumpToRejittedCode     = 0x00000002,

        kStateMask              = 0x0000000F,
    };

    Key                     m_key;
    DWORD                   m_dwInternalFlags;

    // The beginning of the rejitted code
    PCODE                   m_pCode;
    
    // The parent SharedReJitInfo, which manages the rejit request for all
    // instantiations.
    PTR_SharedReJitInfo const m_pShared;

    // My next sibling ReJitInfo for this rejit request (e.g., another
    // generic instantiation of the same method)
    PTR_ReJitInfo             m_pNext;
    
    // The originally JITted code that was overwritten with the jmp stamp.
    BYTE                    m_rgSavedCode[JumpStubSize];


    ReJitInfo(PTR_MethodDesc pMD, SharedReJitInfo * pShared);
    ReJitInfo(PTR_Module pModule, mdMethodDef methodDef, SharedReJitInfo * pShared);

    // Intentionally no destructor. ReJitInfo is allocated on a loader heap,
    // and will be freed (along with its associated SharedReJitInfo) when the
    // AD is unloaded.

    Key GetKey();
    PTR_MethodDesc GetMethodDesc();
    void GetModuleAndToken(Module ** ppModule, mdMethodDef * pMethodDef);
    void GetModuleAndTokenRegardlessOfKeyType(Module ** ppModule, mdMethodDef * pMethodDef);
    InternalFlags GetState();

    COR_ILMETHOD * GetIL();

    HRESULT JumpStampNativeCode(PCODE pCode = NULL);
    HRESULT UndoJumpStampNativeCode(BOOL fEESuspended);
    HRESULT UpdateJumpTarget(BOOL fEESuspended, PCODE pRejittedCode);
    HRESULT UpdateJumpStampHelper(BYTE* pbCode, INT64 i64OldValue, INT64 i64newValue, BOOL fContentionPossible);
    

protected:
    void CommonInit();
    INDEBUG(BOOL CodeIsSaved();)
};

//---------------------------------------------------------------------------------------
// Used by the SHash inside ReJitManager which maintains the set of ReJitInfo instances.
// 
class ReJitInfoTraits : public DefaultSHashTraits<PTR_ReJitInfo>
{
public:

    // explicitly declare local typedefs for these traits types, otherwise 
    // the compiler may get confused
    typedef DefaultSHashTraits<PTR_ReJitInfo> PARENT;
    typedef PARENT::element_t element_t;
    typedef PARENT::count_t count_t;

    typedef ReJitInfo::Key key_t;

    static key_t GetKey(const element_t &e);
    static BOOL Equals(key_t k1, key_t k2);
    static count_t Hash(key_t k);
    static bool IsNull(const element_t &e);
};

// RequestRejit and RequestRevert use these batches to accumulate ReJitInfos that need their
// jump stamps updated
class ReJitManager;
struct ReJitManagerJumpStampBatch
{
    ReJitManagerJumpStampBatch(ReJitManager * pReJitManager) : undoMethods(), preStubMethods()
    {
        LIMITED_METHOD_CONTRACT;
        this->pReJitManager = pReJitManager;
    }

    ReJitManager* pReJitManager;
    CDynArray<ReJitInfo *> undoMethods;
    CDynArray<ReJitInfo *> preStubMethods;
};

class ReJitManagerJumpStampBatchTraits : public DefaultSHashTraits<ReJitManagerJumpStampBatch *>
{
public:

    // explicitly declare local typedefs for these traits types, otherwise 
    // the compiler may get confused
    typedef DefaultSHashTraits<ReJitManagerJumpStampBatch *> PARENT;
    typedef PARENT::element_t element_t;
    typedef PARENT::count_t count_t;

    typedef ReJitManager * key_t;

    static key_t GetKey(const element_t &e)
    {
        return e->pReJitManager;
    }

    static BOOL Equals(key_t k1, key_t k2)
    {
        return (k1 == k2);
    }

    static count_t Hash(key_t k)
    {
        return (count_t)k;
    }

    static bool IsNull(const element_t &e)
    {
        return (e == NULL);
    }
};

struct ReJitReportErrorWorkItem
{
    Module* pModule;
    mdMethodDef methodDef;
    MethodDesc* pMethodDesc;
    HRESULT hrStatus;
};


#endif  // FEATURE_REJIT

//
// These holders are used by runtime code that is making new code
// available for execution, either by publishing jitted code
// or restoring NGEN code. It ensures the publishing is synchronized
// with rejit requests
//
class ReJitPublishMethodHolder
{
public:
#if !defined(FEATURE_REJIT) || defined(DACCESS_COMPILE) || defined(CROSSGEN_COMPILE)
    ReJitPublishMethodHolder(MethodDesc* pMethod, PCODE pCode) { }
#else
    ReJitPublishMethodHolder(MethodDesc* pMethod, PCODE pCode);
    ~ReJitPublishMethodHolder();
#endif

private:
#if defined(FEATURE_REJIT)
    MethodDesc * m_pMD;
    HRESULT m_hr;
#endif
};

class ReJitPublishMethodTableHolder
{
public:
#if !defined(FEATURE_REJIT) || defined(DACCESS_COMPILE) || defined(CROSSGEN_COMPILE)
    ReJitPublishMethodTableHolder(MethodTable* pMethodTable) { }
#else
    ReJitPublishMethodTableHolder(MethodTable* pMethodTable);
    ~ReJitPublishMethodTableHolder();
#endif

private:
#if defined(FEATURE_REJIT)
    MethodTable* m_pMethodTable;
    CDynArray<ReJitReportErrorWorkItem> m_errors;
#endif
};

//---------------------------------------------------------------------------------------
// The big honcho.  One of these per AppDomain, plus one for the
// SharedDomain.  Contains the hash table of ReJitInfo structures to manage
// every rejit and revert request for its owning domain.
// 
class ReJitManager
{
    friend class ClrDataAccess;
    friend class DacDbiInterfaceImpl;

    //I would have prefered to make these inner classes, but
    //then I can't friend them from crst easily.
    friend class ReJitPublishMethodHolder;
    friend class ReJitPublishMethodTableHolder;

private:

#ifdef FEATURE_REJIT

    // Hash table mapping MethodDesc* (or (ModuleID, mdMethodDef)) to its
    // ReJitInfos. One key may map to multiple ReJitInfos if there have been
    // multiple rejit requests made for the same MD. See
    // code:ReJitManager::ReJitManager#Invariants for more information.
    typedef SHash<ReJitInfoTraits> ReJitInfoHash;

    // One global crst (for the entire CLR instance) to synchronize
    // cross-ReJitManager operations, such as batch calls to RequestRejit and
    // RequestRevert (which modify multiple ReJitManager instances).
    static CrstStatic s_csGlobalRequest;

    // All The ReJitInfos (and their linked SharedReJitInfos) for this domain.
    ReJitInfoHash m_table;

    // The crst that synchronizes the data in m_table, including
    // adding/removing to m_table, as well as state changes made to
    // individual ReJitInfos & SharedReJitInfos in m_table.
    CrstExplicitInit    m_crstTable;

#endif //FEATURE_REJIT

public:
    // The ReJITManager takes care of grabbing its m_crstTable when necessary.  However,
    // for clients who need to do this explicitly (like ETW rundown), this holder may be
    // used.
    class TableLockHolder
#ifdef FEATURE_REJIT
        : public CrstHolder
#endif
    {
    public:
        TableLockHolder(ReJitManager * pReJitManager);
    };

    static void InitStatic();

    static BOOL IsReJITEnabled();

    static void OnAppDomainExit(AppDomain * pAppDomain);

    static HRESULT RequestReJIT(
        ULONG       cFunctions,
        ModuleID    rgModuleIDs[],
        mdMethodDef rgMethodDefs[]);
    
    static HRESULT RequestRevert(
        ULONG       cFunctions,
        ModuleID    rgModuleIDs[],
        mdMethodDef rgMethodDefs[],
        HRESULT     rgHrStatuses[]);

    static PCODE DoReJitIfNecessary(PTR_MethodDesc pMD);  // Invokes the jit, or returns previously rejitted code
    
    static void DoJumpStampForAssemblyIfNecessary(Assembly* pAssemblyToSearch);

    static DWORD GetCurrentReJitFlags(PTR_MethodDesc pMD);

    ReJitManager();

    void PreInit(BOOL fSharedDomain);

    ReJITID GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart);

    ReJITID GetReJitIdNoLock(PTR_MethodDesc pMD, PCODE pCodeStart);

    PCODE GetCodeStart(PTR_MethodDesc pMD, ReJITID reJitId);

    HRESULT GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * pcReJitIds, ReJITID reJitIds[]);

#ifdef FEATURE_REJIT


    INDEBUG(BOOL IsTableCrstOwnedByCurrentThread());

private:
    static HRESULT IsMethodSafeForReJit(PTR_MethodDesc pMD);
    static void ReportReJITError(ReJitReportErrorWorkItem* pErrorRecord);
    static void ReportReJITError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus);
    static HRESULT AddReJITError(ReJitInfo* pReJitInfo, HRESULT hrStatus, CDynArray<ReJitReportErrorWorkItem> * pErrors);
    static HRESULT AddReJITError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus, CDynArray<ReJitReportErrorWorkItem> * pErrors);
    HRESULT BatchUpdateJumpStamps(CDynArray<ReJitInfo *> * pUndoMethods, CDynArray<ReJitInfo *> * pPreStubMethods, CDynArray<ReJitReportErrorWorkItem> * pErrors);

    PCODE DoReJitIfNecessaryWorker(PTR_MethodDesc pMD);  // Invokes the jit, or returns previously rejitted code
    DWORD GetCurrentReJitFlagsWorker(PTR_MethodDesc pMD);

    HRESULT MarkAllInstantiationsForReJit(
        SharedReJitInfo * pSharedForAllGenericInstantiations,
        AppDomain * pAppDomainToSearch,
        PTR_Module pModuleContainingGenericDefinition,
        mdMethodDef methodDef,
        ReJitManagerJumpStampBatch* pJumpStampBatch,
        CDynArray<ReJitReportErrorWorkItem> * pRejitErrors);

    INDEBUG(BaseDomain * m_pDomain;)
        INDEBUG(void Dump(LPCSTR szIntroText);)
        INDEBUG(void AssertRestOfEntriesAreReverted(
        ReJitInfoHash::KeyIterator iter,
        ReJitInfoHash::KeyIterator end);)


    HRESULT DoJumpStampIfNecessary(MethodDesc* pMD, PCODE pCode);
    HRESULT MarkForReJit(PTR_MethodDesc pMD, SharedReJitInfo * pSharedToReuse, ReJitManagerJumpStampBatch* pJumpStampBatch, CDynArray<ReJitReportErrorWorkItem> * pRejitErrors, SharedReJitInfo ** ppSharedUsed);
    HRESULT MarkForReJit(PTR_Module pModule, mdMethodDef methodDef, ReJitManagerJumpStampBatch* pJumpStampBatch, CDynArray<ReJitReportErrorWorkItem> * pRejitErrors, SharedReJitInfo ** ppSharedUsed);
    HRESULT MarkForReJitHelper(
        PTR_MethodDesc pMD, 
        PTR_Module pModule, 
        mdMethodDef methodDef,
        SharedReJitInfo * pSharedToReuse,
        ReJitManagerJumpStampBatch* pJumpStampBatch,
        CDynArray<ReJitReportErrorWorkItem> * pRejitErrors,
        /* out */ SharedReJitInfo ** ppSharedUsed);
    HRESULT AddNewReJitInfo(
        PTR_MethodDesc pMD, 
        PTR_Module pModule,
        mdMethodDef methodDef,
        SharedReJitInfo * pShared,
        ReJitInfo ** ppInfo);
    HRESULT RequestRevertByToken(PTR_Module pModule, mdMethodDef methodDef);
    PTR_ReJitInfo FindReJitInfo(PTR_MethodDesc pMD, PCODE pCodeStart, ReJITID reJitId);
    PTR_ReJitInfo FindNonRevertedReJitInfo(PTR_Module pModule, mdMethodDef methodDef);
    PTR_ReJitInfo FindNonRevertedReJitInfo(PTR_MethodDesc pMD);
    PTR_ReJitInfo FindNonRevertedReJitInfoHelper(PTR_MethodDesc pMD, PTR_Module pModule, mdMethodDef methodDef);
    ReJitInfo* FindPreReJittedReJitInfo(ReJitInfoHash::KeyIterator beginIter, ReJitInfoHash::KeyIterator endIter);
    HRESULT Revert(SharedReJitInfo * pShared, ReJitManagerJumpStampBatch* pJumpStampBatch);
    PCODE   DoReJit(ReJitInfo * pInfo);
    ReJitInfoHash::KeyIterator GetBeginIterator(PTR_MethodDesc pMD);
    ReJitInfoHash::KeyIterator GetEndIterator(PTR_MethodDesc pMD);
    ReJitInfoHash::KeyIterator GetBeginIterator(PTR_Module pModule, mdMethodDef methodDef);
    ReJitInfoHash::KeyIterator GetEndIterator(PTR_Module pModule, mdMethodDef methodDef);
    void RemoveReJitInfosFromDomain(AppDomain * pAppDomain);

#endif // FEATURE_REJIT

};

#include "rejit.inl"

#endif // _REJIT_H_