Fix reading Time zone rules using Julian days (#17672)
[platform/upstream/coreclr.git] / src / jit / gcinfo.cpp
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
6 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7 XX                                                                           XX
8 XX                          GCInfo                                           XX
9 XX                                                                           XX
10 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
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
32 extern 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 /*
42 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
43 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
44 XX                          GCInfo                                           XX
45 XX                                                                           XX
46 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
47 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
48 */
49
50 GCInfo::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
73 void 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
87 void 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
116 void 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
148 void 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
175 void 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
193 void 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
212 void 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
232 GCInfo::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 #ifndef LEGACY_BACKEND
263         case GT_STOREIND:
264 #endif               // !LEGACY_BACKEND
265         case GT_IND: /* Could be the managed heap */
266             if (tgt->TypeGet() == TYP_BYREF)
267             {
268                 // Byref values cannot be in managed heap.
269                 // This case occurs for Span<T>.
270                 return WBF_NoBarrier;
271             }
272             return gcWriteBarrierFormFromTargetAddress(tgt->gtOp.gtOp1);
273
274         case GT_LEA:
275             return gcWriteBarrierFormFromTargetAddress(tgt->AsAddrMode()->Base());
276
277         case GT_ARR_ELEM: /* Definitely in the managed heap */
278         case GT_CLS_VAR:
279             return WBF_BarrierUnchecked;
280
281         case GT_REG_VAR: /* Definitely not in the managed heap  */
282         case GT_LCL_VAR:
283         case GT_LCL_FLD:
284         case GT_STORE_LCL_VAR:
285         case GT_STORE_LCL_FLD:
286             return WBF_NoBarrier;
287
288         default:
289             break;
290     }
291
292     assert(!"Missing case in gcIsWriteBarrierCandidate");
293
294     return WBF_NoBarrier;
295 }
296
297 bool GCInfo::gcIsWriteBarrierAsgNode(GenTree* op)
298 {
299     if (op->gtOper == GT_ASG)
300     {
301         return gcIsWriteBarrierCandidate(op->gtOp.gtOp1, op->gtOp.gtOp2) != WBF_NoBarrier;
302     }
303 #ifndef LEGACY_BACKEND
304     else if (op->gtOper == GT_STOREIND)
305     {
306         return gcIsWriteBarrierCandidate(op, op->gtOp.gtOp2) != WBF_NoBarrier;
307     }
308 #endif // !LEGACY_BACKEND
309     else
310     {
311         return false;
312     }
313 }
314
315 /*****************************************************************************/
316 /*****************************************************************************
317  *
318  *  If the given tree value is sitting in a register, free it now.
319  */
320
321 #ifdef LEGACY_BACKEND
322 void GCInfo::gcMarkRegPtrVal(GenTree* tree)
323 {
324     if (varTypeIsGC(tree->TypeGet()))
325     {
326         if (tree->gtOper == GT_LCL_VAR)
327             compiler->codeGen->genMarkLclVar(tree);
328         if (tree->InReg())
329         {
330             gcMarkRegSetNpt(genRegMask(tree->gtRegNum));
331         }
332     }
333 }
334 #endif // LEGACY_BACKEND
335
336 /*****************************************************************************/
337 /*****************************************************************************
338  *
339  *  Initialize the non-register pointer variable tracking logic.
340  */
341
342 void GCInfo::gcVarPtrSetInit()
343 {
344     VarSetOps::AssignNoCopy(compiler, gcVarPtrSetCur, VarSetOps::MakeEmpty(compiler));
345
346     /* Initialize the list of lifetime entries */
347     gcVarPtrList = gcVarPtrLast = nullptr;
348 }
349
350 /*****************************************************************************
351  *
352  *  Allocate a new pointer register set / pointer argument entry and append
353  *  it to the list.
354  */
355
356 GCInfo::regPtrDsc* GCInfo::gcRegPtrAllocDsc()
357 {
358     regPtrDsc* regPtrNext;
359
360     assert(compiler->genFullPtrRegMap);
361
362     /* Allocate a new entry and initialize it */
363
364     regPtrNext = new (compiler, CMK_GC) regPtrDsc;
365
366     regPtrNext->rpdIsThis = FALSE;
367
368     regPtrNext->rpdOffs = 0;
369     regPtrNext->rpdNext = nullptr;
370
371     // Append the entry to the end of the list.
372     if (gcRegPtrLast == nullptr)
373     {
374         assert(gcRegPtrList == nullptr);
375         gcRegPtrList = gcRegPtrLast = regPtrNext;
376     }
377     else
378     {
379         assert(gcRegPtrList != nullptr);
380         gcRegPtrLast->rpdNext = regPtrNext;
381         gcRegPtrLast          = regPtrNext;
382     }
383
384 #if MEASURE_PTRTAB_SIZE
385     s_gcRegPtrDscSize += sizeof(*regPtrNext);
386 #endif
387
388     return regPtrNext;
389 }
390
391 /*****************************************************************************
392  *
393  *  Compute the various counts that get stored in the info block header.
394  */
395
396 void GCInfo::gcCountForHeader(UNALIGNED unsigned int* untrackedCount, UNALIGNED unsigned int* varPtrTableSize)
397 {
398     unsigned   varNum;
399     LclVarDsc* varDsc;
400     varPtrDsc* varTmp;
401
402     bool         thisKeptAliveIsInUntracked = false; // did we track "this" in a synchronized method?
403     unsigned int count                      = 0;
404
405     /* Count the untracked locals and non-enregistered args */
406
407     for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; varNum++, varDsc++)
408     {
409         if (varTypeIsGC(varDsc->TypeGet()))
410         {
411             if (compiler->lvaIsFieldOfDependentlyPromotedStruct(varDsc))
412             {
413                 // Field local of a PROMOTION_TYPE_DEPENDENT struct must have been
414                 // reported through its parent local
415                 continue;
416             }
417
418             /* Do we have an argument or local variable? */
419             if (!varDsc->lvIsParam)
420             {
421                 if (varDsc->lvTracked || !varDsc->lvOnFrame)
422                 {
423                     continue;
424                 }
425             }
426             else
427             {
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
431                  */
432
433                 /* Has this argument been fully enregistered? */
434                 CLANG_FORMAT_COMMENT_ANCHOR;
435
436 #ifndef LEGACY_BACKEND
437                 if (!varDsc->lvOnFrame)
438 #else  // LEGACY_BACKEND
439                 if (varDsc->lvRegister)
440 #endif // LEGACY_BACKEND
441                 {
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
446                        is set */
447                     if (!compiler->compJmpOpUsed)
448                     {
449                         continue;
450                     }
451                 }
452                 else
453                 {
454                     if (!varDsc->lvOnFrame)
455                     {
456                         /* If this non-enregistered pointer arg is never
457                          * used, we don't need to report it
458                          */
459                         assert(varDsc->lvRefCnt == 0);
460                         continue;
461                     }
462                     else if (varDsc->lvIsRegArg && varDsc->lvTracked)
463                     {
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
469                          */
470
471                         continue;
472                     }
473                 }
474             }
475
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())
480             {
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.
484
485                 thisKeptAliveIsInUntracked = true;
486                 continue;
487             }
488 #endif
489
490 #ifdef DEBUG
491             if (compiler->verbose)
492             {
493                 int offs = varDsc->lvStkOffs;
494
495                 printf("GCINFO: untrckd %s lcl at [%s", varTypeGCstring(varDsc->TypeGet()),
496                        compiler->genEmitter->emitGetFrameReg());
497
498                 if (offs < 0)
499                 {
500                     printf("-%02XH", -offs);
501                 }
502                 else if (offs > 0)
503                 {
504                     printf("+%02XH", +offs);
505                 }
506
507                 printf("]\n");
508             }
509 #endif
510
511             count++;
512         }
513         else if (varDsc->lvType == TYP_STRUCT && varDsc->lvOnFrame && (varDsc->lvExactSize >= TARGET_POINTER_SIZE))
514         {
515             unsigned slots  = compiler->lvaLclSize(varNum) / TARGET_POINTER_SIZE;
516             BYTE*    gcPtrs = compiler->lvaGetGcLayout(varNum);
517
518             // walk each member of the array
519             for (unsigned i = 0; i < slots; i++)
520             {
521                 if (gcPtrs[i] != TYPE_GC_NONE)
522                 { // count only gc slots
523                     count++;
524                 }
525             }
526         }
527     }
528
529     /* Also count spill temps that hold pointers */
530
531     assert(compiler->tmpAllFree());
532     for (TempDsc* tempThis = compiler->tmpListBeg(); tempThis != nullptr; tempThis = compiler->tmpListNxt(tempThis))
533     {
534         if (varTypeIsGC(tempThis->tdTempType()) == false)
535         {
536             continue;
537         }
538
539 #ifdef DEBUG
540         if (compiler->verbose)
541         {
542             int offs = tempThis->tdTempOffs();
543
544             printf("GCINFO: untrck %s Temp at [%s", varTypeGCstring(varDsc->TypeGet()),
545                    compiler->genEmitter->emitGetFrameReg());
546
547             if (offs < 0)
548             {
549                 printf("-%02XH", -offs);
550             }
551             else if (offs > 0)
552             {
553                 printf("+%02XH", +offs);
554             }
555
556             printf("]\n");
557         }
558 #endif
559
560         count++;
561     }
562
563 #ifdef DEBUG
564     if (compiler->verbose)
565     {
566         printf("GCINFO: untrckVars = %u\n", count);
567     }
568 #endif
569
570     *untrackedCount = count;
571
572     /* Count the number of entries in the table of non-register pointer
573        variable lifetimes. */
574
575     count = 0;
576
577     if (thisKeptAliveIsInUntracked)
578     {
579         count++;
580     }
581
582     if (gcVarPtrList)
583     {
584         /* We'll use a delta encoding for the lifetime offsets */
585
586         for (varTmp = gcVarPtrList; varTmp; varTmp = varTmp->vpdNext)
587         {
588             /* Special case: skip any 0-length lifetimes */
589
590             if (varTmp->vpdBegOfs == varTmp->vpdEndOfs)
591             {
592                 continue;
593             }
594
595             count++;
596         }
597     }
598
599 #ifdef DEBUG
600     if (compiler->verbose)
601     {
602         printf("GCINFO: trackdLcls = %u\n", count);
603     }
604 #endif
605
606     *varPtrTableSize = count;
607 }
608
609 #ifdef JIT32_GCENCODER
610 /*****************************************************************************
611  *
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().
616  */
617
618 BYTE* GCInfo::gcPtrTableSave(BYTE* destPtr, const InfoHdr& header, unsigned codeSize, size_t* pArgTabOffset)
619 {
620     /* Write the tables to the info block */
621
622     return destPtr + gcMakeRegPtrTable(destPtr, -1, header, codeSize, pArgTabOffset);
623 }
624 #endif
625
626 /*****************************************************************************
627  *
628  *  Initialize the 'pointer value' register/argument tracking logic.
629  */
630
631 void GCInfo::gcRegPtrSetInit()
632 {
633     gcRegGCrefSetCur = gcRegByrefSetCur = 0;
634
635     if (compiler->genFullPtrRegMap)
636     {
637         gcRegPtrList = gcRegPtrLast = nullptr;
638     }
639     else
640     {
641         /* Initialize the 'call descriptor' list */
642         gcCallDescList = gcCallDescLast = nullptr;
643     }
644 }
645
646 #ifdef JIT32_GCENCODER
647
648 /*****************************************************************************
649  *
650  *  Helper passed to genEmitter.emitGenEpilogLst() to generate
651  *  the table of epilogs.
652  */
653
654 /* static */ size_t GCInfo::gcRecordEpilog(void* pCallBackData, unsigned offset)
655 {
656     GCInfo* gcInfo = (GCInfo*)pCallBackData;
657
658     assert(gcInfo);
659
660     size_t result = encodeUDelta(gcInfo->gcEpilogTable, offset, gcInfo->gcEpilogPrevOffset);
661
662     if (gcInfo->gcEpilogTable)
663         gcInfo->gcEpilogTable += result;
664
665     gcInfo->gcEpilogPrevOffset = offset;
666
667     return result;
668 }
669
670 #endif // JIT32_GCENCODER
671
672 GCInfo::WriteBarrierForm GCInfo::gcWriteBarrierFormFromTargetAddress(GenTree* tgtAddr)
673 {
674     GCInfo::WriteBarrierForm result = GCInfo::WBF_BarrierUnknown; // Default case, we have no information.
675
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)
678     {
679         return GCInfo::WBF_BarrierChecked; // Why isn't this GCInfo::WBF_BarrierUnknown?
680     }
681
682     // Otherwise...
683     assert(tgtAddr->TypeGet() == TYP_BYREF);
684     bool simplifiedExpr = true;
685     while (simplifiedExpr)
686     {
687         simplifiedExpr = false;
688
689         tgtAddr = tgtAddr->gtSkipReloadOrCopy();
690
691         while (tgtAddr->OperGet() == GT_ADDR && tgtAddr->gtOp.gtOp1->OperGet() == GT_IND)
692         {
693             tgtAddr        = tgtAddr->gtOp.gtOp1->gtOp.gtOp1;
694             simplifiedExpr = true;
695             assert(tgtAddr->TypeGet() == TYP_BYREF);
696         }
697         // For additions, one of the operands is a byref or a ref (and the other is not).  Follow this down to its
698         // source.
699         while (tgtAddr->OperGet() == GT_ADD || tgtAddr->OperGet() == GT_LEA)
700         {
701             if (tgtAddr->OperGet() == GT_ADD)
702             {
703                 if (tgtAddr->gtOp.gtOp1->TypeGet() == TYP_BYREF || tgtAddr->gtOp.gtOp1->TypeGet() == TYP_REF)
704                 {
705                     assert(!(tgtAddr->gtOp.gtOp2->TypeGet() == TYP_BYREF || tgtAddr->gtOp.gtOp2->TypeGet() == TYP_REF));
706                     tgtAddr        = tgtAddr->gtOp.gtOp1;
707                     simplifiedExpr = true;
708                 }
709                 else if (tgtAddr->gtOp.gtOp2->TypeGet() == TYP_BYREF || tgtAddr->gtOp.gtOp2->TypeGet() == TYP_REF)
710                 {
711                     tgtAddr        = tgtAddr->gtOp.gtOp2;
712                     simplifiedExpr = true;
713                 }
714                 else
715                 {
716                     // We might have a native int. For example:
717                     //        const     int    0
718                     //    +         byref
719                     //        lclVar    int    V06 loc5  // this is a local declared "valuetype VType*"
720                     return GCInfo::WBF_BarrierUnknown;
721                 }
722             }
723             else
724             {
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)
729                 {
730                     simplifiedExpr = true;
731                 }
732                 else
733                 {
734                     // We might have a native int.
735                     return GCInfo::WBF_BarrierUnknown;
736                 }
737             }
738         }
739     }
740     if (tgtAddr->IsLocalAddrExpr() != nullptr)
741     {
742         // No need for a GC barrier when writing to a local variable.
743         return GCInfo::WBF_NoBarrier;
744     }
745     if (tgtAddr->OperGet() == GT_LCL_VAR || tgtAddr->OperGet() == GT_REG_VAR)
746     {
747         unsigned lclNum = 0;
748         if (tgtAddr->gtOper == GT_LCL_VAR)
749         {
750             lclNum = tgtAddr->gtLclVar.gtLclNum;
751         }
752         else
753         {
754             assert(tgtAddr->gtOper == GT_REG_VAR);
755             lclNum = tgtAddr->gtRegVar.gtLclNum;
756         }
757
758         LclVarDsc* varDsc = &compiler->lvaTable[lclNum];
759
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)
763
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.
766         //
767         if (varDsc->lvStackByref)
768         {
769             assert(varDsc->TypeGet() == TYP_BYREF);
770             return GCInfo::WBF_NoBarrier;
771         }
772
773         // We don't eliminate for inlined methods, where we (can) know where the "retBuff" points.
774         if (!compiler->compIsForInlining() && lclNum == compiler->info.compRetBuffArg)
775         {
776             assert(compiler->info.compRetType == TYP_STRUCT); // Else shouldn't have a ret buff.
777
778             // Are we assured that the ret buff pointer points into the stack of a caller?
779             if (compiler->info.compRetBuffDefStack)
780             {
781 #if 0
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.
785 #ifdef DEBUG
786                 return WBF_NoBarrier_CheckNotHeapInDebug;
787 #else
788                 return WBF_NoBarrier;
789 #endif
790 #else  // 0
791                 return GCInfo::WBF_NoBarrier;
792 #endif // 0
793             }
794         }
795     }
796     if (tgtAddr->TypeGet() == TYP_REF)
797     {
798         return GCInfo::WBF_BarrierUnchecked;
799     }
800     // Otherwise, we have no information.
801     return GCInfo::WBF_BarrierUnknown;
802 }
803
804 #ifndef LEGACY_BACKEND
805 //------------------------------------------------------------------------
806 // gcUpdateForRegVarMove: Update the masks when a variable is moved
807 //
808 // Arguments:
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
812 //
813 // Return Value:
814 //    None
815 //
816 // Notes:
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.
820
821 void GCInfo::gcUpdateForRegVarMove(regMaskTP srcMask, regMaskTP dstMask, LclVarDsc* varDsc)
822 {
823     var_types type    = varDsc->TypeGet();
824     bool      isGCRef = (type == TYP_REF);
825     bool      isByRef = (type == TYP_BYREF);
826
827     if (srcMask != RBM_NONE)
828     {
829         regSet->RemoveMaskVars(srcMask);
830         if (isGCRef)
831         {
832             assert((gcRegByrefSetCur & srcMask) == 0);
833             gcRegGCrefSetCur &= ~srcMask;
834             gcRegGCrefSetCur |= dstMask; // safe if no dst, i.e. RBM_NONE
835         }
836         else if (isByRef)
837         {
838             assert((gcRegGCrefSetCur & srcMask) == 0);
839             gcRegByrefSetCur &= ~srcMask;
840             gcRegByrefSetCur |= dstMask; // safe if no dst, i.e. RBM_NONE
841         }
842     }
843     else if (isGCRef || isByRef)
844     {
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);
848     }
849     if (dstMask != RBM_NONE)
850     {
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)
855         {
856             if (isGCRef)
857             {
858                 gcRegGCrefSetCur |= dstMask;
859             }
860             else if (isByRef)
861             {
862                 gcRegByrefSetCur |= dstMask;
863             }
864         }
865     }
866     else if (isGCRef || isByRef)
867     {
868         VarSetOps::AddElemD(compiler, gcVarPtrSetCur, varDsc->lvVarIndex);
869     }
870 }
871 #endif // !LEGACY_BACKEND
872
873 /*****************************************************************************/
874 /*****************************************************************************/