Fix reading Time zone rules using Julian days (#17672)
[platform/upstream/coreclr.git] / src / jit / lsraarmarch.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              Register Requirements for ARM and ARM64 common code          XX
9 XX                                                                           XX
10 XX  This encapsulates common logic for setting register requirements for     XX
11 XX  the ARM and ARM64 architectures.                                         XX
12 XX                                                                           XX
13 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
14 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
15 */
16
17 #include "jitpch.h"
18 #ifdef _MSC_VER
19 #pragma hdrstop
20 #endif
21
22 #ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator
23
24 #ifdef _TARGET_ARMARCH_ // This file is ONLY used for ARM and ARM64 architectures
25
26 #include "jit.h"
27 #include "sideeffects.h"
28 #include "lower.h"
29 #include "lsra.h"
30
31 //------------------------------------------------------------------------
32 // BuildIndir: Specify register requirements for address expression
33 //                       of an indirection operation.
34 //
35 // Arguments:
36 //    indirTree - GT_IND, GT_STOREIND or block gentree node
37 //
38 void LinearScan::BuildIndir(GenTreeIndir* indirTree)
39 {
40     TreeNodeInfo* info = currentNodeInfo;
41     // If this is the rhs of a block copy (i.e. non-enregisterable struct),
42     // it has no register requirements.
43     if (indirTree->TypeGet() == TYP_STRUCT)
44     {
45         return;
46     }
47
48     bool isStore   = (indirTree->gtOper == GT_STOREIND);
49     info->srcCount = GetIndirInfo(indirTree);
50
51     GenTree* addr  = indirTree->Addr();
52     GenTree* index = nullptr;
53     int      cns   = 0;
54
55 #ifdef _TARGET_ARM_
56     // Unaligned loads/stores for floating point values must first be loaded into integer register(s)
57     if (indirTree->gtFlags & GTF_IND_UNALIGNED)
58     {
59         var_types type = TYP_UNDEF;
60         if (indirTree->OperGet() == GT_STOREIND)
61         {
62             type = indirTree->AsStoreInd()->Data()->TypeGet();
63         }
64         else if (indirTree->OperGet() == GT_IND)
65         {
66             type = indirTree->TypeGet();
67         }
68
69         if (type == TYP_FLOAT)
70         {
71             info->internalIntCount = 1;
72         }
73         else if (type == TYP_DOUBLE)
74         {
75             info->internalIntCount = 2;
76         }
77     }
78 #endif
79
80     if (addr->isContained())
81     {
82         assert(addr->OperGet() == GT_LEA);
83         GenTreeAddrMode* lea = addr->AsAddrMode();
84         index                = lea->Index();
85         cns                  = lea->Offset();
86
87         // On ARM we may need a single internal register
88         // (when both conditions are true then we still only need a single internal register)
89         if ((index != nullptr) && (cns != 0))
90         {
91             // ARM does not support both Index and offset so we need an internal register
92             info->internalIntCount++;
93         }
94         else if (!emitter::emitIns_valid_imm_for_ldst_offset(cns, emitTypeSize(indirTree)))
95         {
96             // This offset can't be contained in the ldr/str instruction, so we need an internal register
97             info->internalIntCount++;
98         }
99     }
100
101 #ifdef FEATURE_SIMD
102     if (indirTree->TypeGet() == TYP_SIMD12)
103     {
104         // If indirTree is of TYP_SIMD12, addr is not contained. See comment in LowerIndir().
105         assert(!indirTree->Addr()->isContained());
106
107         // Vector3 is read/written as two reads/writes: 8 byte and 4 byte.
108         // To assemble the vector properly we would need an additional int register
109         info->internalIntCount = 1;
110     }
111 #endif // FEATURE_SIMD
112 }
113
114 //------------------------------------------------------------------------
115 // BuildCall: Set the NodeInfo for a call.
116 //
117 // Arguments:
118 //    call - The call node of interest
119 //
120 // Return Value:
121 //    None.
122 //
123 void LinearScan::BuildCall(GenTreeCall* call)
124 {
125     TreeNodeInfo*   info              = currentNodeInfo;
126     bool            hasMultiRegRetVal = false;
127     ReturnTypeDesc* retTypeDesc       = nullptr;
128
129     info->srcCount = 0;
130     if (call->TypeGet() != TYP_VOID)
131     {
132         hasMultiRegRetVal = call->HasMultiRegRetVal();
133         if (hasMultiRegRetVal)
134         {
135             // dst count = number of registers in which the value is returned by call
136             retTypeDesc    = call->GetReturnTypeDesc();
137             info->dstCount = retTypeDesc->GetReturnRegCount();
138         }
139         else
140         {
141             info->dstCount = 1;
142         }
143     }
144     else
145     {
146         info->dstCount = 0;
147     }
148
149     GenTree*              ctrlExpr     = call->gtControlExpr;
150     LocationInfoListNode* ctrlExprInfo = nullptr;
151     if (call->gtCallType == CT_INDIRECT)
152     {
153         // either gtControlExpr != null or gtCallAddr != null.
154         // Both cannot be non-null at the same time.
155         assert(ctrlExpr == nullptr);
156         assert(call->gtCallAddr != nullptr);
157         ctrlExpr = call->gtCallAddr;
158     }
159
160     // set reg requirements on call target represented as control sequence.
161     if (ctrlExpr != nullptr)
162     {
163         ctrlExprInfo = getLocationInfo(ctrlExpr);
164
165         // we should never see a gtControlExpr whose type is void.
166         assert(ctrlExpr->TypeGet() != TYP_VOID);
167
168         // In case of fast tail implemented as jmp, make sure that gtControlExpr is
169         // computed into a register.
170         if (call->IsFastTailCall())
171         {
172             // Fast tail call - make sure that call target is always computed in R12(ARM32)/IP0(ARM64)
173             // so that epilog sequence can generate "br xip0/r12" to achieve fast tail call.
174             ctrlExprInfo->info.setSrcCandidates(this, RBM_FASTTAILCALL_TARGET);
175         }
176     }
177 #ifdef _TARGET_ARM_
178     else
179     {
180         info->internalIntCount = 1;
181     }
182 #endif // _TARGET_ARM_
183
184     RegisterType registerType = call->TypeGet();
185
186 // Set destination candidates for return value of the call.
187
188 #ifdef _TARGET_ARM_
189     if (call->IsHelperCall(compiler, CORINFO_HELP_INIT_PINVOKE_FRAME))
190     {
191         // The ARM CORINFO_HELP_INIT_PINVOKE_FRAME helper uses a custom calling convention that returns with
192         // TCB in REG_PINVOKE_TCB. fgMorphCall() sets the correct argument registers.
193         info->setDstCandidates(this, RBM_PINVOKE_TCB);
194     }
195     else
196 #endif // _TARGET_ARM_
197         if (hasMultiRegRetVal)
198     {
199         assert(retTypeDesc != nullptr);
200         info->setDstCandidates(this, retTypeDesc->GetABIReturnRegs());
201     }
202     else if (varTypeIsFloating(registerType))
203     {
204         info->setDstCandidates(this, RBM_FLOATRET);
205     }
206     else if (registerType == TYP_LONG)
207     {
208         info->setDstCandidates(this, RBM_LNGRET);
209     }
210     else
211     {
212         info->setDstCandidates(this, RBM_INTRET);
213     }
214
215     // First, count reg args
216     // Each register argument corresponds to one source.
217     bool callHasFloatRegArgs = false;
218
219     for (GenTree* list = call->gtCallLateArgs; list; list = list->MoveNext())
220     {
221         assert(list->OperIsList());
222
223         GenTree* argNode = list->Current();
224
225 #ifdef DEBUG
226         // During Build, we only use the ArgTabEntry for validation,
227         // as getting it is rather expensive.
228         fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, argNode);
229         regNumber      argReg         = curArgTabEntry->regNum;
230         assert(curArgTabEntry);
231 #endif
232
233         if (argNode->gtOper == GT_PUTARG_STK)
234         {
235             // late arg that is not passed in a register
236             assert(curArgTabEntry->regNum == REG_STK);
237             GenTree* putArgChild = argNode->gtGetOp1();
238             if (!varTypeIsStruct(putArgChild) && !putArgChild->OperIs(GT_FIELD_LIST))
239             {
240                 unsigned expectedSlots = 1;
241 #ifdef _TARGET_ARM_
242                 // The `double` types could been transformed to `long` on arm, while the actual longs
243                 // have been decomposed.
244                 if (putArgChild->TypeGet() == TYP_LONG)
245                 {
246                     useList.GetTreeNodeInfo(argNode).srcCount = 2;
247                     expectedSlots                             = 2;
248                 }
249                 else if (putArgChild->TypeGet() == TYP_DOUBLE)
250                 {
251                     expectedSlots = 2;
252                 }
253 #endif // !_TARGET_ARM_
254                 // Validate the slot count for this arg.
255                 assert(curArgTabEntry->numSlots == expectedSlots);
256             }
257             continue;
258         }
259
260         // A GT_FIELD_LIST has a TYP_VOID, but is used to represent a multireg struct
261         if (argNode->OperGet() == GT_FIELD_LIST)
262         {
263             assert(argNode->isContained());
264
265             // There could be up to 2-4 PUTARG_REGs in the list (3 or 4 can only occur for HFAs)
266             for (GenTreeFieldList* entry = argNode->AsFieldList(); entry != nullptr; entry = entry->Rest())
267             {
268                 info->srcCount++;
269                 appendLocationInfoToList(entry->Current());
270 #ifdef DEBUG
271                 assert(entry->Current()->OperIs(GT_PUTARG_REG));
272                 assert(entry->Current()->gtRegNum == argReg);
273                 // Update argReg for the next putarg_reg (if any)
274                 argReg = genRegArgNext(argReg);
275
276 #if defined(_TARGET_ARM_)
277                 // A double register is modelled as an even-numbered single one
278                 if (entry->Current()->TypeGet() == TYP_DOUBLE)
279                 {
280                     argReg = genRegArgNext(argReg);
281                 }
282 #endif // _TARGET_ARM_
283 #endif
284             }
285         }
286 #ifdef _TARGET_ARM_
287         else if (argNode->OperGet() == GT_PUTARG_SPLIT)
288         {
289             unsigned regCount = argNode->AsPutArgSplit()->gtNumRegs;
290             assert(regCount == curArgTabEntry->numRegs);
291             info->srcCount += regCount;
292             appendLocationInfoToList(argNode);
293         }
294 #endif
295         else
296         {
297             assert(argNode->OperIs(GT_PUTARG_REG));
298             assert(argNode->gtRegNum == argReg);
299             HandleFloatVarArgs(call, argNode, &callHasFloatRegArgs);
300 #ifdef _TARGET_ARM_
301             // The `double` types have been transformed to `long` on armel,
302             // while the actual long types have been decomposed.
303             // On ARM we may have bitcasts from DOUBLE to LONG.
304             if (argNode->TypeGet() == TYP_LONG)
305             {
306                 assert(argNode->IsMultiRegNode());
307                 info->srcCount += 2;
308                 appendLocationInfoToList(argNode);
309             }
310             else
311 #endif // _TARGET_ARM_
312             {
313                 appendLocationInfoToList(argNode);
314                 info->srcCount++;
315             }
316         }
317     }
318
319     // Now, count stack args
320     // Note that these need to be computed into a register, but then
321     // they're just stored to the stack - so the reg doesn't
322     // need to remain live until the call.  In fact, it must not
323     // because the code generator doesn't actually consider it live,
324     // so it can't be spilled.
325
326     GenTree* args = call->gtCallArgs;
327     while (args)
328     {
329         GenTree* arg = args->gtOp.gtOp1;
330
331         // Skip arguments that have been moved to the Late Arg list
332         if (!(args->gtFlags & GTF_LATE_ARG))
333         {
334 #ifdef DEBUG
335             fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, arg);
336             assert(curArgTabEntry);
337 #endif
338 #ifdef _TARGET_ARM_
339             // PUTARG_SPLIT nodes must be in the gtCallLateArgs list, since they
340             // define registers used by the call.
341             assert(arg->OperGet() != GT_PUTARG_SPLIT);
342 #endif
343             if (arg->gtOper == GT_PUTARG_STK)
344             {
345                 assert(curArgTabEntry->regNum == REG_STK);
346             }
347             else
348             {
349                 assert(!arg->IsValue() || arg->IsUnusedValue());
350             }
351         }
352         args = args->gtOp.gtOp2;
353     }
354
355     // If it is a fast tail call, it is already preferenced to use IP0.
356     // Therefore, no need set src candidates on call tgt again.
357     if (call->IsVarargs() && callHasFloatRegArgs && !call->IsFastTailCall() && (ctrlExprInfo != nullptr))
358     {
359         NYI_ARM("float reg varargs");
360
361         // Don't assign the call target to any of the argument registers because
362         // we will use them to also pass floating point arguments as required
363         // by Arm64 ABI.
364         ctrlExprInfo->info.setSrcCandidates(this, allRegs(TYP_INT) & ~(RBM_ARG_REGS));
365     }
366
367     if (ctrlExprInfo != nullptr)
368     {
369         useList.Append(ctrlExprInfo);
370         info->srcCount++;
371     }
372
373 #ifdef _TARGET_ARM_
374
375     if (call->NeedsNullCheck())
376     {
377         info->internalIntCount++;
378     }
379
380 #endif // _TARGET_ARM_
381 }
382
383 //------------------------------------------------------------------------
384 // BuildPutArgStk: Set the NodeInfo for a GT_PUTARG_STK node
385 //
386 // Arguments:
387 //    argNode - a GT_PUTARG_STK node
388 //
389 // Return Value:
390 //    None.
391 //
392 // Notes:
393 //    Set the child node(s) to be contained when we have a multireg arg
394 //
395 void LinearScan::BuildPutArgStk(GenTreePutArgStk* argNode)
396 {
397     TreeNodeInfo* info = currentNodeInfo;
398     assert(argNode->gtOper == GT_PUTARG_STK);
399
400     GenTree* putArgChild = argNode->gtOp.gtOp1;
401
402     info->srcCount = 0;
403     info->dstCount = 0;
404
405     // Do we have a TYP_STRUCT argument (or a GT_FIELD_LIST), if so it must be a multireg pass-by-value struct
406     if ((putArgChild->TypeGet() == TYP_STRUCT) || (putArgChild->OperGet() == GT_FIELD_LIST))
407     {
408         // We will use store instructions that each write a register sized value
409
410         if (putArgChild->OperGet() == GT_FIELD_LIST)
411         {
412             assert(putArgChild->isContained());
413             // We consume all of the items in the GT_FIELD_LIST
414             for (GenTreeFieldList* current = putArgChild->AsFieldList(); current != nullptr; current = current->Rest())
415             {
416                 appendLocationInfoToList(current->Current());
417                 info->srcCount++;
418             }
419         }
420         else
421         {
422 #ifdef _TARGET_ARM64_
423             // We could use a ldp/stp sequence so we need two internal registers
424             info->internalIntCount = 2;
425 #else  // _TARGET_ARM_
426             // We could use a ldr/str sequence so we need a internal register
427             info->internalIntCount = 1;
428 #endif // _TARGET_ARM_
429
430             if (putArgChild->OperGet() == GT_OBJ)
431             {
432                 assert(putArgChild->isContained());
433                 GenTree* objChild = putArgChild->gtOp.gtOp1;
434                 if (objChild->OperGet() == GT_LCL_VAR_ADDR)
435                 {
436                     // We will generate all of the code for the GT_PUTARG_STK, the GT_OBJ and the GT_LCL_VAR_ADDR
437                     // as one contained operation, and there are no source registers.
438                     //
439                     assert(objChild->isContained());
440                 }
441                 else
442                 {
443                     // We will generate all of the code for the GT_PUTARG_STK and its child node
444                     // as one contained operation
445                     //
446                     appendLocationInfoToList(objChild);
447                     info->srcCount = 1;
448                 }
449             }
450             else
451             {
452                 // No source registers.
453                 putArgChild->OperIs(GT_LCL_VAR);
454             }
455         }
456     }
457     else
458     {
459         assert(!putArgChild->isContained());
460         info->srcCount = GetOperandInfo(putArgChild);
461     }
462 }
463
464 #ifdef _TARGET_ARM_
465 //------------------------------------------------------------------------
466 // BuildPutArgSplit: Set the NodeInfo for a GT_PUTARG_SPLIT node
467 //
468 // Arguments:
469 //    argNode - a GT_PUTARG_SPLIT node
470 //
471 // Return Value:
472 //    None.
473 //
474 // Notes:
475 //    Set the child node(s) to be contained
476 //
477 void LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode)
478 {
479     TreeNodeInfo* info = currentNodeInfo;
480     assert(argNode->gtOper == GT_PUTARG_SPLIT);
481
482     GenTree* putArgChild = argNode->gtOp.gtOp1;
483
484     // Registers for split argument corresponds to source
485     info->dstCount = argNode->gtNumRegs;
486
487     regNumber argReg  = argNode->gtRegNum;
488     regMaskTP argMask = RBM_NONE;
489     for (unsigned i = 0; i < argNode->gtNumRegs; i++)
490     {
491         argMask |= genRegMask((regNumber)((unsigned)argReg + i));
492     }
493     info->setDstCandidates(this, argMask);
494     info->setSrcCandidates(this, argMask);
495
496     if (putArgChild->OperGet() == GT_FIELD_LIST)
497     {
498         // Generated code:
499         // 1. Consume all of the items in the GT_FIELD_LIST (source)
500         // 2. Store to target slot and move to target registers (destination) from source
501         //
502         unsigned sourceRegCount = 0;
503
504         // To avoid redundant moves, have the argument operand computed in the
505         // register in which the argument is passed to the call.
506
507         for (GenTreeFieldList* fieldListPtr = putArgChild->AsFieldList(); fieldListPtr != nullptr;
508              fieldListPtr                   = fieldListPtr->Rest())
509         {
510             GenTree* node = fieldListPtr->gtGetOp1();
511             assert(!node->isContained());
512             LocationInfoListNode* nodeInfo        = getLocationInfo(node);
513             unsigned              currentRegCount = nodeInfo->info.dstCount;
514             regMaskTP             sourceMask      = RBM_NONE;
515             if (sourceRegCount < argNode->gtNumRegs)
516             {
517                 for (unsigned regIndex = 0; regIndex < currentRegCount; regIndex++)
518                 {
519                     sourceMask |= genRegMask((regNumber)((unsigned)argReg + sourceRegCount + regIndex));
520                 }
521                 nodeInfo->info.setSrcCandidates(this, sourceMask);
522             }
523             sourceRegCount += currentRegCount;
524             useList.Append(nodeInfo);
525         }
526         info->srcCount += sourceRegCount;
527         assert(putArgChild->isContained());
528     }
529     else
530     {
531         assert(putArgChild->TypeGet() == TYP_STRUCT);
532         assert(putArgChild->OperGet() == GT_OBJ);
533
534         // We can use a ldr/str sequence so we need an internal register
535         info->internalIntCount = 1;
536         regMaskTP internalMask = RBM_ALLINT & ~argMask;
537         info->setInternalCandidates(this, internalMask);
538
539         GenTree* objChild = putArgChild->gtOp.gtOp1;
540         if (objChild->OperGet() == GT_LCL_VAR_ADDR)
541         {
542             // We will generate all of the code for the GT_PUTARG_SPLIT, the GT_OBJ and the GT_LCL_VAR_ADDR
543             // as one contained operation
544             //
545             assert(objChild->isContained());
546         }
547         else
548         {
549             info->srcCount = GetIndirInfo(putArgChild->AsIndir());
550         }
551         assert(putArgChild->isContained());
552     }
553 }
554 #endif // _TARGET_ARM_
555
556 //------------------------------------------------------------------------
557 // BuildBlockStore: Set the NodeInfo for a block store.
558 //
559 // Arguments:
560 //    blkNode       - The block store node of interest
561 //
562 // Return Value:
563 //    None.
564 //
565 void LinearScan::BuildBlockStore(GenTreeBlk* blkNode)
566 {
567     TreeNodeInfo* info    = currentNodeInfo;
568     GenTree*      dstAddr = blkNode->Addr();
569     unsigned      size    = blkNode->gtBlkSize;
570     GenTree*      source  = blkNode->Data();
571
572     LocationInfoListNode* dstAddrInfo = nullptr;
573     LocationInfoListNode* sourceInfo  = nullptr;
574     LocationInfoListNode* sizeInfo    = nullptr;
575
576     // Sources are dest address and initVal or source.
577     // We may require an additional source or temp register for the size.
578     if (!dstAddr->isContained())
579     {
580         info->srcCount++;
581         dstAddrInfo = getLocationInfo(dstAddr);
582     }
583     assert(info->dstCount == 0);
584     GenTree* srcAddrOrFill = nullptr;
585     bool     isInitBlk     = blkNode->OperIsInitBlkOp();
586
587     regMaskTP dstAddrRegMask = RBM_NONE;
588     regMaskTP sourceRegMask  = RBM_NONE;
589     regMaskTP blkSizeRegMask = RBM_NONE;
590
591     short     internalIntCount      = 0;
592     regMaskTP internalIntCandidates = RBM_NONE;
593
594     if (isInitBlk)
595     {
596         GenTree* initVal = source;
597         if (initVal->OperIsInitVal())
598         {
599             assert(initVal->isContained());
600             initVal = initVal->gtGetOp1();
601         }
602         srcAddrOrFill = initVal;
603         if (!initVal->isContained())
604         {
605             info->srcCount++;
606             sourceInfo = getLocationInfo(initVal);
607         }
608
609         if (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll)
610         {
611             // TODO-ARM-CQ: Currently we generate a helper call for every
612             // initblk we encounter.  Later on we should implement loop unrolling
613             // code sequences to improve CQ.
614             // For reference see the code in lsraxarch.cpp.
615             NYI_ARM("initblk loop unrolling is currently not implemented.");
616         }
617         else
618         {
619             assert(blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper);
620             assert(!initVal->isContained());
621             // The helper follows the regular ABI.
622             dstAddrRegMask = RBM_ARG_0;
623             sourceRegMask  = RBM_ARG_1;
624             blkSizeRegMask = RBM_ARG_2;
625         }
626     }
627     else
628     {
629         // CopyObj or CopyBlk
630         // Sources are src and dest and size if not constant.
631         if (source->gtOper == GT_IND)
632         {
633             assert(source->isContained());
634             srcAddrOrFill = source->gtGetOp1();
635             assert(!srcAddrOrFill->isContained());
636             sourceInfo = getLocationInfo(srcAddrOrFill);
637             info->srcCount++;
638         }
639         if (blkNode->OperGet() == GT_STORE_OBJ)
640         {
641             // CopyObj
642             // We don't need to materialize the struct size but we still need
643             // a temporary register to perform the sequence of loads and stores.
644             internalIntCount = 1;
645
646             if (size >= 2 * REGSIZE_BYTES)
647             {
648                 // We will use ldp/stp to reduce code size and improve performance
649                 // so we need to reserve an extra internal register
650                 internalIntCount++;
651             }
652
653             // We can't use the special Write Barrier registers, so exclude them from the mask
654             internalIntCandidates = RBM_ALLINT & ~(RBM_WRITE_BARRIER_DST_BYREF | RBM_WRITE_BARRIER_SRC_BYREF);
655
656             // If we have a dest address we want it in RBM_WRITE_BARRIER_DST_BYREF.
657             dstAddrRegMask = RBM_WRITE_BARRIER_DST_BYREF;
658
659             // If we have a source address we want it in REG_WRITE_BARRIER_SRC_BYREF.
660             // Otherwise, if it is a local, codegen will put its address in REG_WRITE_BARRIER_SRC_BYREF,
661             // which is killed by a StoreObj (and thus needn't be reserved).
662             if (srcAddrOrFill != nullptr)
663             {
664                 sourceRegMask = RBM_WRITE_BARRIER_SRC_BYREF;
665             }
666         }
667         else
668         {
669             // CopyBlk
670             if (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll)
671             {
672                 // In case of a CpBlk with a constant size and less than CPBLK_UNROLL_LIMIT size
673                 // we should unroll the loop to improve CQ.
674                 // For reference see the code in lsraxarch.cpp.
675
676                 internalIntCount      = 1;
677                 internalIntCandidates = RBM_ALLINT;
678
679 #ifdef _TARGET_ARM64_
680                 if (size >= 2 * REGSIZE_BYTES)
681                 {
682                     // We will use ldp/stp to reduce code size and improve performance
683                     // so we need to reserve an extra internal register
684                     internalIntCount++;
685                 }
686 #endif // _TARGET_ARM64_
687             }
688             else
689             {
690                 assert(blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper);
691                 dstAddrRegMask = RBM_ARG_0;
692                 // The srcAddr goes in arg1.
693                 if (srcAddrOrFill != nullptr)
694                 {
695                     sourceRegMask = RBM_ARG_1;
696                 }
697                 blkSizeRegMask = RBM_ARG_2;
698             }
699         }
700     }
701     if (dstAddrInfo != nullptr)
702     {
703         if (dstAddrRegMask != RBM_NONE)
704         {
705             dstAddrInfo->info.setSrcCandidates(this, dstAddrRegMask);
706         }
707         useList.Append(dstAddrInfo);
708     }
709     if (sourceRegMask != RBM_NONE)
710     {
711         if (sourceInfo != nullptr)
712         {
713             sourceInfo->info.setSrcCandidates(this, sourceRegMask);
714         }
715         else
716         {
717             // This is a local source; we'll use a temp register for its address.
718             internalIntCandidates |= sourceRegMask;
719             internalIntCount++;
720         }
721     }
722     if (sourceInfo != nullptr)
723     {
724         useList.Add(sourceInfo, blkNode->IsReverseOp());
725     }
726
727     if (blkNode->OperIs(GT_STORE_DYN_BLK))
728     {
729         // The block size argument is a third argument to GT_STORE_DYN_BLK
730         info->srcCount++;
731
732         GenTree* blockSize = blkNode->AsDynBlk()->gtDynamicSize;
733         sizeInfo           = getLocationInfo(blockSize);
734         useList.Add(sizeInfo, blkNode->AsDynBlk()->gtEvalSizeFirst);
735     }
736
737     if (blkSizeRegMask != RBM_NONE)
738     {
739         if (size != 0)
740         {
741             // Reserve a temp register for the block size argument.
742             internalIntCandidates |= blkSizeRegMask;
743             internalIntCount++;
744         }
745         else
746         {
747             // The block size argument is a third argument to GT_STORE_DYN_BLK
748             assert((blkNode->gtOper == GT_STORE_DYN_BLK) && (sizeInfo != nullptr));
749             info->setSrcCount(3);
750             sizeInfo->info.setSrcCandidates(this, blkSizeRegMask);
751         }
752     }
753     if (internalIntCount != 0)
754     {
755         info->internalIntCount = internalIntCount;
756         info->setInternalCandidates(this, internalIntCandidates);
757     }
758 }
759
760 #endif // _TARGET_ARMARCH_
761
762 #endif // !LEGACY_BACKEND