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 void LinearScan::BuildLclHeap(GenTree* tree)
34 TreeNodeInfo* info = currentNodeInfo;
35 assert(info->dstCount == 1);
37 // Need a variable number of temp regs (see genLclHeap() in codegenarm.cpp):
38 // Here '-' means don't care.
40 // Size? Init Memory? # temp regs
42 // const and <=4 str instr - hasPspSym ? 1 : 0
43 // const and <PageSize No hasPspSym ? 1 : 0
44 // >4 ptr words Yes hasPspSym ? 2 : 1
45 // Non-const Yes hasPspSym ? 2 : 1
46 // Non-const No hasPspSym ? 2 : 1
49 #if FEATURE_EH_FUNCLETS
50 hasPspSym = (compiler->lvaPSPSym != BAD_VAR_NUM);
55 GenTree* size = tree->gtOp.gtOp1;
56 if (size->IsCnsIntOrI())
58 assert(size->isContained());
61 size_t sizeVal = size->gtIntCon.gtIconVal;
64 info->internalIntCount = 0;
68 sizeVal = AlignUp(sizeVal, STACK_ALIGN);
69 size_t cntStackAlignedWidthItems = (sizeVal >> STACK_ALIGN_SHIFT);
71 // For small allocations up to 4 store instructions
72 if (cntStackAlignedWidthItems <= 4)
74 info->internalIntCount = 0;
76 else if (!compiler->info.compInitMem)
78 // No need to initialize allocated stack space.
79 if (sizeVal < compiler->eeGetPageSize())
81 info->internalIntCount = 0;
85 info->internalIntCount = 1;
90 info->internalIntCount = 1;
95 info->internalIntCount++;
101 // target (regCnt) + tmp + [psp]
103 info->internalIntCount = hasPspSym ? 2 : 1;
104 appendLocationInfoToList(size);
107 // If we are needed in temporary registers we should be sure that
108 // it's different from target (regCnt)
109 if (info->internalIntCount > 0)
111 info->isInternalRegDelayFree = true;
115 //------------------------------------------------------------------------
116 // BuildShiftLongCarry: Set the node info for GT_LSH_HI or GT_RSH_LO.
119 // tree - The node of interest
121 // Note: these operands have uses that interfere with the def and need the special handling.
123 void LinearScan::BuildShiftLongCarry(GenTree* tree)
125 assert(tree->OperGet() == GT_LSH_HI || tree->OperGet() == GT_RSH_LO);
127 GenTree* source = tree->gtOp.gtOp1;
128 assert((source->OperGet() == GT_LONG) && source->isContained());
130 TreeNodeInfo* info = currentNodeInfo;
133 LocationInfoListNode* sourceLoInfo = getLocationInfo(source->gtOp.gtOp1);
134 LocationInfoListNode* sourceHiInfo = getLocationInfo(source->gtOp.gtOp2);
135 if (tree->OperGet() == GT_LSH_HI)
137 sourceLoInfo->info.isDelayFree = true;
141 sourceHiInfo->info.isDelayFree = true;
143 useList.Append(sourceLoInfo);
144 useList.Append(sourceHiInfo);
145 info->hasDelayFreeSrc = true;
147 GenTree* shiftBy = tree->gtOp.gtOp2;
148 if (!shiftBy->isContained())
150 appendLocationInfoToList(shiftBy);
155 //------------------------------------------------------------------------
156 // BuildNode: Set the register requirements for RA.
159 // Takes care of annotating the register requirements
160 // for every TreeNodeInfo struct that maps to each tree node.
163 // LSRA has been initialized and there is a TreeNodeInfo node
164 // already allocated and initialized for every tree in the IR.
167 // Every TreeNodeInfo instance has the right annotations on register
168 // requirements needed by LSRA to build the Interval Table (source,
169 // destination and internal [temp] register counts).
171 void LinearScan::BuildNode(GenTree* tree)
173 TreeNodeInfo* info = currentNodeInfo;
174 unsigned kind = tree->OperKind();
175 RegisterType registerType = TypeGet(tree);
177 if (tree->isContained())
180 assert(info->srcCount == 0);
184 // Set the default dstCount. This may be modified below.
188 if (tree->IsUnusedValue())
190 info->isLocalDefUse = true;
198 switch (tree->OperGet())
203 case GT_STORE_LCL_FLD:
204 case GT_STORE_LCL_VAR:
205 BuildStoreLoc(tree->AsLclVarCommon());
209 // A GT_NOP is either a passthrough (if it is void, or if it has
210 // a child), but must be considered to produce a dummy value if it
211 // has a type but no child
213 if (tree->TypeGet() != TYP_VOID && tree->gtOp.gtOp1 == nullptr)
215 assert(info->dstCount == 1);
219 assert(info->dstCount == 0);
225 // TODO-ARM: Implement other type of intrinsics (round, sqrt and etc.)
226 // Both operand and its result must be of the same floating point type.
227 op1 = tree->gtOp.gtOp1;
228 assert(varTypeIsFloating(op1));
229 assert(op1->TypeGet() == tree->TypeGet());
230 appendLocationInfoToList(op1);
232 switch (tree->gtIntrinsic.gtIntrinsicId)
234 case CORINFO_INTRINSIC_Abs:
235 case CORINFO_INTRINSIC_Sqrt:
237 assert(info->dstCount == 1);
248 assert(info->dstCount == 1);
250 // Non-overflow casts to/from float/double are done using SSE2 instructions
251 // and that allow the source operand to be either a reg or memop. Given the
252 // fact that casts from small int to float/double are done as two-level casts,
253 // the source operand is always guaranteed to be of size 4 or 8 bytes.
254 var_types castToType = tree->CastToType();
255 GenTree* castOp = tree->gtCast.CastOp();
256 var_types castOpType = castOp->TypeGet();
257 info->srcCount = GetOperandInfo(castOp);
258 if (tree->gtFlags & GTF_UNSIGNED)
260 castOpType = genUnsignedType(castOpType);
263 if (varTypeIsLong(castOpType))
265 assert((castOp->OperGet() == GT_LONG) && castOp->isContained());
269 // FloatToIntCast needs a temporary register
270 if (varTypeIsFloating(castOpType) && varTypeIsIntOrI(tree))
272 info->setInternalCandidates(this, RBM_ALLFLOAT);
273 info->internalFloatCount = 1;
274 info->isInternalRegDelayFree = true;
277 Lowering::CastInfo castInfo;
279 // Get information about the cast.
280 Lowering::getCastDescription(tree, &castInfo);
282 if (castInfo.requiresOverflowCheck)
284 var_types srcType = castOp->TypeGet();
285 emitAttr cmpSize = EA_ATTR(genTypeSize(srcType));
287 // If we cannot store data in an immediate for instructions,
288 // then we will need to reserve a temporary register.
290 if (!castInfo.signCheckOnly) // In case of only sign check, temp regs are not needeed.
292 if (castInfo.unsignedSource || castInfo.unsignedDest)
295 bool canStoreTypeMask = emitter::emitIns_valid_imm_for_alu(castInfo.typeMask);
296 if (!canStoreTypeMask)
298 info->internalIntCount = 1;
303 // For comparing against the max or min value
304 bool canStoreMaxValue =
305 emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, INS_FLAGS_DONT_CARE);
306 bool canStoreMinValue =
307 emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, INS_FLAGS_DONT_CARE);
309 if (!canStoreMaxValue || !canStoreMinValue)
311 info->internalIntCount = 1;
321 assert(info->dstCount == 0);
326 assert(info->dstCount == 0);
330 // This should never occur since switch nodes must not be visible at this
333 noway_assert(!"Switch must be lowered at this point");
338 assert(info->dstCount == 1);
341 case GT_SWITCH_TABLE:
342 assert(info->dstCount == 0);
343 info->srcCount = appendBinaryLocationInfoToList(tree->AsOp());
344 assert(info->srcCount == 2);
348 noway_assert(!"We should never hit any assignment operator in lowering");
358 if (varTypeIsFloating(tree->TypeGet()))
360 // overflow operations aren't supported on float/double types.
361 assert(!tree->gtOverflow());
363 // No implicit conversions at this stage as the expectation is that
364 // everything is made explicit by adding casts.
365 assert(tree->gtOp.gtOp1->TypeGet() == tree->gtOp.gtOp2->TypeGet());
367 assert(info->dstCount == 1);
368 info->srcCount = appendBinaryLocationInfoToList(tree->AsOp());
369 assert(info->srcCount == 2);
382 assert(info->dstCount == 1);
383 info->srcCount = appendBinaryLocationInfoToList(tree->AsOp());
384 assert(info->srcCount == (tree->gtOp.gtOp2->isContained() ? 1 : 2));
389 assert(info->dstCount == 1);
390 BuildShiftLongCarry(tree);
391 assert(info->srcCount == (tree->gtOp.gtOp2->isContained() ? 2 : 3));
395 // this just turns into a compare of its child with an int
396 // + a conditional call
398 assert(info->dstCount == 0);
399 appendLocationInfoToList(tree->gtOp.gtOp1);
403 if (tree->gtOverflow())
405 // Need a register different from target reg to check for overflow.
406 info->internalIntCount = 1;
407 info->isInternalRegDelayFree = true;
415 assert(info->dstCount == 1);
416 info->srcCount = appendBinaryLocationInfoToList(tree->AsOp());
417 assert(info->srcCount == 2);
423 info->srcCount = appendBinaryLocationInfoToList(tree->AsOp());
424 assert(info->srcCount == 2);
428 // These should always be contained. We don't correctly allocate or
429 // generate code for a non-contained GT_FIELD_LIST.
430 noway_assert(!"Non-contained GT_FIELD_LIST");
439 assert(info->dstCount == 0);
443 assert(tree->IsUnusedValue()); // Contained nodes are already processed, only unused GT_LONG can reach here.
444 // An unused GT_LONG doesn't produce any registers.
445 tree->gtType = TYP_VOID;
446 tree->ClearUnusedValue();
447 info->isLocalDefUse = false;
449 // An unused GT_LONG node needs to consume its sources, but need not produce a register.
452 appendLocationInfoToList(tree->gtGetOp1());
453 appendLocationInfoToList(tree->gtGetOp2());
458 assert(info->dstCount == 1);
459 if (tree->TypeGet() == TYP_FLOAT)
461 // An int register for float constant
462 info->internalIntCount = 1;
467 assert(tree->TypeGet() == TYP_DOUBLE);
469 // Two int registers for double constant
470 info->internalIntCount = 2;
479 assert(info->dstCount == 0);
480 if (tree->TypeGet() == TYP_VOID)
486 assert(tree->TypeGet() == TYP_INT);
489 info->setSrcCandidates(this, RBM_INTRET);
490 LocationInfoListNode* locationInfo = getLocationInfo(tree->gtOp.gtOp1);
491 locationInfo->info.setSrcCandidates(this, RBM_INTRET);
492 useList.Append(locationInfo);
496 case GT_ARR_BOUNDS_CHECK:
499 #endif // FEATURE_SIMD
501 // Consumes arrLen & index - has no result
503 assert(info->dstCount == 0);
504 appendLocationInfoToList(tree->AsBoundsChk()->gtIndex);
505 appendLocationInfoToList(tree->AsBoundsChk()->gtArrLen);
510 // These must have been lowered to GT_ARR_INDEX
511 noway_assert(!"We should never see a GT_ARR_ELEM in lowering");
513 assert(info->dstCount == 0);
519 assert(info->dstCount == 1);
520 info->internalIntCount = 1;
521 info->isInternalRegDelayFree = true;
523 // For GT_ARR_INDEX, the lifetime of the arrObj must be extended because it is actually used multiple
524 // times while the result is being computed.
525 LocationInfoListNode* arrObjInfo = getLocationInfo(tree->AsArrIndex()->ArrObj());
526 arrObjInfo->info.isDelayFree = true;
527 useList.Append(arrObjInfo);
528 useList.Append(getLocationInfo(tree->AsArrIndex()->IndexExpr()));
529 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;
549 appendLocationInfoToList(tree->AsArrOffs()->gtOffset);
551 appendLocationInfoToList(tree->AsArrOffs()->gtIndex);
552 appendLocationInfoToList(tree->AsArrOffs()->gtArrObj);
557 GenTreeAddrMode* lea = tree->AsAddrMode();
558 int offset = lea->Offset();
560 // This LEA is instantiating an address, so we set up the srcCount and dstCount here.
562 assert(info->dstCount == 1);
566 appendLocationInfoToList(tree->AsAddrMode()->Base());
571 appendLocationInfoToList(tree->AsAddrMode()->Index());
574 // An internal register may be needed too; the logic here should be in sync with the
575 // genLeaInstruction()'s requirements for a such register.
576 if (lea->HasBase() && lea->HasIndex())
580 // We need a register when we have all three: base reg, index reg and a non-zero offset.
581 info->internalIntCount = 1;
584 else if (lea->HasBase())
586 if (!emitter::emitIns_valid_imm_for_add(offset, INS_FLAGS_DONT_CARE))
588 // We need a register when we have an offset that is too large to encode in the add instruction.
589 info->internalIntCount = 1;
597 assert(info->dstCount == 1);
598 appendLocationInfoToList(tree->gtOp.gtOp1);
603 assert(info->dstCount == 1);
604 appendLocationInfoToList(tree->gtOp.gtOp1);
619 assert(info->dstCount == 1);
620 info->internalIntCount = 1;
621 appendLocationInfoToList(tree->gtOp.gtOp1);
625 BuildCall(tree->AsCall());
630 // For a GT_ADDR, the child node should not be evaluated into a register
631 GenTree* child = tree->gtOp.gtOp1;
632 assert(!isCandidateLocalRef(child));
633 assert(child->isContained());
634 assert(info->dstCount == 1);
641 case GT_STORE_DYN_BLK:
642 BuildBlockStore(tree->AsBlk());
646 // Always a passthrough of its child's value.
647 assert(!"INIT_VAL should always be contained");
656 assert(info->dstCount == 0);
657 GenTree* src = tree->gtOp.gtOp2;
659 if (compiler->codeGen->gcInfo.gcIsWriteBarrierAsgNode(tree))
662 BuildGCWriteBarrier(tree);
666 BuildIndir(tree->AsIndir());
667 // No contained source on ARM.
668 assert(!src->isContained());
670 appendLocationInfoToList(src);
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;
680 appendLocationInfoToList(tree->gtOp.gtOp1);
684 assert(info->dstCount == 1);
686 BuildIndir(tree->AsIndir());
691 assert(info->dstCount == 1);
692 info->setDstCandidates(this, RBM_EXCEPTION_OBJECT);
697 // GT_CLS_VAR, by the time we reach the backend, must always
699 // It will produce a result of the type of the
700 // node, and use an internal register for the address.
702 assert(info->dstCount == 1);
703 assert((tree->gtFlags & (GTF_VAR_DEF | GTF_VAR_USEASG)) == 0);
704 info->internalIntCount = 1;
710 // This case currently only occurs for double types that are passed as TYP_LONG;
711 // actual long types would have been decomposed by now.
712 if (tree->TypeGet() == TYP_LONG)
719 assert(info->dstCount == 1);
721 appendLocationInfoToList(tree->gtOp.gtOp1);
724 case GT_PUTARG_SPLIT:
725 BuildPutArgSplit(tree->AsPutArgSplit());
729 BuildPutArgStk(tree->AsPutArgStk());
733 BuildPutArgReg(tree->AsUnOp());
739 assert(info->dstCount == 1);
740 LocationInfoListNode* locationInfo = getLocationInfo(tree->gtOp.gtOp1);
741 useList.Append(locationInfo);
742 regNumber argReg = tree->gtRegNum;
743 regMaskTP argMask = genRegMask(argReg);
745 // If type of node is `long` then it is actually `double`.
746 // The actual `long` types must have been transformed as a field list with two fields.
747 if (tree->TypeGet() == TYP_LONG)
750 assert(genRegArgNext(argReg) == REG_NEXT(argReg));
751 argMask |= genRegMask(REG_NEXT(argReg));
754 info->setDstCandidates(this, argMask);
755 info->setSrcCandidates(this, argMask);
760 case GT_LCL_FLD_ADDR:
762 case GT_LCL_VAR_ADDR:
764 case GT_CLS_VAR_ADDR:
768 case GT_PINVOKE_PROLOG:
771 case GT_MEMORYBARRIER:
778 info->internalIntCount = 1;
779 info->srcCount = appendBinaryLocationInfoToList(tree->AsOp());
780 assert(info->srcCount == 2);
786 _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
787 GenTree::OpName(tree->OperGet()));
791 } // end switch (tree->OperGet())
793 if (tree->IsUnusedValue() && (info->dstCount != 0))
795 info->isLocalDefUse = true;
797 // We need to be sure that we've set info->srcCount and info->dstCount appropriately
798 assert((info->dstCount < 2) || tree->IsMultiRegNode());
799 assert(info->isLocalDefUse == (tree->IsValue() && tree->IsUnusedValue()));
800 assert(!tree->IsUnusedValue() || (info->dstCount != 0));
801 assert(info->dstCount == tree->GetRegisterDstCount());
804 #endif // _TARGET_ARM_
806 #endif // !LEGACY_BACKEND