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 and ARM64 common code XX
10 XX This encapsulates common logic for setting register requirements for XX
11 XX the ARM and ARM64 architectures. XX
13 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
14 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
22 #ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator
24 #ifdef _TARGET_ARMARCH_ // This file is ONLY used for ARM and ARM64 architectures
27 #include "sideeffects.h"
31 //------------------------------------------------------------------------
32 // TreeNodeInfoInitStoreLoc: Set register requirements for a store of a lclVar
35 // storeLoc - the local store (GT_STORE_LCL_FLD or GT_STORE_LCL_VAR)
39 // - Setting the appropriate candidates for a store of a multi-reg call return value.
40 // - Handling of contained immediates.
42 void LinearScan::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc)
44 TreeNodeInfo* info = &(storeLoc->gtLsraInfo);
45 GenTree* op1 = storeLoc->gtGetOp1();
47 assert(info->dstCount == 0);
49 if (varTypeIsLong(op1))
52 assert(!op1->OperIs(GT_LONG) || op1->isContained());
55 #endif // _TARGET_ARM_
56 if (op1->isContained())
60 else if (op1->IsMultiRegCall())
62 // This is the case of var = call where call is returning
63 // a value in multiple return registers.
64 // Must be a store lclvar.
65 assert(storeLoc->OperGet() == GT_STORE_LCL_VAR);
67 // srcCount = number of registers in which the value is returned by call
68 GenTreeCall* call = op1->AsCall();
69 ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc();
70 info->srcCount = retTypeDesc->GetReturnRegCount();
72 // Call node srcCandidates = Bitwise-OR(allregs(GetReturnRegType(i))) for all i=0..RetRegCount-1
73 regMaskTP srcCandidates = allMultiRegCallNodeRegs(call);
74 op1->gtLsraInfo.setSrcCandidates(this, srcCandidates);
82 //------------------------------------------------------------------------
83 // TreeNodeInfoInitCmp: Lower a GT comparison node.
86 // tree - the node to lower
91 void LinearScan::TreeNodeInfoInitCmp(GenTreePtr tree)
93 TreeNodeInfo* info = &(tree->gtLsraInfo);
95 info->srcCount = tree->gtOp.gtOp2->isContained() ? 1 : 2;
96 if (info->isNoRegCompare)
102 assert((info->dstCount == 1) || tree->OperIs(GT_CMP));
106 void LinearScan::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
108 GenTreePtr dst = tree;
109 GenTreePtr addr = tree->gtOp.gtOp1;
110 GenTreePtr src = tree->gtOp.gtOp2;
112 if (addr->OperGet() == GT_LEA)
114 // In the case where we are doing a helper assignment, if the dst
115 // is an indir through an lea, we need to actually instantiate the
117 GenTreeAddrMode* lea = addr->AsAddrMode();
119 short leaSrcCount = 0;
120 if (lea->Base() != nullptr)
124 if (lea->Index() != nullptr)
128 lea->gtLsraInfo.srcCount = leaSrcCount;
129 lea->gtLsraInfo.dstCount = 1;
132 #if NOGC_WRITE_BARRIERS
133 NYI_ARM("NOGC_WRITE_BARRIERS");
135 // For the NOGC JIT Helper calls
137 // the 'addr' goes into x14 (REG_WRITE_BARRIER_DST_BYREF)
138 // the 'src' goes into x15 (REG_WRITE_BARRIER)
140 addr->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER_DST_BYREF);
141 src->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER);
143 // For the standard JIT Helper calls
144 // op1 goes into REG_ARG_0 and
145 // op2 goes into REG_ARG_1
147 addr->gtLsraInfo.setSrcCandidates(this, RBM_ARG_0);
148 src->gtLsraInfo.setSrcCandidates(this, RBM_ARG_1);
149 #endif // NOGC_WRITE_BARRIERS
151 // Both src and dst must reside in a register, which they should since we haven't set
152 // either of them as contained.
153 assert(addr->gtLsraInfo.dstCount == 1);
154 assert(src->gtLsraInfo.dstCount == 1);
157 //------------------------------------------------------------------------
158 // TreeNodeInfoInitIndir: Specify register requirements for address expression
159 // of an indirection operation.
162 // indirTree - GT_IND, GT_STOREIND, block node or GT_NULLCHECK gentree node
164 void LinearScan::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
166 // If this is the rhs of a block copy (i.e. non-enregisterable struct),
167 // it has no register requirements.
168 if (indirTree->TypeGet() == TYP_STRUCT)
173 TreeNodeInfo* info = &(indirTree->gtLsraInfo);
174 bool isStore = (indirTree->gtOper == GT_STOREIND);
175 info->srcCount = GetIndirSourceCount(indirTree);
177 GenTree* addr = indirTree->Addr();
178 GenTree* index = nullptr;
182 // Unaligned loads/stores for floating point values must first be loaded into integer register(s)
183 if (indirTree->gtFlags & GTF_IND_UNALIGNED)
185 var_types type = TYP_UNDEF;
186 if (indirTree->OperGet() == GT_STOREIND)
188 type = indirTree->AsStoreInd()->Data()->TypeGet();
190 else if (indirTree->OperGet() == GT_IND)
192 type = indirTree->TypeGet();
195 if (type == TYP_FLOAT)
197 info->internalIntCount = 1;
199 else if (type == TYP_DOUBLE)
201 info->internalIntCount = 2;
206 if (addr->isContained())
208 assert(addr->OperGet() == GT_LEA);
209 GenTreeAddrMode* lea = addr->AsAddrMode();
210 index = lea->Index();
213 // On ARM we may need a single internal register
214 // (when both conditions are true then we still only need a single internal register)
215 if ((index != nullptr) && (cns != 0))
217 // ARM does not support both Index and offset so we need an internal register
218 info->internalIntCount++;
220 else if (!emitter::emitIns_valid_imm_for_ldst_offset(cns, emitTypeSize(indirTree)))
222 // This offset can't be contained in the ldr/str instruction, so we need an internal register
223 info->internalIntCount++;
228 //------------------------------------------------------------------------
229 // TreeNodeInfoInitShiftRotate: Set the NodeInfo for a shift or rotate.
232 // tree - The node of interest
237 void LinearScan::TreeNodeInfoInitShiftRotate(GenTree* tree)
239 TreeNodeInfo* info = &(tree->gtLsraInfo);
241 GenTreePtr shiftBy = tree->gtOp.gtOp2;
242 info->srcCount = shiftBy->isContained() ? 1 : 2;
247 // The first operand of a GT_LSH_HI and GT_RSH_LO oper is a GT_LONG so that
248 // we can have a three operand form. Increment the srcCount.
249 GenTreePtr source = tree->gtOp.gtOp1;
250 if (tree->OperGet() == GT_LSH_HI || tree->OperGet() == GT_RSH_LO)
252 assert((source->OperGet() == GT_LONG) && source->isContained());
255 if (tree->OperGet() == GT_LSH_HI)
257 GenTreePtr sourceLo = source->gtOp.gtOp1;
258 sourceLo->gtLsraInfo.isDelayFree = true;
262 GenTreePtr sourceHi = source->gtOp.gtOp2;
263 sourceHi->gtLsraInfo.isDelayFree = true;
266 source->gtLsraInfo.hasDelayFreeSrc = true;
267 info->hasDelayFreeSrc = true;
270 #endif // _TARGET_ARM_
273 //------------------------------------------------------------------------
274 // TreeNodeInfoInitPutArgReg: Set the NodeInfo for a PUTARG_REG.
277 // node - The PUTARG_REG node.
278 // argReg - The register in which to pass the argument.
279 // info - The info for the node's using call.
280 // isVarArgs - True if the call uses a varargs calling convention.
281 // callHasFloatRegArgs - Set to true if this PUTARG_REG uses an FP register.
286 void LinearScan::TreeNodeInfoInitPutArgReg(GenTreeUnOp* node)
288 assert(node != nullptr);
289 assert(node->OperIsPutArgReg());
290 node->gtLsraInfo.srcCount = 1;
291 regNumber argReg = node->gtRegNum;
292 assert(argReg != REG_NA);
294 // Set the register requirements for the node.
295 regMaskTP argMask = genRegMask(argReg);
297 // If type of node is `long` then it is actually `double`.
298 // The actual `long` types must have been transformed as a field list with two fields.
299 if (node->TypeGet() == TYP_LONG)
301 node->gtLsraInfo.srcCount++;
302 assert(genRegArgNext(argReg) == REG_NEXT(argReg));
303 argMask |= genRegMask(REG_NEXT(argReg));
306 node->gtLsraInfo.setDstCandidates(this, argMask);
307 node->gtLsraInfo.setSrcCandidates(this, argMask);
309 // To avoid redundant moves, have the argument operand computed in the
310 // register in which the argument is passed to the call.
311 node->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, getUseCandidates(node));
314 //------------------------------------------------------------------------
315 // HandleFloatVarArgs: Handle additional register requirements for a varargs call
318 // call - The call node of interest
319 // argNode - The current argument
325 // In the case of a varargs call, the ABI dictates that if we have floating point args,
326 // we must pass the enregistered arguments in both the integer and floating point registers.
327 // Since the integer register is not associated with the arg node, we will reserve it as
328 // an internal register on the call so that it is not used during the evaluation of the call node
329 // (e.g. for the target).
330 void LinearScan::HandleFloatVarArgs(GenTreeCall* call, GenTree* argNode, bool* callHasFloatRegArgs)
333 if (call->IsVarargs() && varTypeIsFloating(argNode))
335 *callHasFloatRegArgs = true;
337 regNumber argReg = argNode->gtRegNum;
338 regNumber targetReg = compiler->getCallArgIntRegister(argReg);
339 call->gtLsraInfo.setInternalIntCount(call->gtLsraInfo.internalIntCount + 1);
340 call->gtLsraInfo.addInternalCandidates(this, genRegMask(targetReg));
342 #endif // FEATURE_VARARG
345 //------------------------------------------------------------------------
346 // TreeNodeInfoInitCall: Set the NodeInfo for a call.
349 // call - The call node of interest
354 void LinearScan::TreeNodeInfoInitCall(GenTreeCall* call)
356 TreeNodeInfo* info = &(call->gtLsraInfo);
357 bool hasMultiRegRetVal = false;
358 ReturnTypeDesc* retTypeDesc = nullptr;
361 if (call->TypeGet() != TYP_VOID)
363 hasMultiRegRetVal = call->HasMultiRegRetVal();
364 if (hasMultiRegRetVal)
366 // dst count = number of registers in which the value is returned by call
367 retTypeDesc = call->GetReturnTypeDesc();
368 info->dstCount = retTypeDesc->GetReturnRegCount();
380 GenTree* ctrlExpr = call->gtControlExpr;
381 if (call->gtCallType == CT_INDIRECT)
383 // either gtControlExpr != null or gtCallAddr != null.
384 // Both cannot be non-null at the same time.
385 assert(ctrlExpr == nullptr);
386 assert(call->gtCallAddr != nullptr);
387 ctrlExpr = call->gtCallAddr;
390 // set reg requirements on call target represented as control sequence.
391 if (ctrlExpr != nullptr)
393 // we should never see a gtControlExpr whose type is void.
394 assert(ctrlExpr->TypeGet() != TYP_VOID);
398 // In case of fast tail implemented as jmp, make sure that gtControlExpr is
399 // computed into a register.
400 if (call->IsFastTailCall())
402 NYI_ARM("tail call");
404 #ifdef _TARGET_ARM64_
405 // Fast tail call - make sure that call target is always computed in IP0
406 // so that epilog sequence can generate "br xip0" to achieve fast tail call.
407 ctrlExpr->gtLsraInfo.setSrcCandidates(this, genRegMask(REG_IP0));
408 #endif // _TARGET_ARM64_
414 info->internalIntCount = 1;
416 #endif // _TARGET_ARM_
418 RegisterType registerType = call->TypeGet();
420 // Set destination candidates for return value of the call.
423 if (call->IsHelperCall(compiler, CORINFO_HELP_INIT_PINVOKE_FRAME))
425 // The ARM CORINFO_HELP_INIT_PINVOKE_FRAME helper uses a custom calling convention that returns with
426 // TCB in REG_PINVOKE_TCB. fgMorphCall() sets the correct argument registers.
427 info->setDstCandidates(this, RBM_PINVOKE_TCB);
430 #endif // _TARGET_ARM_
431 if (hasMultiRegRetVal)
433 assert(retTypeDesc != nullptr);
434 info->setDstCandidates(this, retTypeDesc->GetABIReturnRegs());
436 else if (varTypeIsFloating(registerType))
438 info->setDstCandidates(this, RBM_FLOATRET);
440 else if (registerType == TYP_LONG)
442 info->setDstCandidates(this, RBM_LNGRET);
446 info->setDstCandidates(this, RBM_INTRET);
449 // First, count reg args
450 // Each register argument corresponds to one source.
451 bool callHasFloatRegArgs = false;
453 for (GenTreePtr list = call->gtCallLateArgs; list; list = list->MoveNext())
455 assert(list->OperIsList());
457 GenTreePtr argNode = list->Current();
460 // During TreeNodeInfoInit, we only use the ArgTabEntry for validation,
461 // as getting it is rather expensive.
462 fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, argNode);
463 regNumber argReg = curArgTabEntry->regNum;
464 assert(curArgTabEntry);
467 if (argNode->gtOper == GT_PUTARG_STK)
469 // late arg that is not passed in a register
470 assert(curArgTabEntry->regNum == REG_STK);
471 GenTree* putArgChild = argNode->gtGetOp1();
472 if (!varTypeIsStruct(putArgChild) && !putArgChild->OperIs(GT_FIELD_LIST))
475 // The `double` types have been transformed to `long` on armel, while the actual longs
476 // have been decomposed.
477 const bool isDouble = putArgChild->TypeGet() == TYP_LONG;
480 argNode->gtLsraInfo.srcCount = 2;
482 #endif // ARM_SOFT_FP
485 // Validate the slot count for this arg.
488 const bool isDouble = (curArgTabEntry->numSlots == 2) && (putArgChild->TypeGet() == TYP_DOUBLE);
489 #endif // !ARM_SOFTFP
491 // We must not have a multi-reg struct; double uses 2 slots and isn't a multi-reg struct
492 assert((curArgTabEntry->numSlots == 1) || isDouble);
494 #else // !_TARGET_ARM_
495 // We must not have a multi-reg struct
496 assert(curArgTabEntry->numSlots == 1);
497 #endif // !_TARGET_ARM_
503 // A GT_FIELD_LIST has a TYP_VOID, but is used to represent a multireg struct
504 if (argNode->OperGet() == GT_FIELD_LIST)
506 assert(argNode->isContained());
508 // There could be up to 2-4 PUTARG_REGs in the list (3 or 4 can only occur for HFAs)
509 for (GenTreeFieldList* entry = argNode->AsFieldList(); entry != nullptr; entry = entry->Rest())
513 assert(entry->Current()->OperIs(GT_PUTARG_REG));
514 assert(entry->Current()->gtRegNum == argReg);
515 // Update argReg for the next putarg_reg (if any)
516 argReg = genRegArgNext(argReg);
518 #if defined(_TARGET_ARM_)
519 // A double register is modelled as an even-numbered single one
520 if (entry->Current()->TypeGet() == TYP_DOUBLE)
522 argReg = genRegArgNext(argReg);
524 #endif // _TARGET_ARM_
529 else if (argNode->OperGet() == GT_PUTARG_SPLIT)
531 fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, argNode);
532 info->srcCount += argNode->AsPutArgSplit()->gtNumRegs;
537 assert(argNode->OperIs(GT_PUTARG_REG));
538 assert(argNode->gtRegNum == argReg);
539 HandleFloatVarArgs(call, argNode, &callHasFloatRegArgs);
544 // Now, count stack args
545 // Note that these need to be computed into a register, but then
546 // they're just stored to the stack - so the reg doesn't
547 // need to remain live until the call. In fact, it must not
548 // because the code generator doesn't actually consider it live,
549 // so it can't be spilled.
551 GenTreePtr args = call->gtCallArgs;
554 GenTreePtr arg = args->gtOp.gtOp1;
556 // Skip arguments that have been moved to the Late Arg list
557 if (!(args->gtFlags & GTF_LATE_ARG))
560 fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, arg);
561 assert(curArgTabEntry);
563 if (arg->gtOper == GT_PUTARG_STK)
565 assert(curArgTabEntry->regNum == REG_STK);
568 else if (arg->OperGet() == GT_PUTARG_SPLIT)
570 assert(arg->AsPutArgSplit()->gtNumRegs == curArgTabEntry->numRegs);
571 info->srcCount += arg->gtLsraInfo.dstCount;
576 TreeNodeInfo* argInfo = &(arg->gtLsraInfo);
577 assert((argInfo->dstCount == 0) || (argInfo->isLocalDefUse));
580 args = args->gtOp.gtOp2;
583 // If it is a fast tail call, it is already preferenced to use IP0.
584 // Therefore, no need set src candidates on call tgt again.
585 if (call->IsVarargs() && callHasFloatRegArgs && !call->IsFastTailCall() && (ctrlExpr != nullptr))
587 NYI_ARM("float reg varargs");
589 // Don't assign the call target to any of the argument registers because
590 // we will use them to also pass floating point arguments as required
592 ctrlExpr->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~(RBM_ARG_REGS));
597 if (call->NeedsNullCheck())
599 info->internalIntCount++;
602 #endif // _TARGET_ARM_
605 //------------------------------------------------------------------------
606 // TreeNodeInfoInitPutArgStk: Set the NodeInfo for a GT_PUTARG_STK node
609 // argNode - a GT_PUTARG_STK node
615 // Set the child node(s) to be contained when we have a multireg arg
617 void LinearScan::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode)
619 assert(argNode->gtOper == GT_PUTARG_STK);
621 GenTreePtr putArgChild = argNode->gtOp.gtOp1;
623 argNode->gtLsraInfo.srcCount = 0;
624 argNode->gtLsraInfo.dstCount = 0;
626 // Do we have a TYP_STRUCT argument (or a GT_FIELD_LIST), if so it must be a multireg pass-by-value struct
627 if ((putArgChild->TypeGet() == TYP_STRUCT) || (putArgChild->OperGet() == GT_FIELD_LIST))
629 // We will use store instructions that each write a register sized value
631 if (putArgChild->OperGet() == GT_FIELD_LIST)
633 assert(putArgChild->isContained());
634 // We consume all of the items in the GT_FIELD_LIST
635 for (GenTreeFieldList* current = putArgChild->AsFieldList(); current != nullptr; current = current->Rest())
637 argNode->gtLsraInfo.srcCount++;
642 #ifdef _TARGET_ARM64_
643 // We could use a ldp/stp sequence so we need two internal registers
644 argNode->gtLsraInfo.internalIntCount = 2;
645 #else // _TARGET_ARM_
646 // We could use a ldr/str sequence so we need a internal register
647 argNode->gtLsraInfo.internalIntCount = 1;
648 #endif // _TARGET_ARM_
650 if (putArgChild->OperGet() == GT_OBJ)
652 GenTreePtr objChild = putArgChild->gtOp.gtOp1;
653 if (objChild->OperGet() == GT_LCL_VAR_ADDR)
655 // We will generate all of the code for the GT_PUTARG_STK, the GT_OBJ and the GT_LCL_VAR_ADDR
656 // as one contained operation
658 assert(objChild->isContained());
662 // We will generate all of the code for the GT_PUTARG_STK and its child node
663 // as one contained operation
665 argNode->gtLsraInfo.srcCount = putArgChild->gtLsraInfo.srcCount;
666 assert(putArgChild->isContained());
671 #if defined(_TARGET_ARM_) && defined(ARM_SOFTFP)
672 // The `double` types have been transformed to `long` on armel,
673 // while the actual long types have been decomposed.
674 const bool isDouble = (putArgChild->TypeGet() == TYP_LONG);
677 argNode->gtLsraInfo.srcCount = 2;
680 #endif // defined(_TARGET_ARM_) && defined(ARM_SOFTFP)
682 argNode->gtLsraInfo.srcCount = 1;
688 //------------------------------------------------------------------------
689 // TreeNodeInfoInitPutArgSplit: Set the NodeInfo for a GT_PUTARG_SPLIT node
692 // argNode - a GT_PUTARG_SPLIT node
698 // Set the child node(s) to be contained
700 void LinearScan::TreeNodeInfoInitPutArgSplit(GenTreePutArgSplit* argNode)
702 assert(argNode->gtOper == GT_PUTARG_SPLIT);
704 GenTreePtr putArgChild = argNode->gtOp.gtOp1;
706 // Registers for split argument corresponds to source
707 argNode->gtLsraInfo.dstCount = argNode->gtNumRegs;
709 regNumber argReg = argNode->gtRegNum;
710 regMaskTP argMask = RBM_NONE;
711 for (unsigned i = 0; i < argNode->gtNumRegs; i++)
713 argMask |= genRegMask((regNumber)((unsigned)argReg + i));
715 argNode->gtLsraInfo.setDstCandidates(this, argMask);
716 argNode->gtLsraInfo.setSrcCandidates(this, argMask);
718 if (putArgChild->OperGet() == GT_FIELD_LIST)
721 // 1. Consume all of the items in the GT_FIELD_LIST (source)
722 // 2. Store to target slot and move to target registers (destination) from source
724 unsigned slotCount = 0;
726 // To avoid redundant moves, have the argument operand computed in the
727 // register in which the argument is passed to the call.
728 GenTreeFieldList* fieldListPtr = putArgChild->AsFieldList();
729 for (unsigned idx = 0; fieldListPtr != nullptr; fieldListPtr = fieldListPtr->Rest(), idx++)
731 if (idx < argNode->gtNumRegs)
733 GenTreePtr node = fieldListPtr->gtGetOp1();
734 node->gtLsraInfo.setSrcCandidates(this, genRegMask((regNumber)((unsigned)argReg + idx)));
741 argNode->gtLsraInfo.srcCount = argNode->gtNumRegs + slotCount;
742 assert(putArgChild->isContained());
746 assert(putArgChild->TypeGet() == TYP_STRUCT);
747 assert(putArgChild->OperGet() == GT_OBJ);
749 // We can use a ldr/str sequence so we need an internal register
750 argNode->gtLsraInfo.internalIntCount = 1;
751 regMaskTP internalMask = RBM_ALLINT & ~argMask;
752 argNode->gtLsraInfo.setInternalCandidates(this, internalMask);
754 GenTreePtr objChild = putArgChild->gtOp.gtOp1;
755 if (objChild->OperGet() == GT_LCL_VAR_ADDR)
757 // We will generate all of the code for the GT_PUTARG_SPLIT, the GT_OBJ and the GT_LCL_VAR_ADDR
758 // as one contained operation
760 assert(objChild->isContained());
764 argNode->gtLsraInfo.srcCount = GetIndirSourceCount(putArgChild->AsIndir());
766 assert(putArgChild->isContained());
769 #endif // _TARGET_ARM_
771 //------------------------------------------------------------------------
772 // TreeNodeInfoInitBlockStore: Set the NodeInfo for a block store.
775 // blkNode - The block store node of interest
780 void LinearScan::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
782 GenTree* dstAddr = blkNode->Addr();
783 unsigned size = blkNode->gtBlkSize;
784 GenTree* source = blkNode->Data();
786 // Sources are dest address and initVal or source.
787 // We may require an additional source or temp register for the size.
788 blkNode->gtLsraInfo.srcCount = GetOperandSourceCount(dstAddr);
789 assert(blkNode->gtLsraInfo.dstCount == 0);
790 GenTreePtr srcAddrOrFill = nullptr;
791 bool isInitBlk = blkNode->OperIsInitBlkOp();
795 GenTreePtr initVal = source;
796 if (initVal->OperIsInitVal())
798 assert(initVal->isContained());
799 initVal = initVal->gtGetOp1();
801 srcAddrOrFill = initVal;
803 if (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll)
805 // TODO-ARM-CQ: Currently we generate a helper call for every
806 // initblk we encounter. Later on we should implement loop unrolling
807 // code sequences to improve CQ.
808 // For reference see the code in lsraxarch.cpp.
809 NYI_ARM("initblk loop unrolling is currently not implemented.");
810 if (!initVal->isContained())
812 blkNode->gtLsraInfo.srcCount++;
817 assert(blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper);
818 // The helper follows the regular ABI.
819 dstAddr->gtLsraInfo.setSrcCandidates(this, RBM_ARG_0);
820 assert(!initVal->isContained());
821 blkNode->gtLsraInfo.srcCount++;
822 initVal->gtLsraInfo.setSrcCandidates(this, RBM_ARG_1);
825 // Reserve a temp register for the block size argument.
826 blkNode->gtLsraInfo.setInternalCandidates(this, RBM_ARG_2);
827 blkNode->gtLsraInfo.internalIntCount = 1;
831 // The block size argument is a third argument to GT_STORE_DYN_BLK
832 noway_assert(blkNode->gtOper == GT_STORE_DYN_BLK);
833 blkNode->gtLsraInfo.setSrcCount(3);
834 GenTree* sizeNode = blkNode->AsDynBlk()->gtDynamicSize;
835 sizeNode->gtLsraInfo.setSrcCandidates(this, RBM_ARG_2);
841 // CopyObj or CopyBlk
842 // Sources are src and dest and size if not constant.
843 if (source->gtOper == GT_IND)
845 srcAddrOrFill = blkNode->Data()->gtGetOp1();
847 if (blkNode->OperGet() == GT_STORE_OBJ)
850 // We don't need to materialize the struct size but we still need
851 // a temporary register to perform the sequence of loads and stores.
852 blkNode->gtLsraInfo.internalIntCount = 1;
854 if (size >= 2 * REGSIZE_BYTES)
856 // We will use ldp/stp to reduce code size and improve performance
857 // so we need to reserve an extra internal register
858 blkNode->gtLsraInfo.internalIntCount++;
861 // We can't use the special Write Barrier registers, so exclude them from the mask
862 regMaskTP internalIntCandidates = RBM_ALLINT & ~(RBM_WRITE_BARRIER_DST_BYREF | RBM_WRITE_BARRIER_SRC_BYREF);
863 blkNode->gtLsraInfo.setInternalCandidates(this, internalIntCandidates);
865 // If we have a dest address we want it in RBM_WRITE_BARRIER_DST_BYREF.
866 dstAddr->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER_DST_BYREF);
868 // If we have a source address we want it in REG_WRITE_BARRIER_SRC_BYREF.
869 // Otherwise, if it is a local, codegen will put its address in REG_WRITE_BARRIER_SRC_BYREF,
870 // which is killed by a StoreObj (and thus needn't be reserved).
871 if (srcAddrOrFill != nullptr)
873 srcAddrOrFill->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER_SRC_BYREF);
879 short internalIntCount = 0;
880 regMaskTP internalIntCandidates = RBM_NONE;
882 if (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll)
884 // TODO-ARM-CQ: cpblk loop unrolling is currently not implemented.
885 // In case of a CpBlk with a constant size and less than CPBLK_UNROLL_LIMIT size
886 // we should unroll the loop to improve CQ.
887 // For reference see the code in lsraxarch.cpp.
888 NYI_ARM("cpblk loop unrolling is currently not implemented.");
890 #ifdef _TARGET_ARM64_
892 internalIntCount = 1;
893 internalIntCandidates = RBM_ALLINT;
895 if (size >= 2 * REGSIZE_BYTES)
897 // We will use ldp/stp to reduce code size and improve performance
898 // so we need to reserve an extra internal register
902 #endif // _TARGET_ARM64_
906 assert(blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper);
907 dstAddr->gtLsraInfo.setSrcCandidates(this, RBM_ARG_0);
908 // The srcAddr goes in arg1.
909 if (srcAddrOrFill != nullptr)
911 srcAddrOrFill->gtLsraInfo.setSrcCandidates(this, RBM_ARG_1);
915 // Reserve a temp register for the block size argument.
916 internalIntCandidates |= RBM_ARG_2;
921 // The block size argument is a third argument to GT_STORE_DYN_BLK
922 assert(blkNode->gtOper == GT_STORE_DYN_BLK);
923 blkNode->gtLsraInfo.srcCount++;
924 GenTree* blockSize = blkNode->AsDynBlk()->gtDynamicSize;
925 blockSize->gtLsraInfo.setSrcCandidates(this, RBM_ARG_2);
928 if (internalIntCount != 0)
930 blkNode->gtLsraInfo.internalIntCount = internalIntCount;
931 blkNode->gtLsraInfo.setInternalCandidates(this, internalIntCandidates);
934 blkNode->gtLsraInfo.srcCount += GetOperandSourceCount(source);
938 //------------------------------------------------------------------------
939 // GetOperandSourceCount: Get the source registers for an operand that might be contained.
942 // node - The node of interest
945 // The number of source registers used by the *parent* of this node.
947 int LinearScan::GetOperandSourceCount(GenTree* node)
949 if (!node->isContained())
954 #if !defined(_TARGET_64BIT_)
955 if (node->OperIs(GT_LONG))
959 #endif // !defined(_TARGET_64BIT_)
961 if (node->OperIsIndir())
963 const unsigned srcCount = GetIndirSourceCount(node->AsIndir());
970 #endif // _TARGET_ARMARCH_
972 #endif // !LEGACY_BACKEND