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.
5 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
8 XX Register Requirements for ARM XX
10 XX This encapsulates all the logic for setting register requirements for XX
11 XX the ARM architecture. XX
14 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
15 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
23 #ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator
28 #include "sideeffects.h"
32 //------------------------------------------------------------------------
33 // TreeNodeInfoInitReturn: Set the NodeInfo for a GT_RETURN.
36 // tree - The node of interest
41 void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
43 ContainCheckRet(tree->AsOp());
45 TreeNodeInfo* info = &(tree->gtLsraInfo);
46 LinearScan* l = m_lsra;
47 Compiler* compiler = comp;
48 GenTree* op1 = tree->gtGetOp1();
50 assert(info->dstCount == 0);
51 if (tree->TypeGet() == TYP_LONG)
53 assert((op1->OperGet() == GT_LONG) && op1->isContained());
54 GenTree* loVal = op1->gtGetOp1();
55 GenTree* hiVal = op1->gtGetOp2();
57 loVal->gtLsraInfo.setSrcCandidates(l, RBM_LNGRET_LO);
58 hiVal->gtLsraInfo.setSrcCandidates(l, RBM_LNGRET_HI);
62 regMaskTP useCandidates = RBM_NONE;
64 info->srcCount = ((tree->TypeGet() == TYP_VOID) || op1->isContained()) ? 0 : 1;
66 if (varTypeIsStruct(tree))
68 // op1 has to be either an lclvar or a multi-reg returning call
69 if (op1->OperGet() != GT_LCL_VAR)
71 noway_assert(op1->IsMultiRegCall());
73 ReturnTypeDesc* retTypeDesc = op1->AsCall()->GetReturnTypeDesc();
74 info->srcCount = retTypeDesc->GetReturnRegCount();
75 useCandidates = retTypeDesc->GetABIReturnRegs();
80 // Non-struct type return - determine useCandidates
81 switch (tree->TypeGet())
84 useCandidates = RBM_NONE;
87 useCandidates = RBM_FLOATRET;
90 useCandidates = RBM_DOUBLERET;
93 useCandidates = RBM_LNGRET;
96 useCandidates = RBM_INTRET;
101 if (useCandidates != RBM_NONE)
103 tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, useCandidates);
108 void Lowering::TreeNodeInfoInitLclHeap(GenTree* tree)
110 ContainCheckLclHeap(tree->AsOp());
112 TreeNodeInfo* info = &(tree->gtLsraInfo);
113 LinearScan* l = m_lsra;
114 Compiler* compiler = comp;
116 assert(info->dstCount == 1);
118 // Need a variable number of temp regs (see genLclHeap() in codegenarm.cpp):
119 // Here '-' means don't care.
121 // Size? Init Memory? # temp regs
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
130 #if FEATURE_EH_FUNCLETS
131 hasPspSym = (compiler->lvaPSPSym != BAD_VAR_NUM);
136 GenTreePtr size = tree->gtOp.gtOp1;
137 if (size->IsCnsIntOrI())
139 assert(size->isContained());
142 size_t sizeVal = size->gtIntCon.gtIconVal;
145 info->internalIntCount = 0;
149 sizeVal = AlignUp(sizeVal, STACK_ALIGN);
150 size_t cntStackAlignedWidthItems = (sizeVal >> STACK_ALIGN_SHIFT);
152 // For small allocations up to 4 store instructions
153 if (cntStackAlignedWidthItems <= 4)
155 info->internalIntCount = 0;
157 else if (!compiler->info.compInitMem)
159 // No need to initialize allocated stack space.
160 if (sizeVal < compiler->eeGetPageSize())
162 info->internalIntCount = 0;
166 info->internalIntCount = 1;
171 info->internalIntCount = 1;
176 info->internalIntCount++;
182 // target (regCnt) + tmp + [psp]
184 info->internalIntCount = hasPspSym ? 2 : 1;
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)
191 info->isInternalRegDelayFree = true;
195 //------------------------------------------------------------------------
196 // TreeNodeInfoInit: Set the register requirements for RA.
199 // Takes care of annotating the register requirements
200 // for every TreeNodeInfo struct that maps to each tree node.
203 // LSRA has been initialized and there is a TreeNodeInfo node
204 // already allocated and initialized for every tree in the IR.
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).
211 void Lowering::TreeNodeInfoInit(GenTree* tree)
213 LinearScan* l = m_lsra;
214 Compiler* compiler = comp;
216 unsigned kind = tree->OperKind();
217 TreeNodeInfo* info = &(tree->gtLsraInfo);
218 RegisterType registerType = TypeGet(tree);
220 if (tree->isContained())
223 assert(info->srcCount == 0);
227 // Set the default dstCount. This may be modified below.
228 info->dstCount = tree->IsValue() ? 1 : 0;
230 switch (tree->OperGet())
235 case GT_STORE_LCL_FLD:
236 case GT_STORE_LCL_VAR:
237 TreeNodeInfoInitStoreLoc(tree->AsLclVarCommon());
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
245 if (tree->TypeGet() != TYP_VOID && tree->gtOp.gtOp1 == nullptr)
247 assert(info->dstCount == 1);
251 assert(info->dstCount == 0);
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());
263 switch (tree->gtIntrinsic.gtIntrinsicId)
265 case CORINFO_INTRINSIC_Abs:
266 case CORINFO_INTRINSIC_Sqrt:
268 assert(info->dstCount == 1);
271 NYI_ARM("Lowering::TreeNodeInfoInit for GT_INTRINSIC");
279 ContainCheckCast(tree->AsCast());
281 assert(info->dstCount == 1);
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)
292 castOpType = genUnsignedType(castOpType);
295 if (!tree->gtOverflow() && (varTypeIsFloating(castToType) || varTypeIsFloating(castOpType)))
297 // If converting to float/double, the operand must be 4 or 8 byte in size.
298 if (varTypeIsFloating(castToType))
300 unsigned opSize = genTypeSize(castOpType);
301 assert(opSize == 4 || opSize == 8);
306 if (varTypeIsLong(castOpType))
308 assert((castOp->OperGet() == GT_LONG) && castOp->isContained());
312 // FloatToIntCast needs a temporary register
313 if (varTypeIsFloating(castOpType) && varTypeIsIntOrI(tree))
315 info->setInternalCandidates(m_lsra, RBM_ALLFLOAT);
316 info->internalFloatCount = 1;
317 info->isInternalRegDelayFree = true;
322 // Get information about the cast.
323 getCastDescription(tree, &castInfo);
325 if (castInfo.requiresOverflowCheck)
327 var_types srcType = castOp->TypeGet();
328 emitAttr cmpSize = EA_ATTR(genTypeSize(srcType));
330 // If we cannot store data in an immediate for instructions,
331 // then we will need to reserve a temporary register.
333 if (!castInfo.signCheckOnly) // In case of only sign check, temp regs are not needeed.
335 if (castInfo.unsignedSource || castInfo.unsignedDest)
338 bool canStoreTypeMask = emitter::emitIns_valid_imm_for_alu(castInfo.typeMask);
339 if (!canStoreTypeMask)
341 info->internalIntCount = 1;
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);
352 if (!canStoreMaxValue || !canStoreMinValue)
354 info->internalIntCount = 1;
364 assert(info->dstCount == 0);
369 assert(info->dstCount == 0);
373 // This should never occur since switch nodes must not be visible at this
376 noway_assert(!"Switch must be lowered at this point");
381 assert(info->dstCount == 1);
384 case GT_SWITCH_TABLE:
386 assert(info->dstCount == 0);
392 noway_assert(!"We should never hit any assignment operator in lowering");
402 if (varTypeIsFloating(tree->TypeGet()))
404 // overflow operations aren't supported on float/double types.
405 assert(!tree->gtOverflow());
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());
412 assert(info->dstCount == 1);
422 ContainCheckBinary(tree->AsOp());
423 info->srcCount = tree->gtOp.gtOp2->isContained() ? 1 : 2;
424 assert(info->dstCount == 1);
428 // this just turns into a compare of its child with an int
429 // + a conditional call
431 assert(info->dstCount == 0);
435 if (tree->gtOverflow())
437 // Need a register different from target reg to check for overflow.
438 info->internalIntCount = 1;
439 info->isInternalRegDelayFree = true;
448 assert(info->dstCount == 1);
464 assert(info->dstCount == 0);
468 if (tree->IsUnusedValue())
470 // An unused GT_LONG node needs to consume its sources.
479 assert(info->dstCount == 0);
484 assert(info->dstCount == 1);
485 if (tree->TypeGet() == TYP_FLOAT)
487 // An int register for float constant
488 info->internalIntCount = 1;
493 assert(tree->TypeGet() == TYP_DOUBLE);
495 // Two int registers for double constant
496 info->internalIntCount = 2;
501 TreeNodeInfoInitReturn(tree);
505 assert(info->dstCount == 0);
506 if (tree->TypeGet() == TYP_VOID)
512 assert(tree->TypeGet() == TYP_INT);
515 info->setSrcCandidates(l, RBM_INTRET);
516 tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, RBM_INTRET);
520 case GT_ARR_BOUNDS_CHECK:
523 #endif // FEATURE_SIMD
525 // Consumes arrLen & index - has no result
527 assert(info->dstCount == 0);
532 // These must have been lowered to GT_ARR_INDEX
533 noway_assert(!"We should never see a GT_ARR_ELEM in lowering");
535 assert(info->dstCount == 0);
540 assert(info->dstCount == 1);
541 info->internalIntCount = 1;
542 info->isInternalRegDelayFree = true;
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;
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);
556 if (tree->gtArrOffs.gtOffset->isContained())
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;
571 GenTreeAddrMode* lea = tree->AsAddrMode();
572 unsigned offset = lea->gtOffset;
574 // This LEA is instantiating an address, so we set up the srcCount and dstCount here.
584 assert(info->dstCount == 1);
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())
592 // We need a register when we have all three: base reg, index reg and a non-zero offset.
593 info->internalIntCount = 1;
596 else if (lea->HasBase())
598 if (!emitter::emitIns_valid_imm_for_add(offset, INS_FLAGS_DONT_CARE))
600 // We need a register when we have an offset that is too large to encode in the add instruction.
601 info->internalIntCount = 1;
609 assert(info->dstCount == 1);
614 assert(info->dstCount == 1);
623 TreeNodeInfoInitShiftRotate(tree);
633 TreeNodeInfoInitCmp(tree);
638 assert(info->dstCount == 1);
639 info->internalIntCount = 1;
643 TreeNodeInfoInitCall(tree->AsCall());
648 case GT_STORE_DYN_BLK:
649 LowerBlockStore(tree->AsBlk());
650 TreeNodeInfoInitBlockStore(tree->AsBlk());
654 // Always a passthrough of its child's value.
655 assert(!"INIT_VAL should always be contained");
659 TreeNodeInfoInitLclHeap(tree);
664 assert(info->dstCount == 0);
665 GenTree* src = tree->gtOp.gtOp2;
667 if (compiler->codeGen->gcInfo.gcIsWriteBarrierAsgNode(tree))
669 TreeNodeInfoInitGCWriteBarrier(tree);
673 TreeNodeInfoInitIndir(tree->AsIndir());
674 // No contained source on ARM.
675 assert(!src->isContained());
681 assert(info->dstCount == 0);
683 info->isLocalDefUse = true;
684 // null check is an indirection on an addr
685 TreeNodeInfoInitIndir(tree->AsIndir());
689 assert(info->dstCount == 1);
691 TreeNodeInfoInitIndir(tree->AsIndir());
696 assert(info->dstCount == 1);
697 info->setDstCandidates(l, RBM_EXCEPTION_OBJECT);
702 // GT_CLS_VAR, by the time we reach the backend, must always
704 // It will produce a result of the type of the
705 // node, and use an internal register for the address.
707 assert(info->dstCount == 1);
708 assert((tree->gtFlags & (GTF_VAR_DEF | GTF_VAR_USEASG)) == 0);
709 info->internalIntCount = 1;
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)
724 assert(info->dstCount == 1);
729 case GT_PUTARG_SPLIT:
730 TreeNodeInfoInitPutArgSplit(tree->AsPutArgSplit());
734 TreeNodeInfoInitPutArgStk(tree->AsPutArgStk());
738 TreeNodeInfoInitPutArgReg(tree->AsUnOp());
744 _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
745 GenTree::OpName(tree->OperGet()));
748 NYI_ARM("TreeNodeInfoInit default case");
751 case GT_LCL_FLD_ADDR:
753 case GT_LCL_VAR_ADDR:
755 case GT_CLS_VAR_ADDR:
759 case GT_PINVOKE_PROLOG:
762 case GT_MEMORYBARRIER:
764 assert(info->dstCount == (tree->IsValue() ? 1 : 0));
765 if (kind & (GTK_CONST | GTK_LEAF))
769 else if (kind & (GTK_SMPOP))
771 if (tree->gtGetOp2IfPresent() != nullptr)
785 } // end switch (tree->OperGet())
787 // We need to be sure that we've set info->srcCount and info->dstCount appropriately
788 assert((info->dstCount < 2) || tree->IsMultiRegNode());
791 #endif // _TARGET_ARM_
793 #endif // !LEGACY_BACKEND