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
22struct 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
46struct 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
71typedef JitHashTable<RegSlotIdKey, RegSlotIdKey, GcSlotId> RegSlotMap;
72typedef JitHashTable<StackSlotIdKey, StackSlotIdKey, GcSlotId> StackSlotMap;
73#endif
74
75typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, VARSET_TP*> NodeToVarsetPtrMap;
76
77class GCInfo
78{
79 friend class CodeGen;
80
81private:
82 Compiler* compiler;
83 RegSet* regSet;
84
85public:
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
95#ifdef DEBUG
96 void gcDspGCrefSetChanges(regMaskTP gcRegGCrefSetNew DEBUGARG(bool forceOutput = false));
97 void gcDspByrefSetChanges(regMaskTP gcRegByrefSetNew DEBUGARG(bool forceOutput = false));
98#endif // DEBUG
99
100 /*****************************************************************************/
101
102 //-------------------------------------------------------------------------
103 //
104 // The following keeps track of which registers currently hold pointer
105 // values.
106 //
107
108 regMaskTP gcRegGCrefSetCur; // current regs holding GCrefs
109 regMaskTP gcRegByrefSetCur; // current regs holding Byrefs
110
111 VARSET_TP gcTrkStkPtrLcls; // set of tracked stack ptr lcls (GCref and Byref) - no args
112 VARSET_TP gcVarPtrSetCur; // currently live part of "gcTrkStkPtrLcls"
113
114 //-------------------------------------------------------------------------
115 //
116 // The following keeps track of the lifetimes of non-register variables that
117 // hold pointers.
118 //
119
120 struct varPtrDsc
121 {
122 varPtrDsc* vpdNext;
123
124 unsigned vpdVarNum; // which variable is this about?
125
126 unsigned vpdBegOfs; // the offset where life starts
127 unsigned vpdEndOfs; // the offset where life starts
128 };
129
130 varPtrDsc* gcVarPtrList;
131 varPtrDsc* gcVarPtrLast;
132
133 void gcVarPtrSetInit();
134
135 /*****************************************************************************/
136
137 // 'pointer value' register tracking and argument pushes/pops tracking.
138
139 enum rpdArgType_t
140 {
141 rpdARG_POP,
142 rpdARG_PUSH,
143 rpdARG_KILL
144 };
145
146 struct regPtrDsc
147 {
148 regPtrDsc* rpdNext; // next entry in the list
149 unsigned rpdOffs; // the offset of the instruction
150
151 union // 2-16 byte union (depending on architecture)
152 {
153 struct // 2-16 byte structure (depending on architecture)
154 {
155 regMaskSmall rpdAdd; // regptr bitset being added
156 regMaskSmall rpdDel; // regptr bitset being removed
157 } rpdCompiler;
158
159 unsigned short rpdPtrArg; // arg offset or popped arg count
160 };
161
162#ifndef JIT32_GCENCODER
163 unsigned char rpdCallInstrSize; // Length of the call instruction.
164#endif
165
166 unsigned short rpdArg : 1; // is this an argument descriptor?
167 unsigned short rpdArgType : 2; // is this an argument push,pop, or kill?
168 rpdArgType_t rpdArgTypeGet()
169 {
170 return (rpdArgType_t)rpdArgType;
171 }
172
173 unsigned short rpdGCtype : 2; // is this a pointer, after all?
174 GCtype rpdGCtypeGet()
175 {
176 return (GCtype)rpdGCtype;
177 }
178
179 unsigned short rpdIsThis : 1; // is it the 'this' pointer
180 unsigned short rpdCall : 1; // is this a true call site?
181 unsigned short : 1; // Padding bit, so next two start on a byte boundary
182 unsigned short rpdCallGCrefRegs : CNT_CALLEE_SAVED; // Callee-saved registers containing GC pointers.
183 unsigned short rpdCallByrefRegs : CNT_CALLEE_SAVED; // Callee-saved registers containing byrefs.
184
185#ifndef JIT32_GCENCODER
186 bool rpdIsCallInstr()
187 {
188 return rpdCall && rpdCallInstrSize != 0;
189 }
190#endif
191 };
192
193 regPtrDsc* gcRegPtrList;
194 regPtrDsc* gcRegPtrLast;
195 unsigned gcPtrArgCnt;
196
197#ifndef JIT32_GCENCODER
198 enum MakeRegPtrMode
199 {
200 MAKE_REG_PTR_MODE_ASSIGN_SLOTS,
201 MAKE_REG_PTR_MODE_DO_WORK
202 };
203
204 // This method has two modes. In the "assign slots" mode, it figures out what stack locations are
205 // used to contain GC references, and whether those locations contain byrefs or pinning references,
206 // building up mappings from tuples of <offset X byref/pinning> to the corresponding slot id.
207 // In the "do work" mode, we use these slot ids to actually declare live ranges to the encoder.
208 void gcMakeVarPtrTable(GcInfoEncoder* gcInfoEncoder, MakeRegPtrMode mode);
209
210 // At instruction offset "instrOffset," the set of registers indicated by "regMask" is becoming live or dead,
211 // depending on whether "newState" is "GC_SLOT_DEAD" or "GC_SLOT_LIVE". The subset of registers whose corresponding
212 // bits are set in "byRefMask" contain by-refs rather than regular GC pointers. "*pPtrRegs" is the set of
213 // registers currently known to contain pointers. If "mode" is "ASSIGN_SLOTS", computes and records slot
214 // ids for the registers. If "mode" is "DO_WORK", informs "gcInfoEncoder" about the state transition,
215 // using the previously assigned slot ids, and updates "*pPtrRegs" appropriately.
216 void gcInfoRecordGCRegStateChange(GcInfoEncoder* gcInfoEncoder,
217 MakeRegPtrMode mode,
218 unsigned instrOffset,
219 regMaskSmall regMask,
220 GcSlotState newState,
221 regMaskSmall byRefMask,
222 regMaskSmall* pPtrRegs);
223
224 // regPtrDsc is also used to encode writes to the outgoing argument space (as if they were pushes)
225 void gcInfoRecordGCStackArgLive(GcInfoEncoder* gcInfoEncoder, MakeRegPtrMode mode, regPtrDsc* genStackPtr);
226
227 // Walk all the pushes between genStackPtrFirst (inclusive) and genStackPtrLast (exclusive)
228 // and mark them as going dead at instrOffset
229 void gcInfoRecordGCStackArgsDead(GcInfoEncoder* gcInfoEncoder,
230 unsigned instrOffset,
231 regPtrDsc* genStackPtrFirst,
232 regPtrDsc* genStackPtrLast);
233
234 // Update the flags for a stack allocated object
235 void gcUpdateFlagForStackAllocatedObjects(GcSlotFlags& flags);
236
237#endif
238
239#if MEASURE_PTRTAB_SIZE
240 static size_t s_gcRegPtrDscSize;
241 static size_t s_gcTotalPtrTabSize;
242#endif
243
244 regPtrDsc* gcRegPtrAllocDsc();
245
246 /*****************************************************************************/
247
248 //-------------------------------------------------------------------------
249 //
250 // If we're not generating fully interruptible code, we create a simple
251 // linked list of call descriptors.
252 //
253
254 struct CallDsc
255 {
256 CallDsc* cdNext;
257 void* cdBlock; // the code block of the call
258 unsigned cdOffs; // the offset of the call
259#ifndef JIT32_GCENCODER
260 unsigned short cdCallInstrSize; // the size of the call instruction.
261#endif
262
263 unsigned short cdArgCnt;
264
265 union {
266 struct // used if cdArgCnt == 0
267 {
268 unsigned cdArgMask; // ptr arg bitfield
269 unsigned cdByrefArgMask; // byref qualifier for cdArgMask
270 } u1;
271
272 unsigned* cdArgTable; // used if cdArgCnt != 0
273 };
274
275 regMaskSmall cdGCrefRegs;
276 regMaskSmall cdByrefRegs;
277 };
278
279 CallDsc* gcCallDescList;
280 CallDsc* gcCallDescLast;
281
282 //-------------------------------------------------------------------------
283
284 void gcCountForHeader(UNALIGNED unsigned int* untrackedCount, UNALIGNED unsigned int* varPtrTableSize);
285
286#ifdef JIT32_GCENCODER
287 size_t gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, unsigned codeSize, size_t* pArgTabOffset);
288#else
289 RegSlotMap* m_regSlotMap;
290 StackSlotMap* m_stackSlotMap;
291 // This method has two modes. In the "assign slots" mode, it figures out what registers and stack
292 // locations are used to contain GC references, and whether those locations contain byrefs or pinning
293 // references, building up mappings from tuples of <reg/offset X byref/pinning> to the corresponding
294 // slot id (in the two member fields declared above). In the "do work" mode, we use these slot ids to
295 // actually declare live ranges to the encoder.
296 void gcMakeRegPtrTable(GcInfoEncoder* gcInfoEncoder,
297 unsigned codeSize,
298 unsigned prologSize,
299 MakeRegPtrMode mode,
300 unsigned* callCntRef);
301#endif
302
303#ifdef JIT32_GCENCODER
304 size_t gcPtrTableSize(const InfoHdr& header, unsigned codeSize, size_t* pArgTabOffset);
305 BYTE* gcPtrTableSave(BYTE* destPtr, const InfoHdr& header, unsigned codeSize, size_t* pArgTabOffset);
306#endif
307 void gcRegPtrSetInit();
308 /*****************************************************************************/
309
310 // This enumeration yields the result of the analysis below, whether a store
311 // requires a write barrier:
312 enum WriteBarrierForm
313 {
314 WBF_NoBarrier, // No barrier is required
315 WBF_BarrierUnknown, // A barrier is required, no information on checked/unchecked.
316 WBF_BarrierChecked, // A checked barrier is required.
317 WBF_BarrierUnchecked, // An unchecked barrier is required.
318 WBF_NoBarrier_CheckNotHeapInDebug, // We believe that no barrier is required because the
319 // target is not in the heap -- but in debug build use a
320 // barrier call that verifies this property. (Because the
321 // target not being in the heap relies on a convention that
322 // might accidentally be violated in the future.)
323 };
324
325 WriteBarrierForm gcIsWriteBarrierCandidate(GenTree* tgt, GenTree* assignVal);
326 bool gcIsWriteBarrierStoreIndNode(GenTree* op);
327
328 // Returns a WriteBarrierForm decision based on the form of "tgtAddr", which is assumed to be the
329 // argument of a GT_IND LHS.
330 WriteBarrierForm gcWriteBarrierFormFromTargetAddress(GenTree* tgtAddr);
331
332 //-------------------------------------------------------------------------
333 //
334 // These record the info about the procedure in the info-block
335 //
336 CLANG_FORMAT_COMMENT_ANCHOR;
337
338#ifdef JIT32_GCENCODER
339private:
340 BYTE* gcEpilogTable;
341
342 unsigned gcEpilogPrevOffset;
343
344 size_t gcInfoBlockHdrSave(BYTE* dest,
345 int mask,
346 unsigned methodSize,
347 unsigned prologSize,
348 unsigned epilogSize,
349 InfoHdr* header,
350 int* s_cached);
351
352public:
353 static void gcInitEncoderLookupTable();
354
355private:
356 static size_t gcRecordEpilog(void* pCallBackData, unsigned offset);
357#else // JIT32_GCENCODER
358 void gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSize, unsigned prologSize);
359
360#endif // JIT32_GCENCODER
361
362#if !defined(JIT32_GCENCODER) || defined(WIN64EXCEPTIONS)
363
364 // This method expands the tracked stack variables lifetimes so that any lifetimes within filters
365 // are reported as pinned.
366 void gcMarkFilterVarsPinned();
367
368 // Insert a varPtrDsc to gcVarPtrList that was generated by splitting lifetimes
369 void gcInsertVarPtrDscSplit(varPtrDsc* desc, varPtrDsc* begin);
370
371#ifdef DEBUG
372 void gcDumpVarPtrDsc(varPtrDsc* desc);
373#endif // DEBUG
374
375#endif // !defined(JIT32_GCENCODER) || defined(WIN64EXCEPTIONS)
376
377#if DUMP_GC_TABLES
378
379 void gcFindPtrsInFrame(const void* infoBlock, const void* codeBlock, unsigned offs);
380
381#ifdef JIT32_GCENCODER
382 unsigned gcInfoBlockHdrDump(const BYTE* table,
383 InfoHdr* header, /* OUT */
384 unsigned* methodSize); /* OUT */
385
386 unsigned gcDumpPtrTable(const BYTE* table, const InfoHdr& header, unsigned methodSize);
387
388#endif // JIT32_GCENCODER
389#endif // DUMP_GC_TABLES
390
391public:
392 // This method updates the appropriate reg masks when a variable is moved.
393 void gcUpdateForRegVarMove(regMaskTP srcMask, regMaskTP dstMask, LclVarDsc* varDsc);
394
395private:
396 ReturnKind getReturnKind();
397};
398
399inline 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
426inline unsigned char encodeUDelta(BYTE* dest, unsigned value, unsigned lastValue)
427{
428 assert(value >= lastValue);
429 return encodeUnsigned(dest, value - lastValue);
430}
431
432inline 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_
467