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