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.
5 // Garbage-collector information
6 // Keeps track of which variables hold pointers.
7 // Generates the GC-tables
12 #include "gcinfotypes.h"
14 #ifndef JIT32_GCENCODER
15 #include "gcinfoencoder.h"
18 /*****************************************************************************/
20 #ifndef JIT32_GCENCODER
24 unsigned short m_regNum;
25 unsigned short m_flags;
31 RegSlotIdKey(unsigned short regNum, unsigned short flags) : m_regNum(regNum), m_flags(flags)
35 static unsigned GetHashCode(RegSlotIdKey rsk)
37 return (rsk.m_flags << (8 * sizeof(unsigned short))) + rsk.m_regNum;
40 static bool Equals(RegSlotIdKey rsk1, RegSlotIdKey rsk2)
42 return rsk1.m_regNum == rsk2.m_regNum && rsk1.m_flags == rsk2.m_flags;
50 unsigned short m_flags;
56 StackSlotIdKey(int offset, bool fpRel, unsigned short flags) : m_offset(offset), m_fpRel(fpRel), m_flags(flags)
60 static unsigned GetHashCode(StackSlotIdKey ssk)
62 return (ssk.m_flags << (8 * sizeof(unsigned short))) ^ (unsigned)ssk.m_offset ^ (ssk.m_fpRel ? 0x1000000 : 0);
65 static bool Equals(StackSlotIdKey ssk1, StackSlotIdKey ssk2)
67 return ssk1.m_offset == ssk2.m_offset && ssk1.m_fpRel == ssk2.m_fpRel && ssk1.m_flags == ssk2.m_flags;
71 typedef JitHashTable<RegSlotIdKey, RegSlotIdKey, GcSlotId> RegSlotMap;
72 typedef JitHashTable<StackSlotIdKey, StackSlotIdKey, GcSlotId> StackSlotMap;
75 typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, VARSET_TP*> NodeToVarsetPtrMap;
86 GCInfo(Compiler* theCompiler);
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);
97 void gcDspGCrefSetChanges(regMaskTP gcRegGCrefSetNew DEBUGARG(bool forceOutput = false));
98 void gcDspByrefSetChanges(regMaskTP gcRegByrefSetNew DEBUGARG(bool forceOutput = false));
101 /*****************************************************************************/
103 //-------------------------------------------------------------------------
105 // The following keeps track of which registers currently hold pointer
109 regMaskTP gcRegGCrefSetCur; // current regs holding GCrefs
110 regMaskTP gcRegByrefSetCur; // current regs holding Byrefs
112 VARSET_TP gcTrkStkPtrLcls; // set of tracked stack ptr lcls (GCref and Byref) - no args
113 VARSET_TP gcVarPtrSetCur; // currently live part of "gcTrkStkPtrLcls"
115 //-------------------------------------------------------------------------
117 // The following keeps track of the lifetimes of non-register variables that
125 unsigned vpdVarNum; // which variable is this about?
127 unsigned vpdBegOfs; // the offset where life starts
128 unsigned vpdEndOfs; // the offset where life starts
131 varPtrDsc* gcVarPtrList;
132 varPtrDsc* gcVarPtrLast;
134 void gcVarPtrSetInit();
136 /*****************************************************************************/
138 // 'pointer value' register tracking and argument pushes/pops tracking.
149 regPtrDsc* rpdNext; // next entry in the list
150 unsigned rpdOffs; // the offset of the instruction
152 union // 2-16 byte union (depending on architecture)
154 struct // 2-16 byte structure (depending on architecture)
156 regMaskSmall rpdAdd; // regptr bitset being added
157 regMaskSmall rpdDel; // regptr bitset being removed
160 unsigned short rpdPtrArg; // arg offset or popped arg count
163 #ifndef JIT32_GCENCODER
164 unsigned char rpdCallInstrSize; // Length of the call instruction.
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()
171 return (rpdArgType_t)rpdArgType;
174 unsigned short rpdGCtype : 2; // is this a pointer, after all?
175 GCtype rpdGCtypeGet()
177 return (GCtype)rpdGCtype;
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.
186 #ifndef JIT32_GCENCODER
187 bool rpdIsCallInstr()
189 return rpdCall && rpdCallInstrSize != 0;
194 regPtrDsc* gcRegPtrList;
195 regPtrDsc* gcRegPtrLast;
196 unsigned gcPtrArgCnt;
198 #ifndef JIT32_GCENCODER
201 MAKE_REG_PTR_MODE_ASSIGN_SLOTS,
202 MAKE_REG_PTR_MODE_DO_WORK
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);
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,
219 unsigned instrOffset,
220 regMaskSmall regMask,
221 GcSlotState newState,
222 regMaskSmall byRefMask,
223 regMaskSmall* pPtrRegs);
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);
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);
237 #if MEASURE_PTRTAB_SIZE
238 static size_t s_gcRegPtrDscSize;
239 static size_t s_gcTotalPtrTabSize;
242 regPtrDsc* gcRegPtrAllocDsc();
244 /*****************************************************************************/
246 //-------------------------------------------------------------------------
248 // If we're not generating fully interruptible code, we create a simple
249 // linked list of call descriptors.
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.
261 unsigned short cdArgCnt;
264 struct // used if cdArgCnt == 0
266 unsigned cdArgMask; // ptr arg bitfield
267 unsigned cdByrefArgMask; // byref qualifier for cdArgMask
270 unsigned* cdArgTable; // used if cdArgCnt != 0
273 regMaskSmall cdGCrefRegs;
274 regMaskSmall cdByrefRegs;
277 CallDsc* gcCallDescList;
278 CallDsc* gcCallDescLast;
280 //-------------------------------------------------------------------------
282 void gcCountForHeader(UNALIGNED unsigned int* untrackedCount, UNALIGNED unsigned int* varPtrTableSize);
284 #ifdef JIT32_GCENCODER
285 size_t gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, unsigned codeSize, size_t* pArgTabOffset);
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,
298 unsigned* callCntRef);
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);
305 void gcRegPtrSetInit();
306 /*****************************************************************************/
308 // This enumeration yields the result of the analysis below, whether a store
309 // requires a write barrier:
310 enum WriteBarrierForm
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.)
323 WriteBarrierForm gcIsWriteBarrierCandidate(GenTree* tgt, GenTree* assignVal);
324 bool gcIsWriteBarrierAsgNode(GenTree* op);
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);
330 //-------------------------------------------------------------------------
332 // These record the info about the procedure in the info-block
334 CLANG_FORMAT_COMMENT_ANCHOR;
336 #ifdef JIT32_GCENCODER
340 unsigned gcEpilogPrevOffset;
342 size_t gcInfoBlockHdrSave(BYTE* dest,
351 static void gcInitEncoderLookupTable();
354 static size_t gcRecordEpilog(void* pCallBackData, unsigned offset);
355 #else // JIT32_GCENCODER
356 void gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSize, unsigned prologSize);
358 #endif // JIT32_GCENCODER
360 #if !defined(JIT32_GCENCODER) || defined(WIN64EXCEPTIONS)
362 // This method expands the tracked stack variables lifetimes so that any lifetimes within filters
363 // are reported as pinned.
364 void gcMarkFilterVarsPinned();
366 // Insert a varPtrDsc to gcVarPtrList that was generated by splitting lifetimes
367 void gcInsertVarPtrDscSplit(varPtrDsc* desc, varPtrDsc* begin);
370 void gcDumpVarPtrDsc(varPtrDsc* desc);
373 #endif // !defined(JIT32_GCENCODER) || defined(WIN64EXCEPTIONS)
377 void gcFindPtrsInFrame(const void* infoBlock, const void* codeBlock, unsigned offs);
379 #ifdef JIT32_GCENCODER
380 unsigned gcInfoBlockHdrDump(const BYTE* table,
381 InfoHdr* header, /* OUT */
382 unsigned* methodSize); /* OUT */
384 unsigned gcDumpPtrTable(const BYTE* table, const InfoHdr& header, unsigned methodSize);
386 #endif // JIT32_GCENCODER
387 #endif // DUMP_GC_TABLES
389 #ifndef LEGACY_BACKEND
390 // This method updates the appropriate reg masks when a variable is moved.
392 void gcUpdateForRegVarMove(regMaskTP srcMask, regMaskTP dstMask, LclVarDsc* varDsc);
393 #endif // !LEGACY_BACKEND
396 ReturnKind getReturnKind();
399 inline unsigned char encodeUnsigned(BYTE* dest, unsigned value)
401 unsigned char size = 1;
402 unsigned tmp = value;
406 assert(size < 6); // Invariant.
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
416 *--p = cont | (value & 0x7f);
418 cont = 0x80; // Non last bytes have a continuation flag
420 *--p = cont | (BYTE)value; // Now write the first byte
426 inline unsigned char encodeUDelta(BYTE* dest, unsigned value, unsigned lastValue)
428 assert(value >= lastValue);
429 return encodeUnsigned(dest, value - lastValue);
432 inline unsigned char encodeSigned(BYTE* dest, int val)
434 unsigned char size = 1;
435 unsigned value = val;
442 unsigned tmp = value;
446 assert(size < 16); // Definitely sufficient for unsigned. Fits in an unsigned char, certainly.
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
456 *--p = cont | (value & 0x7f);
458 cont = 0x80; // Non last bytes have a continuation flag
460 *--p = neg | cont | (BYTE)value; // Now write the first byte
466 #endif // _JITGCINFO_H_