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