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