summaryrefslogtreecommitdiff
path: root/src/jit/jitgcinfo.h
blob: 4063bafe153b5d9a6c5ec36f527f78acd6db9275 (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
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

//  Garbage-collector information
//  Keeps track of which variables hold pointers.
//  Generates the GC-tables

#ifndef _JITGCINFO_H_
#define _JITGCINFO_H_
#include "gcinfo.h"

#ifndef JIT32_GCENCODER
#include "gcinfoencoder.h"
#include "gcinfotypes.h"
#endif

/*****************************************************************************/

#ifndef JIT32_GCENCODER
// Shash typedefs
struct RegSlotIdKey
{
    unsigned short m_regNum;
    unsigned short m_flags;

    RegSlotIdKey() {}

    RegSlotIdKey(unsigned short regNum, unsigned short flags) : m_regNum(regNum), m_flags(flags) {}

    static unsigned GetHashCode(RegSlotIdKey rsk)
    {
        return (rsk.m_flags << (8*sizeof(unsigned short))) + rsk.m_regNum;
    }

    static bool Equals(RegSlotIdKey rsk1, RegSlotIdKey rsk2)
    {
        return rsk1.m_regNum == rsk2.m_regNum && rsk1.m_flags == rsk2.m_flags;
    }
};

struct StackSlotIdKey
{
    int m_offset;
    bool  m_fpRel;
    unsigned short m_flags;

    StackSlotIdKey() {}

    StackSlotIdKey(int offset, bool fpRel, unsigned short flags) : m_offset(offset), m_fpRel(fpRel), m_flags(flags) {}

    static unsigned GetHashCode(StackSlotIdKey ssk)
    {
        return (ssk.m_flags << (8*sizeof(unsigned short))) ^ (unsigned)ssk.m_offset ^ (ssk.m_fpRel ? 0x1000000 : 0);
    }

    static bool Equals(StackSlotIdKey ssk1, StackSlotIdKey ssk2)
    {
        return ssk1.m_offset == ssk2.m_offset && ssk1.m_fpRel == ssk2.m_fpRel && ssk1.m_flags == ssk2.m_flags;
    }
};

typedef SimplerHashTable<RegSlotIdKey, RegSlotIdKey, GcSlotId, DefaultSimplerHashBehavior>     RegSlotMap;
typedef SimplerHashTable<StackSlotIdKey, StackSlotIdKey, GcSlotId, DefaultSimplerHashBehavior> StackSlotMap;
#endif

typedef SimplerHashTable<GenTreePtr, PtrKeyFuncs<GenTree>, VARSET_TP*, DefaultSimplerHashBehavior> NodeToVarsetPtrMap;

class GCInfo
{
    friend class CodeGen;

private:
    Compiler*           compiler;
    RegSet*             regSet;

public :

    GCInfo(Compiler* theCompiler);

    void                gcResetForBB        ();

    void                gcMarkRegSetGCref   (regMaskTP  regMask DEBUG_ARG(bool forceOutput = false));
    void                gcMarkRegSetByref   (regMaskTP  regMask DEBUG_ARG(bool forceOutput = false));
    void                gcMarkRegSetNpt     (regMaskTP  regMask DEBUG_ARG(bool forceOutput = false));
    void                gcMarkRegPtrVal     (regNumber  reg, var_types type);
    void                gcMarkRegPtrVal     (GenTreePtr tree);

#ifdef DEBUG    
    void                gcDspGCrefSetChanges(regMaskTP gcRegGCrefSetNew DEBUG_ARG(bool forceOutput = false));
    void                gcDspByrefSetChanges(regMaskTP gcRegByrefSetNew DEBUG_ARG(bool forceOutput = false));
#endif // DEBUG    

/*****************************************************************************/


    //-------------------------------------------------------------------------
    //
    //  The following keeps track of which registers currently hold pointer
    //  values.
    //

    regMaskTP           gcRegGCrefSetCur;   // current regs holding GCrefs
    regMaskTP           gcRegByrefSetCur;   // current regs holding Byrefs

    VARSET_TP           gcTrkStkPtrLcls;    // set of tracked stack ptr lcls (GCref and Byref) - no args
    VARSET_TP           gcVarPtrSetCur;     // currently live part of "gcTrkStkPtrLcls"

    //-------------------------------------------------------------------------
    //
    //  The following keeps track of the lifetimes of non-register variables that
    //  hold pointers.
    //

    struct varPtrDsc
    {
        varPtrDsc   *   vpdNext;

        unsigned        vpdVarNum;         // which variable is this about?

        unsigned        vpdBegOfs ;        // the offset where life starts
        unsigned        vpdEndOfs;         // the offset where life starts
    };

    varPtrDsc   *       gcVarPtrList;
    varPtrDsc   *       gcVarPtrLast;

    void                gcVarPtrSetInit();

/*****************************************************************************/

    //  'pointer value' register tracking and argument pushes/pops tracking.

    enum    rpdArgType_t    { rpdARG_POP, rpdARG_PUSH, rpdARG_KILL };

    struct  regPtrDsc
    {
        regPtrDsc  *          rpdNext;            // next entry in the list
        unsigned              rpdOffs;            // the offset of the instruction

        union                                     // 2-16 byte union (depending on architecture)
        {
            struct                                // 2-16 byte structure (depending on architecture)
            {
                regMaskSmall  rpdAdd;             // regptr bitset being added
                regMaskSmall  rpdDel;             // regptr bitset being removed
            }
                              rpdCompiler;

            unsigned short    rpdPtrArg;          // arg offset or popped arg count
        };

#ifndef JIT32_GCENCODER
        unsigned char         rpdCallInstrSize;    // Length of the call instruction.
#endif

        unsigned short        rpdArg          :1;  // is this an argument descriptor?
        unsigned short        rpdArgType      :2;  // is this an argument push,pop, or kill?
        rpdArgType_t          rpdArgTypeGet() { return (rpdArgType_t) rpdArgType; }

        unsigned short        rpdGCtype       :2;  // is this a pointer, after all?
        GCtype                rpdGCtypeGet()  { return (GCtype) rpdGCtype; }

        unsigned short        rpdIsThis       :1;  // is it the 'this' pointer
        unsigned short        rpdCall         :1;  // is this a true call site?
        unsigned short                        :1;  // Padding bit, so next two start on a byte boundary
        unsigned short        rpdCallGCrefRegs:CNT_CALLEE_SAVED; // Callee-saved registers containing GC pointers.
        unsigned short        rpdCallByrefRegs:CNT_CALLEE_SAVED; // Callee-saved registers containing byrefs.

#ifndef JIT32_GCENCODER
        bool                  rpdIsCallInstr()
        {
            return rpdCall && rpdCallInstrSize != 0;
        }
#endif
    };

    regPtrDsc  *        gcRegPtrList;
    regPtrDsc  *        gcRegPtrLast;
    unsigned            gcPtrArgCnt;

#ifndef JIT32_GCENCODER
    enum MakeRegPtrMode
    {
        MAKE_REG_PTR_MODE_ASSIGN_SLOTS,
        MAKE_REG_PTR_MODE_DO_WORK
    };

    // This method has two modes.  In the "assign slots" mode, it figures out what stack locations are
    // used to contain GC references, and whether those locations contain byrefs or pinning references,
    // building up mappings from tuples of <offset X byref/pinning> to the corresponding slot id.
    // In the "do work" mode, we use these slot ids to actually declare live ranges to the encoder.
    void                gcMakeVarPtrTable (GcInfoEncoder* gcInfoEncoder,
                                           MakeRegPtrMode mode);

    // This method expands the tracked stack variables lifetimes so that any lifetimes within filters
    // are reported as pinned.
    void                gcMarkFilterVarsPinned();


    // At instruction offset "instrOffset," the set of registers indicated by "regMask" is becoming live or dead, depending
    // on whether "newState" is "GC_SLOT_DEAD" or "GC_SLOT_LIVE".  The subset of registers whose corresponding
    // bits are set in "byRefMask" contain by-refs rather than regular GC pointers. "*pPtrRegs" is the set of
    // registers currently known to contain pointers.  If "mode" is "ASSIGN_SLOTS", computes and records slot
    // ids for the registers.  If "mode" is "DO_WORK", informs "gcInfoEncoder" about the state transition,
    // using the previously assigned slot ids, and updates "*pPtrRegs" appropriately.
    void gcInfoRecordGCRegStateChange(GcInfoEncoder* gcInfoEncoder,
                                      MakeRegPtrMode mode,
                                      unsigned instrOffset,
                                      regMaskSmall regMask, 
                                      GcSlotState newState,
                                      regMaskSmall byRefMask,
                                      regMaskSmall* pPtrRegs);

    // regPtrDsc is also used to encode writes to the outgoing argument space (as if they were pushes)
    void gcInfoRecordGCStackArgLive  (GcInfoEncoder* gcInfoEncoder,
                                      MakeRegPtrMode mode,
                                      regPtrDsc* genStackPtr);

    // Walk all the pushes between genStackPtrFirst (inclusive) and genStackPtrLast (exclusive)
    // and mark them as going dead at instrOffset
    void gcInfoRecordGCStackArgsDead (GcInfoEncoder* gcInfoEncoder,
                                      unsigned instrOffset,
                                      regPtrDsc* genStackPtrFirst,
                                      regPtrDsc* genStackPtrLast);

#endif

#if MEASURE_PTRTAB_SIZE
    static size_t       s_gcRegPtrDscSize;
    static size_t       s_gcTotalPtrTabSize;
#endif

    regPtrDsc  *        gcRegPtrAllocDsc      ();

/*****************************************************************************/


    //-------------------------------------------------------------------------
    //
    //  If we're not generating fully interruptible code, we create a simple
    //  linked list of call descriptors.
    //

    struct  CallDsc
    {
        CallDsc     *       cdNext;
        void        *       cdBlock;        // the code block of the call
        unsigned            cdOffs;         // the offset     of the call
#ifndef JIT32_GCENCODER
        unsigned short      cdCallInstrSize;// the size       of the call instruction.
#endif

        unsigned short      cdArgCnt;

        union
        {
            struct                          // used if cdArgCnt == 0
            {
                unsigned    cdArgMask;      // ptr arg bitfield
                unsigned    cdByrefArgMask; // byref qualifier for cdArgMask
            } u1;

            unsigned    *   cdArgTable;     // used if cdArgCnt != 0
        };

        regMaskSmall        cdGCrefRegs;
        regMaskSmall        cdByrefRegs;
    };

    CallDsc    *        gcCallDescList;
    CallDsc    *        gcCallDescLast;

    //-------------------------------------------------------------------------

    void                gcCountForHeader  (UNALIGNED unsigned int * untrackedCount,
                                           UNALIGNED unsigned int * varPtrTableSize);

#ifdef JIT32_GCENCODER
    size_t              gcMakeRegPtrTable (BYTE *         dest,
                                           int            mask,
                                           const InfoHdr& header,
                                           unsigned       codeSize,
                                           size_t*       pArgTabOffset);
#else
    RegSlotMap*   m_regSlotMap;
    StackSlotMap* m_stackSlotMap;
    // This method has two modes.  In the "assign slots" mode, it figures out what registers and stack
    // locations are used to contain GC references, and whether those locations contain byrefs or pinning
    // references, building up mappings from tuples of <reg/offset X byref/pinning> to the corresponding
    // slot id (in the two member fields declared above).  In the "do work" mode, we use these slot ids to 
    // actually declare live ranges to the encoder.
    void                gcMakeRegPtrTable (GcInfoEncoder* gcInfoEncoder,
                                           unsigned       codeSize,
                                           unsigned       prologSize,
                                           MakeRegPtrMode mode);
#endif

#ifdef JIT32_GCENCODER
    size_t              gcPtrTableSize    (const InfoHdr& header,
                                           unsigned       codeSize,
                                           size_t*       pArgTabOffset);
    BYTE    *           gcPtrTableSave    (BYTE *         destPtr,
                                           const InfoHdr& header,
                                           unsigned       codeSize,
                                           size_t*       pArgTabOffset);
#endif
    void                gcRegPtrSetInit   ();
/*****************************************************************************/

    // This enumeration yields the result of the analysis below, whether a store
    // requires a write barrier:
    enum WriteBarrierForm
    {
        WBF_NoBarrier,          // No barrier is required
        WBF_BarrierUnknown,     // A barrier is required, no information on checked/unchecked.
        WBF_BarrierChecked,     // A checked barrier is required.
        WBF_BarrierUnchecked,   // An unchecked barrier is required.
        WBF_NoBarrier_CheckNotHeapInDebug,  // We believe that no barrier is required because the
                                            // target is not in the heap -- but in debug build use a
                                            // barrier call that verifies this property.  (Because the
                                            // target not being in the heap relies on a convention that
                                            // might accidentally be violated in the future.)
    };

    WriteBarrierForm        gcIsWriteBarrierCandidate(GenTreePtr tgt, GenTreePtr assignVal);
    bool                    gcIsWriteBarrierAsgNode  (GenTreePtr op);

    // Returns a WriteBarrierForm decision based on the form of "tgtAddr", which is assumed to be the
    // argument of a GT_IND LHS.
    WriteBarrierForm        gcWriteBarrierFormFromTargetAddress(GenTreePtr tgtAddr);

    //-------------------------------------------------------------------------
    //
    //  These record the info about the procedure in the info-block
    //

#ifdef JIT32_GCENCODER
private:
    BYTE    *           gcEpilogTable;

    unsigned            gcEpilogPrevOffset;

    size_t              gcInfoBlockHdrSave(BYTE *       dest,
                                           int          mask,
                                           unsigned     methodSize,
                                           unsigned     prologSize,
                                           unsigned     epilogSize,
                                           InfoHdr *    header,
                                           int *        s_cached);
public:
    static void         gcInitEncoderLookupTable ();
private:
    static size_t       gcRecordEpilog    (void *       pCallBackData,
                                           unsigned     offset);
#else // JIT32_GCENCODER
    void                gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder,
                                           unsigned       methodSize,
                                           unsigned       prologSize);

#ifdef DEBUG
    void                gcDumpVarPtrDsc   (varPtrDsc*     desc);
#endif // DEBUG

#endif // JIT32_GCENCODER


#if DUMP_GC_TABLES

    void                gcFindPtrsInFrame (const void * infoBlock,
                                           const void * codeBlock,
                                           unsigned     offs);

#ifdef JIT32_GCENCODER
    unsigned            gcInfoBlockHdrDump(const BYTE * table,
                                           InfoHdr    * header,       /* OUT */
                                           unsigned   * methodSize);  /* OUT */

    unsigned            gcDumpPtrTable    (const BYTE *   table,
                                           const InfoHdr& header,
                                           unsigned       methodSize);

#endif // JIT32_GCENCODER
#endif // DUMP_GC_TABLES

#ifndef LEGACY_BACKEND
    // This method updates the appropriate reg masks when a variable is moved.
public:
    void                gcUpdateForRegVarMove(regMaskTP srcMask, regMaskTP dstMask, LclVarDsc *varDsc);
#endif // !LEGACY_BACKEND
};


inline
unsigned char encodeUnsigned(BYTE *dest, unsigned value)
{
    unsigned char size = 1;
    unsigned tmp = value;
    while (tmp > 0x7F) {
        tmp >>= 7;
        assert(size < 6);  // Invariant.
        size++;
    }
    if (dest) {
        // write the bytes starting at the end of dest in LSB to MSB order
        BYTE* p    = dest + size;
        BYTE  cont = 0; // The last byte has no continuation flag
        while (value > 0x7F) {
            *--p = cont | (value & 0x7f);
            value >>= 7;
            cont = 0x80;        // Non last bytes have a continuation flag
        }
        *--p = cont | (BYTE)value;    // Now write the first byte
        assert(p == dest);
    }
    return size;
}

inline
unsigned char encodeUDelta(BYTE *dest, unsigned value, unsigned lastValue)
{
    assert(value >= lastValue);
    return encodeUnsigned(dest, value - lastValue);
}

inline
unsigned char encodeSigned(BYTE *dest, int val)
{
    unsigned char size  = 1;
    unsigned value = val;
    BYTE  neg = 0;
    if ( val < 0) {
        value = -val;
        neg = 0x40;
    }
    unsigned tmp   = value;
    while (tmp > 0x3F) {
        tmp >>= 7;
        assert(size < 16); // Definitely sufficient for unsigned.  Fits in an unsigned char, certainly.
        size++;
    }
    if (dest) {
        // write the bytes starting at the end of dest in LSB to MSB order
        BYTE* p    = dest + size;
        BYTE  cont = 0; // The last byte has no continuation flag
        while (value > 0x3F) {
            *--p = cont | (value & 0x7f);
            value >>= 7;
            cont = 0x80;        // Non last bytes have a continuation flag
        }
        *--p = neg | cont | (BYTE)value; // Now write the first byte
        assert(p == dest);
    }
    return size;
}



#endif // _JITGCINFO_H_