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 // BuildIndir: Specify register requirements for address expression
33 // of an indirection operation.
36 // indirTree - GT_IND, GT_STOREIND or block gentree node
38 void LinearScan::BuildIndir(GenTreeIndir* indirTree)
40 TreeNodeInfo* info = currentNodeInfo;
41 // If this is the rhs of a block copy (i.e. non-enregisterable struct),
42 // it has no register requirements.
43 if (indirTree->TypeGet() == TYP_STRUCT)
48 bool isStore = (indirTree->gtOper == GT_STOREIND);
49 info->srcCount = GetIndirInfo(indirTree);
51 GenTree* addr = indirTree->Addr();
52 GenTree* index = nullptr;
56 // Unaligned loads/stores for floating point values must first be loaded into integer register(s)
57 if (indirTree->gtFlags & GTF_IND_UNALIGNED)
59 var_types type = TYP_UNDEF;
60 if (indirTree->OperGet() == GT_STOREIND)
62 type = indirTree->AsStoreInd()->Data()->TypeGet();
64 else if (indirTree->OperGet() == GT_IND)
66 type = indirTree->TypeGet();
69 if (type == TYP_FLOAT)
71 info->internalIntCount = 1;
73 else if (type == TYP_DOUBLE)
75 info->internalIntCount = 2;
80 if (addr->isContained())
82 assert(addr->OperGet() == GT_LEA);
83 GenTreeAddrMode* lea = addr->AsAddrMode();
87 // On ARM we may need a single internal register
88 // (when both conditions are true then we still only need a single internal register)
89 if ((index != nullptr) && (cns != 0))
91 // ARM does not support both Index and offset so we need an internal register
92 info->internalIntCount++;
94 else if (!emitter::emitIns_valid_imm_for_ldst_offset(cns, emitTypeSize(indirTree)))
96 // This offset can't be contained in the ldr/str instruction, so we need an internal register
97 info->internalIntCount++;
102 if (indirTree->TypeGet() == TYP_SIMD12)
104 // If indirTree is of TYP_SIMD12, addr is not contained. See comment in LowerIndir().
105 assert(!indirTree->Addr()->isContained());
107 // Vector3 is read/written as two reads/writes: 8 byte and 4 byte.
108 // To assemble the vector properly we would need an additional int register
109 info->internalIntCount = 1;
111 #endif // FEATURE_SIMD
114 //------------------------------------------------------------------------
115 // BuildCall: Set the NodeInfo for a call.
118 // call - The call node of interest
123 void LinearScan::BuildCall(GenTreeCall* call)
125 TreeNodeInfo* info = currentNodeInfo;
126 bool hasMultiRegRetVal = false;
127 ReturnTypeDesc* retTypeDesc = nullptr;
130 if (call->TypeGet() != TYP_VOID)
132 hasMultiRegRetVal = call->HasMultiRegRetVal();
133 if (hasMultiRegRetVal)
135 // dst count = number of registers in which the value is returned by call
136 retTypeDesc = call->GetReturnTypeDesc();
137 info->dstCount = retTypeDesc->GetReturnRegCount();
149 GenTree* ctrlExpr = call->gtControlExpr;
150 LocationInfoListNode* ctrlExprInfo = nullptr;
151 if (call->gtCallType == CT_INDIRECT)
153 // either gtControlExpr != null or gtCallAddr != null.
154 // Both cannot be non-null at the same time.
155 assert(ctrlExpr == nullptr);
156 assert(call->gtCallAddr != nullptr);
157 ctrlExpr = call->gtCallAddr;
160 // set reg requirements on call target represented as control sequence.
161 if (ctrlExpr != nullptr)
163 ctrlExprInfo = getLocationInfo(ctrlExpr);
165 // we should never see a gtControlExpr whose type is void.
166 assert(ctrlExpr->TypeGet() != TYP_VOID);
168 // In case of fast tail implemented as jmp, make sure that gtControlExpr is
169 // computed into a register.
170 if (call->IsFastTailCall())
172 // Fast tail call - make sure that call target is always computed in R12(ARM32)/IP0(ARM64)
173 // so that epilog sequence can generate "br xip0/r12" to achieve fast tail call.
174 ctrlExprInfo->info.setSrcCandidates(this, RBM_FASTTAILCALL_TARGET);
180 info->internalIntCount = 1;
182 #endif // _TARGET_ARM_
184 RegisterType registerType = call->TypeGet();
186 // Set destination candidates for return value of the call.
189 if (call->IsHelperCall(compiler, CORINFO_HELP_INIT_PINVOKE_FRAME))
191 // The ARM CORINFO_HELP_INIT_PINVOKE_FRAME helper uses a custom calling convention that returns with
192 // TCB in REG_PINVOKE_TCB. fgMorphCall() sets the correct argument registers.
193 info->setDstCandidates(this, RBM_PINVOKE_TCB);
196 #endif // _TARGET_ARM_
197 if (hasMultiRegRetVal)
199 assert(retTypeDesc != nullptr);
200 info->setDstCandidates(this, retTypeDesc->GetABIReturnRegs());
202 else if (varTypeIsFloating(registerType))
204 info->setDstCandidates(this, RBM_FLOATRET);
206 else if (registerType == TYP_LONG)
208 info->setDstCandidates(this, RBM_LNGRET);
212 info->setDstCandidates(this, RBM_INTRET);
215 // First, count reg args
216 // Each register argument corresponds to one source.
217 bool callHasFloatRegArgs = false;
219 for (GenTree* list = call->gtCallLateArgs; list; list = list->MoveNext())
221 assert(list->OperIsList());
223 GenTree* argNode = list->Current();
226 // During Build, we only use the ArgTabEntry for validation,
227 // as getting it is rather expensive.
228 fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, argNode);
229 regNumber argReg = curArgTabEntry->regNum;
230 assert(curArgTabEntry);
233 if (argNode->gtOper == GT_PUTARG_STK)
235 // late arg that is not passed in a register
236 assert(curArgTabEntry->regNum == REG_STK);
237 GenTree* putArgChild = argNode->gtGetOp1();
238 if (!varTypeIsStruct(putArgChild) && !putArgChild->OperIs(GT_FIELD_LIST))
240 unsigned expectedSlots = 1;
242 // The `double` types could been transformed to `long` on arm, while the actual longs
243 // have been decomposed.
244 if (putArgChild->TypeGet() == TYP_LONG)
246 useList.GetTreeNodeInfo(argNode).srcCount = 2;
249 else if (putArgChild->TypeGet() == TYP_DOUBLE)
253 #endif // !_TARGET_ARM_
254 // Validate the slot count for this arg.
255 assert(curArgTabEntry->numSlots == expectedSlots);
260 // A GT_FIELD_LIST has a TYP_VOID, but is used to represent a multireg struct
261 if (argNode->OperGet() == GT_FIELD_LIST)
263 assert(argNode->isContained());
265 // There could be up to 2-4 PUTARG_REGs in the list (3 or 4 can only occur for HFAs)
266 for (GenTreeFieldList* entry = argNode->AsFieldList(); entry != nullptr; entry = entry->Rest())
269 appendLocationInfoToList(entry->Current());
271 assert(entry->Current()->OperIs(GT_PUTARG_REG));
272 assert(entry->Current()->gtRegNum == argReg);
273 // Update argReg for the next putarg_reg (if any)
274 argReg = genRegArgNext(argReg);
276 #if defined(_TARGET_ARM_)
277 // A double register is modelled as an even-numbered single one
278 if (entry->Current()->TypeGet() == TYP_DOUBLE)
280 argReg = genRegArgNext(argReg);
282 #endif // _TARGET_ARM_
287 else if (argNode->OperGet() == GT_PUTARG_SPLIT)
289 unsigned regCount = argNode->AsPutArgSplit()->gtNumRegs;
290 assert(regCount == curArgTabEntry->numRegs);
291 info->srcCount += regCount;
292 appendLocationInfoToList(argNode);
297 assert(argNode->OperIs(GT_PUTARG_REG));
298 assert(argNode->gtRegNum == argReg);
299 HandleFloatVarArgs(call, argNode, &callHasFloatRegArgs);
301 // The `double` types have been transformed to `long` on armel,
302 // while the actual long types have been decomposed.
303 // On ARM we may have bitcasts from DOUBLE to LONG.
304 if (argNode->TypeGet() == TYP_LONG)
306 assert(argNode->IsMultiRegNode());
308 appendLocationInfoToList(argNode);
311 #endif // _TARGET_ARM_
313 appendLocationInfoToList(argNode);
319 // Now, count stack args
320 // Note that these need to be computed into a register, but then
321 // they're just stored to the stack - so the reg doesn't
322 // need to remain live until the call. In fact, it must not
323 // because the code generator doesn't actually consider it live,
324 // so it can't be spilled.
326 GenTree* args = call->gtCallArgs;
329 GenTree* arg = args->gtOp.gtOp1;
331 // Skip arguments that have been moved to the Late Arg list
332 if (!(args->gtFlags & GTF_LATE_ARG))
335 fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, arg);
336 assert(curArgTabEntry);
339 // PUTARG_SPLIT nodes must be in the gtCallLateArgs list, since they
340 // define registers used by the call.
341 assert(arg->OperGet() != GT_PUTARG_SPLIT);
343 if (arg->gtOper == GT_PUTARG_STK)
345 assert(curArgTabEntry->regNum == REG_STK);
349 assert(!arg->IsValue() || arg->IsUnusedValue());
352 args = args->gtOp.gtOp2;
355 // If it is a fast tail call, it is already preferenced to use IP0.
356 // Therefore, no need set src candidates on call tgt again.
357 if (call->IsVarargs() && callHasFloatRegArgs && !call->IsFastTailCall() && (ctrlExprInfo != nullptr))
359 NYI_ARM("float reg varargs");
361 // Don't assign the call target to any of the argument registers because
362 // we will use them to also pass floating point arguments as required
364 ctrlExprInfo->info.setSrcCandidates(this, allRegs(TYP_INT) & ~(RBM_ARG_REGS));
367 if (ctrlExprInfo != nullptr)
369 useList.Append(ctrlExprInfo);
375 if (call->NeedsNullCheck())
377 info->internalIntCount++;
380 #endif // _TARGET_ARM_
383 //------------------------------------------------------------------------
384 // BuildPutArgStk: Set the NodeInfo for a GT_PUTARG_STK node
387 // argNode - a GT_PUTARG_STK node
393 // Set the child node(s) to be contained when we have a multireg arg
395 void LinearScan::BuildPutArgStk(GenTreePutArgStk* argNode)
397 TreeNodeInfo* info = currentNodeInfo;
398 assert(argNode->gtOper == GT_PUTARG_STK);
400 GenTree* putArgChild = argNode->gtOp.gtOp1;
405 // Do we have a TYP_STRUCT argument (or a GT_FIELD_LIST), if so it must be a multireg pass-by-value struct
406 if ((putArgChild->TypeGet() == TYP_STRUCT) || (putArgChild->OperGet() == GT_FIELD_LIST))
408 // We will use store instructions that each write a register sized value
410 if (putArgChild->OperGet() == GT_FIELD_LIST)
412 assert(putArgChild->isContained());
413 // We consume all of the items in the GT_FIELD_LIST
414 for (GenTreeFieldList* current = putArgChild->AsFieldList(); current != nullptr; current = current->Rest())
416 appendLocationInfoToList(current->Current());
422 #ifdef _TARGET_ARM64_
423 // We could use a ldp/stp sequence so we need two internal registers
424 info->internalIntCount = 2;
425 #else // _TARGET_ARM_
426 // We could use a ldr/str sequence so we need a internal register
427 info->internalIntCount = 1;
428 #endif // _TARGET_ARM_
430 if (putArgChild->OperGet() == GT_OBJ)
432 assert(putArgChild->isContained());
433 GenTree* objChild = putArgChild->gtOp.gtOp1;
434 if (objChild->OperGet() == GT_LCL_VAR_ADDR)
436 // We will generate all of the code for the GT_PUTARG_STK, the GT_OBJ and the GT_LCL_VAR_ADDR
437 // as one contained operation, and there are no source registers.
439 assert(objChild->isContained());
443 // We will generate all of the code for the GT_PUTARG_STK and its child node
444 // as one contained operation
446 appendLocationInfoToList(objChild);
452 // No source registers.
453 putArgChild->OperIs(GT_LCL_VAR);
459 assert(!putArgChild->isContained());
460 info->srcCount = GetOperandInfo(putArgChild);
465 //------------------------------------------------------------------------
466 // BuildPutArgSplit: Set the NodeInfo for a GT_PUTARG_SPLIT node
469 // argNode - a GT_PUTARG_SPLIT node
475 // Set the child node(s) to be contained
477 void LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode)
479 TreeNodeInfo* info = currentNodeInfo;
480 assert(argNode->gtOper == GT_PUTARG_SPLIT);
482 GenTree* putArgChild = argNode->gtOp.gtOp1;
484 // Registers for split argument corresponds to source
485 info->dstCount = argNode->gtNumRegs;
487 regNumber argReg = argNode->gtRegNum;
488 regMaskTP argMask = RBM_NONE;
489 for (unsigned i = 0; i < argNode->gtNumRegs; i++)
491 argMask |= genRegMask((regNumber)((unsigned)argReg + i));
493 info->setDstCandidates(this, argMask);
494 info->setSrcCandidates(this, argMask);
496 if (putArgChild->OperGet() == GT_FIELD_LIST)
499 // 1. Consume all of the items in the GT_FIELD_LIST (source)
500 // 2. Store to target slot and move to target registers (destination) from source
502 unsigned sourceRegCount = 0;
504 // To avoid redundant moves, have the argument operand computed in the
505 // register in which the argument is passed to the call.
507 for (GenTreeFieldList* fieldListPtr = putArgChild->AsFieldList(); fieldListPtr != nullptr;
508 fieldListPtr = fieldListPtr->Rest())
510 GenTree* node = fieldListPtr->gtGetOp1();
511 assert(!node->isContained());
512 LocationInfoListNode* nodeInfo = getLocationInfo(node);
513 unsigned currentRegCount = nodeInfo->info.dstCount;
514 regMaskTP sourceMask = RBM_NONE;
515 if (sourceRegCount < argNode->gtNumRegs)
517 for (unsigned regIndex = 0; regIndex < currentRegCount; regIndex++)
519 sourceMask |= genRegMask((regNumber)((unsigned)argReg + sourceRegCount + regIndex));
521 nodeInfo->info.setSrcCandidates(this, sourceMask);
523 sourceRegCount += currentRegCount;
524 useList.Append(nodeInfo);
526 info->srcCount += sourceRegCount;
527 assert(putArgChild->isContained());
531 assert(putArgChild->TypeGet() == TYP_STRUCT);
532 assert(putArgChild->OperGet() == GT_OBJ);
534 // We can use a ldr/str sequence so we need an internal register
535 info->internalIntCount = 1;
536 regMaskTP internalMask = RBM_ALLINT & ~argMask;
537 info->setInternalCandidates(this, internalMask);
539 GenTree* objChild = putArgChild->gtOp.gtOp1;
540 if (objChild->OperGet() == GT_LCL_VAR_ADDR)
542 // We will generate all of the code for the GT_PUTARG_SPLIT, the GT_OBJ and the GT_LCL_VAR_ADDR
543 // as one contained operation
545 assert(objChild->isContained());
549 info->srcCount = GetIndirInfo(putArgChild->AsIndir());
551 assert(putArgChild->isContained());
554 #endif // _TARGET_ARM_
556 //------------------------------------------------------------------------
557 // BuildBlockStore: Set the NodeInfo for a block store.
560 // blkNode - The block store node of interest
565 void LinearScan::BuildBlockStore(GenTreeBlk* blkNode)
567 TreeNodeInfo* info = currentNodeInfo;
568 GenTree* dstAddr = blkNode->Addr();
569 unsigned size = blkNode->gtBlkSize;
570 GenTree* source = blkNode->Data();
572 LocationInfoListNode* dstAddrInfo = nullptr;
573 LocationInfoListNode* sourceInfo = nullptr;
574 LocationInfoListNode* sizeInfo = nullptr;
576 // Sources are dest address and initVal or source.
577 // We may require an additional source or temp register for the size.
578 if (!dstAddr->isContained())
581 dstAddrInfo = getLocationInfo(dstAddr);
583 assert(info->dstCount == 0);
584 GenTree* srcAddrOrFill = nullptr;
585 bool isInitBlk = blkNode->OperIsInitBlkOp();
587 regMaskTP dstAddrRegMask = RBM_NONE;
588 regMaskTP sourceRegMask = RBM_NONE;
589 regMaskTP blkSizeRegMask = RBM_NONE;
591 short internalIntCount = 0;
592 regMaskTP internalIntCandidates = RBM_NONE;
596 GenTree* initVal = source;
597 if (initVal->OperIsInitVal())
599 assert(initVal->isContained());
600 initVal = initVal->gtGetOp1();
602 srcAddrOrFill = initVal;
603 if (!initVal->isContained())
606 sourceInfo = getLocationInfo(initVal);
609 if (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll)
611 // TODO-ARM-CQ: Currently we generate a helper call for every
612 // initblk we encounter. Later on we should implement loop unrolling
613 // code sequences to improve CQ.
614 // For reference see the code in lsraxarch.cpp.
615 NYI_ARM("initblk loop unrolling is currently not implemented.");
619 assert(blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper);
620 assert(!initVal->isContained());
621 // The helper follows the regular ABI.
622 dstAddrRegMask = RBM_ARG_0;
623 sourceRegMask = RBM_ARG_1;
624 blkSizeRegMask = RBM_ARG_2;
629 // CopyObj or CopyBlk
630 // Sources are src and dest and size if not constant.
631 if (source->gtOper == GT_IND)
633 assert(source->isContained());
634 srcAddrOrFill = source->gtGetOp1();
635 assert(!srcAddrOrFill->isContained());
636 sourceInfo = getLocationInfo(srcAddrOrFill);
639 if (blkNode->OperGet() == GT_STORE_OBJ)
642 // We don't need to materialize the struct size but we still need
643 // a temporary register to perform the sequence of loads and stores.
644 internalIntCount = 1;
646 if (size >= 2 * REGSIZE_BYTES)
648 // We will use ldp/stp to reduce code size and improve performance
649 // so we need to reserve an extra internal register
653 // We can't use the special Write Barrier registers, so exclude them from the mask
654 internalIntCandidates = RBM_ALLINT & ~(RBM_WRITE_BARRIER_DST_BYREF | RBM_WRITE_BARRIER_SRC_BYREF);
656 // If we have a dest address we want it in RBM_WRITE_BARRIER_DST_BYREF.
657 dstAddrRegMask = RBM_WRITE_BARRIER_DST_BYREF;
659 // If we have a source address we want it in REG_WRITE_BARRIER_SRC_BYREF.
660 // Otherwise, if it is a local, codegen will put its address in REG_WRITE_BARRIER_SRC_BYREF,
661 // which is killed by a StoreObj (and thus needn't be reserved).
662 if (srcAddrOrFill != nullptr)
664 sourceRegMask = RBM_WRITE_BARRIER_SRC_BYREF;
670 if (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll)
672 // In case of a CpBlk with a constant size and less than CPBLK_UNROLL_LIMIT size
673 // we should unroll the loop to improve CQ.
674 // For reference see the code in lsraxarch.cpp.
676 internalIntCount = 1;
677 internalIntCandidates = RBM_ALLINT;
679 #ifdef _TARGET_ARM64_
680 if (size >= 2 * REGSIZE_BYTES)
682 // We will use ldp/stp to reduce code size and improve performance
683 // so we need to reserve an extra internal register
686 #endif // _TARGET_ARM64_
690 assert(blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper);
691 dstAddrRegMask = RBM_ARG_0;
692 // The srcAddr goes in arg1.
693 if (srcAddrOrFill != nullptr)
695 sourceRegMask = RBM_ARG_1;
697 blkSizeRegMask = RBM_ARG_2;
701 if (dstAddrInfo != nullptr)
703 if (dstAddrRegMask != RBM_NONE)
705 dstAddrInfo->info.setSrcCandidates(this, dstAddrRegMask);
707 useList.Append(dstAddrInfo);
709 if (sourceRegMask != RBM_NONE)
711 if (sourceInfo != nullptr)
713 sourceInfo->info.setSrcCandidates(this, sourceRegMask);
717 // This is a local source; we'll use a temp register for its address.
718 internalIntCandidates |= sourceRegMask;
722 if (sourceInfo != nullptr)
724 useList.Add(sourceInfo, blkNode->IsReverseOp());
727 if (blkNode->OperIs(GT_STORE_DYN_BLK))
729 // The block size argument is a third argument to GT_STORE_DYN_BLK
732 GenTree* blockSize = blkNode->AsDynBlk()->gtDynamicSize;
733 sizeInfo = getLocationInfo(blockSize);
734 useList.Add(sizeInfo, blkNode->AsDynBlk()->gtEvalSizeFirst);
737 if (blkSizeRegMask != RBM_NONE)
741 // Reserve a temp register for the block size argument.
742 internalIntCandidates |= blkSizeRegMask;
747 // The block size argument is a third argument to GT_STORE_DYN_BLK
748 assert((blkNode->gtOper == GT_STORE_DYN_BLK) && (sizeInfo != nullptr));
749 info->setSrcCount(3);
750 sizeInfo->info.setSrcCandidates(this, blkSizeRegMask);
753 if (internalIntCount != 0)
755 info->internalIntCount = internalIntCount;
756 info->setInternalCandidates(this, internalIntCandidates);
760 #endif // _TARGET_ARMARCH_
762 #endif // !LEGACY_BACKEND