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