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 assert((info->dstCount == 1) || (tree->TypeGet() == TYP_VOID));
96 info->srcCount = tree->gtOp.gtOp2->isContained() ? 1 : 2;
99 void LinearScan::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
101 GenTreePtr dst = tree;
102 GenTreePtr addr = tree->gtOp.gtOp1;
103 GenTreePtr src = tree->gtOp.gtOp2;
105 if (addr->OperGet() == GT_LEA)
107 // In the case where we are doing a helper assignment, if the dst
108 // is an indir through an lea, we need to actually instantiate the
110 GenTreeAddrMode* lea = addr->AsAddrMode();
112 short leaSrcCount = 0;
113 if (lea->Base() != nullptr)
117 if (lea->Index() != nullptr)
121 lea->gtLsraInfo.srcCount = leaSrcCount;
122 lea->gtLsraInfo.dstCount = 1;
125 #if NOGC_WRITE_BARRIERS
126 NYI_ARM("NOGC_WRITE_BARRIERS");
128 // For the NOGC JIT Helper calls
130 // the 'addr' goes into x14 (REG_WRITE_BARRIER_DST_BYREF)
131 // the 'src' goes into x15 (REG_WRITE_BARRIER)
133 addr->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER_DST_BYREF);
134 src->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER);
136 // For the standard JIT Helper calls
137 // op1 goes into REG_ARG_0 and
138 // op2 goes into REG_ARG_1
140 addr->gtLsraInfo.setSrcCandidates(this, RBM_ARG_0);
141 src->gtLsraInfo.setSrcCandidates(this, RBM_ARG_1);
142 #endif // NOGC_WRITE_BARRIERS
144 // Both src and dst must reside in a register, which they should since we haven't set
145 // either of them as contained.
146 assert(addr->gtLsraInfo.dstCount == 1);
147 assert(src->gtLsraInfo.dstCount == 1);
150 //------------------------------------------------------------------------
151 // TreeNodeInfoInitIndir: Specify register requirements for address expression
152 // of an indirection operation.
155 // indirTree - GT_IND, GT_STOREIND or block gentree node
157 void LinearScan::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
159 // If this is the rhs of a block copy (i.e. non-enregisterable struct),
160 // it has no register requirements.
161 if (indirTree->TypeGet() == TYP_STRUCT)
166 TreeNodeInfo* info = &(indirTree->gtLsraInfo);
167 bool isStore = (indirTree->gtOper == GT_STOREIND);
168 info->srcCount = GetIndirSourceCount(indirTree);
170 GenTree* addr = indirTree->Addr();
171 GenTree* index = nullptr;
175 // Unaligned loads/stores for floating point values must first be loaded into integer register(s)
176 if (indirTree->gtFlags & GTF_IND_UNALIGNED)
178 var_types type = TYP_UNDEF;
179 if (indirTree->OperGet() == GT_STOREIND)
181 type = indirTree->AsStoreInd()->Data()->TypeGet();
183 else if (indirTree->OperGet() == GT_IND)
185 type = indirTree->TypeGet();
188 if (type == TYP_FLOAT)
190 info->internalIntCount = 1;
192 else if (type == TYP_DOUBLE)
194 info->internalIntCount = 2;
199 if (addr->isContained())
201 assert(addr->OperGet() == GT_LEA);
202 GenTreeAddrMode* lea = addr->AsAddrMode();
203 index = lea->Index();
206 // On ARM we may need a single internal register
207 // (when both conditions are true then we still only need a single internal register)
208 if ((index != nullptr) && (cns != 0))
210 // ARM does not support both Index and offset so we need an internal register
211 info->internalIntCount++;
213 else if (!emitter::emitIns_valid_imm_for_ldst_offset(cns, emitTypeSize(indirTree)))
215 // This offset can't be contained in the ldr/str instruction, so we need an internal register
216 info->internalIntCount++;
221 //------------------------------------------------------------------------
222 // TreeNodeInfoInitShiftRotate: Set the NodeInfo for a shift or rotate.
225 // tree - The node of interest
230 void LinearScan::TreeNodeInfoInitShiftRotate(GenTree* tree)
232 TreeNodeInfo* info = &(tree->gtLsraInfo);
234 GenTreePtr shiftBy = tree->gtOp.gtOp2;
235 info->srcCount = shiftBy->isContained() ? 1 : 2;
240 // The first operand of a GT_LSH_HI and GT_RSH_LO oper is a GT_LONG so that
241 // we can have a three operand form. Increment the srcCount.
242 GenTreePtr source = tree->gtOp.gtOp1;
243 if (tree->OperGet() == GT_LSH_HI || tree->OperGet() == GT_RSH_LO)
245 assert((source->OperGet() == GT_LONG) && source->isContained());
248 if (tree->OperGet() == GT_LSH_HI)
250 GenTreePtr sourceLo = source->gtOp.gtOp1;
251 sourceLo->gtLsraInfo.isDelayFree = true;
255 GenTreePtr sourceHi = source->gtOp.gtOp2;
256 sourceHi->gtLsraInfo.isDelayFree = true;
259 source->gtLsraInfo.hasDelayFreeSrc = true;
260 info->hasDelayFreeSrc = true;
263 #endif // _TARGET_ARM_
266 //------------------------------------------------------------------------
267 // TreeNodeInfoInitPutArgReg: Set the NodeInfo for a PUTARG_REG.
270 // node - The PUTARG_REG node.
271 // argReg - The register in which to pass the argument.
272 // info - The info for the node's using call.
273 // isVarArgs - True if the call uses a varargs calling convention.
274 // callHasFloatRegArgs - Set to true if this PUTARG_REG uses an FP register.
279 void LinearScan::TreeNodeInfoInitPutArgReg(GenTreeUnOp* node)
281 assert(node != nullptr);
282 assert(node->OperIsPutArgReg());
283 node->gtLsraInfo.srcCount = 1;
284 regNumber argReg = node->gtRegNum;
285 assert(argReg != REG_NA);
287 // Set the register requirements for the node.
288 regMaskTP argMask = genRegMask(argReg);
291 // If type of node is `long` then it is actually `double`.
292 // The actual `long` types must have been transformed as a field list with two fields.
293 if (node->TypeGet() == TYP_LONG)
295 node->gtLsraInfo.srcCount++;
296 node->gtLsraInfo.dstCount = node->gtLsraInfo.srcCount;
297 assert(genRegArgNext(argReg) == REG_NEXT(argReg));
298 argMask |= genRegMask(REG_NEXT(argReg));
300 #endif // _TARGET_ARM_
302 node->gtLsraInfo.setDstCandidates(this, argMask);
303 node->gtLsraInfo.setSrcCandidates(this, argMask);
305 // To avoid redundant moves, have the argument operand computed in the
306 // register in which the argument is passed to the call.
307 node->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, getUseCandidates(node));
310 //------------------------------------------------------------------------
311 // HandleFloatVarArgs: Handle additional register requirements for a varargs call
314 // call - The call node of interest
315 // argNode - The current argument
321 // In the case of a varargs call, the ABI dictates that if we have floating point args,
322 // we must pass the enregistered arguments in both the integer and floating point registers.
323 // Since the integer register is not associated with the arg node, we will reserve it as
324 // an internal register on the call so that it is not used during the evaluation of the call node
325 // (e.g. for the target).
326 void LinearScan::HandleFloatVarArgs(GenTreeCall* call, GenTree* argNode, bool* callHasFloatRegArgs)
329 if (call->IsVarargs() && varTypeIsFloating(argNode))
331 *callHasFloatRegArgs = true;
333 regNumber argReg = argNode->gtRegNum;
334 regNumber targetReg = compiler->getCallArgIntRegister(argReg);
335 call->gtLsraInfo.setInternalIntCount(call->gtLsraInfo.internalIntCount + 1);
336 call->gtLsraInfo.addInternalCandidates(this, genRegMask(targetReg));
338 #endif // FEATURE_VARARG
341 //------------------------------------------------------------------------
342 // TreeNodeInfoInitCall: Set the NodeInfo for a call.
345 // call - The call node of interest
350 void LinearScan::TreeNodeInfoInitCall(GenTreeCall* call)
352 TreeNodeInfo* info = &(call->gtLsraInfo);
353 bool hasMultiRegRetVal = false;
354 ReturnTypeDesc* retTypeDesc = nullptr;
357 if (call->TypeGet() != TYP_VOID)
359 hasMultiRegRetVal = call->HasMultiRegRetVal();
360 if (hasMultiRegRetVal)
362 // dst count = number of registers in which the value is returned by call
363 retTypeDesc = call->GetReturnTypeDesc();
364 info->dstCount = retTypeDesc->GetReturnRegCount();
376 GenTree* ctrlExpr = call->gtControlExpr;
377 if (call->gtCallType == CT_INDIRECT)
379 // either gtControlExpr != null or gtCallAddr != null.
380 // Both cannot be non-null at the same time.
381 assert(ctrlExpr == nullptr);
382 assert(call->gtCallAddr != nullptr);
383 ctrlExpr = call->gtCallAddr;
386 // set reg requirements on call target represented as control sequence.
387 if (ctrlExpr != nullptr)
389 // we should never see a gtControlExpr whose type is void.
390 assert(ctrlExpr->TypeGet() != TYP_VOID);
394 // In case of fast tail implemented as jmp, make sure that gtControlExpr is
395 // computed into a register.
396 if (call->IsFastTailCall())
398 // Fast tail call - make sure that call target is always computed in R12(ARM32)/IP0(ARM64)
399 // so that epilog sequence can generate "br xip0/r12" to achieve fast tail call.
400 ctrlExpr->gtLsraInfo.setSrcCandidates(this, RBM_FASTTAILCALL_TARGET);
406 info->internalIntCount = 1;
408 #endif // _TARGET_ARM_
410 RegisterType registerType = call->TypeGet();
412 // Set destination candidates for return value of the call.
415 if (call->IsHelperCall(compiler, CORINFO_HELP_INIT_PINVOKE_FRAME))
417 // The ARM CORINFO_HELP_INIT_PINVOKE_FRAME helper uses a custom calling convention that returns with
418 // TCB in REG_PINVOKE_TCB. fgMorphCall() sets the correct argument registers.
419 info->setDstCandidates(this, RBM_PINVOKE_TCB);
422 #endif // _TARGET_ARM_
423 if (hasMultiRegRetVal)
425 assert(retTypeDesc != nullptr);
426 info->setDstCandidates(this, retTypeDesc->GetABIReturnRegs());
428 else if (varTypeIsFloating(registerType))
430 info->setDstCandidates(this, RBM_FLOATRET);
432 else if (registerType == TYP_LONG)
434 info->setDstCandidates(this, RBM_LNGRET);
438 info->setDstCandidates(this, RBM_INTRET);
441 // First, count reg args
442 // Each register argument corresponds to one source.
443 bool callHasFloatRegArgs = false;
445 for (GenTreePtr list = call->gtCallLateArgs; list; list = list->MoveNext())
447 assert(list->OperIsList());
449 GenTreePtr argNode = list->Current();
452 // During TreeNodeInfoInit, we only use the ArgTabEntry for validation,
453 // as getting it is rather expensive.
454 fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, argNode);
455 regNumber argReg = curArgTabEntry->regNum;
456 assert(curArgTabEntry);
459 if (argNode->gtOper == GT_PUTARG_STK)
461 // late arg that is not passed in a register
462 assert(curArgTabEntry->regNum == REG_STK);
463 GenTree* putArgChild = argNode->gtGetOp1();
464 if (!varTypeIsStruct(putArgChild) && !putArgChild->OperIs(GT_FIELD_LIST))
467 // The `double` types could been transformed to `long` on arm, while the actual longs
468 // have been decomposed.
469 const bool isDouble = (putArgChild->TypeGet() == TYP_LONG || putArgChild->TypeGet() == TYP_DOUBLE);
472 argNode->gtLsraInfo.srcCount = 2;
475 // We must not have a multi-reg struct; double uses 2 slots and isn't a multi-reg struct
476 assert((curArgTabEntry->numSlots == 1) || isDouble);
477 #else // !_TARGET_ARM_
478 // Validate the slot count for this arg.
479 // We must not have a multi-reg struct
480 assert(curArgTabEntry->numSlots == 1);
481 #endif // !_TARGET_ARM_
486 // A GT_FIELD_LIST has a TYP_VOID, but is used to represent a multireg struct
487 if (argNode->OperGet() == GT_FIELD_LIST)
489 assert(argNode->isContained());
491 // There could be up to 2-4 PUTARG_REGs in the list (3 or 4 can only occur for HFAs)
492 for (GenTreeFieldList* entry = argNode->AsFieldList(); entry != nullptr; entry = entry->Rest())
496 assert(entry->Current()->OperIs(GT_PUTARG_REG));
497 assert(entry->Current()->gtRegNum == argReg);
498 // Update argReg for the next putarg_reg (if any)
499 argReg = genRegArgNext(argReg);
501 #if defined(_TARGET_ARM_)
502 // A double register is modelled as an even-numbered single one
503 if (entry->Current()->TypeGet() == TYP_DOUBLE)
505 argReg = genRegArgNext(argReg);
507 #endif // _TARGET_ARM_
512 else if (argNode->OperGet() == GT_PUTARG_SPLIT)
514 fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, argNode);
515 info->srcCount += argNode->AsPutArgSplit()->gtNumRegs;
520 assert(argNode->OperIs(GT_PUTARG_REG));
521 assert(argNode->gtRegNum == argReg);
522 HandleFloatVarArgs(call, argNode, &callHasFloatRegArgs);
526 // The `double` types have been transformed to `long` on arm,
527 // while the actual long types have been decomposed.
528 if (argNode->TypeGet() == TYP_LONG)
532 #endif // _TARGET_ARM_
536 // Now, count stack args
537 // Note that these need to be computed into a register, but then
538 // they're just stored to the stack - so the reg doesn't
539 // need to remain live until the call. In fact, it must not
540 // because the code generator doesn't actually consider it live,
541 // so it can't be spilled.
543 GenTreePtr args = call->gtCallArgs;
546 GenTreePtr arg = args->gtOp.gtOp1;
548 // Skip arguments that have been moved to the Late Arg list
549 if (!(args->gtFlags & GTF_LATE_ARG))
552 fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, arg);
553 assert(curArgTabEntry);
555 if (arg->gtOper == GT_PUTARG_STK)
557 assert(curArgTabEntry->regNum == REG_STK);
560 else if (arg->OperGet() == GT_PUTARG_SPLIT)
562 assert(arg->AsPutArgSplit()->gtNumRegs == curArgTabEntry->numRegs);
563 info->srcCount += arg->gtLsraInfo.dstCount;
568 TreeNodeInfo* argInfo = &(arg->gtLsraInfo);
569 assert((argInfo->dstCount == 0) || (argInfo->isLocalDefUse));
572 args = args->gtOp.gtOp2;
575 // If it is a fast tail call, it is already preferenced to use IP0.
576 // Therefore, no need set src candidates on call tgt again.
577 if (call->IsVarargs() && callHasFloatRegArgs && !call->IsFastTailCall() && (ctrlExpr != nullptr))
579 NYI_ARM("float reg varargs");
581 // Don't assign the call target to any of the argument registers because
582 // we will use them to also pass floating point arguments as required
584 ctrlExpr->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~(RBM_ARG_REGS));
589 if (call->NeedsNullCheck())
591 info->internalIntCount++;
594 #endif // _TARGET_ARM_
597 //------------------------------------------------------------------------
598 // TreeNodeInfoInitPutArgStk: Set the NodeInfo for a GT_PUTARG_STK node
601 // argNode - a GT_PUTARG_STK node
607 // Set the child node(s) to be contained when we have a multireg arg
609 void LinearScan::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode)
611 assert(argNode->gtOper == GT_PUTARG_STK);
613 GenTreePtr putArgChild = argNode->gtOp.gtOp1;
615 argNode->gtLsraInfo.srcCount = 0;
616 argNode->gtLsraInfo.dstCount = 0;
618 // Do we have a TYP_STRUCT argument (or a GT_FIELD_LIST), if so it must be a multireg pass-by-value struct
619 if ((putArgChild->TypeGet() == TYP_STRUCT) || (putArgChild->OperGet() == GT_FIELD_LIST))
621 // We will use store instructions that each write a register sized value
623 if (putArgChild->OperGet() == GT_FIELD_LIST)
625 assert(putArgChild->isContained());
626 // We consume all of the items in the GT_FIELD_LIST
627 for (GenTreeFieldList* current = putArgChild->AsFieldList(); current != nullptr; current = current->Rest())
629 argNode->gtLsraInfo.srcCount++;
634 #ifdef _TARGET_ARM64_
635 // We could use a ldp/stp sequence so we need two internal registers
636 argNode->gtLsraInfo.internalIntCount = 2;
637 #else // _TARGET_ARM_
638 // We could use a ldr/str sequence so we need a internal register
639 argNode->gtLsraInfo.internalIntCount = 1;
640 #endif // _TARGET_ARM_
642 if (putArgChild->OperGet() == GT_OBJ)
644 GenTreePtr objChild = putArgChild->gtOp.gtOp1;
645 if (objChild->OperGet() == GT_LCL_VAR_ADDR)
647 // We will generate all of the code for the GT_PUTARG_STK, the GT_OBJ and the GT_LCL_VAR_ADDR
648 // as one contained operation
650 assert(objChild->isContained());
654 // We will generate all of the code for the GT_PUTARG_STK and its child node
655 // as one contained operation
657 argNode->gtLsraInfo.srcCount = putArgChild->gtLsraInfo.srcCount;
658 assert(putArgChild->isContained());
663 #if defined(_TARGET_ARM_)
664 // The `double` types have been transformed to `long` on armel,
665 // while the actual long types have been decomposed.
666 const bool isDouble = (putArgChild->TypeGet() == TYP_LONG);
669 argNode->gtLsraInfo.srcCount = 2;
672 #endif // defined(_TARGET_ARM_)
674 argNode->gtLsraInfo.srcCount = 1;
680 //------------------------------------------------------------------------
681 // TreeNodeInfoInitPutArgSplit: Set the NodeInfo for a GT_PUTARG_SPLIT node
684 // argNode - a GT_PUTARG_SPLIT node
690 // Set the child node(s) to be contained
692 void LinearScan::TreeNodeInfoInitPutArgSplit(GenTreePutArgSplit* argNode)
694 assert(argNode->gtOper == GT_PUTARG_SPLIT);
696 GenTreePtr putArgChild = argNode->gtOp.gtOp1;
698 // Registers for split argument corresponds to source
699 argNode->gtLsraInfo.dstCount = argNode->gtNumRegs;
701 regNumber argReg = argNode->gtRegNum;
702 regMaskTP argMask = RBM_NONE;
703 for (unsigned i = 0; i < argNode->gtNumRegs; i++)
705 argMask |= genRegMask((regNumber)((unsigned)argReg + i));
707 argNode->gtLsraInfo.setDstCandidates(this, argMask);
708 argNode->gtLsraInfo.setSrcCandidates(this, argMask);
710 if (putArgChild->OperGet() == GT_FIELD_LIST)
713 // 1. Consume all of the items in the GT_FIELD_LIST (source)
714 // 2. Store to target slot and move to target registers (destination) from source
716 unsigned slotCount = 0;
718 // To avoid redundant moves, have the argument operand computed in the
719 // register in which the argument is passed to the call.
720 GenTreeFieldList* fieldListPtr = putArgChild->AsFieldList();
721 for (unsigned idx = 0; fieldListPtr != nullptr; fieldListPtr = fieldListPtr->Rest(), idx++)
723 if (idx < argNode->gtNumRegs)
725 GenTreePtr node = fieldListPtr->gtGetOp1();
726 node->gtLsraInfo.setSrcCandidates(this, genRegMask((regNumber)((unsigned)argReg + idx)));
733 argNode->gtLsraInfo.srcCount = argNode->gtNumRegs + slotCount;
734 assert(putArgChild->isContained());
738 assert(putArgChild->TypeGet() == TYP_STRUCT);
739 assert(putArgChild->OperGet() == GT_OBJ);
741 // We can use a ldr/str sequence so we need an internal register
742 argNode->gtLsraInfo.internalIntCount = 1;
743 regMaskTP internalMask = RBM_ALLINT & ~argMask;
744 argNode->gtLsraInfo.setInternalCandidates(this, internalMask);
746 GenTreePtr objChild = putArgChild->gtOp.gtOp1;
747 if (objChild->OperGet() == GT_LCL_VAR_ADDR)
749 // We will generate all of the code for the GT_PUTARG_SPLIT, the GT_OBJ and the GT_LCL_VAR_ADDR
750 // as one contained operation
752 assert(objChild->isContained());
756 argNode->gtLsraInfo.srcCount = GetIndirSourceCount(putArgChild->AsIndir());
758 assert(putArgChild->isContained());
761 #endif // _TARGET_ARM_
763 //------------------------------------------------------------------------
764 // TreeNodeInfoInitBlockStore: Set the NodeInfo for a block store.
767 // blkNode - The block store node of interest
772 void LinearScan::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
774 GenTree* dstAddr = blkNode->Addr();
775 unsigned size = blkNode->gtBlkSize;
776 GenTree* source = blkNode->Data();
778 // Sources are dest address and initVal or source.
779 // We may require an additional source or temp register for the size.
780 blkNode->gtLsraInfo.srcCount = GetOperandSourceCount(dstAddr);
781 assert(blkNode->gtLsraInfo.dstCount == 0);
782 GenTreePtr srcAddrOrFill = nullptr;
783 bool isInitBlk = blkNode->OperIsInitBlkOp();
787 GenTreePtr initVal = source;
788 if (initVal->OperIsInitVal())
790 assert(initVal->isContained());
791 initVal = initVal->gtGetOp1();
793 srcAddrOrFill = initVal;
795 if (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll)
797 // TODO-ARM-CQ: Currently we generate a helper call for every
798 // initblk we encounter. Later on we should implement loop unrolling
799 // code sequences to improve CQ.
800 // For reference see the code in lsraxarch.cpp.
801 NYI_ARM("initblk loop unrolling is currently not implemented.");
802 if (!initVal->isContained())
804 blkNode->gtLsraInfo.srcCount++;
809 assert(blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper);
810 // The helper follows the regular ABI.
811 dstAddr->gtLsraInfo.setSrcCandidates(this, RBM_ARG_0);
812 assert(!initVal->isContained());
813 blkNode->gtLsraInfo.srcCount++;
814 initVal->gtLsraInfo.setSrcCandidates(this, RBM_ARG_1);
817 // Reserve a temp register for the block size argument.
818 blkNode->gtLsraInfo.setInternalCandidates(this, RBM_ARG_2);
819 blkNode->gtLsraInfo.internalIntCount = 1;
823 // The block size argument is a third argument to GT_STORE_DYN_BLK
824 noway_assert(blkNode->gtOper == GT_STORE_DYN_BLK);
825 blkNode->gtLsraInfo.setSrcCount(3);
826 GenTree* sizeNode = blkNode->AsDynBlk()->gtDynamicSize;
827 sizeNode->gtLsraInfo.setSrcCandidates(this, RBM_ARG_2);
833 // CopyObj or CopyBlk
834 // Sources are src and dest and size if not constant.
835 if (source->gtOper == GT_IND)
837 srcAddrOrFill = blkNode->Data()->gtGetOp1();
839 if (blkNode->OperGet() == GT_STORE_OBJ)
842 // We don't need to materialize the struct size but we still need
843 // a temporary register to perform the sequence of loads and stores.
844 blkNode->gtLsraInfo.internalIntCount = 1;
846 if (size >= 2 * REGSIZE_BYTES)
848 // We will use ldp/stp to reduce code size and improve performance
849 // so we need to reserve an extra internal register
850 blkNode->gtLsraInfo.internalIntCount++;
853 // We can't use the special Write Barrier registers, so exclude them from the mask
854 regMaskTP internalIntCandidates = RBM_ALLINT & ~(RBM_WRITE_BARRIER_DST_BYREF | RBM_WRITE_BARRIER_SRC_BYREF);
855 blkNode->gtLsraInfo.setInternalCandidates(this, internalIntCandidates);
857 // If we have a dest address we want it in RBM_WRITE_BARRIER_DST_BYREF.
858 dstAddr->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER_DST_BYREF);
860 // If we have a source address we want it in REG_WRITE_BARRIER_SRC_BYREF.
861 // Otherwise, if it is a local, codegen will put its address in REG_WRITE_BARRIER_SRC_BYREF,
862 // which is killed by a StoreObj (and thus needn't be reserved).
863 if (srcAddrOrFill != nullptr)
865 srcAddrOrFill->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER_SRC_BYREF);
871 short internalIntCount = 0;
872 regMaskTP internalIntCandidates = RBM_NONE;
874 if (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll)
876 // In case of a CpBlk with a constant size and less than CPBLK_UNROLL_LIMIT size
877 // we should unroll the loop to improve CQ.
878 // For reference see the code in lsraxarch.cpp.
880 internalIntCount = 1;
881 internalIntCandidates = RBM_ALLINT;
883 #ifdef _TARGET_ARM64_
884 if (size >= 2 * REGSIZE_BYTES)
886 // We will use ldp/stp to reduce code size and improve performance
887 // so we need to reserve an extra internal register
890 #endif // _TARGET_ARM64_
894 assert(blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper);
895 dstAddr->gtLsraInfo.setSrcCandidates(this, RBM_ARG_0);
896 // The srcAddr goes in arg1.
897 if (srcAddrOrFill != nullptr)
899 srcAddrOrFill->gtLsraInfo.setSrcCandidates(this, RBM_ARG_1);
903 // Reserve a temp register for the block size argument.
904 internalIntCandidates |= RBM_ARG_2;
909 // The block size argument is a third argument to GT_STORE_DYN_BLK
910 assert(blkNode->gtOper == GT_STORE_DYN_BLK);
911 blkNode->gtLsraInfo.srcCount++;
912 GenTree* blockSize = blkNode->AsDynBlk()->gtDynamicSize;
913 blockSize->gtLsraInfo.setSrcCandidates(this, RBM_ARG_2);
916 if (internalIntCount != 0)
918 blkNode->gtLsraInfo.internalIntCount = internalIntCount;
919 blkNode->gtLsraInfo.setInternalCandidates(this, internalIntCandidates);
922 blkNode->gtLsraInfo.srcCount += GetOperandSourceCount(source);
926 //------------------------------------------------------------------------
927 // GetOperandSourceCount: Get the source registers for an operand that might be contained.
930 // node - The node of interest
933 // The number of source registers used by the *parent* of this node.
935 int LinearScan::GetOperandSourceCount(GenTree* node)
937 if (!node->isContained())
942 #if !defined(_TARGET_64BIT_)
943 if (node->OperIs(GT_LONG))
947 #endif // !defined(_TARGET_64BIT_)
949 if (node->OperIsIndir())
951 const unsigned srcCount = GetIndirSourceCount(node->AsIndir());
958 #endif // _TARGET_ARMARCH_
960 #endif // !LEGACY_BACKEND