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 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
10 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
21 #include "jitgcinfo.h"
24 #include "gcinfoencoder.h" //this includes a LOT of other files too
27 /*****************************************************************************/
28 /*****************************************************************************/
30 /*****************************************************************************/
32 extern int JITGcBarrierCall;
34 /*****************************************************************************/
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
42 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
43 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
46 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
47 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
50 GCInfo::GCInfo(Compiler* theCompiler) : compiler(theCompiler)
53 gcVarPtrList = nullptr;
54 gcVarPtrLast = nullptr;
55 gcRegPtrList = nullptr;
56 gcRegPtrLast = nullptr;
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
68 /*****************************************************************************/
69 /*****************************************************************************
70 * Reset tracking info at the start of a basic block.
73 void GCInfo::gcResetForBB()
75 gcRegGCrefSetCur = RBM_NONE;
76 gcRegByrefSetCur = RBM_NONE;
77 VarSetOps::AssignNoCopy(compiler, gcVarPtrSetCur, VarSetOps::MakeEmpty(compiler));
82 /*****************************************************************************
84 * Print the changes in the gcRegGCrefSetCur sets.
87 void GCInfo::gcDspGCrefSetChanges(regMaskTP gcRegGCrefSetNew DEBUGARG(bool forceOutput))
89 if (compiler->verbose)
91 if (forceOutput || (gcRegGCrefSetCur != gcRegGCrefSetNew))
93 printf("\t\t\t\t\t\t\tGC regs: ");
94 if (gcRegGCrefSetCur == gcRegGCrefSetNew)
96 printf("(unchanged) ");
100 printRegMaskInt(gcRegGCrefSetCur);
101 compiler->getEmitter()->emitDispRegSet(gcRegGCrefSetCur);
104 printRegMaskInt(gcRegGCrefSetNew);
105 compiler->getEmitter()->emitDispRegSet(gcRegGCrefSetNew);
111 /*****************************************************************************
113 * Print the changes in the gcRegByrefSetCur sets.
116 void GCInfo::gcDspByrefSetChanges(regMaskTP gcRegByrefSetNew DEBUGARG(bool forceOutput))
118 if (compiler->verbose)
120 if (forceOutput || (gcRegByrefSetCur != gcRegByrefSetNew))
122 printf("\t\t\t\t\t\t\tByref regs: ");
123 if (gcRegByrefSetCur == gcRegByrefSetNew)
125 printf("(unchanged) ");
129 printRegMaskInt(gcRegByrefSetCur);
130 compiler->getEmitter()->emitDispRegSet(gcRegByrefSetCur);
133 printRegMaskInt(gcRegByrefSetNew);
134 compiler->getEmitter()->emitDispRegSet(gcRegByrefSetNew);
142 /*****************************************************************************
144 * Mark the set of registers given by the specified mask as holding
145 * GCref pointer values.
148 void GCInfo::gcMarkRegSetGCref(regMaskTP regMask DEBUGARG(bool forceOutput))
151 if (compiler->compRegSetCheckLevel == 0)
153 // This set of registers are going to hold REFs.
154 // Make sure they were not holding BYREFs.
155 assert((gcRegByrefSetCur & regMask) == 0);
159 regMaskTP gcRegByrefSetNew = gcRegByrefSetCur & ~regMask; // Clear it if set in Byref mask
160 regMaskTP gcRegGCrefSetNew = gcRegGCrefSetCur | regMask; // Set it in GCref mask
162 INDEBUG(gcDspGCrefSetChanges(gcRegGCrefSetNew, forceOutput));
163 INDEBUG(gcDspByrefSetChanges(gcRegByrefSetNew));
165 gcRegByrefSetCur = gcRegByrefSetNew;
166 gcRegGCrefSetCur = gcRegGCrefSetNew;
169 /*****************************************************************************
171 * Mark the set of registers given by the specified mask as holding
172 * Byref pointer values.
175 void GCInfo::gcMarkRegSetByref(regMaskTP regMask DEBUGARG(bool forceOutput))
177 regMaskTP gcRegByrefSetNew = gcRegByrefSetCur | regMask; // Set it in Byref mask
178 regMaskTP gcRegGCrefSetNew = gcRegGCrefSetCur & ~regMask; // Clear it if set in GCref mask
180 INDEBUG(gcDspGCrefSetChanges(gcRegGCrefSetNew));
181 INDEBUG(gcDspByrefSetChanges(gcRegByrefSetNew, forceOutput));
183 gcRegByrefSetCur = gcRegByrefSetNew;
184 gcRegGCrefSetCur = gcRegGCrefSetNew;
187 /*****************************************************************************
189 * Mark the set of registers given by the specified mask as holding
190 * non-pointer values.
193 void GCInfo::gcMarkRegSetNpt(regMaskTP regMask DEBUGARG(bool forceOutput))
195 /* NOTE: don't unmark any live register variables */
197 regMaskTP gcRegByrefSetNew = gcRegByrefSetCur & ~(regMask & ~regSet->rsMaskVars);
198 regMaskTP gcRegGCrefSetNew = gcRegGCrefSetCur & ~(regMask & ~regSet->rsMaskVars);
200 INDEBUG(gcDspGCrefSetChanges(gcRegGCrefSetNew, forceOutput));
201 INDEBUG(gcDspByrefSetChanges(gcRegByrefSetNew, forceOutput));
203 gcRegByrefSetCur = gcRegByrefSetNew;
204 gcRegGCrefSetCur = gcRegGCrefSetNew;
207 /*****************************************************************************
209 * Mark the specified register as now holding a value of the given type.
212 void GCInfo::gcMarkRegPtrVal(regNumber reg, var_types type)
214 regMaskTP regMask = genRegMask(reg);
219 gcMarkRegSetGCref(regMask);
222 gcMarkRegSetByref(regMask);
225 gcMarkRegSetNpt(regMask);
230 /*****************************************************************************/
232 GCInfo::WriteBarrierForm GCInfo::gcIsWriteBarrierCandidate(GenTree* tgt, GenTree* assignVal)
234 /* Are we storing a GC ptr? */
236 if (!varTypeIsGC(tgt->TypeGet()))
238 return WBF_NoBarrier;
241 /* Ignore any assignments of NULL */
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())
247 return WBF_NoBarrier;
250 if (assignVal->gtOper == GT_CNS_INT && assignVal->gtIntCon.gtIconVal == 0)
252 return WBF_NoBarrier;
255 /* Where are we storing into? */
257 tgt = tgt->gtEffectiveVal();
262 #ifndef LEGACY_BACKEND
264 #endif // !LEGACY_BACKEND
265 case GT_IND: /* Could be the managed heap */
266 if (tgt->TypeGet() == TYP_BYREF)
268 // Byref values cannot be in managed heap.
269 // This case occurs for Span<T>.
270 return WBF_NoBarrier;
272 return gcWriteBarrierFormFromTargetAddress(tgt->gtOp.gtOp1);
275 return gcWriteBarrierFormFromTargetAddress(tgt->AsAddrMode()->Base());
277 case GT_ARR_ELEM: /* Definitely in the managed heap */
279 return WBF_BarrierUnchecked;
281 case GT_REG_VAR: /* Definitely not in the managed heap */
284 case GT_STORE_LCL_VAR:
285 case GT_STORE_LCL_FLD:
286 return WBF_NoBarrier;
292 assert(!"Missing case in gcIsWriteBarrierCandidate");
294 return WBF_NoBarrier;
297 bool GCInfo::gcIsWriteBarrierAsgNode(GenTree* op)
299 if (op->gtOper == GT_ASG)
301 return gcIsWriteBarrierCandidate(op->gtOp.gtOp1, op->gtOp.gtOp2) != WBF_NoBarrier;
303 #ifndef LEGACY_BACKEND
304 else if (op->gtOper == GT_STOREIND)
306 return gcIsWriteBarrierCandidate(op, op->gtOp.gtOp2) != WBF_NoBarrier;
308 #endif // !LEGACY_BACKEND
315 /*****************************************************************************/
316 /*****************************************************************************
318 * If the given tree value is sitting in a register, free it now.
321 #ifdef LEGACY_BACKEND
322 void GCInfo::gcMarkRegPtrVal(GenTree* tree)
324 if (varTypeIsGC(tree->TypeGet()))
326 if (tree->gtOper == GT_LCL_VAR)
327 compiler->codeGen->genMarkLclVar(tree);
330 gcMarkRegSetNpt(genRegMask(tree->gtRegNum));
334 #endif // LEGACY_BACKEND
336 /*****************************************************************************/
337 /*****************************************************************************
339 * Initialize the non-register pointer variable tracking logic.
342 void GCInfo::gcVarPtrSetInit()
344 VarSetOps::AssignNoCopy(compiler, gcVarPtrSetCur, VarSetOps::MakeEmpty(compiler));
346 /* Initialize the list of lifetime entries */
347 gcVarPtrList = gcVarPtrLast = nullptr;
350 /*****************************************************************************
352 * Allocate a new pointer register set / pointer argument entry and append
356 GCInfo::regPtrDsc* GCInfo::gcRegPtrAllocDsc()
358 regPtrDsc* regPtrNext;
360 assert(compiler->genFullPtrRegMap);
362 /* Allocate a new entry and initialize it */
364 regPtrNext = new (compiler, CMK_GC) regPtrDsc;
366 regPtrNext->rpdIsThis = FALSE;
368 regPtrNext->rpdOffs = 0;
369 regPtrNext->rpdNext = nullptr;
371 // Append the entry to the end of the list.
372 if (gcRegPtrLast == nullptr)
374 assert(gcRegPtrList == nullptr);
375 gcRegPtrList = gcRegPtrLast = regPtrNext;
379 assert(gcRegPtrList != nullptr);
380 gcRegPtrLast->rpdNext = regPtrNext;
381 gcRegPtrLast = regPtrNext;
384 #if MEASURE_PTRTAB_SIZE
385 s_gcRegPtrDscSize += sizeof(*regPtrNext);
391 /*****************************************************************************
393 * Compute the various counts that get stored in the info block header.
396 void GCInfo::gcCountForHeader(UNALIGNED unsigned int* untrackedCount, UNALIGNED unsigned int* varPtrTableSize)
402 bool thisKeptAliveIsInUntracked = false; // did we track "this" in a synchronized method?
403 unsigned int count = 0;
405 /* Count the untracked locals and non-enregistered args */
407 for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; varNum++, varDsc++)
409 if (varTypeIsGC(varDsc->TypeGet()))
411 if (compiler->lvaIsFieldOfDependentlyPromotedStruct(varDsc))
413 // Field local of a PROMOTION_TYPE_DEPENDENT struct must have been
414 // reported through its parent local
418 /* Do we have an argument or local variable? */
419 if (!varDsc->lvIsParam)
421 if (varDsc->lvTracked || !varDsc->lvOnFrame)
428 /* Stack-passed arguments which are not enregistered
429 * are always reported in this "untracked stack
430 * pointers" section of the GC info even if lvTracked==true
433 /* Has this argument been fully enregistered? */
434 CLANG_FORMAT_COMMENT_ANCHOR;
436 #ifndef LEGACY_BACKEND
437 if (!varDsc->lvOnFrame)
438 #else // LEGACY_BACKEND
439 if (varDsc->lvRegister)
440 #endif // LEGACY_BACKEND
442 /* if a CEE_JMP has been used, then we need to report all the arguments
443 even if they are enregistered, since we will be using this value
444 in JMP call. Note that this is subtle as we require that
445 argument offsets are always fixed up properly even if lvRegister
447 if (!compiler->compJmpOpUsed)
454 if (!varDsc->lvOnFrame)
456 /* If this non-enregistered pointer arg is never
457 * used, we don't need to report it
459 assert(varDsc->lvRefCnt == 0);
462 else if (varDsc->lvIsRegArg && varDsc->lvTracked)
464 /* If this register-passed arg is tracked, then
465 * it has been allocated space near the other
466 * pointer variables and we have accurate life-
467 * time info. It will be reported with
468 * gcVarPtrList in the "tracked-pointer" section
476 #if !defined(JIT32_GCENCODER) || !defined(WIN64EXCEPTIONS)
477 // For x86/WIN64EXCEPTIONS, "this" must always be in untracked variables
478 // so we cannot have "this" in variable lifetimes
479 if (compiler->lvaIsOriginalThisArg(varNum) && compiler->lvaKeepAliveAndReportThis())
481 // Encoding of untracked variables does not support reporting
482 // "this". So report it as a tracked variable with a liveness
483 // extending over the entire method.
485 thisKeptAliveIsInUntracked = true;
491 if (compiler->verbose)
493 int offs = varDsc->lvStkOffs;
495 printf("GCINFO: untrckd %s lcl at [%s", varTypeGCstring(varDsc->TypeGet()),
496 compiler->genEmitter->emitGetFrameReg());
500 printf("-%02XH", -offs);
504 printf("+%02XH", +offs);
513 else if (varDsc->lvType == TYP_STRUCT && varDsc->lvOnFrame && (varDsc->lvExactSize >= TARGET_POINTER_SIZE))
515 unsigned slots = compiler->lvaLclSize(varNum) / TARGET_POINTER_SIZE;
516 BYTE* gcPtrs = compiler->lvaGetGcLayout(varNum);
518 // walk each member of the array
519 for (unsigned i = 0; i < slots; i++)
521 if (gcPtrs[i] != TYPE_GC_NONE)
522 { // count only gc slots
529 /* Also count spill temps that hold pointers */
531 assert(compiler->tmpAllFree());
532 for (TempDsc* tempThis = compiler->tmpListBeg(); tempThis != nullptr; tempThis = compiler->tmpListNxt(tempThis))
534 if (varTypeIsGC(tempThis->tdTempType()) == false)
540 if (compiler->verbose)
542 int offs = tempThis->tdTempOffs();
544 printf("GCINFO: untrck %s Temp at [%s", varTypeGCstring(varDsc->TypeGet()),
545 compiler->genEmitter->emitGetFrameReg());
549 printf("-%02XH", -offs);
553 printf("+%02XH", +offs);
564 if (compiler->verbose)
566 printf("GCINFO: untrckVars = %u\n", count);
570 *untrackedCount = count;
572 /* Count the number of entries in the table of non-register pointer
573 variable lifetimes. */
577 if (thisKeptAliveIsInUntracked)
584 /* We'll use a delta encoding for the lifetime offsets */
586 for (varTmp = gcVarPtrList; varTmp; varTmp = varTmp->vpdNext)
588 /* Special case: skip any 0-length lifetimes */
590 if (varTmp->vpdBegOfs == varTmp->vpdEndOfs)
600 if (compiler->verbose)
602 printf("GCINFO: trackdLcls = %u\n", count);
606 *varPtrTableSize = count;
609 #ifdef JIT32_GCENCODER
610 /*****************************************************************************
612 * Shutdown the 'pointer value' register tracking logic and save the necessary
613 * info (which will be used at runtime to locate all pointers) at the specified
614 * address. The number of bytes written to 'destPtr' must be identical to that
615 * returned from gcPtrTableSize().
618 BYTE* GCInfo::gcPtrTableSave(BYTE* destPtr, const InfoHdr& header, unsigned codeSize, size_t* pArgTabOffset)
620 /* Write the tables to the info block */
622 return destPtr + gcMakeRegPtrTable(destPtr, -1, header, codeSize, pArgTabOffset);
626 /*****************************************************************************
628 * Initialize the 'pointer value' register/argument tracking logic.
631 void GCInfo::gcRegPtrSetInit()
633 gcRegGCrefSetCur = gcRegByrefSetCur = 0;
635 if (compiler->genFullPtrRegMap)
637 gcRegPtrList = gcRegPtrLast = nullptr;
641 /* Initialize the 'call descriptor' list */
642 gcCallDescList = gcCallDescLast = nullptr;
646 #ifdef JIT32_GCENCODER
648 /*****************************************************************************
650 * Helper passed to genEmitter.emitGenEpilogLst() to generate
651 * the table of epilogs.
654 /* static */ size_t GCInfo::gcRecordEpilog(void* pCallBackData, unsigned offset)
656 GCInfo* gcInfo = (GCInfo*)pCallBackData;
660 size_t result = encodeUDelta(gcInfo->gcEpilogTable, offset, gcInfo->gcEpilogPrevOffset);
662 if (gcInfo->gcEpilogTable)
663 gcInfo->gcEpilogTable += result;
665 gcInfo->gcEpilogPrevOffset = offset;
670 #endif // JIT32_GCENCODER
672 GCInfo::WriteBarrierForm GCInfo::gcWriteBarrierFormFromTargetAddress(GenTree* tgtAddr)
674 GCInfo::WriteBarrierForm result = GCInfo::WBF_BarrierUnknown; // Default case, we have no information.
676 // If we store through an int to a GC_REF field, we'll assume that needs to use a checked barriers.
677 if (tgtAddr->TypeGet() == TYP_I_IMPL)
679 return GCInfo::WBF_BarrierChecked; // Why isn't this GCInfo::WBF_BarrierUnknown?
683 assert(tgtAddr->TypeGet() == TYP_BYREF);
684 bool simplifiedExpr = true;
685 while (simplifiedExpr)
687 simplifiedExpr = false;
689 tgtAddr = tgtAddr->gtSkipReloadOrCopy();
691 while (tgtAddr->OperGet() == GT_ADDR && tgtAddr->gtOp.gtOp1->OperGet() == GT_IND)
693 tgtAddr = tgtAddr->gtOp.gtOp1->gtOp.gtOp1;
694 simplifiedExpr = true;
695 assert(tgtAddr->TypeGet() == TYP_BYREF);
697 // For additions, one of the operands is a byref or a ref (and the other is not). Follow this down to its
699 while (tgtAddr->OperGet() == GT_ADD || tgtAddr->OperGet() == GT_LEA)
701 if (tgtAddr->OperGet() == GT_ADD)
703 if (tgtAddr->gtOp.gtOp1->TypeGet() == TYP_BYREF || tgtAddr->gtOp.gtOp1->TypeGet() == TYP_REF)
705 assert(!(tgtAddr->gtOp.gtOp2->TypeGet() == TYP_BYREF || tgtAddr->gtOp.gtOp2->TypeGet() == TYP_REF));
706 tgtAddr = tgtAddr->gtOp.gtOp1;
707 simplifiedExpr = true;
709 else if (tgtAddr->gtOp.gtOp2->TypeGet() == TYP_BYREF || tgtAddr->gtOp.gtOp2->TypeGet() == TYP_REF)
711 tgtAddr = tgtAddr->gtOp.gtOp2;
712 simplifiedExpr = true;
716 // We might have a native int. For example:
719 // lclVar int V06 loc5 // this is a local declared "valuetype VType*"
720 return GCInfo::WBF_BarrierUnknown;
725 // Must be an LEA (i.e., an AddrMode)
726 assert(tgtAddr->OperGet() == GT_LEA);
727 tgtAddr = tgtAddr->AsAddrMode()->Base();
728 if (tgtAddr->TypeGet() == TYP_BYREF || tgtAddr->TypeGet() == TYP_REF)
730 simplifiedExpr = true;
734 // We might have a native int.
735 return GCInfo::WBF_BarrierUnknown;
740 if (tgtAddr->IsLocalAddrExpr() != nullptr)
742 // No need for a GC barrier when writing to a local variable.
743 return GCInfo::WBF_NoBarrier;
745 if (tgtAddr->OperGet() == GT_LCL_VAR || tgtAddr->OperGet() == GT_REG_VAR)
748 if (tgtAddr->gtOper == GT_LCL_VAR)
750 lclNum = tgtAddr->gtLclVar.gtLclNum;
754 assert(tgtAddr->gtOper == GT_REG_VAR);
755 lclNum = tgtAddr->gtRegVar.gtLclNum;
758 LclVarDsc* varDsc = &compiler->lvaTable[lclNum];
760 // Instead of marking LclVar with 'lvStackByref',
761 // Consider decomposing the Value Number given to this LclVar to see if it was
762 // created using a GT_ADDR(GT_LCLVAR) or a GT_ADD( GT_ADDR(GT_LCLVAR), Constant)
764 // We may have an internal compiler temp created in fgMorphCopyBlock() that we know
765 // points at one of our stack local variables, it will have lvStackByref set to true.
767 if (varDsc->lvStackByref)
769 assert(varDsc->TypeGet() == TYP_BYREF);
770 return GCInfo::WBF_NoBarrier;
773 // We don't eliminate for inlined methods, where we (can) know where the "retBuff" points.
774 if (!compiler->compIsForInlining() && lclNum == compiler->info.compRetBuffArg)
776 assert(compiler->info.compRetType == TYP_STRUCT); // Else shouldn't have a ret buff.
778 // Are we assured that the ret buff pointer points into the stack of a caller?
779 if (compiler->info.compRetBuffDefStack)
782 // This is an optional debugging mode. If the #if 0 above is changed to #if 1,
783 // every barrier we remove for stores to GC ref fields of a retbuff use a special
784 // helper that asserts that the target is not in the heap.
786 return WBF_NoBarrier_CheckNotHeapInDebug;
788 return WBF_NoBarrier;
791 return GCInfo::WBF_NoBarrier;
796 if (tgtAddr->TypeGet() == TYP_REF)
798 return GCInfo::WBF_BarrierUnchecked;
800 // Otherwise, we have no information.
801 return GCInfo::WBF_BarrierUnknown;
804 #ifndef LEGACY_BACKEND
805 //------------------------------------------------------------------------
806 // gcUpdateForRegVarMove: Update the masks when a variable is moved
809 // srcMask - The register mask for the register(s) from which it is being moved
810 // dstMask - The register mask for the register(s) to which it is being moved
811 // type - The type of the variable
817 // This is called during codegen when a var is moved due to an LSRA_ASG.
818 // It is also called by LinearScan::recordVarLocationAtStartOfBB() which is in turn called by
819 // CodeGen::genCodeForBBList() at the block boundary.
821 void GCInfo::gcUpdateForRegVarMove(regMaskTP srcMask, regMaskTP dstMask, LclVarDsc* varDsc)
823 var_types type = varDsc->TypeGet();
824 bool isGCRef = (type == TYP_REF);
825 bool isByRef = (type == TYP_BYREF);
827 if (srcMask != RBM_NONE)
829 regSet->RemoveMaskVars(srcMask);
832 assert((gcRegByrefSetCur & srcMask) == 0);
833 gcRegGCrefSetCur &= ~srcMask;
834 gcRegGCrefSetCur |= dstMask; // safe if no dst, i.e. RBM_NONE
838 assert((gcRegGCrefSetCur & srcMask) == 0);
839 gcRegByrefSetCur &= ~srcMask;
840 gcRegByrefSetCur |= dstMask; // safe if no dst, i.e. RBM_NONE
843 else if (isGCRef || isByRef)
845 // In this case, we are moving it from the stack to a register,
846 // so remove it from the set of live stack gc refs
847 VarSetOps::RemoveElemD(compiler, gcVarPtrSetCur, varDsc->lvVarIndex);
849 if (dstMask != RBM_NONE)
851 regSet->AddMaskVars(dstMask);
852 // If the source is a reg, then the gc sets have been set appropriately
853 // Otherwise, we have to determine whether to set them
854 if (srcMask == RBM_NONE)
858 gcRegGCrefSetCur |= dstMask;
862 gcRegByrefSetCur |= dstMask;
866 else if (isGCRef || isByRef)
868 VarSetOps::AddElemD(compiler, gcVarPtrSetCur, varDsc->lvVarIndex);
871 #endif // !LEGACY_BACKEND
873 /*****************************************************************************/
874 /*****************************************************************************/