Fix reading Time zone rules using Julian days (#17672)
[platform/upstream/coreclr.git] / src / jit / jitgcinfo.h
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 //  Garbage-collector information
6 //  Keeps track of which variables hold pointers.
7 //  Generates the GC-tables
8
9 #ifndef _JITGCINFO_H_
10 #define _JITGCINFO_H_
11
12 #include "gcinfotypes.h"
13
14 #ifndef JIT32_GCENCODER
15 #include "gcinfoencoder.h"
16 #endif
17
18 /*****************************************************************************/
19
20 #ifndef JIT32_GCENCODER
21 // Shash typedefs
22 struct RegSlotIdKey
23 {
24     unsigned short m_regNum;
25     unsigned short m_flags;
26
27     RegSlotIdKey()
28     {
29     }
30
31     RegSlotIdKey(unsigned short regNum, unsigned short flags) : m_regNum(regNum), m_flags(flags)
32     {
33     }
34
35     static unsigned GetHashCode(RegSlotIdKey rsk)
36     {
37         return (rsk.m_flags << (8 * sizeof(unsigned short))) + rsk.m_regNum;
38     }
39
40     static bool Equals(RegSlotIdKey rsk1, RegSlotIdKey rsk2)
41     {
42         return rsk1.m_regNum == rsk2.m_regNum && rsk1.m_flags == rsk2.m_flags;
43     }
44 };
45
46 struct StackSlotIdKey
47 {
48     int            m_offset;
49     bool           m_fpRel;
50     unsigned short m_flags;
51
52     StackSlotIdKey()
53     {
54     }
55
56     StackSlotIdKey(int offset, bool fpRel, unsigned short flags) : m_offset(offset), m_fpRel(fpRel), m_flags(flags)
57     {
58     }
59
60     static unsigned GetHashCode(StackSlotIdKey ssk)
61     {
62         return (ssk.m_flags << (8 * sizeof(unsigned short))) ^ (unsigned)ssk.m_offset ^ (ssk.m_fpRel ? 0x1000000 : 0);
63     }
64
65     static bool Equals(StackSlotIdKey ssk1, StackSlotIdKey ssk2)
66     {
67         return ssk1.m_offset == ssk2.m_offset && ssk1.m_fpRel == ssk2.m_fpRel && ssk1.m_flags == ssk2.m_flags;
68     }
69 };
70
71 typedef JitHashTable<RegSlotIdKey, RegSlotIdKey, GcSlotId>     RegSlotMap;
72 typedef JitHashTable<StackSlotIdKey, StackSlotIdKey, GcSlotId> StackSlotMap;
73 #endif
74
75 typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, VARSET_TP*> NodeToVarsetPtrMap;
76
77 class GCInfo
78 {
79     friend class CodeGen;
80
81 private:
82     Compiler* compiler;
83     RegSet*   regSet;
84
85 public:
86     GCInfo(Compiler* theCompiler);
87
88     void gcResetForBB();
89
90     void gcMarkRegSetGCref(regMaskTP regMask DEBUGARG(bool forceOutput = false));
91     void gcMarkRegSetByref(regMaskTP regMask DEBUGARG(bool forceOutput = false));
92     void gcMarkRegSetNpt(regMaskTP regMask DEBUGARG(bool forceOutput = false));
93     void gcMarkRegPtrVal(regNumber reg, var_types type);
94     void gcMarkRegPtrVal(GenTree* tree);
95
96 #ifdef DEBUG
97     void gcDspGCrefSetChanges(regMaskTP gcRegGCrefSetNew DEBUGARG(bool forceOutput = false));
98     void gcDspByrefSetChanges(regMaskTP gcRegByrefSetNew DEBUGARG(bool forceOutput = false));
99 #endif // DEBUG
100
101     /*****************************************************************************/
102
103     //-------------------------------------------------------------------------
104     //
105     //  The following keeps track of which registers currently hold pointer
106     //  values.
107     //
108
109     regMaskTP gcRegGCrefSetCur; // current regs holding GCrefs
110     regMaskTP gcRegByrefSetCur; // current regs holding Byrefs
111
112     VARSET_TP gcTrkStkPtrLcls; // set of tracked stack ptr lcls (GCref and Byref) - no args
113     VARSET_TP gcVarPtrSetCur;  // currently live part of "gcTrkStkPtrLcls"
114
115     //-------------------------------------------------------------------------
116     //
117     //  The following keeps track of the lifetimes of non-register variables that
118     //  hold pointers.
119     //
120
121     struct varPtrDsc
122     {
123         varPtrDsc* vpdNext;
124
125         unsigned vpdVarNum; // which variable is this about?
126
127         unsigned vpdBegOfs; // the offset where life starts
128         unsigned vpdEndOfs; // the offset where life starts
129     };
130
131     varPtrDsc* gcVarPtrList;
132     varPtrDsc* gcVarPtrLast;
133
134     void gcVarPtrSetInit();
135
136     /*****************************************************************************/
137
138     //  'pointer value' register tracking and argument pushes/pops tracking.
139
140     enum rpdArgType_t
141     {
142         rpdARG_POP,
143         rpdARG_PUSH,
144         rpdARG_KILL
145     };
146
147     struct regPtrDsc
148     {
149         regPtrDsc* rpdNext; // next entry in the list
150         unsigned   rpdOffs; // the offset of the instruction
151
152         union // 2-16 byte union (depending on architecture)
153         {
154             struct // 2-16 byte structure (depending on architecture)
155             {
156                 regMaskSmall rpdAdd; // regptr bitset being added
157                 regMaskSmall rpdDel; // regptr bitset being removed
158             } rpdCompiler;
159
160             unsigned short rpdPtrArg; // arg offset or popped arg count
161         };
162
163 #ifndef JIT32_GCENCODER
164         unsigned char rpdCallInstrSize; // Length of the call instruction.
165 #endif
166
167         unsigned short rpdArg : 1;     // is this an argument descriptor?
168         unsigned short rpdArgType : 2; // is this an argument push,pop, or kill?
169         rpdArgType_t   rpdArgTypeGet()
170         {
171             return (rpdArgType_t)rpdArgType;
172         }
173
174         unsigned short rpdGCtype : 2; // is this a pointer, after all?
175         GCtype         rpdGCtypeGet()
176         {
177             return (GCtype)rpdGCtype;
178         }
179
180         unsigned short rpdIsThis : 1;                       // is it the 'this' pointer
181         unsigned short rpdCall : 1;                         // is this a true call site?
182         unsigned short : 1;                                 // Padding bit, so next two start on a byte boundary
183         unsigned short rpdCallGCrefRegs : CNT_CALLEE_SAVED; // Callee-saved registers containing GC pointers.
184         unsigned short rpdCallByrefRegs : CNT_CALLEE_SAVED; // Callee-saved registers containing byrefs.
185
186 #ifndef JIT32_GCENCODER
187         bool rpdIsCallInstr()
188         {
189             return rpdCall && rpdCallInstrSize != 0;
190         }
191 #endif
192     };
193
194     regPtrDsc* gcRegPtrList;
195     regPtrDsc* gcRegPtrLast;
196     unsigned   gcPtrArgCnt;
197
198 #ifndef JIT32_GCENCODER
199     enum MakeRegPtrMode
200     {
201         MAKE_REG_PTR_MODE_ASSIGN_SLOTS,
202         MAKE_REG_PTR_MODE_DO_WORK
203     };
204
205     // This method has two modes.  In the "assign slots" mode, it figures out what stack locations are
206     // used to contain GC references, and whether those locations contain byrefs or pinning references,
207     // building up mappings from tuples of <offset X byref/pinning> to the corresponding slot id.
208     // In the "do work" mode, we use these slot ids to actually declare live ranges to the encoder.
209     void gcMakeVarPtrTable(GcInfoEncoder* gcInfoEncoder, MakeRegPtrMode mode);
210
211     // At instruction offset "instrOffset," the set of registers indicated by "regMask" is becoming live or dead,
212     // depending on whether "newState" is "GC_SLOT_DEAD" or "GC_SLOT_LIVE".  The subset of registers whose corresponding
213     // bits are set in "byRefMask" contain by-refs rather than regular GC pointers. "*pPtrRegs" is the set of
214     // registers currently known to contain pointers.  If "mode" is "ASSIGN_SLOTS", computes and records slot
215     // ids for the registers.  If "mode" is "DO_WORK", informs "gcInfoEncoder" about the state transition,
216     // using the previously assigned slot ids, and updates "*pPtrRegs" appropriately.
217     void gcInfoRecordGCRegStateChange(GcInfoEncoder* gcInfoEncoder,
218                                       MakeRegPtrMode mode,
219                                       unsigned       instrOffset,
220                                       regMaskSmall   regMask,
221                                       GcSlotState    newState,
222                                       regMaskSmall   byRefMask,
223                                       regMaskSmall*  pPtrRegs);
224
225     // regPtrDsc is also used to encode writes to the outgoing argument space (as if they were pushes)
226     void gcInfoRecordGCStackArgLive(GcInfoEncoder* gcInfoEncoder, MakeRegPtrMode mode, regPtrDsc* genStackPtr);
227
228     // Walk all the pushes between genStackPtrFirst (inclusive) and genStackPtrLast (exclusive)
229     // and mark them as going dead at instrOffset
230     void gcInfoRecordGCStackArgsDead(GcInfoEncoder* gcInfoEncoder,
231                                      unsigned       instrOffset,
232                                      regPtrDsc*     genStackPtrFirst,
233                                      regPtrDsc*     genStackPtrLast);
234
235 #endif
236
237 #if MEASURE_PTRTAB_SIZE
238     static size_t s_gcRegPtrDscSize;
239     static size_t s_gcTotalPtrTabSize;
240 #endif
241
242     regPtrDsc* gcRegPtrAllocDsc();
243
244     /*****************************************************************************/
245
246     //-------------------------------------------------------------------------
247     //
248     //  If we're not generating fully interruptible code, we create a simple
249     //  linked list of call descriptors.
250     //
251
252     struct CallDsc
253     {
254         CallDsc* cdNext;
255         void*    cdBlock; // the code block of the call
256         unsigned cdOffs;  // the offset     of the call
257 #ifndef JIT32_GCENCODER
258         unsigned short cdCallInstrSize; // the size       of the call instruction.
259 #endif
260
261         unsigned short cdArgCnt;
262
263         union {
264             struct // used if cdArgCnt == 0
265             {
266                 unsigned cdArgMask;      // ptr arg bitfield
267                 unsigned cdByrefArgMask; // byref qualifier for cdArgMask
268             } u1;
269
270             unsigned* cdArgTable; // used if cdArgCnt != 0
271         };
272
273         regMaskSmall cdGCrefRegs;
274         regMaskSmall cdByrefRegs;
275     };
276
277     CallDsc* gcCallDescList;
278     CallDsc* gcCallDescLast;
279
280     //-------------------------------------------------------------------------
281
282     void gcCountForHeader(UNALIGNED unsigned int* untrackedCount, UNALIGNED unsigned int* varPtrTableSize);
283
284 #ifdef JIT32_GCENCODER
285     size_t gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, unsigned codeSize, size_t* pArgTabOffset);
286 #else
287     RegSlotMap*   m_regSlotMap;
288     StackSlotMap* m_stackSlotMap;
289     // This method has two modes.  In the "assign slots" mode, it figures out what registers and stack
290     // locations are used to contain GC references, and whether those locations contain byrefs or pinning
291     // references, building up mappings from tuples of <reg/offset X byref/pinning> to the corresponding
292     // slot id (in the two member fields declared above).  In the "do work" mode, we use these slot ids to
293     // actually declare live ranges to the encoder.
294     void gcMakeRegPtrTable(GcInfoEncoder* gcInfoEncoder,
295                            unsigned       codeSize,
296                            unsigned       prologSize,
297                            MakeRegPtrMode mode,
298                            unsigned*      callCntRef);
299 #endif
300
301 #ifdef JIT32_GCENCODER
302     size_t gcPtrTableSize(const InfoHdr& header, unsigned codeSize, size_t* pArgTabOffset);
303     BYTE* gcPtrTableSave(BYTE* destPtr, const InfoHdr& header, unsigned codeSize, size_t* pArgTabOffset);
304 #endif
305     void gcRegPtrSetInit();
306     /*****************************************************************************/
307
308     // This enumeration yields the result of the analysis below, whether a store
309     // requires a write barrier:
310     enum WriteBarrierForm
311     {
312         WBF_NoBarrier,                     // No barrier is required
313         WBF_BarrierUnknown,                // A barrier is required, no information on checked/unchecked.
314         WBF_BarrierChecked,                // A checked barrier is required.
315         WBF_BarrierUnchecked,              // An unchecked barrier is required.
316         WBF_NoBarrier_CheckNotHeapInDebug, // We believe that no barrier is required because the
317                                            // target is not in the heap -- but in debug build use a
318                                            // barrier call that verifies this property.  (Because the
319                                            // target not being in the heap relies on a convention that
320                                            // might accidentally be violated in the future.)
321     };
322
323     WriteBarrierForm gcIsWriteBarrierCandidate(GenTree* tgt, GenTree* assignVal);
324     bool gcIsWriteBarrierAsgNode(GenTree* op);
325
326     // Returns a WriteBarrierForm decision based on the form of "tgtAddr", which is assumed to be the
327     // argument of a GT_IND LHS.
328     WriteBarrierForm gcWriteBarrierFormFromTargetAddress(GenTree* tgtAddr);
329
330     //-------------------------------------------------------------------------
331     //
332     //  These record the info about the procedure in the info-block
333     //
334     CLANG_FORMAT_COMMENT_ANCHOR;
335
336 #ifdef JIT32_GCENCODER
337 private:
338     BYTE* gcEpilogTable;
339
340     unsigned gcEpilogPrevOffset;
341
342     size_t gcInfoBlockHdrSave(BYTE*    dest,
343                               int      mask,
344                               unsigned methodSize,
345                               unsigned prologSize,
346                               unsigned epilogSize,
347                               InfoHdr* header,
348                               int*     s_cached);
349
350 public:
351     static void gcInitEncoderLookupTable();
352
353 private:
354     static size_t gcRecordEpilog(void* pCallBackData, unsigned offset);
355 #else // JIT32_GCENCODER
356     void gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSize, unsigned prologSize);
357
358 #endif // JIT32_GCENCODER
359
360 #if !defined(JIT32_GCENCODER) || defined(WIN64EXCEPTIONS)
361
362     // This method expands the tracked stack variables lifetimes so that any lifetimes within filters
363     // are reported as pinned.
364     void gcMarkFilterVarsPinned();
365
366     // Insert a varPtrDsc to gcVarPtrList that was generated by splitting lifetimes
367     void gcInsertVarPtrDscSplit(varPtrDsc* desc, varPtrDsc* begin);
368
369 #ifdef DEBUG
370     void gcDumpVarPtrDsc(varPtrDsc* desc);
371 #endif // DEBUG
372
373 #endif // !defined(JIT32_GCENCODER) || defined(WIN64EXCEPTIONS)
374
375 #if DUMP_GC_TABLES
376
377     void gcFindPtrsInFrame(const void* infoBlock, const void* codeBlock, unsigned offs);
378
379 #ifdef JIT32_GCENCODER
380     unsigned gcInfoBlockHdrDump(const BYTE* table,
381                                 InfoHdr*    header,      /* OUT */
382                                 unsigned*   methodSize); /* OUT */
383
384     unsigned gcDumpPtrTable(const BYTE* table, const InfoHdr& header, unsigned methodSize);
385
386 #endif // JIT32_GCENCODER
387 #endif // DUMP_GC_TABLES
388
389 #ifndef LEGACY_BACKEND
390     // This method updates the appropriate reg masks when a variable is moved.
391 public:
392     void gcUpdateForRegVarMove(regMaskTP srcMask, regMaskTP dstMask, LclVarDsc* varDsc);
393 #endif // !LEGACY_BACKEND
394
395 private:
396     ReturnKind getReturnKind();
397 };
398
399 inline unsigned char encodeUnsigned(BYTE* dest, unsigned value)
400 {
401     unsigned char size = 1;
402     unsigned      tmp  = value;
403     while (tmp > 0x7F)
404     {
405         tmp >>= 7;
406         assert(size < 6); // Invariant.
407         size++;
408     }
409     if (dest)
410     {
411         // write the bytes starting at the end of dest in LSB to MSB order
412         BYTE* p    = dest + size;
413         BYTE  cont = 0; // The last byte has no continuation flag
414         while (value > 0x7F)
415         {
416             *--p = cont | (value & 0x7f);
417             value >>= 7;
418             cont = 0x80; // Non last bytes have a continuation flag
419         }
420         *--p = cont | (BYTE)value; // Now write the first byte
421         assert(p == dest);
422     }
423     return size;
424 }
425
426 inline unsigned char encodeUDelta(BYTE* dest, unsigned value, unsigned lastValue)
427 {
428     assert(value >= lastValue);
429     return encodeUnsigned(dest, value - lastValue);
430 }
431
432 inline unsigned char encodeSigned(BYTE* dest, int val)
433 {
434     unsigned char size  = 1;
435     unsigned      value = val;
436     BYTE          neg   = 0;
437     if (val < 0)
438     {
439         value = -val;
440         neg   = 0x40;
441     }
442     unsigned tmp = value;
443     while (tmp > 0x3F)
444     {
445         tmp >>= 7;
446         assert(size < 16); // Definitely sufficient for unsigned.  Fits in an unsigned char, certainly.
447         size++;
448     }
449     if (dest)
450     {
451         // write the bytes starting at the end of dest in LSB to MSB order
452         BYTE* p    = dest + size;
453         BYTE  cont = 0; // The last byte has no continuation flag
454         while (value > 0x3F)
455         {
456             *--p = cont | (value & 0x7f);
457             value >>= 7;
458             cont = 0x80; // Non last bytes have a continuation flag
459         }
460         *--p = neg | cont | (BYTE)value; // Now write the first byte
461         assert(p == dest);
462     }
463     return size;
464 }
465
466 #endif // _JITGCINFO_H_