2bfcfa265156e4878ab5faa979bc0df8442b1567
[platform/upstream/coreclr.git] / src / jit / lsraarm64.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 ARM64                        XX
9 XX                                                                           XX
10 XX  This encapsulates all the logic for setting register requirements for    XX
11 XX  the ARM64 architecture.                                                  XX
12 XX                                                                           XX
13 XX                                                                           XX
14 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
15 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
16 */
17
18 #include "jitpch.h"
19 #ifdef _MSC_VER
20 #pragma hdrstop
21 #endif
22
23 #ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator
24
25 #ifdef _TARGET_ARM64_
26
27 #include "jit.h"
28 #include "sideeffects.h"
29 #include "lower.h"
30
31 //------------------------------------------------------------------------
32 // TreeNodeInfoInit: Set the register requirements for RA.
33 //
34 // Notes:
35 //    Takes care of annotating the register requirements
36 //    for every TreeNodeInfo struct that maps to each tree node.
37 //
38 // Preconditions:
39 //    LSRA has been initialized and there is a TreeNodeInfo node
40 //    already allocated and initialized for every tree in the IR.
41 //
42 // Postconditions:
43 //    Every TreeNodeInfo instance has the right annotations on register
44 //    requirements needed by LSRA to build the Interval Table (source,
45 //    destination and internal [temp] register counts).
46 //
47 void LinearScan::TreeNodeInfoInit(GenTree* tree, TreeNodeInfo* info)
48 {
49     unsigned     kind         = tree->OperKind();
50     RegisterType registerType = TypeGet(tree);
51
52     if (tree->isContained())
53     {
54         info->dstCount = 0;
55         assert(info->srcCount == 0);
56         return;
57     }
58
59     // Set the default dstCount. This may be modified below.
60     if (tree->IsValue())
61     {
62         info->dstCount = 1;
63         if (tree->IsUnusedValue())
64         {
65             info->isLocalDefUse = true;
66         }
67     }
68     else
69     {
70         info->dstCount = 0;
71     }
72
73     switch (tree->OperGet())
74     {
75         GenTree* op1;
76         GenTree* op2;
77
78         default:
79             if (kind & (GTK_CONST | GTK_LEAF))
80             {
81                 info->srcCount = 0;
82             }
83             else if (kind & (GTK_SMPOP))
84             {
85                 info->srcCount = appendBinaryLocationInfoToList(tree->AsOp());
86             }
87             else
88             {
89                 unreached();
90             }
91             break;
92
93         case GT_STORE_LCL_FLD:
94         case GT_STORE_LCL_VAR:
95             info->srcCount = 1;
96             assert(info->dstCount == 0);
97             TreeNodeInfoInitStoreLoc(tree->AsLclVarCommon(), info);
98             break;
99
100         case GT_LIST:
101         case GT_FIELD_LIST:
102         case GT_ARGPLACE:
103         case GT_NO_OP:
104         case GT_START_NONGC:
105         case GT_PROF_HOOK:
106             info->srcCount = 0;
107             assert(info->dstCount == 0);
108             break;
109
110         case GT_CNS_DBL:
111             info->srcCount = 0;
112             assert(info->dstCount == 1);
113             {
114                 GenTreeDblCon* dblConst   = tree->AsDblCon();
115                 double         constValue = dblConst->gtDblCon.gtDconVal;
116
117                 if (emitter::emitIns_valid_imm_for_fmov(constValue))
118                 {
119                     // Directly encode constant to instructions.
120                 }
121                 else
122                 {
123                     // Reserve int to load constant from memory (IF_LARGELDC)
124                     info->internalIntCount = 1;
125                 }
126             }
127             break;
128
129         case GT_BOX:
130         case GT_COMMA:
131         case GT_QMARK:
132         case GT_COLON:
133             info->srcCount = 0;
134             assert(info->dstCount == 0);
135             unreached();
136             break;
137
138         case GT_RETURN:
139             TreeNodeInfoInitReturn(tree, info);
140             break;
141
142         case GT_RETFILT:
143             if (tree->TypeGet() == TYP_VOID)
144             {
145                 info->srcCount = 0;
146                 assert(info->dstCount == 0);
147             }
148             else
149             {
150                 assert(tree->TypeGet() == TYP_INT);
151
152                 info->srcCount = 1;
153                 assert(info->dstCount == 0);
154
155                 info->setSrcCandidates(this, RBM_INTRET);
156                 LocationInfoListNode* locationInfo = getLocationInfo(tree->gtOp.gtOp1);
157                 locationInfo->info.setSrcCandidates(this, RBM_INTRET);
158                 useList.Append(locationInfo);
159             }
160             break;
161
162         case GT_NOP:
163             // A GT_NOP is either a passthrough (if it is void, or if it has
164             // a child), but must be considered to produce a dummy value if it
165             // has a type but no child
166             info->srcCount = 0;
167             if (tree->TypeGet() != TYP_VOID && tree->gtOp.gtOp1 == nullptr)
168             {
169                 assert(info->dstCount == 1);
170             }
171             else
172             {
173                 assert(info->dstCount == 0);
174             }
175             break;
176
177         case GT_JTRUE:
178             info->srcCount = 0;
179             assert(info->dstCount == 0);
180             break;
181
182         case GT_JMP:
183             info->srcCount = 0;
184             assert(info->dstCount == 0);
185             break;
186
187         case GT_SWITCH:
188             // This should never occur since switch nodes must not be visible at this
189             // point in the JIT.
190             info->srcCount = 0;
191             noway_assert(!"Switch must be lowered at this point");
192             break;
193
194         case GT_JMPTABLE:
195             info->srcCount = 0;
196             assert(info->dstCount == 1);
197             break;
198
199         case GT_SWITCH_TABLE:
200             info->srcCount         = appendBinaryLocationInfoToList(tree->AsOp());
201             info->internalIntCount = 1;
202             assert(info->dstCount == 0);
203             break;
204
205         case GT_ASG:
206             noway_assert(!"We should never hit any assignment operator in lowering");
207             info->srcCount = 0;
208             break;
209
210         case GT_ADD:
211         case GT_SUB:
212             if (varTypeIsFloating(tree->TypeGet()))
213             {
214                 // overflow operations aren't supported on float/double types.
215                 assert(!tree->gtOverflow());
216
217                 // No implicit conversions at this stage as the expectation is that
218                 // everything is made explicit by adding casts.
219                 assert(tree->gtOp.gtOp1->TypeGet() == tree->gtOp.gtOp2->TypeGet());
220             }
221
222             __fallthrough;
223
224         case GT_AND:
225         case GT_OR:
226         case GT_XOR:
227             info->srcCount = appendBinaryLocationInfoToList(tree->AsOp());
228             assert(info->dstCount == 1);
229             break;
230
231         case GT_RETURNTRAP:
232             // this just turns into a compare of its child with an int
233             // + a conditional call
234             appendLocationInfoToList(tree->gtGetOp1());
235             info->srcCount = 1;
236             assert(info->dstCount == 0);
237             break;
238
239         case GT_MOD:
240         case GT_UMOD:
241             NYI_IF(varTypeIsFloating(tree->TypeGet()), "FP Remainder in ARM64");
242             assert(!"Shouldn't see an integer typed GT_MOD node in ARM64");
243             break;
244
245         case GT_MUL:
246             if (tree->gtOverflow())
247             {
248                 // Need a register different from target reg to check for overflow.
249                 info->internalIntCount       = 1;
250                 info->isInternalRegDelayFree = true;
251             }
252             __fallthrough;
253
254         case GT_DIV:
255         case GT_MULHI:
256         case GT_UDIV:
257         {
258             info->srcCount = appendBinaryLocationInfoToList(tree->AsOp());
259             assert(info->dstCount == 1);
260         }
261         break;
262
263         case GT_INTRINSIC:
264         {
265             noway_assert((tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Abs) ||
266                          (tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Ceiling) ||
267                          (tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Floor) ||
268                          (tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Round) ||
269                          (tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Sqrt));
270
271             // Both operand and its result must be of the same floating point type.
272             op1 = tree->gtOp.gtOp1;
273             assert(varTypeIsFloating(op1));
274             assert(op1->TypeGet() == tree->TypeGet());
275
276             appendLocationInfoToList(op1);
277             info->srcCount = 1;
278             assert(info->dstCount == 1);
279         }
280         break;
281
282 #ifdef FEATURE_SIMD
283         case GT_SIMD:
284             TreeNodeInfoInitSIMD(tree->AsSIMD(), info);
285             break;
286 #endif // FEATURE_SIMD
287
288 #ifdef FEATURE_HW_INTRINSICS
289         case GT_HWIntrinsic:
290             TreeNodeInfoInitHWIntrinsic(tree->AsHWIntrinsic(), info);
291             break;
292 #endif // FEATURE_HW_INTRINSICS
293
294         case GT_CAST:
295         {
296             // TODO-ARM64-CQ: Int-To-Int conversions - castOp cannot be a memory op and must have an assigned
297             //                register.
298             //         see CodeGen::genIntToIntCast()
299
300             appendLocationInfoToList(tree->gtGetOp1());
301             info->srcCount = 1;
302             assert(info->dstCount == 1);
303
304             // Non-overflow casts to/from float/double are done using SSE2 instructions
305             // and that allow the source operand to be either a reg or memop. Given the
306             // fact that casts from small int to float/double are done as two-level casts,
307             // the source operand is always guaranteed to be of size 4 or 8 bytes.
308             var_types castToType = tree->CastToType();
309             GenTree*  castOp     = tree->gtCast.CastOp();
310             var_types castOpType = castOp->TypeGet();
311             if (tree->gtFlags & GTF_UNSIGNED)
312             {
313                 castOpType = genUnsignedType(castOpType);
314             }
315
316             // Some overflow checks need a temp reg
317
318             Lowering::CastInfo castInfo;
319             // Get information about the cast.
320             Lowering::getCastDescription(tree, &castInfo);
321
322             if (castInfo.requiresOverflowCheck)
323             {
324                 var_types srcType = castOp->TypeGet();
325                 emitAttr  cmpSize = EA_ATTR(genTypeSize(srcType));
326
327                 // If we cannot store the comparisons in an immediate for either
328                 // comparing against the max or min value, then we will need to
329                 // reserve a temporary register.
330
331                 bool canStoreMaxValue = emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, cmpSize);
332                 bool canStoreMinValue = emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, cmpSize);
333
334                 if (!canStoreMaxValue || !canStoreMinValue)
335                 {
336                     info->internalIntCount = 1;
337                 }
338             }
339         }
340         break;
341
342         case GT_NEG:
343         case GT_NOT:
344             appendLocationInfoToList(tree->gtGetOp1());
345             info->srcCount = 1;
346             assert(info->dstCount == 1);
347             break;
348
349         case GT_LSH:
350         case GT_RSH:
351         case GT_RSZ:
352         case GT_ROR:
353             TreeNodeInfoInitShiftRotate(tree, info);
354             break;
355
356         case GT_EQ:
357         case GT_NE:
358         case GT_LT:
359         case GT_LE:
360         case GT_GE:
361         case GT_GT:
362         case GT_TEST_EQ:
363         case GT_TEST_NE:
364         case GT_JCMP:
365             TreeNodeInfoInitCmp(tree, info);
366             break;
367
368         case GT_CKFINITE:
369             appendLocationInfoToList(tree->gtOp.gtOp1);
370             info->srcCount = 1;
371             assert(info->dstCount == 1);
372             info->internalIntCount = 1;
373             break;
374
375         case GT_CMPXCHG:
376         {
377             GenTreeCmpXchg* cmpXchgNode = tree->AsCmpXchg();
378             info->srcCount              = cmpXchgNode->gtOpComparand->isContained() ? 2 : 3;
379             assert(info->dstCount == 1);
380
381             info->internalIntCount = 1;
382
383             // For ARMv8 exclusives the lifetime of the addr and data must be extended because
384             // it may be used used multiple during retries
385             LocationInfoListNode* locationInfo = getLocationInfo(tree->gtCmpXchg.gtOpLocation);
386             locationInfo->info.isDelayFree     = true;
387             useList.Append(locationInfo);
388             LocationInfoListNode* valueInfo = getLocationInfo(tree->gtCmpXchg.gtOpValue);
389             valueInfo->info.isDelayFree     = true;
390             useList.Append(valueInfo);
391             if (!cmpXchgNode->gtOpComparand->isContained())
392             {
393                 LocationInfoListNode* comparandInfo = getLocationInfo(tree->gtCmpXchg.gtOpComparand);
394                 comparandInfo->info.isDelayFree     = true;
395                 useList.Append(comparandInfo);
396             }
397             info->hasDelayFreeSrc = true;
398
399             // Internals may not collide with target
400             info->isInternalRegDelayFree = true;
401         }
402         break;
403
404         case GT_LOCKADD:
405         case GT_XADD:
406         case GT_XCHG:
407         {
408             assert(info->dstCount == (tree->TypeGet() == TYP_VOID) ? 0 : 1);
409             info->srcCount         = tree->gtOp.gtOp2->isContained() ? 1 : 2;
410             info->internalIntCount = (tree->OperGet() == GT_XCHG) ? 1 : 2;
411
412             // For ARMv8 exclusives the lifetime of the addr and data must be extended because
413             // it may be used used multiple during retries
414             assert(!tree->gtOp.gtOp1->isContained());
415             LocationInfoListNode* op1Info = getLocationInfo(tree->gtOp.gtOp1);
416             op1Info->info.isDelayFree     = true;
417             useList.Append(op1Info);
418             if (!tree->gtOp.gtOp2->isContained())
419             {
420                 LocationInfoListNode* op2Info = getLocationInfo(tree->gtOp.gtOp2);
421                 op2Info->info.isDelayFree     = true;
422                 useList.Append(op2Info);
423             }
424             info->hasDelayFreeSrc = true;
425
426             // Internals may not collide with target
427             info->isInternalRegDelayFree = true;
428         }
429         break;
430
431         case GT_PUTARG_STK:
432             TreeNodeInfoInitPutArgStk(tree->AsPutArgStk(), info);
433             break;
434
435         case GT_PUTARG_REG:
436             TreeNodeInfoInitPutArgReg(tree->AsUnOp(), info);
437             break;
438
439         case GT_CALL:
440             TreeNodeInfoInitCall(tree->AsCall(), info);
441             break;
442
443         case GT_ADDR:
444         {
445             // For a GT_ADDR, the child node should not be evaluated into a register
446             GenTree* child = tree->gtOp.gtOp1;
447             assert(!isCandidateLocalRef(child));
448             assert(child->isContained());
449             assert(info->dstCount == 1);
450             info->srcCount = 0;
451         }
452         break;
453
454         case GT_BLK:
455         case GT_DYN_BLK:
456             // These should all be eliminated prior to Lowering.
457             assert(!"Non-store block node in Lowering");
458             info->srcCount = 0;
459             break;
460
461         case GT_STORE_BLK:
462         case GT_STORE_OBJ:
463         case GT_STORE_DYN_BLK:
464             TreeNodeInfoInitBlockStore(tree->AsBlk(), info);
465             break;
466
467         case GT_INIT_VAL:
468             // Always a passthrough of its child's value.
469             assert(!"INIT_VAL should always be contained");
470             break;
471
472         case GT_LCLHEAP:
473         {
474             assert(info->dstCount == 1);
475
476             // Need a variable number of temp regs (see genLclHeap() in codegenamd64.cpp):
477             // Here '-' means don't care.
478             //
479             //  Size?                   Init Memory?    # temp regs
480             //   0                          -               0
481             //   const and <=6 ptr words    -               0
482             //   const and <PageSize        No              0
483             //   >6 ptr words               Yes           hasPspSym ? 1 : 0
484             //   Non-const                  Yes           hasPspSym ? 1 : 0
485             //   Non-const                  No              2
486             //
487             // PSPSym - If the method has PSPSym increment internalIntCount by 1.
488             //
489             bool hasPspSym;
490 #if FEATURE_EH_FUNCLETS
491             hasPspSym = (compiler->lvaPSPSym != BAD_VAR_NUM);
492 #else
493             hasPspSym = false;
494 #endif
495
496             GenTree* size = tree->gtOp.gtOp1;
497             if (size->IsCnsIntOrI())
498             {
499                 assert(size->isContained());
500                 info->srcCount = 0;
501
502                 size_t sizeVal = size->gtIntCon.gtIconVal;
503
504                 if (sizeVal == 0)
505                 {
506                     info->internalIntCount = 0;
507                 }
508                 else
509                 {
510                     // Compute the amount of memory to properly STACK_ALIGN.
511                     // Note: The Gentree node is not updated here as it is cheap to recompute stack aligned size.
512                     // This should also help in debugging as we can examine the original size specified with
513                     // localloc.
514                     sizeVal                          = AlignUp(sizeVal, STACK_ALIGN);
515                     size_t cntStackAlignedWidthItems = (sizeVal >> STACK_ALIGN_SHIFT);
516
517                     // For small allocations upto 4 'stp' instructions (i.e. 64 bytes of localloc)
518                     //
519                     if (cntStackAlignedWidthItems <= 4)
520                     {
521                         info->internalIntCount = 0;
522                     }
523                     else if (!compiler->info.compInitMem)
524                     {
525                         // No need to initialize allocated stack space.
526                         if (sizeVal < compiler->eeGetPageSize())
527                         {
528                             info->internalIntCount = 0;
529                         }
530                         else
531                         {
532                             // We need two registers: regCnt and RegTmp
533                             info->internalIntCount = 2;
534                         }
535                     }
536                     else
537                     {
538                         // greater than 4 and need to zero initialize allocated stack space.
539                         // If the method has PSPSym, we need an internal register to hold regCnt
540                         // since targetReg allocated to GT_LCLHEAP node could be the same as one of
541                         // the the internal registers.
542                         info->internalIntCount = hasPspSym ? 1 : 0;
543                     }
544                 }
545             }
546             else
547             {
548                 appendLocationInfoToList(size);
549                 info->srcCount = 1;
550                 if (!compiler->info.compInitMem)
551                 {
552                     info->internalIntCount = 2;
553                 }
554                 else
555                 {
556                     // If the method has PSPSym, we need an internal register to hold regCnt
557                     // since targetReg allocated to GT_LCLHEAP node could be the same as one of
558                     // the the internal registers.
559                     info->internalIntCount = hasPspSym ? 1 : 0;
560                 }
561             }
562
563             // If the method has PSPSym, we would need an addtional register to relocate it on stack.
564             if (hasPspSym)
565             {
566                 // Exclude const size 0
567                 if (!size->IsCnsIntOrI() || (size->gtIntCon.gtIconVal > 0))
568                     info->internalIntCount++;
569             }
570         }
571         break;
572
573         case GT_ARR_BOUNDS_CHECK:
574 #ifdef FEATURE_SIMD
575         case GT_SIMD_CHK:
576 #endif // FEATURE_SIMD
577         {
578             GenTreeBoundsChk* node = tree->AsBoundsChk();
579             // Consumes arrLen & index - has no result
580             assert(info->dstCount == 0);
581
582             GenTree* intCns = nullptr;
583             GenTree* other  = nullptr;
584             info->srcCount  = GetOperandInfo(tree->AsBoundsChk()->gtIndex);
585             info->srcCount += GetOperandInfo(tree->AsBoundsChk()->gtArrLen);
586         }
587         break;
588
589         case GT_ARR_ELEM:
590             // These must have been lowered to GT_ARR_INDEX
591             noway_assert(!"We should never see a GT_ARR_ELEM in lowering");
592             info->srcCount = 0;
593             assert(info->dstCount == 0);
594             break;
595
596         case GT_ARR_INDEX:
597         {
598             info->srcCount = 2;
599             assert(info->dstCount == 1);
600             info->internalIntCount       = 1;
601             info->isInternalRegDelayFree = true;
602
603             // For GT_ARR_INDEX, the lifetime of the arrObj must be extended because it is actually used multiple
604             // times while the result is being computed.
605             LocationInfoListNode* arrObjInfo = getLocationInfo(tree->AsArrIndex()->ArrObj());
606             arrObjInfo->info.isDelayFree     = true;
607             useList.Append(arrObjInfo);
608             useList.Append(getLocationInfo(tree->AsArrIndex()->IndexExpr()));
609             info->hasDelayFreeSrc = true;
610         }
611         break;
612
613         case GT_ARR_OFFSET:
614             // This consumes the offset, if any, the arrObj and the effective index,
615             // and produces the flattened offset for this dimension.
616             info->srcCount = 2;
617             if (!tree->gtArrOffs.gtOffset->isContained())
618             {
619                 appendLocationInfoToList(tree->AsArrOffs()->gtOffset);
620                 info->srcCount++;
621             }
622             appendLocationInfoToList(tree->AsArrOffs()->gtIndex);
623             appendLocationInfoToList(tree->AsArrOffs()->gtArrObj);
624             assert(info->dstCount == 1);
625             info->internalIntCount = 1;
626             break;
627
628         case GT_LEA:
629         {
630             GenTreeAddrMode* lea = tree->AsAddrMode();
631
632             GenTree* base  = lea->Base();
633             GenTree* index = lea->Index();
634             int      cns   = lea->Offset();
635
636             // This LEA is instantiating an address, so we set up the srcCount here.
637             info->srcCount = 0;
638             if (base != nullptr)
639             {
640                 info->srcCount++;
641                 appendLocationInfoToList(base);
642             }
643             if (index != nullptr)
644             {
645                 info->srcCount++;
646                 appendLocationInfoToList(index);
647             }
648             assert(info->dstCount == 1);
649
650             // On ARM64 we may need a single internal register
651             // (when both conditions are true then we still only need a single internal register)
652             if ((index != nullptr) && (cns != 0))
653             {
654                 // ARM64 does not support both Index and offset so we need an internal register
655                 info->internalIntCount = 1;
656             }
657             else if (!emitter::emitIns_valid_imm_for_add(cns, EA_8BYTE))
658             {
659                 // This offset can't be contained in the add instruction, so we need an internal register
660                 info->internalIntCount = 1;
661             }
662         }
663         break;
664
665         case GT_STOREIND:
666         {
667             assert(info->dstCount == 0);
668
669             if (compiler->codeGen->gcInfo.gcIsWriteBarrierAsgNode(tree))
670             {
671                 info->srcCount = 2;
672                 TreeNodeInfoInitGCWriteBarrier(tree, info);
673                 break;
674             }
675
676             TreeNodeInfoInitIndir(tree->AsIndir(), info);
677             if (!tree->gtGetOp2()->isContained())
678             {
679                 appendLocationInfoToList(tree->gtGetOp2());
680                 info->srcCount++;
681             }
682         }
683         break;
684
685         case GT_NULLCHECK:
686             // Unlike ARM, ARM64 implements NULLCHECK as a load to REG_ZR, so no internal register
687             // is required, and it is not a localDefUse.
688             assert(info->dstCount == 0);
689             assert(!tree->gtGetOp1()->isContained());
690             appendLocationInfoToList(tree->gtOp.gtOp1);
691             info->srcCount = 1;
692             break;
693
694         case GT_IND:
695             assert(info->dstCount == 1);
696             TreeNodeInfoInitIndir(tree->AsIndir(), info);
697             break;
698
699         case GT_CATCH_ARG:
700             info->srcCount = 0;
701             assert(info->dstCount == 1);
702             info->setDstCandidates(this, RBM_EXCEPTION_OBJECT);
703             break;
704
705         case GT_CLS_VAR:
706             info->srcCount = 0;
707             // GT_CLS_VAR, by the time we reach the backend, must always
708             // be a pure use.
709             // It will produce a result of the type of the
710             // node, and use an internal register for the address.
711
712             assert(info->dstCount == 1);
713             assert((tree->gtFlags & (GTF_VAR_DEF | GTF_VAR_USEASG)) == 0);
714             info->internalIntCount = 1;
715             break;
716
717         case GT_INDEX_ADDR:
718             assert(info->dstCount == 1);
719             info->srcCount         = appendBinaryLocationInfoToList(tree->AsOp());
720             info->internalIntCount = 1;
721             break;
722     } // end switch (tree->OperGet())
723
724     if (tree->IsUnusedValue() && (info->dstCount != 0))
725     {
726         info->isLocalDefUse = true;
727     }
728     // We need to be sure that we've set info->srcCount and info->dstCount appropriately
729     assert((info->dstCount < 2) || tree->IsMultiRegCall());
730     assert(info->isLocalDefUse == (tree->IsValue() && tree->IsUnusedValue()));
731     assert(!tree->IsUnusedValue() || (info->dstCount != 0));
732     assert(info->dstCount == tree->GetRegisterDstCount());
733 }
734
735 //------------------------------------------------------------------------
736 // TreeNodeInfoInitReturn: Set the NodeInfo for a GT_RETURN.
737 //
738 // Arguments:
739 //    tree      - The node of interest
740 //
741 // Return Value:
742 //    None.
743 //
744 void LinearScan::TreeNodeInfoInitReturn(GenTree* tree, TreeNodeInfo* info)
745 {
746     GenTree*  op1           = tree->gtGetOp1();
747     regMaskTP useCandidates = RBM_NONE;
748
749     info->srcCount = ((tree->TypeGet() == TYP_VOID) || op1->isContained()) ? 0 : 1;
750     assert(info->dstCount == 0);
751
752     if ((tree->TypeGet() != TYP_VOID) && !op1->isContained())
753     {
754         if (varTypeIsStruct(tree))
755         {
756             // op1 has to be either an lclvar or a multi-reg returning call
757             if (op1->OperGet() != GT_LCL_VAR)
758             {
759                 noway_assert(op1->IsMultiRegCall());
760
761                 ReturnTypeDesc* retTypeDesc = op1->AsCall()->GetReturnTypeDesc();
762                 info->srcCount              = retTypeDesc->GetReturnRegCount();
763                 useCandidates               = retTypeDesc->GetABIReturnRegs();
764             }
765         }
766         else
767         {
768             // Non-struct type return - determine useCandidates
769             switch (tree->TypeGet())
770             {
771                 case TYP_VOID:
772                     useCandidates = RBM_NONE;
773                     break;
774                 case TYP_FLOAT:
775                     useCandidates = RBM_FLOATRET;
776                     break;
777                 case TYP_DOUBLE:
778                     useCandidates = RBM_DOUBLERET;
779                     break;
780                 case TYP_LONG:
781                     useCandidates = RBM_LNGRET;
782                     break;
783                 default:
784                     useCandidates = RBM_INTRET;
785                     break;
786             }
787         }
788
789         LocationInfoListNode* locationInfo = getLocationInfo(op1);
790         if (useCandidates != RBM_NONE)
791         {
792             locationInfo->info.setSrcCandidates(this, useCandidates);
793         }
794         useList.Append(locationInfo);
795     }
796 }
797
798 #ifdef FEATURE_SIMD
799 //------------------------------------------------------------------------
800 // TreeNodeInfoInitSIMD: Set the NodeInfo for a GT_SIMD tree.
801 //
802 // Arguments:
803 //    tree       - The GT_SIMD node of interest
804 //
805 // Return Value:
806 //    None.
807
808 void LinearScan::TreeNodeInfoInitSIMD(GenTreeSIMD* simdTree, TreeNodeInfo* info)
809 {
810     // Only SIMDIntrinsicInit can be contained
811     if (simdTree->isContained())
812     {
813         assert(simdTree->gtSIMDIntrinsicID == SIMDIntrinsicInit);
814     }
815     assert(info->dstCount == 1);
816
817     GenTree* op1 = simdTree->gtOp.gtOp1;
818     GenTree* op2 = simdTree->gtOp.gtOp2;
819     if (!op1->OperIs(GT_LIST))
820     {
821         info->srcCount += GetOperandInfo(op1);
822     }
823     if ((op2 != nullptr) && !op2->isContained())
824     {
825         info->srcCount += GetOperandInfo(op2);
826     }
827
828     switch (simdTree->gtSIMDIntrinsicID)
829     {
830         case SIMDIntrinsicInit:
831             assert(info->srcCount == (simdTree->gtGetOp1()->isContained() ? 0 : 1));
832             break;
833
834         case SIMDIntrinsicCast:
835         case SIMDIntrinsicSqrt:
836         case SIMDIntrinsicAbs:
837         case SIMDIntrinsicConvertToSingle:
838         case SIMDIntrinsicConvertToInt32:
839         case SIMDIntrinsicConvertToDouble:
840         case SIMDIntrinsicConvertToInt64:
841         case SIMDIntrinsicWidenLo:
842         case SIMDIntrinsicWidenHi:
843             assert(info->srcCount == 1);
844             break;
845
846         case SIMDIntrinsicGetItem:
847         {
848             op1 = simdTree->gtGetOp1();
849             op2 = simdTree->gtGetOp2();
850
851             // We have an object and an index, either of which may be contained.
852             if (!op2->IsCnsIntOrI() && (!op1->isContained() || op1->OperIsLocal()))
853             {
854                 // If the index is not a constant and not contained or is a local
855                 // we will need a general purpose register to calculate the address
856                 info->internalIntCount = 1;
857
858                 // internal register must not clobber input index
859                 LocationInfoListNode* op2Info =
860                     (op1->isContained()) ? useList.Begin() : useList.GetSecond(INDEBUG(op2));
861                 op2Info->info.isDelayFree = true;
862                 info->hasDelayFreeSrc     = true;
863             }
864
865             if (!op2->IsCnsIntOrI() && (!op1->isContained()))
866             {
867                 // If vector is not already in memory (contained) and the index is not a constant,
868                 // we will use the SIMD temp location to store the vector.
869                 compiler->getSIMDInitTempVarNum();
870             }
871         }
872         break;
873
874         case SIMDIntrinsicAdd:
875         case SIMDIntrinsicSub:
876         case SIMDIntrinsicMul:
877         case SIMDIntrinsicDiv:
878         case SIMDIntrinsicBitwiseAnd:
879         case SIMDIntrinsicBitwiseAndNot:
880         case SIMDIntrinsicBitwiseOr:
881         case SIMDIntrinsicBitwiseXor:
882         case SIMDIntrinsicMin:
883         case SIMDIntrinsicMax:
884         case SIMDIntrinsicEqual:
885         case SIMDIntrinsicLessThan:
886         case SIMDIntrinsicGreaterThan:
887         case SIMDIntrinsicLessThanOrEqual:
888         case SIMDIntrinsicGreaterThanOrEqual:
889             assert(info->srcCount == 2);
890             break;
891
892         case SIMDIntrinsicSetX:
893         case SIMDIntrinsicSetY:
894         case SIMDIntrinsicSetZ:
895         case SIMDIntrinsicSetW:
896         case SIMDIntrinsicNarrow:
897             assert(info->srcCount == 2);
898
899             // Op1 will write to dst before Op2 is free
900             useList.GetSecond(INDEBUG(simdTree->gtGetOp2()))->info.isDelayFree = true;
901             info->hasDelayFreeSrc                                              = true;
902             break;
903
904         case SIMDIntrinsicInitN:
905         {
906             var_types baseType = simdTree->gtSIMDBaseType;
907             info->srcCount     = (short)(simdTree->gtSIMDSize / genTypeSize(baseType));
908             int initCount      = 0;
909             for (GenTree* list = op1; list != nullptr; list = list->gtGetOp2())
910             {
911                 assert(list->OperGet() == GT_LIST);
912                 GenTree* listItem = list->gtGetOp1();
913                 assert(listItem->TypeGet() == baseType);
914                 assert(!listItem->isContained());
915                 appendLocationInfoToList(listItem);
916                 initCount++;
917             }
918             assert(initCount == info->srcCount);
919
920             if (varTypeIsFloating(simdTree->gtSIMDBaseType))
921             {
922                 // Need an internal register to stitch together all the values into a single vector in a SIMD reg.
923                 info->setInternalCandidates(this, RBM_ALLFLOAT);
924                 info->internalFloatCount = 1;
925             }
926             break;
927         }
928
929         case SIMDIntrinsicInitArray:
930             // We have an array and an index, which may be contained.
931             assert(info->srcCount == (simdTree->gtGetOp2()->isContained() ? 1 : 2));
932             break;
933
934         case SIMDIntrinsicOpEquality:
935         case SIMDIntrinsicOpInEquality:
936             assert(info->srcCount == (simdTree->gtGetOp2()->isContained() ? 1 : 2));
937             info->setInternalCandidates(this, RBM_ALLFLOAT);
938             info->internalFloatCount = 1;
939             break;
940
941         case SIMDIntrinsicDotProduct:
942             assert(info->srcCount == 2);
943             info->setInternalCandidates(this, RBM_ALLFLOAT);
944             info->internalFloatCount = 1;
945             break;
946
947         case SIMDIntrinsicSelect:
948             // TODO-ARM64-CQ Allow lowering to see SIMDIntrinsicSelect so we can generate BSL VC, VA, VB
949             // bsl target register must be VC.  Reserve a temp in case we need to shuffle things.
950             // This will require a different approach, as GenTreeSIMD has only two operands.
951             assert(!"SIMDIntrinsicSelect not yet supported");
952             assert(info->srcCount == 3);
953             info->setInternalCandidates(this, RBM_ALLFLOAT);
954             info->internalFloatCount = 1;
955             break;
956
957         case SIMDIntrinsicInitArrayX:
958         case SIMDIntrinsicInitFixed:
959         case SIMDIntrinsicCopyToArray:
960         case SIMDIntrinsicCopyToArrayX:
961         case SIMDIntrinsicNone:
962         case SIMDIntrinsicGetCount:
963         case SIMDIntrinsicGetOne:
964         case SIMDIntrinsicGetZero:
965         case SIMDIntrinsicGetAllOnes:
966         case SIMDIntrinsicGetX:
967         case SIMDIntrinsicGetY:
968         case SIMDIntrinsicGetZ:
969         case SIMDIntrinsicGetW:
970         case SIMDIntrinsicInstEquals:
971         case SIMDIntrinsicHWAccel:
972         case SIMDIntrinsicWiden:
973         case SIMDIntrinsicInvalid:
974             assert(!"These intrinsics should not be seen during register allocation");
975             __fallthrough;
976
977         default:
978             noway_assert(!"Unimplemented SIMD node type.");
979             unreached();
980     }
981 }
982 #endif // FEATURE_SIMD
983
984 #ifdef FEATURE_HW_INTRINSICS
985 //------------------------------------------------------------------------
986 // TreeNodeInfoInitHWIntrinsic: Set the NodeInfo for a GT_HWIntrinsic tree.
987 //
988 // Arguments:
989 //    tree       - The GT_HWIntrinsic node of interest
990 //
991 // Return Value:
992 //    None.
993
994 void LinearScan::TreeNodeInfoInitHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, TreeNodeInfo* info)
995 {
996     NamedIntrinsic intrinsicID = intrinsicTree->gtHWIntrinsicId;
997     info->srcCount += GetOperandInfo(intrinsicTree->gtOp.gtOp1);
998     if (intrinsicTree->gtGetOp2IfPresent() != nullptr)
999     {
1000         info->srcCount += GetOperandInfo(intrinsicTree->gtOp.gtOp2);
1001     }
1002 }
1003 #endif
1004
1005 #endif // _TARGET_ARM64_
1006
1007 #endif // !LEGACY_BACKEND