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 LinearScan::TreeNodeInfoInitReturn(GenTree* tree)
43 TreeNodeInfo* info = &(tree->gtLsraInfo);
44 GenTree* op1 = tree->gtGetOp1();
46 assert(info->dstCount == 0);
47 if (tree->TypeGet() == TYP_LONG)
49 assert((op1->OperGet() == GT_LONG) && op1->isContained());
50 GenTree* loVal = op1->gtGetOp1();
51 GenTree* hiVal = op1->gtGetOp2();
53 loVal->gtLsraInfo.setSrcCandidates(this, RBM_LNGRET_LO);
54 hiVal->gtLsraInfo.setSrcCandidates(this, RBM_LNGRET_HI);
58 regMaskTP useCandidates = RBM_NONE;
60 info->srcCount = ((tree->TypeGet() == TYP_VOID) || op1->isContained()) ? 0 : 1;
62 if (varTypeIsStruct(tree))
64 // op1 has to be either an lclvar or a multi-reg returning call
65 if (op1->OperGet() != GT_LCL_VAR)
67 noway_assert(op1->IsMultiRegCall());
69 ReturnTypeDesc* retTypeDesc = op1->AsCall()->GetReturnTypeDesc();
70 info->srcCount = retTypeDesc->GetReturnRegCount();
71 useCandidates = retTypeDesc->GetABIReturnRegs();
76 // Non-struct type return - determine useCandidates
77 switch (tree->TypeGet())
80 useCandidates = RBM_NONE;
83 useCandidates = RBM_FLOATRET;
86 useCandidates = RBM_DOUBLERET;
89 useCandidates = RBM_LNGRET;
92 useCandidates = RBM_INTRET;
97 if (useCandidates != RBM_NONE)
99 tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, useCandidates);
104 void LinearScan::TreeNodeInfoInitLclHeap(GenTree* tree)
106 TreeNodeInfo* info = &(tree->gtLsraInfo);
108 assert(info->dstCount == 1);
110 // Need a variable number of temp regs (see genLclHeap() in codegenarm.cpp):
111 // Here '-' means don't care.
113 // Size? Init Memory? # temp regs
115 // const and <=4 str instr - hasPspSym ? 1 : 0
116 // const and <PageSize No hasPspSym ? 1 : 0
117 // >4 ptr words Yes hasPspSym ? 2 : 1
118 // Non-const Yes hasPspSym ? 2 : 1
119 // Non-const No hasPspSym ? 2 : 1
122 #if FEATURE_EH_FUNCLETS
123 hasPspSym = (compiler->lvaPSPSym != BAD_VAR_NUM);
128 GenTreePtr size = tree->gtOp.gtOp1;
129 if (size->IsCnsIntOrI())
131 assert(size->isContained());
134 size_t sizeVal = size->gtIntCon.gtIconVal;
137 info->internalIntCount = 0;
141 sizeVal = AlignUp(sizeVal, STACK_ALIGN);
142 size_t cntStackAlignedWidthItems = (sizeVal >> STACK_ALIGN_SHIFT);
144 // For small allocations up to 4 store instructions
145 if (cntStackAlignedWidthItems <= 4)
147 info->internalIntCount = 0;
149 else if (!compiler->info.compInitMem)
151 // No need to initialize allocated stack space.
152 if (sizeVal < compiler->eeGetPageSize())
154 info->internalIntCount = 0;
158 info->internalIntCount = 1;
163 info->internalIntCount = 1;
168 info->internalIntCount++;
174 // target (regCnt) + tmp + [psp]
176 info->internalIntCount = hasPspSym ? 2 : 1;
179 // If we are needed in temporary registers we should be sure that
180 // it's different from target (regCnt)
181 if (info->internalIntCount > 0)
183 info->isInternalRegDelayFree = true;
187 //------------------------------------------------------------------------
188 // TreeNodeInfoInit: Set the register requirements for RA.
191 // Takes care of annotating the register requirements
192 // for every TreeNodeInfo struct that maps to each tree node.
195 // LSRA has been initialized and there is a TreeNodeInfo node
196 // already allocated and initialized for every tree in the IR.
199 // Every TreeNodeInfo instance has the right annotations on register
200 // requirements needed by LSRA to build the Interval Table (source,
201 // destination and internal [temp] register counts).
203 void LinearScan::TreeNodeInfoInit(GenTree* tree)
205 unsigned kind = tree->OperKind();
206 TreeNodeInfo* info = &(tree->gtLsraInfo);
207 RegisterType registerType = TypeGet(tree);
209 if (tree->isContained())
212 assert(info->srcCount == 0);
216 // Set the default dstCount. This may be modified below.
220 if (tree->IsUnusedValue())
222 info->isLocalDefUse = true;
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("LinearScan::TreeNodeInfoInit for GT_INTRINSIC");
280 assert(info->dstCount == 1);
282 // Non-overflow casts to/from float/double are done using SSE2 instructions
283 // and that allow the source operand to be either a reg or memop. Given the
284 // fact that casts from small int to float/double are done as two-level casts,
285 // the source operand is always guaranteed to be of size 4 or 8 bytes.
286 var_types castToType = tree->CastToType();
287 GenTreePtr castOp = tree->gtCast.CastOp();
288 var_types castOpType = castOp->TypeGet();
289 if (tree->gtFlags & GTF_UNSIGNED)
291 castOpType = genUnsignedType(castOpType);
294 if (varTypeIsLong(castOpType))
296 assert((castOp->OperGet() == GT_LONG) && castOp->isContained());
300 // FloatToIntCast needs a temporary register
301 if (varTypeIsFloating(castOpType) && varTypeIsIntOrI(tree))
303 info->setInternalCandidates(this, RBM_ALLFLOAT);
304 info->internalFloatCount = 1;
305 info->isInternalRegDelayFree = true;
308 Lowering::CastInfo castInfo;
310 // Get information about the cast.
311 Lowering::getCastDescription(tree, &castInfo);
313 if (castInfo.requiresOverflowCheck)
315 var_types srcType = castOp->TypeGet();
316 emitAttr cmpSize = EA_ATTR(genTypeSize(srcType));
318 // If we cannot store data in an immediate for instructions,
319 // then we will need to reserve a temporary register.
321 if (!castInfo.signCheckOnly) // In case of only sign check, temp regs are not needeed.
323 if (castInfo.unsignedSource || castInfo.unsignedDest)
326 bool canStoreTypeMask = emitter::emitIns_valid_imm_for_alu(castInfo.typeMask);
327 if (!canStoreTypeMask)
329 info->internalIntCount = 1;
334 // For comparing against the max or min value
335 bool canStoreMaxValue =
336 emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, INS_FLAGS_DONT_CARE);
337 bool canStoreMinValue =
338 emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, INS_FLAGS_DONT_CARE);
340 if (!canStoreMaxValue || !canStoreMinValue)
342 info->internalIntCount = 1;
352 assert(info->dstCount == 0);
357 assert(info->dstCount == 0);
361 // This should never occur since switch nodes must not be visible at this
364 noway_assert(!"Switch must be lowered at this point");
369 assert(info->dstCount == 1);
372 case GT_SWITCH_TABLE:
374 assert(info->dstCount == 0);
380 noway_assert(!"We should never hit any assignment operator in lowering");
390 if (varTypeIsFloating(tree->TypeGet()))
392 // overflow operations aren't supported on float/double types.
393 assert(!tree->gtOverflow());
395 // No implicit conversions at this stage as the expectation is that
396 // everything is made explicit by adding casts.
397 assert(tree->gtOp.gtOp1->TypeGet() == tree->gtOp.gtOp2->TypeGet());
400 assert(info->dstCount == 1);
410 info->srcCount = tree->gtOp.gtOp2->isContained() ? 1 : 2;
411 assert(info->dstCount == 1);
415 // this just turns into a compare of its child with an int
416 // + a conditional call
418 assert(info->dstCount == 0);
422 if (tree->gtOverflow())
424 // Need a register different from target reg to check for overflow.
425 info->internalIntCount = 1;
426 info->isInternalRegDelayFree = true;
435 assert(info->dstCount == 1);
451 assert(info->dstCount == 0);
455 assert(tree->IsUnusedValue()); // Contained nodes are already processed, only unused GT_LONG can reach here.
456 // An unused GT_LONG doesn't produce any registers.
457 tree->gtType = TYP_VOID;
458 tree->ClearUnusedValue();
459 info->isLocalDefUse = false;
461 // An unused GT_LONG node needs to consume its sources.
468 assert(info->dstCount == 1);
469 if (tree->TypeGet() == TYP_FLOAT)
471 // An int register for float constant
472 info->internalIntCount = 1;
477 assert(tree->TypeGet() == TYP_DOUBLE);
479 // Two int registers for double constant
480 info->internalIntCount = 2;
485 TreeNodeInfoInitReturn(tree);
489 assert(info->dstCount == 0);
490 if (tree->TypeGet() == TYP_VOID)
496 assert(tree->TypeGet() == TYP_INT);
499 info->setSrcCandidates(this, RBM_INTRET);
500 tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, RBM_INTRET);
504 case GT_ARR_BOUNDS_CHECK:
507 #endif // FEATURE_SIMD
509 // Consumes arrLen & index - has no result
511 assert(info->dstCount == 0);
516 // These must have been lowered to GT_ARR_INDEX
517 noway_assert(!"We should never see a GT_ARR_ELEM in lowering");
519 assert(info->dstCount == 0);
524 assert(info->dstCount == 1);
525 info->internalIntCount = 1;
526 info->isInternalRegDelayFree = true;
528 // For GT_ARR_INDEX, the lifetime of the arrObj must be extended because it is actually used multiple
529 // times while the result is being computed.
530 tree->AsArrIndex()->ArrObj()->gtLsraInfo.isDelayFree = true;
531 info->hasDelayFreeSrc = true;
535 // This consumes the offset, if any, the arrObj and the effective index,
536 // and produces the flattened offset for this dimension.
537 assert(info->dstCount == 1);
539 if (tree->gtArrOffs.gtOffset->isContained())
545 // Here we simply need an internal register, which must be different
546 // from any of the operand's registers, but may be the same as targetReg.
547 info->internalIntCount = 1;
554 GenTreeAddrMode* lea = tree->AsAddrMode();
555 int offset = lea->Offset();
557 // This LEA is instantiating an address, so we set up the srcCount and dstCount here.
567 assert(info->dstCount == 1);
569 // An internal register may be needed too; the logic here should be in sync with the
570 // genLeaInstruction()'s requirements for a such register.
571 if (lea->HasBase() && lea->HasIndex())
575 // We need a register when we have all three: base reg, index reg and a non-zero offset.
576 info->internalIntCount = 1;
579 else if (lea->HasBase())
581 if (!emitter::emitIns_valid_imm_for_add(offset, INS_FLAGS_DONT_CARE))
583 // We need a register when we have an offset that is too large to encode in the add instruction.
584 info->internalIntCount = 1;
592 assert(info->dstCount == 1);
597 assert(info->dstCount == 1);
606 TreeNodeInfoInitShiftRotate(tree);
616 TreeNodeInfoInitCmp(tree);
621 assert(info->dstCount == 1);
622 info->internalIntCount = 1;
626 TreeNodeInfoInitCall(tree->AsCall());
631 // For a GT_ADDR, the child node should not be evaluated into a register
632 GenTreePtr child = tree->gtOp.gtOp1;
633 assert(!isCandidateLocalRef(child));
634 assert(child->isContained());
635 assert(info->dstCount == 1);
642 case GT_STORE_DYN_BLK:
643 TreeNodeInfoInitBlockStore(tree->AsBlk());
647 // Always a passthrough of its child's value.
648 assert(!"INIT_VAL should always be contained");
652 TreeNodeInfoInitLclHeap(tree);
657 assert(info->dstCount == 0);
658 GenTree* src = tree->gtOp.gtOp2;
660 if (compiler->codeGen->gcInfo.gcIsWriteBarrierAsgNode(tree))
663 TreeNodeInfoInitGCWriteBarrier(tree);
667 TreeNodeInfoInitIndir(tree->AsIndir());
668 // No contained source on ARM.
669 assert(!src->isContained());
675 // It requires a internal register on ARM, as it is implemented as a load
676 assert(info->dstCount == 0);
677 assert(!tree->gtGetOp1()->isContained());
679 info->internalIntCount = 1;
683 assert(info->dstCount == 1);
685 TreeNodeInfoInitIndir(tree->AsIndir());
690 assert(info->dstCount == 1);
691 info->setDstCandidates(this, RBM_EXCEPTION_OBJECT);
696 // GT_CLS_VAR, by the time we reach the backend, must always
698 // It will produce a result of the type of the
699 // node, and use an internal register for the address.
701 assert(info->dstCount == 1);
702 assert((tree->gtFlags & (GTF_VAR_DEF | GTF_VAR_USEASG)) == 0);
703 info->internalIntCount = 1;
709 // This case currently only occurs for double types that are passed as TYP_LONG;
710 // actual long types would have been decomposed by now.
711 if (tree->TypeGet() == TYP_LONG)
718 assert(info->dstCount == 1);
722 case GT_PUTARG_SPLIT:
723 TreeNodeInfoInitPutArgSplit(tree->AsPutArgSplit());
727 TreeNodeInfoInitPutArgStk(tree->AsPutArgStk());
731 TreeNodeInfoInitPutArgReg(tree->AsUnOp());
737 assert(info->dstCount == 1);
738 regNumber argReg = tree->gtRegNum;
739 regMaskTP argMask = genRegMask(argReg);
741 // If type of node is `long` then it is actually `double`.
742 // The actual `long` types must have been transformed as a field list with two fields.
743 if (tree->TypeGet() == TYP_LONG)
746 assert(genRegArgNext(argReg) == REG_NEXT(argReg));
747 argMask |= genRegMask(REG_NEXT(argReg));
750 info->setDstCandidates(this, argMask);
751 info->setSrcCandidates(this, argMask);
752 tree->AsUnOp()->gtOp1->gtLsraInfo.isTgtPref = true;
759 _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
760 GenTree::OpName(tree->OperGet()));
763 NYI_ARM("TreeNodeInfoInit default case");
766 case GT_LCL_FLD_ADDR:
768 case GT_LCL_VAR_ADDR:
770 case GT_CLS_VAR_ADDR:
774 case GT_PINVOKE_PROLOG:
777 case GT_MEMORYBARRIER:
779 assert(info->dstCount == (tree->IsValue() ? 1 : 0));
780 if (kind & (GTK_CONST | GTK_LEAF))
784 else if (kind & (GTK_SMPOP))
786 if (tree->gtGetOp2IfPresent() != nullptr)
804 info->internalIntCount = 1;
806 } // end switch (tree->OperGet())
808 if (tree->IsUnusedValue() && (info->dstCount != 0))
810 info->isLocalDefUse = true;
812 // We need to be sure that we've set info->srcCount and info->dstCount appropriately
813 assert((info->dstCount < 2) || tree->IsMultiRegNode());
814 assert(info->isLocalDefUse == (tree->IsValue() && tree->IsUnusedValue()));
815 assert(!tree->IsUnusedValue() || (info->dstCount != 0));
818 #endif // _TARGET_ARM_
820 #endif // !LEGACY_BACKEND