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/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7XX XX
8XX GCInfo XX
9XX XX
10XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12*/
13
14#include "jitpch.h"
15#ifdef _MSC_VER
16#pragma hdrstop
17#endif
18
19#include "gcinfo.h"
20#include "emit.h"
21#include "jitgcinfo.h"
22
23#ifdef _TARGET_AMD64_
24#include "gcinfoencoder.h" //this includes a LOT of other files too
25#endif
26
27/*****************************************************************************/
28/*****************************************************************************/
29
30/*****************************************************************************/
31
32extern int JITGcBarrierCall;
33
34/*****************************************************************************/
35
36#if MEASURE_PTRTAB_SIZE
37/* static */ size_t GCInfo::s_gcRegPtrDscSize = 0;
38/* static */ size_t GCInfo::s_gcTotalPtrTabSize = 0;
39#endif // MEASURE_PTRTAB_SIZE
40
41/*
42XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
43XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
44XX GCInfo XX
45XX XX
46XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
47XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
48*/
49
50GCInfo::GCInfo(Compiler* theCompiler) : compiler(theCompiler)
51{
52 regSet = nullptr;
53 gcVarPtrList = nullptr;
54 gcVarPtrLast = nullptr;
55 gcRegPtrList = nullptr;
56 gcRegPtrLast = nullptr;
57 gcPtrArgCnt = 0;
58 gcCallDescList = nullptr;
59 gcCallDescLast = nullptr;
60#ifdef JIT32_GCENCODER
61 gcEpilogTable = nullptr;
62#else // !JIT32_GCENCODER
63 m_regSlotMap = nullptr;
64 m_stackSlotMap = nullptr;
65#endif // JIT32_GCENCODER
66}
67
68/*****************************************************************************/
69/*****************************************************************************
70 * Reset tracking info at the start of a basic block.
71 */
72
73void GCInfo::gcResetForBB()
74{
75 gcRegGCrefSetCur = RBM_NONE;
76 gcRegByrefSetCur = RBM_NONE;
77 VarSetOps::AssignNoCopy(compiler, gcVarPtrSetCur, VarSetOps::MakeEmpty(compiler));
78}
79
80#ifdef DEBUG
81
82/*****************************************************************************
83 *
84 * Print the changes in the gcRegGCrefSetCur sets.
85 */
86
87void GCInfo::gcDspGCrefSetChanges(regMaskTP gcRegGCrefSetNew DEBUGARG(bool forceOutput))
88{
89 if (compiler->verbose)
90 {
91 if (forceOutput || (gcRegGCrefSetCur != gcRegGCrefSetNew))
92 {
93 printf("\t\t\t\t\t\t\tGC regs: ");
94 if (gcRegGCrefSetCur == gcRegGCrefSetNew)
95 {
96 printf("(unchanged) ");
97 }
98 else
99 {
100 printRegMaskInt(gcRegGCrefSetCur);
101 compiler->getEmitter()->emitDispRegSet(gcRegGCrefSetCur);
102 printf(" => ");
103 }
104 printRegMaskInt(gcRegGCrefSetNew);
105 compiler->getEmitter()->emitDispRegSet(gcRegGCrefSetNew);
106 printf("\n");
107 }
108 }
109}
110
111/*****************************************************************************
112 *
113 * Print the changes in the gcRegByrefSetCur sets.
114 */
115
116void GCInfo::gcDspByrefSetChanges(regMaskTP gcRegByrefSetNew DEBUGARG(bool forceOutput))
117{
118 if (compiler->verbose)
119 {
120 if (forceOutput || (gcRegByrefSetCur != gcRegByrefSetNew))
121 {
122 printf("\t\t\t\t\t\t\tByref regs: ");
123 if (gcRegByrefSetCur == gcRegByrefSetNew)
124 {
125 printf("(unchanged) ");
126 }
127 else
128 {
129 printRegMaskInt(gcRegByrefSetCur);
130 compiler->getEmitter()->emitDispRegSet(gcRegByrefSetCur);
131 printf(" => ");
132 }
133 printRegMaskInt(gcRegByrefSetNew);
134 compiler->getEmitter()->emitDispRegSet(gcRegByrefSetNew);
135 printf("\n");
136 }
137 }
138}
139
140#endif // DEBUG
141
142/*****************************************************************************
143 *
144 * Mark the set of registers given by the specified mask as holding
145 * GCref pointer values.
146 */
147
148void GCInfo::gcMarkRegSetGCref(regMaskTP regMask DEBUGARG(bool forceOutput))
149{
150#ifdef DEBUG
151 if (compiler->compRegSetCheckLevel == 0)
152 {
153 // This set of registers are going to hold REFs.
154 // Make sure they were not holding BYREFs.
155 assert((gcRegByrefSetCur & regMask) == 0);
156 }
157#endif
158
159 regMaskTP gcRegByrefSetNew = gcRegByrefSetCur & ~regMask; // Clear it if set in Byref mask
160 regMaskTP gcRegGCrefSetNew = gcRegGCrefSetCur | regMask; // Set it in GCref mask
161
162 INDEBUG(gcDspGCrefSetChanges(gcRegGCrefSetNew, forceOutput));
163 INDEBUG(gcDspByrefSetChanges(gcRegByrefSetNew));
164
165 gcRegByrefSetCur = gcRegByrefSetNew;
166 gcRegGCrefSetCur = gcRegGCrefSetNew;
167}
168
169/*****************************************************************************
170 *
171 * Mark the set of registers given by the specified mask as holding
172 * Byref pointer values.
173 */
174
175void GCInfo::gcMarkRegSetByref(regMaskTP regMask DEBUGARG(bool forceOutput))
176{
177 regMaskTP gcRegByrefSetNew = gcRegByrefSetCur | regMask; // Set it in Byref mask
178 regMaskTP gcRegGCrefSetNew = gcRegGCrefSetCur & ~regMask; // Clear it if set in GCref mask
179
180 INDEBUG(gcDspGCrefSetChanges(gcRegGCrefSetNew));
181 INDEBUG(gcDspByrefSetChanges(gcRegByrefSetNew, forceOutput));
182
183 gcRegByrefSetCur = gcRegByrefSetNew;
184 gcRegGCrefSetCur = gcRegGCrefSetNew;
185}
186
187/*****************************************************************************
188 *
189 * Mark the set of registers given by the specified mask as holding
190 * non-pointer values.
191 */
192
193void GCInfo::gcMarkRegSetNpt(regMaskTP regMask DEBUGARG(bool forceOutput))
194{
195 /* NOTE: don't unmark any live register variables */
196
197 regMaskTP gcRegByrefSetNew = gcRegByrefSetCur & ~(regMask & ~regSet->rsMaskVars);
198 regMaskTP gcRegGCrefSetNew = gcRegGCrefSetCur & ~(regMask & ~regSet->rsMaskVars);
199
200 INDEBUG(gcDspGCrefSetChanges(gcRegGCrefSetNew, forceOutput));
201 INDEBUG(gcDspByrefSetChanges(gcRegByrefSetNew, forceOutput));
202
203 gcRegByrefSetCur = gcRegByrefSetNew;
204 gcRegGCrefSetCur = gcRegGCrefSetNew;
205}
206
207/*****************************************************************************
208 *
209 * Mark the specified register as now holding a value of the given type.
210 */
211
212void GCInfo::gcMarkRegPtrVal(regNumber reg, var_types type)
213{
214 regMaskTP regMask = genRegMask(reg);
215
216 switch (type)
217 {
218 case TYP_REF:
219 gcMarkRegSetGCref(regMask);
220 break;
221 case TYP_BYREF:
222 gcMarkRegSetByref(regMask);
223 break;
224 default:
225 gcMarkRegSetNpt(regMask);
226 break;
227 }
228}
229
230/*****************************************************************************/
231
232GCInfo::WriteBarrierForm GCInfo::gcIsWriteBarrierCandidate(GenTree* tgt, GenTree* assignVal)
233{
234 /* Are we storing a GC ptr? */
235
236 if (!varTypeIsGC(tgt->TypeGet()))
237 {
238 return WBF_NoBarrier;
239 }
240
241 /* Ignore any assignments of NULL */
242
243 // 'assignVal' can be the constant Null or something else (LclVar, etc..)
244 // that is known to be null via Value Numbering.
245 if (assignVal->GetVN(VNK_Liberal) == ValueNumStore::VNForNull())
246 {
247 return WBF_NoBarrier;
248 }
249
250 if (assignVal->gtOper == GT_CNS_INT && assignVal->gtIntCon.gtIconVal == 0)
251 {
252 return WBF_NoBarrier;
253 }
254
255 /* Where are we storing into? */
256
257 tgt = tgt->gtEffectiveVal();
258
259 switch (tgt->gtOper)
260 {
261
262 case GT_STOREIND:
263 case GT_IND: /* Could be the managed heap */
264 if (tgt->TypeGet() == TYP_BYREF)
265 {
266 // Byref values cannot be in managed heap.
267 // This case occurs for Span<T>.
268 return WBF_NoBarrier;
269 }
270 return gcWriteBarrierFormFromTargetAddress(tgt->gtOp.gtOp1);
271
272 case GT_LEA:
273 return gcWriteBarrierFormFromTargetAddress(tgt->AsAddrMode()->Base());
274
275 case GT_ARR_ELEM: /* Definitely in the managed heap */
276 case GT_CLS_VAR:
277 return WBF_BarrierUnchecked;
278
279 case GT_LCL_VAR: /* Definitely not in the managed heap */
280 case GT_LCL_FLD:
281 case GT_STORE_LCL_VAR:
282 case GT_STORE_LCL_FLD:
283 return WBF_NoBarrier;
284
285 default:
286 break;
287 }
288
289 assert(!"Missing case in gcIsWriteBarrierCandidate");
290
291 return WBF_NoBarrier;
292}
293
294bool GCInfo::gcIsWriteBarrierStoreIndNode(GenTree* op)
295{
296 assert(op->OperIs(GT_STOREIND));
297
298 return gcIsWriteBarrierCandidate(op, op->gtOp.gtOp2) != WBF_NoBarrier;
299}
300
301/*****************************************************************************/
302/*****************************************************************************
303 *
304 * Initialize the non-register pointer variable tracking logic.
305 */
306
307void GCInfo::gcVarPtrSetInit()
308{
309 VarSetOps::AssignNoCopy(compiler, gcVarPtrSetCur, VarSetOps::MakeEmpty(compiler));
310
311 /* Initialize the list of lifetime entries */
312 gcVarPtrList = gcVarPtrLast = nullptr;
313}
314
315/*****************************************************************************
316 *
317 * Allocate a new pointer register set / pointer argument entry and append
318 * it to the list.
319 */
320
321GCInfo::regPtrDsc* GCInfo::gcRegPtrAllocDsc()
322{
323 regPtrDsc* regPtrNext;
324
325 assert(compiler->genFullPtrRegMap);
326
327 /* Allocate a new entry and initialize it */
328
329 regPtrNext = new (compiler, CMK_GC) regPtrDsc;
330
331 regPtrNext->rpdIsThis = FALSE;
332
333 regPtrNext->rpdOffs = 0;
334 regPtrNext->rpdNext = nullptr;
335
336 // Append the entry to the end of the list.
337 if (gcRegPtrLast == nullptr)
338 {
339 assert(gcRegPtrList == nullptr);
340 gcRegPtrList = gcRegPtrLast = regPtrNext;
341 }
342 else
343 {
344 assert(gcRegPtrList != nullptr);
345 gcRegPtrLast->rpdNext = regPtrNext;
346 gcRegPtrLast = regPtrNext;
347 }
348
349#if MEASURE_PTRTAB_SIZE
350 s_gcRegPtrDscSize += sizeof(*regPtrNext);
351#endif
352
353 return regPtrNext;
354}
355
356/*****************************************************************************
357 *
358 * Compute the various counts that get stored in the info block header.
359 */
360
361void GCInfo::gcCountForHeader(UNALIGNED unsigned int* untrackedCount, UNALIGNED unsigned int* varPtrTableSize)
362{
363 unsigned varNum;
364 LclVarDsc* varDsc;
365 varPtrDsc* varTmp;
366
367 bool thisKeptAliveIsInUntracked = false; // did we track "this" in a synchronized method?
368 unsigned int count = 0;
369
370 /* Count the untracked locals and non-enregistered args */
371
372 for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; varNum++, varDsc++)
373 {
374 if (varTypeIsGC(varDsc->TypeGet()))
375 {
376 if (compiler->lvaIsFieldOfDependentlyPromotedStruct(varDsc))
377 {
378 // Field local of a PROMOTION_TYPE_DEPENDENT struct must have been
379 // reported through its parent local
380 continue;
381 }
382
383 /* Do we have an argument or local variable? */
384 if (!varDsc->lvIsParam)
385 {
386 if (varDsc->lvTracked || !varDsc->lvOnFrame)
387 {
388 continue;
389 }
390 }
391 else
392 {
393 /* Stack-passed arguments which are not enregistered
394 * are always reported in this "untracked stack
395 * pointers" section of the GC info even if lvTracked==true
396 */
397
398 /* Has this argument been fully enregistered? */
399 CLANG_FORMAT_COMMENT_ANCHOR;
400
401 if (!varDsc->lvOnFrame)
402 {
403 /* if a CEE_JMP has been used, then we need to report all the arguments
404 even if they are enregistered, since we will be using this value
405 in JMP call. Note that this is subtle as we require that
406 argument offsets are always fixed up properly even if lvRegister
407 is set */
408 if (!compiler->compJmpOpUsed)
409 {
410 continue;
411 }
412 }
413 else
414 {
415 if (!varDsc->lvOnFrame)
416 {
417 /* If this non-enregistered pointer arg is never
418 * used, we don't need to report it
419 */
420 assert(varDsc->lvRefCnt() == 0);
421 continue;
422 }
423 else if (varDsc->lvIsRegArg && varDsc->lvTracked)
424 {
425 /* If this register-passed arg is tracked, then
426 * it has been allocated space near the other
427 * pointer variables and we have accurate life-
428 * time info. It will be reported with
429 * gcVarPtrList in the "tracked-pointer" section
430 */
431
432 continue;
433 }
434 }
435 }
436
437#if !defined(JIT32_GCENCODER) || !defined(WIN64EXCEPTIONS)
438 // For x86/WIN64EXCEPTIONS, "this" must always be in untracked variables
439 // so we cannot have "this" in variable lifetimes
440 if (compiler->lvaIsOriginalThisArg(varNum) && compiler->lvaKeepAliveAndReportThis())
441 {
442 // Encoding of untracked variables does not support reporting
443 // "this". So report it as a tracked variable with a liveness
444 // extending over the entire method.
445
446 thisKeptAliveIsInUntracked = true;
447 continue;
448 }
449#endif
450
451#ifdef DEBUG
452 if (compiler->verbose)
453 {
454 int offs = varDsc->lvStkOffs;
455
456 printf("GCINFO: untrckd %s lcl at [%s", varTypeGCstring(varDsc->TypeGet()),
457 compiler->genEmitter->emitGetFrameReg());
458
459 if (offs < 0)
460 {
461 printf("-%02XH", -offs);
462 }
463 else if (offs > 0)
464 {
465 printf("+%02XH", +offs);
466 }
467
468 printf("]\n");
469 }
470#endif
471
472 count++;
473 }
474 else if (varDsc->lvType == TYP_STRUCT && varDsc->lvOnFrame && (varDsc->lvExactSize >= TARGET_POINTER_SIZE))
475 {
476 unsigned slots = compiler->lvaLclSize(varNum) / TARGET_POINTER_SIZE;
477 BYTE* gcPtrs = compiler->lvaGetGcLayout(varNum);
478
479 // walk each member of the array
480 for (unsigned i = 0; i < slots; i++)
481 {
482 if (gcPtrs[i] != TYPE_GC_NONE)
483 { // count only gc slots
484 count++;
485 }
486 }
487 }
488 }
489
490 /* Also count spill temps that hold pointers */
491
492 assert(regSet->tmpAllFree());
493 for (TempDsc* tempThis = regSet->tmpListBeg(); tempThis != nullptr; tempThis = regSet->tmpListNxt(tempThis))
494 {
495 if (varTypeIsGC(tempThis->tdTempType()) == false)
496 {
497 continue;
498 }
499
500#ifdef DEBUG
501 if (compiler->verbose)
502 {
503 int offs = tempThis->tdTempOffs();
504
505 printf("GCINFO: untrck %s Temp at [%s", varTypeGCstring(varDsc->TypeGet()),
506 compiler->genEmitter->emitGetFrameReg());
507
508 if (offs < 0)
509 {
510 printf("-%02XH", -offs);
511 }
512 else if (offs > 0)
513 {
514 printf("+%02XH", +offs);
515 }
516
517 printf("]\n");
518 }
519#endif
520
521 count++;
522 }
523
524#ifdef DEBUG
525 if (compiler->verbose)
526 {
527 printf("GCINFO: untrckVars = %u\n", count);
528 }
529#endif
530
531 *untrackedCount = count;
532
533 /* Count the number of entries in the table of non-register pointer
534 variable lifetimes. */
535
536 count = 0;
537
538 if (thisKeptAliveIsInUntracked)
539 {
540 count++;
541 }
542
543 if (gcVarPtrList)
544 {
545 /* We'll use a delta encoding for the lifetime offsets */
546
547 for (varTmp = gcVarPtrList; varTmp; varTmp = varTmp->vpdNext)
548 {
549 /* Special case: skip any 0-length lifetimes */
550
551 if (varTmp->vpdBegOfs == varTmp->vpdEndOfs)
552 {
553 continue;
554 }
555
556 count++;
557 }
558 }
559
560#ifdef DEBUG
561 if (compiler->verbose)
562 {
563 printf("GCINFO: trackdLcls = %u\n", count);
564 }
565#endif
566
567 *varPtrTableSize = count;
568}
569
570#ifdef JIT32_GCENCODER
571/*****************************************************************************
572 *
573 * Shutdown the 'pointer value' register tracking logic and save the necessary
574 * info (which will be used at runtime to locate all pointers) at the specified
575 * address. The number of bytes written to 'destPtr' must be identical to that
576 * returned from gcPtrTableSize().
577 */
578
579BYTE* GCInfo::gcPtrTableSave(BYTE* destPtr, const InfoHdr& header, unsigned codeSize, size_t* pArgTabOffset)
580{
581 /* Write the tables to the info block */
582
583 return destPtr + gcMakeRegPtrTable(destPtr, -1, header, codeSize, pArgTabOffset);
584}
585#endif
586
587/*****************************************************************************
588 *
589 * Initialize the 'pointer value' register/argument tracking logic.
590 */
591
592void GCInfo::gcRegPtrSetInit()
593{
594 gcRegGCrefSetCur = gcRegByrefSetCur = 0;
595
596 if (compiler->genFullPtrRegMap)
597 {
598 gcRegPtrList = gcRegPtrLast = nullptr;
599 }
600 else
601 {
602 /* Initialize the 'call descriptor' list */
603 gcCallDescList = gcCallDescLast = nullptr;
604 }
605}
606
607#ifdef JIT32_GCENCODER
608
609/*****************************************************************************
610 *
611 * Helper passed to genEmitter.emitGenEpilogLst() to generate
612 * the table of epilogs.
613 */
614
615/* static */ size_t GCInfo::gcRecordEpilog(void* pCallBackData, unsigned offset)
616{
617 GCInfo* gcInfo = (GCInfo*)pCallBackData;
618
619 assert(gcInfo);
620
621 size_t result = encodeUDelta(gcInfo->gcEpilogTable, offset, gcInfo->gcEpilogPrevOffset);
622
623 if (gcInfo->gcEpilogTable)
624 gcInfo->gcEpilogTable += result;
625
626 gcInfo->gcEpilogPrevOffset = offset;
627
628 return result;
629}
630
631#endif // JIT32_GCENCODER
632
633GCInfo::WriteBarrierForm GCInfo::gcWriteBarrierFormFromTargetAddress(GenTree* tgtAddr)
634{
635 GCInfo::WriteBarrierForm result = GCInfo::WBF_BarrierUnknown; // Default case, we have no information.
636
637 // If we store through an int to a GC_REF field, we'll assume that needs to use a checked barriers.
638 if (tgtAddr->TypeGet() == TYP_I_IMPL)
639 {
640 return GCInfo::WBF_BarrierChecked; // Why isn't this GCInfo::WBF_BarrierUnknown?
641 }
642
643 // Otherwise...
644 assert(tgtAddr->TypeGet() == TYP_BYREF);
645 bool simplifiedExpr = true;
646 while (simplifiedExpr)
647 {
648 simplifiedExpr = false;
649
650 tgtAddr = tgtAddr->gtSkipReloadOrCopy();
651
652 while (tgtAddr->OperGet() == GT_ADDR && tgtAddr->gtOp.gtOp1->OperGet() == GT_IND)
653 {
654 tgtAddr = tgtAddr->gtOp.gtOp1->gtOp.gtOp1;
655 simplifiedExpr = true;
656 assert(tgtAddr->TypeGet() == TYP_BYREF);
657 }
658 // For additions, one of the operands is a byref or a ref (and the other is not). Follow this down to its
659 // source.
660 while (tgtAddr->OperGet() == GT_ADD || tgtAddr->OperGet() == GT_LEA)
661 {
662 if (tgtAddr->OperGet() == GT_ADD)
663 {
664 if (tgtAddr->gtOp.gtOp1->TypeGet() == TYP_BYREF || tgtAddr->gtOp.gtOp1->TypeGet() == TYP_REF)
665 {
666 assert(!(tgtAddr->gtOp.gtOp2->TypeGet() == TYP_BYREF || tgtAddr->gtOp.gtOp2->TypeGet() == TYP_REF));
667 tgtAddr = tgtAddr->gtOp.gtOp1;
668 simplifiedExpr = true;
669 }
670 else if (tgtAddr->gtOp.gtOp2->TypeGet() == TYP_BYREF || tgtAddr->gtOp.gtOp2->TypeGet() == TYP_REF)
671 {
672 tgtAddr = tgtAddr->gtOp.gtOp2;
673 simplifiedExpr = true;
674 }
675 else
676 {
677 // We might have a native int. For example:
678 // const int 0
679 // + byref
680 // lclVar int V06 loc5 // this is a local declared "valuetype VType*"
681 return GCInfo::WBF_BarrierUnknown;
682 }
683 }
684 else
685 {
686 // Must be an LEA (i.e., an AddrMode)
687 assert(tgtAddr->OperGet() == GT_LEA);
688 tgtAddr = tgtAddr->AsAddrMode()->Base();
689 if (tgtAddr->TypeGet() == TYP_BYREF || tgtAddr->TypeGet() == TYP_REF)
690 {
691 simplifiedExpr = true;
692 }
693 else
694 {
695 // We might have a native int.
696 return GCInfo::WBF_BarrierUnknown;
697 }
698 }
699 }
700 }
701 if (tgtAddr->IsLocalAddrExpr() != nullptr)
702 {
703 // No need for a GC barrier when writing to a local variable.
704 return GCInfo::WBF_NoBarrier;
705 }
706 if (tgtAddr->OperGet() == GT_LCL_VAR)
707 {
708 unsigned lclNum = tgtAddr->AsLclVar()->GetLclNum();
709
710 LclVarDsc* varDsc = &compiler->lvaTable[lclNum];
711
712 // Instead of marking LclVar with 'lvStackByref',
713 // Consider decomposing the Value Number given to this LclVar to see if it was
714 // created using a GT_ADDR(GT_LCLVAR) or a GT_ADD( GT_ADDR(GT_LCLVAR), Constant)
715
716 // We may have an internal compiler temp created in fgMorphCopyBlock() that we know
717 // points at one of our stack local variables, it will have lvStackByref set to true.
718 //
719 if (varDsc->lvStackByref)
720 {
721 assert(varDsc->TypeGet() == TYP_BYREF);
722 return GCInfo::WBF_NoBarrier;
723 }
724
725 // We don't eliminate for inlined methods, where we (can) know where the "retBuff" points.
726 if (!compiler->compIsForInlining() && lclNum == compiler->info.compRetBuffArg)
727 {
728 assert(compiler->info.compRetType == TYP_STRUCT); // Else shouldn't have a ret buff.
729
730 // Are we assured that the ret buff pointer points into the stack of a caller?
731 if (compiler->info.compRetBuffDefStack)
732 {
733#if 0
734 // This is an optional debugging mode. If the #if 0 above is changed to #if 1,
735 // every barrier we remove for stores to GC ref fields of a retbuff use a special
736 // helper that asserts that the target is not in the heap.
737#ifdef DEBUG
738 return WBF_NoBarrier_CheckNotHeapInDebug;
739#else
740 return WBF_NoBarrier;
741#endif
742#else // 0
743 return GCInfo::WBF_NoBarrier;
744#endif // 0
745 }
746 }
747 }
748 if (tgtAddr->TypeGet() == TYP_REF)
749 {
750 return GCInfo::WBF_BarrierUnchecked;
751 }
752 // Otherwise, we have no information.
753 return GCInfo::WBF_BarrierUnknown;
754}
755
756//------------------------------------------------------------------------
757// gcUpdateForRegVarMove: Update the masks when a variable is moved
758//
759// Arguments:
760// srcMask - The register mask for the register(s) from which it is being moved
761// dstMask - The register mask for the register(s) to which it is being moved
762// type - The type of the variable
763//
764// Return Value:
765// None
766//
767// Notes:
768// This is called during codegen when a var is moved due to an LSRA_ASG.
769// It is also called by LinearScan::recordVarLocationAtStartOfBB() which is in turn called by
770// CodeGen::genCodeForBBList() at the block boundary.
771
772void GCInfo::gcUpdateForRegVarMove(regMaskTP srcMask, regMaskTP dstMask, LclVarDsc* varDsc)
773{
774 var_types type = varDsc->TypeGet();
775 bool isGCRef = (type == TYP_REF);
776 bool isByRef = (type == TYP_BYREF);
777
778 if (srcMask != RBM_NONE)
779 {
780 regSet->RemoveMaskVars(srcMask);
781 if (isGCRef)
782 {
783 assert((gcRegByrefSetCur & srcMask) == 0);
784 gcRegGCrefSetCur &= ~srcMask;
785 gcRegGCrefSetCur |= dstMask; // safe if no dst, i.e. RBM_NONE
786 }
787 else if (isByRef)
788 {
789 assert((gcRegGCrefSetCur & srcMask) == 0);
790 gcRegByrefSetCur &= ~srcMask;
791 gcRegByrefSetCur |= dstMask; // safe if no dst, i.e. RBM_NONE
792 }
793 }
794 else if (isGCRef || isByRef)
795 {
796 // In this case, we are moving it from the stack to a register,
797 // so remove it from the set of live stack gc refs
798 VarSetOps::RemoveElemD(compiler, gcVarPtrSetCur, varDsc->lvVarIndex);
799 }
800 if (dstMask != RBM_NONE)
801 {
802 regSet->AddMaskVars(dstMask);
803 // If the source is a reg, then the gc sets have been set appropriately
804 // Otherwise, we have to determine whether to set them
805 if (srcMask == RBM_NONE)
806 {
807 if (isGCRef)
808 {
809 gcRegGCrefSetCur |= dstMask;
810 }
811 else if (isByRef)
812 {
813 gcRegByrefSetCur |= dstMask;
814 }
815 }
816 }
817 else if (isGCRef || isByRef)
818 {
819 VarSetOps::AddElemD(compiler, gcVarPtrSetCur, varDsc->lvVarIndex);
820 }
821}
822
823/*****************************************************************************/
824/*****************************************************************************/
825