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