Merge pull request #12674 from CarolEidt/LowerStoreLoc
[platform/upstream/coreclr.git] / src / jit / lsraarm.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                         XX
9 XX                                                                           XX
10 XX  This encapsulates all the logic for setting register requirements for    XX
11 XX  the ARM  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_ARM_
26
27 #include "jit.h"
28 #include "sideeffects.h"
29 #include "lower.h"
30 #include "lsra.h"
31
32 //------------------------------------------------------------------------
33 // TreeNodeInfoInitReturn: Set the NodeInfo for a GT_RETURN.
34 //
35 // Arguments:
36 //    tree - The node of interest
37 //
38 // Return Value:
39 //    None.
40 //
41 void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
42 {
43     TreeNodeInfo* info     = &(tree->gtLsraInfo);
44     LinearScan*   l        = m_lsra;
45     Compiler*     compiler = comp;
46
47     if (tree->TypeGet() == TYP_LONG)
48     {
49         GenTree* op1 = tree->gtGetOp1();
50         noway_assert(op1->OperGet() == GT_LONG);
51         op1->SetContained();
52         GenTree* loVal = op1->gtGetOp1();
53         GenTree* hiVal = op1->gtGetOp2();
54         info->srcCount = 2;
55         loVal->gtLsraInfo.setSrcCandidates(l, RBM_LNGRET_LO);
56         hiVal->gtLsraInfo.setSrcCandidates(l, RBM_LNGRET_HI);
57         info->dstCount = 0;
58     }
59     else
60     {
61         GenTree*  op1           = tree->gtGetOp1();
62         regMaskTP useCandidates = RBM_NONE;
63
64         info->srcCount = (tree->TypeGet() == TYP_VOID) ? 0 : 1;
65         info->dstCount = 0;
66
67         if (varTypeIsStruct(tree))
68         {
69             // op1 has to be either an lclvar or a multi-reg returning call
70             if (op1->OperGet() == GT_LCL_VAR)
71             {
72                 GenTreeLclVarCommon* lclVarCommon = op1->AsLclVarCommon();
73                 LclVarDsc*           varDsc       = &(compiler->lvaTable[lclVarCommon->gtLclNum]);
74                 assert(varDsc->lvIsMultiRegRet);
75
76                 // Mark var as contained if not enregistrable.
77                 if (!varTypeIsEnregisterableStruct(op1))
78                 {
79                     MakeSrcContained(tree, op1);
80                 }
81             }
82             else
83             {
84                 noway_assert(op1->IsMultiRegCall());
85
86                 ReturnTypeDesc* retTypeDesc = op1->AsCall()->GetReturnTypeDesc();
87                 info->srcCount              = retTypeDesc->GetReturnRegCount();
88                 useCandidates               = retTypeDesc->GetABIReturnRegs();
89             }
90         }
91         else
92         {
93             // Non-struct type return - determine useCandidates
94             switch (tree->TypeGet())
95             {
96                 case TYP_VOID:
97                     useCandidates = RBM_NONE;
98                     break;
99                 case TYP_FLOAT:
100                     useCandidates = RBM_FLOATRET;
101                     break;
102                 case TYP_DOUBLE:
103                     useCandidates = RBM_DOUBLERET;
104                     break;
105                 case TYP_LONG:
106                     useCandidates = RBM_LNGRET;
107                     break;
108                 default:
109                     useCandidates = RBM_INTRET;
110                     break;
111             }
112         }
113
114         if (useCandidates != RBM_NONE)
115         {
116             tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, useCandidates);
117         }
118     }
119 }
120
121 void Lowering::TreeNodeInfoInitLclHeap(GenTree* tree)
122 {
123     TreeNodeInfo* info     = &(tree->gtLsraInfo);
124     LinearScan*   l        = m_lsra;
125     Compiler*     compiler = comp;
126
127     info->srcCount = 1;
128     info->dstCount = 1;
129
130     // Need a variable number of temp regs (see genLclHeap() in codegenarm.cpp):
131     // Here '-' means don't care.
132     //
133     //  Size?                   Init Memory?    # temp regs
134     //   0                          -               0
135     //   const and <=4 str instr    -             hasPspSym ? 1 : 0
136     //   const and <PageSize        No            hasPspSym ? 1 : 0
137     //   >4 ptr words               Yes           hasPspSym ? 2 : 1
138     //   Non-const                  Yes           hasPspSym ? 2 : 1
139     //   Non-const                  No            hasPspSym ? 2 : 1
140
141     bool hasPspSym;
142 #if FEATURE_EH_FUNCLETS
143     hasPspSym = (compiler->lvaPSPSym != BAD_VAR_NUM);
144 #else
145     hasPspSym = false;
146 #endif
147
148     GenTreePtr size = tree->gtOp.gtOp1;
149     if (size->IsCnsIntOrI())
150     {
151         MakeSrcContained(tree, size);
152
153         size_t sizeVal = size->gtIntCon.gtIconVal;
154         if (sizeVal == 0)
155         {
156             info->internalIntCount = 0;
157         }
158         else
159         {
160             sizeVal                          = AlignUp(sizeVal, STACK_ALIGN);
161             size_t cntStackAlignedWidthItems = (sizeVal >> STACK_ALIGN_SHIFT);
162
163             // For small allocations up to 4 store instructions
164             if (cntStackAlignedWidthItems <= 4)
165             {
166                 info->internalIntCount = 0;
167             }
168             else if (!compiler->info.compInitMem)
169             {
170                 // No need to initialize allocated stack space.
171                 if (sizeVal < compiler->eeGetPageSize())
172                 {
173                     info->internalIntCount = 0;
174                 }
175                 else
176                 {
177                     info->internalIntCount = 1;
178                 }
179             }
180             else
181             {
182                 info->internalIntCount = 1;
183             }
184
185             if (hasPspSym)
186             {
187                 info->internalIntCount++;
188             }
189         }
190     }
191     else
192     {
193         // target (regCnt) + tmp + [psp]
194         info->internalIntCount = hasPspSym ? 2 : 1;
195     }
196
197     // If we are needed in temporary registers we should be sure that
198     // it's different from target (regCnt)
199     if (info->internalIntCount > 0)
200     {
201         info->isInternalRegDelayFree = true;
202     }
203 }
204
205 //------------------------------------------------------------------------
206 // TreeNodeInfoInit: Set the register requirements for RA.
207 //
208 // Notes:
209 //    Takes care of annotating the register requirements
210 //    for every TreeNodeInfo struct that maps to each tree node.
211 //
212 // Preconditions:
213 //    LSRA has been initialized and there is a TreeNodeInfo node
214 //    already allocated and initialized for every tree in the IR.
215 //
216 // Postconditions:
217 //    Every TreeNodeInfo instance has the right annotations on register
218 //    requirements needed by LSRA to build the Interval Table (source,
219 //    destination and internal [temp] register counts).
220 //
221 void Lowering::TreeNodeInfoInit(GenTree* tree)
222 {
223     LinearScan* l        = m_lsra;
224     Compiler*   compiler = comp;
225
226     unsigned      kind         = tree->OperKind();
227     TreeNodeInfo* info         = &(tree->gtLsraInfo);
228     RegisterType  registerType = TypeGet(tree);
229
230     JITDUMP("TreeNodeInfoInit for: ");
231     DISPNODE(tree);
232
233     switch (tree->OperGet())
234     {
235         GenTree* op1;
236         GenTree* op2;
237
238         case GT_STORE_LCL_FLD:
239         case GT_STORE_LCL_VAR:
240             if (varTypeIsLong(tree->gtGetOp1()))
241             {
242                 info->srcCount = 2;
243             }
244             else
245             {
246                 info->srcCount = 1;
247             }
248             info->dstCount = 0;
249             TreeNodeInfoInitStoreLoc(tree->AsLclVarCommon());
250             break;
251
252         case GT_NOP:
253             // A GT_NOP is either a passthrough (if it is void, or if it has
254             // a child), but must be considered to produce a dummy value if it
255             // has a type but no child
256             info->srcCount = 0;
257             if (tree->TypeGet() != TYP_VOID && tree->gtOp.gtOp1 == nullptr)
258             {
259                 info->dstCount = 1;
260             }
261             else
262             {
263                 info->dstCount = 0;
264             }
265             break;
266
267         case GT_INTRINSIC:
268         {
269             // TODO-ARM: Implement other type of intrinsics (round, sqrt and etc.)
270             // Both operand and its result must be of the same floating point type.
271             op1 = tree->gtOp.gtOp1;
272             assert(varTypeIsFloating(op1));
273             assert(op1->TypeGet() == tree->TypeGet());
274
275             switch (tree->gtIntrinsic.gtIntrinsicId)
276             {
277                 case CORINFO_INTRINSIC_Abs:
278                 case CORINFO_INTRINSIC_Sqrt:
279                     info->srcCount = 1;
280                     info->dstCount = 1;
281                     break;
282                 default:
283                     NYI_ARM("Lowering::TreeNodeInfoInit for GT_INTRINSIC");
284                     break;
285             }
286         }
287         break;
288
289         case GT_CAST:
290         {
291             info->srcCount = 1;
292             info->dstCount = 1;
293
294             // Non-overflow casts to/from float/double are done using SSE2 instructions
295             // and that allow the source operand to be either a reg or memop. Given the
296             // fact that casts from small int to float/double are done as two-level casts,
297             // the source operand is always guaranteed to be of size 4 or 8 bytes.
298             var_types  castToType = tree->CastToType();
299             GenTreePtr castOp     = tree->gtCast.CastOp();
300             var_types  castOpType = castOp->TypeGet();
301             if (tree->gtFlags & GTF_UNSIGNED)
302             {
303                 castOpType = genUnsignedType(castOpType);
304             }
305 #ifdef DEBUG
306             if (!tree->gtOverflow() && (varTypeIsFloating(castToType) || varTypeIsFloating(castOpType)))
307             {
308                 // If converting to float/double, the operand must be 4 or 8 byte in size.
309                 if (varTypeIsFloating(castToType))
310                 {
311                     unsigned opSize = genTypeSize(castOpType);
312                     assert(opSize == 4 || opSize == 8);
313                 }
314             }
315 #endif // DEBUG
316
317             if (varTypeIsLong(castOpType))
318             {
319                 noway_assert(castOp->OperGet() == GT_LONG);
320                 castOp->SetContained();
321                 info->srcCount = 2;
322             }
323
324             // FloatToIntCast needs a temporary register
325             if (varTypeIsFloating(castOpType) && varTypeIsIntOrI(tree))
326             {
327                 info->setInternalCandidates(m_lsra, RBM_ALLFLOAT);
328                 info->internalFloatCount     = 1;
329                 info->isInternalRegDelayFree = true;
330             }
331
332             CastInfo castInfo;
333
334             // Get information about the cast.
335             getCastDescription(tree, &castInfo);
336
337             if (castInfo.requiresOverflowCheck)
338             {
339                 var_types srcType = castOp->TypeGet();
340                 emitAttr  cmpSize = EA_ATTR(genTypeSize(srcType));
341
342                 // If we cannot store data in an immediate for instructions,
343                 // then we will need to reserve a temporary register.
344
345                 if (!castInfo.signCheckOnly) // In case of only sign check, temp regs are not needeed.
346                 {
347                     if (castInfo.unsignedSource || castInfo.unsignedDest)
348                     {
349                         // check typeMask
350                         bool canStoreTypeMask = emitter::emitIns_valid_imm_for_alu(castInfo.typeMask);
351                         if (!canStoreTypeMask)
352                         {
353                             info->internalIntCount = 1;
354                         }
355                     }
356                     else
357                     {
358                         // For comparing against the max or min value
359                         bool canStoreMaxValue =
360                             emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, INS_FLAGS_DONT_CARE);
361                         bool canStoreMinValue =
362                             emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, INS_FLAGS_DONT_CARE);
363
364                         if (!canStoreMaxValue || !canStoreMinValue)
365                         {
366                             info->internalIntCount = 1;
367                         }
368                     }
369                 }
370             }
371         }
372         break;
373
374         case GT_JTRUE:
375             info->srcCount = 0;
376             info->dstCount = 0;
377             l->clearDstCount(tree->gtOp.gtOp1);
378             break;
379
380         case GT_JMP:
381             info->srcCount = 0;
382             info->dstCount = 0;
383             break;
384
385         case GT_SWITCH:
386             // This should never occur since switch nodes must not be visible at this
387             // point in the JIT.
388             info->srcCount = 0;
389             info->dstCount = 0; // To avoid getting uninit errors.
390             noway_assert(!"Switch must be lowered at this point");
391             break;
392
393         case GT_JMPTABLE:
394             info->srcCount = 0;
395             info->dstCount = 1;
396             break;
397
398         case GT_SWITCH_TABLE:
399             info->srcCount = 2;
400             info->dstCount = 0;
401             break;
402
403         case GT_ASG:
404         case GT_ASG_ADD:
405         case GT_ASG_SUB:
406             noway_assert(!"We should never hit any assignment operator in lowering");
407             info->srcCount = 0;
408             info->dstCount = 0;
409             break;
410
411         case GT_ADD_LO:
412         case GT_ADD_HI:
413         case GT_SUB_LO:
414         case GT_SUB_HI:
415         case GT_ADD:
416         case GT_SUB:
417             if (varTypeIsFloating(tree->TypeGet()))
418             {
419                 // overflow operations aren't supported on float/double types.
420                 assert(!tree->gtOverflow());
421
422                 // No implicit conversions at this stage as the expectation is that
423                 // everything is made explicit by adding casts.
424                 assert(tree->gtOp.gtOp1->TypeGet() == tree->gtOp.gtOp2->TypeGet());
425
426                 info->srcCount = 2;
427                 info->dstCount = 1;
428
429                 break;
430             }
431
432             __fallthrough;
433
434         case GT_AND:
435         case GT_OR:
436         case GT_XOR:
437             info->srcCount = 2;
438             info->dstCount = 1;
439             // Check and make op2 contained (if it is a containable immediate)
440             CheckImmedAndMakeContained(tree, tree->gtOp.gtOp2);
441             break;
442
443         case GT_RETURNTRAP:
444             // this just turns into a compare of its child with an int
445             // + a conditional call
446             info->srcCount = 1;
447             info->dstCount = 0;
448             break;
449
450         case GT_MUL:
451             if (tree->gtOverflow())
452             {
453                 // Need a register different from target reg to check for overflow.
454                 info->internalIntCount       = 1;
455                 info->isInternalRegDelayFree = true;
456             }
457             __fallthrough;
458
459         case GT_DIV:
460         case GT_MULHI:
461         case GT_UDIV:
462         {
463             info->srcCount = 2;
464             info->dstCount = 1;
465         }
466         break;
467
468         case GT_MUL_LONG:
469             info->srcCount = 2;
470             info->dstCount = 2;
471             break;
472
473         case GT_LIST:
474         case GT_FIELD_LIST:
475         case GT_ARGPLACE:
476         case GT_NO_OP:
477         case GT_START_NONGC:
478         case GT_PROF_HOOK:
479             info->srcCount = 0;
480             info->dstCount = 0;
481             break;
482
483         case GT_LONG:
484             if ((tree->gtLIRFlags & LIR::Flags::IsUnusedValue) != 0)
485             {
486                 // An unused GT_LONG node needs to consume its sources.
487                 info->srcCount = 2;
488             }
489             else
490             {
491                 // Passthrough
492                 info->srcCount = 0;
493             }
494
495             info->dstCount = 0;
496             break;
497
498         case GT_CNS_DBL:
499             info->srcCount = 0;
500             info->dstCount = 1;
501             if (tree->TypeGet() == TYP_FLOAT)
502             {
503                 // An int register for float constant
504                 info->internalIntCount = 1;
505             }
506             else
507             {
508                 // TYP_DOUBLE
509                 assert(tree->TypeGet() == TYP_DOUBLE);
510
511                 // Two int registers for double constant
512                 info->internalIntCount = 2;
513             }
514             break;
515
516         case GT_RETURN:
517             TreeNodeInfoInitReturn(tree);
518             break;
519
520         case GT_RETFILT:
521             if (tree->TypeGet() == TYP_VOID)
522             {
523                 info->srcCount = 0;
524                 info->dstCount = 0;
525             }
526             else
527             {
528                 assert(tree->TypeGet() == TYP_INT);
529
530                 info->srcCount = 1;
531                 info->dstCount = 0;
532
533                 info->setSrcCandidates(l, RBM_INTRET);
534                 tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, RBM_INTRET);
535             }
536             break;
537
538         case GT_ARR_BOUNDS_CHECK:
539 #ifdef FEATURE_SIMD
540         case GT_SIMD_CHK:
541 #endif // FEATURE_SIMD
542         {
543             // Consumes arrLen & index - has no result
544             info->srcCount = 2;
545             info->dstCount = 0;
546         }
547         break;
548
549         case GT_ARR_ELEM:
550             // These must have been lowered to GT_ARR_INDEX
551             noway_assert(!"We should never see a GT_ARR_ELEM in lowering");
552             info->srcCount = 0;
553             info->dstCount = 0;
554             break;
555
556         case GT_ARR_INDEX:
557             info->srcCount               = 2;
558             info->dstCount               = 1;
559             info->internalIntCount       = 1;
560             info->isInternalRegDelayFree = true;
561
562             // For GT_ARR_INDEX, the lifetime of the arrObj must be extended because it is actually used multiple
563             // times while the result is being computed.
564             tree->AsArrIndex()->ArrObj()->gtLsraInfo.isDelayFree = true;
565             info->hasDelayFreeSrc                                = true;
566             break;
567
568         case GT_ARR_OFFSET:
569             // This consumes the offset, if any, the arrObj and the effective index,
570             // and produces the flattened offset for this dimension.
571             info->srcCount = 3;
572             info->dstCount = 1;
573
574             // we don't want to generate code for this
575             if (tree->gtArrOffs.gtOffset->IsIntegralConst(0))
576             {
577                 MakeSrcContained(tree, tree->gtArrOffs.gtOffset);
578             }
579             else
580             {
581                 // Here we simply need an internal register, which must be different
582                 // from any of the operand's registers, but may be the same as targetReg.
583                 info->internalIntCount = 1;
584             }
585             break;
586
587         case GT_LEA:
588         {
589             GenTreeAddrMode* lea    = tree->AsAddrMode();
590             unsigned         offset = lea->gtOffset;
591
592             // This LEA is instantiating an address, so we set up the srcCount and dstCount here.
593             info->srcCount = 0;
594             if (lea->HasBase())
595             {
596                 info->srcCount++;
597             }
598             if (lea->HasIndex())
599             {
600                 info->srcCount++;
601             }
602             info->dstCount = 1;
603
604             // An internal register may be needed too; the logic here should be in sync with the
605             // genLeaInstruction()'s requirements for a such register.
606             if (lea->HasBase() && lea->HasIndex())
607             {
608                 if (offset != 0)
609                 {
610                     // We need a register when we have all three: base reg, index reg and a non-zero offset.
611                     info->internalIntCount = 1;
612                 }
613             }
614             else if (lea->HasBase())
615             {
616                 if (!emitter::emitIns_valid_imm_for_add(offset, INS_FLAGS_DONT_CARE))
617                 {
618                     // We need a register when we have an offset that is too large to encode in the add instruction.
619                     info->internalIntCount = 1;
620                 }
621             }
622         }
623         break;
624
625         case GT_NEG:
626             info->srcCount = 1;
627             info->dstCount = 1;
628             break;
629
630         case GT_NOT:
631             info->srcCount = 1;
632             info->dstCount = 1;
633             break;
634
635         case GT_LSH:
636         case GT_RSH:
637         case GT_RSZ:
638         case GT_ROR:
639         case GT_LSH_HI:
640         case GT_RSH_LO:
641             TreeNodeInfoInitShiftRotate(tree);
642             break;
643
644         case GT_EQ:
645         case GT_NE:
646         case GT_LT:
647         case GT_LE:
648         case GT_GE:
649         case GT_GT:
650         case GT_CMP:
651             TreeNodeInfoInitCmp(tree);
652             break;
653
654         case GT_CKFINITE:
655             info->srcCount         = 1;
656             info->dstCount         = 1;
657             info->internalIntCount = 1;
658             break;
659
660         case GT_CALL:
661             TreeNodeInfoInitCall(tree->AsCall());
662             break;
663
664         case GT_STORE_BLK:
665         case GT_STORE_OBJ:
666         case GT_STORE_DYN_BLK:
667             LowerBlockStore(tree->AsBlk());
668             TreeNodeInfoInitBlockStore(tree->AsBlk());
669             break;
670
671         case GT_INIT_VAL:
672             // Always a passthrough of its child's value.
673             info->srcCount = 0;
674             info->dstCount = 0;
675             break;
676
677         case GT_LCLHEAP:
678             TreeNodeInfoInitLclHeap(tree);
679             break;
680
681         case GT_STOREIND:
682         {
683             info->srcCount = 2;
684             info->dstCount = 0;
685             GenTree* src   = tree->gtOp.gtOp2;
686
687             if (compiler->codeGen->gcInfo.gcIsWriteBarrierAsgNode(tree))
688             {
689                 TreeNodeInfoInitGCWriteBarrier(tree);
690                 break;
691             }
692
693             TreeNodeInfoInitIndir(tree);
694         }
695         break;
696
697         case GT_NULLCHECK:
698             info->dstCount      = 0;
699             info->srcCount      = 1;
700             info->isLocalDefUse = true;
701             // null check is an indirection on an addr
702             TreeNodeInfoInitIndir(tree);
703             break;
704
705         case GT_IND:
706             info->dstCount = 1;
707             info->srcCount = 1;
708             TreeNodeInfoInitIndir(tree);
709             break;
710
711         case GT_CATCH_ARG:
712             info->srcCount = 0;
713             info->dstCount = 1;
714             info->setDstCandidates(l, RBM_EXCEPTION_OBJECT);
715             break;
716
717         case GT_CLS_VAR:
718             info->srcCount = 0;
719             // GT_CLS_VAR, by the time we reach the backend, must always
720             // be a pure use.
721             // It will produce a result of the type of the
722             // node, and use an internal register for the address.
723
724             info->dstCount = 1;
725             assert((tree->gtFlags & (GTF_VAR_DEF | GTF_VAR_USEASG | GTF_VAR_USEDEF)) == 0);
726             info->internalIntCount = 1;
727             break;
728
729         case GT_COPY:
730             info->srcCount = 1;
731             info->dstCount = 1;
732 #ifdef ARM_SOFTFP
733             // This case currently only occurs for double types that are passed as TYP_LONG;
734             // actual long types would have been decomposed by now.
735             if (tree->TypeGet() == TYP_LONG)
736             {
737                 info->dstCount = 2;
738             }
739 #endif
740             break;
741
742         case GT_PUTARG_REG:
743 #ifdef ARM_SOFTFP
744             // This case currently only occurs for double types that are passed as TYP_LONG;
745             // actual long types would have been decomposed by now.
746             if (tree->TypeGet() == TYP_LONG)
747             {
748                 info->srcCount = 2;
749             }
750             else
751 #endif
752             {
753                 info->srcCount = 1;
754             }
755             info->dstCount = info->srcCount;
756             break;
757
758         default:
759 #ifdef DEBUG
760             char message[256];
761             _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
762                         GenTree::OpName(tree->OperGet()));
763             NYIRAW(message);
764 #else
765             NYI_ARM("TreeNodeInfoInit default case");
766 #endif
767         case GT_LCL_FLD:
768         case GT_LCL_FLD_ADDR:
769         case GT_LCL_VAR:
770         case GT_LCL_VAR_ADDR:
771         case GT_PHYSREG:
772         case GT_CLS_VAR_ADDR:
773         case GT_IL_OFFSET:
774         case GT_CNS_INT:
775         case GT_PUTARG_STK:
776         case GT_LABEL:
777         case GT_PINVOKE_PROLOG:
778         case GT_JCC:
779         case GT_SETCC:
780         case GT_MEMORYBARRIER:
781         case GT_OBJ:
782         case GT_PUTARG_SPLIT:
783             info->dstCount = tree->IsValue() ? 1 : 0;
784             if (kind & (GTK_CONST | GTK_LEAF))
785             {
786                 info->srcCount = 0;
787             }
788             else if (kind & (GTK_SMPOP))
789             {
790                 if (tree->gtGetOp2IfPresent() != nullptr)
791                 {
792                     info->srcCount = 2;
793                 }
794                 else
795                 {
796                     info->srcCount = 1;
797                 }
798             }
799             else
800             {
801                 unreached();
802             }
803             break;
804     } // end switch (tree->OperGet())
805
806     // We need to be sure that we've set info->srcCount and info->dstCount appropriately
807     assert((info->dstCount < 2) || tree->IsMultiRegNode());
808 }
809
810 #endif // _TARGET_ARM_
811
812 #endif // !LEGACY_BACKEND