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.
4
5/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7XX XX
8XX Register Requirements for ARM and ARM64 common code XX
9XX XX
10XX This encapsulates common logic for setting register requirements for XX
11XX the ARM and ARM64 architectures. XX
12XX XX
13XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
14XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
15*/
16
17#include "jitpch.h"
18#ifdef _MSC_VER
19#pragma hdrstop
20#endif
21
22#ifdef _TARGET_ARMARCH_ // This file is ONLY used for ARM and ARM64 architectures
23
24#include "jit.h"
25#include "sideeffects.h"
26#include "lower.h"
27#include "lsra.h"
28
29//------------------------------------------------------------------------
30// BuildIndir: Specify register requirements for address expression
31// of an indirection operation.
32//
33// Arguments:
34// indirTree - GT_IND, GT_STOREIND or block gentree node
35//
36// Return Value:
37// The number of sources consumed by this node.
38//
39int LinearScan::BuildIndir(GenTreeIndir* indirTree)
40{
41 int srcCount = 0;
42 // If this is the rhs of a block copy (i.e. non-enregisterable struct),
43 // it has no register requirements.
44 if (indirTree->TypeGet() == TYP_STRUCT)
45 {
46 return srcCount;
47 }
48
49 bool isStore = (indirTree->gtOper == GT_STOREIND);
50
51 GenTree* addr = indirTree->Addr();
52 GenTree* index = nullptr;
53 int cns = 0;
54
55#ifdef _TARGET_ARM_
56 // Unaligned loads/stores for floating point values must first be loaded into integer register(s)
57 if (indirTree->gtFlags & GTF_IND_UNALIGNED)
58 {
59 var_types type = TYP_UNDEF;
60 if (indirTree->OperGet() == GT_STOREIND)
61 {
62 type = indirTree->AsStoreInd()->Data()->TypeGet();
63 }
64 else if (indirTree->OperGet() == GT_IND)
65 {
66 type = indirTree->TypeGet();
67 }
68
69 if (type == TYP_FLOAT)
70 {
71 buildInternalIntRegisterDefForNode(indirTree);
72 }
73 else if (type == TYP_DOUBLE)
74 {
75 buildInternalIntRegisterDefForNode(indirTree);
76 buildInternalIntRegisterDefForNode(indirTree);
77 }
78 }
79#endif
80
81 if (addr->isContained())
82 {
83 assert(addr->OperGet() == GT_LEA);
84 GenTreeAddrMode* lea = addr->AsAddrMode();
85 index = lea->Index();
86 cns = lea->Offset();
87
88 // On ARM we may need a single internal register
89 // (when both conditions are true then we still only need a single internal register)
90 if ((index != nullptr) && (cns != 0))
91 {
92 // ARM does not support both Index and offset so we need an internal register
93 buildInternalIntRegisterDefForNode(indirTree);
94 }
95 else if (!emitter::emitIns_valid_imm_for_ldst_offset(cns, emitTypeSize(indirTree)))
96 {
97 // This offset can't be contained in the ldr/str instruction, so we need an internal register
98 buildInternalIntRegisterDefForNode(indirTree);
99 }
100 }
101
102#ifdef FEATURE_SIMD
103 if (indirTree->TypeGet() == TYP_SIMD12)
104 {
105 // If indirTree is of TYP_SIMD12, addr is not contained. See comment in LowerIndir().
106 assert(!addr->isContained());
107
108 // Vector3 is read/written as two reads/writes: 8 byte and 4 byte.
109 // To assemble the vector properly we would need an additional int register
110 buildInternalIntRegisterDefForNode(indirTree);
111 }
112#endif // FEATURE_SIMD
113
114 srcCount = BuildIndirUses(indirTree);
115 buildInternalRegisterUses();
116
117 if (indirTree->gtOper != GT_STOREIND)
118 {
119 BuildDef(indirTree);
120 }
121 return srcCount;
122}
123
124//------------------------------------------------------------------------
125// BuildCall: Set the NodeInfo for a call.
126//
127// Arguments:
128// call - The call node of interest
129//
130// Return Value:
131// The number of sources consumed by this node.
132//
133int LinearScan::BuildCall(GenTreeCall* call)
134{
135 bool hasMultiRegRetVal = false;
136 ReturnTypeDesc* retTypeDesc = nullptr;
137 regMaskTP dstCandidates = RBM_NONE;
138
139 int srcCount = 0;
140 int dstCount = 0;
141 if (call->TypeGet() != TYP_VOID)
142 {
143 hasMultiRegRetVal = call->HasMultiRegRetVal();
144 if (hasMultiRegRetVal)
145 {
146 // dst count = number of registers in which the value is returned by call
147 retTypeDesc = call->GetReturnTypeDesc();
148 dstCount = retTypeDesc->GetReturnRegCount();
149 }
150 else
151 {
152 dstCount = 1;
153 }
154 }
155
156 GenTree* ctrlExpr = call->gtControlExpr;
157 regMaskTP ctrlExprCandidates = RBM_NONE;
158 if (call->gtCallType == CT_INDIRECT)
159 {
160 // either gtControlExpr != null or gtCallAddr != null.
161 // Both cannot be non-null at the same time.
162 assert(ctrlExpr == nullptr);
163 assert(call->gtCallAddr != nullptr);
164 ctrlExpr = call->gtCallAddr;
165 }
166
167 // set reg requirements on call target represented as control sequence.
168 if (ctrlExpr != nullptr)
169 {
170 // we should never see a gtControlExpr whose type is void.
171 assert(ctrlExpr->TypeGet() != TYP_VOID);
172
173 // In case of fast tail implemented as jmp, make sure that gtControlExpr is
174 // computed into a register.
175 if (call->IsFastTailCall())
176 {
177 // Fast tail call - make sure that call target is always computed in R12(ARM32)/IP0(ARM64)
178 // so that epilog sequence can generate "br xip0/r12" to achieve fast tail call.
179 ctrlExprCandidates = RBM_FASTTAILCALL_TARGET;
180 }
181 }
182#ifdef _TARGET_ARM_
183 else
184 {
185 buildInternalIntRegisterDefForNode(call);
186 }
187
188 if (call->NeedsNullCheck())
189 {
190 buildInternalIntRegisterDefForNode(call);
191 }
192
193#endif // _TARGET_ARM_
194
195 RegisterType registerType = call->TypeGet();
196
197// Set destination candidates for return value of the call.
198
199#ifdef _TARGET_ARM_
200 if (call->IsHelperCall(compiler, CORINFO_HELP_INIT_PINVOKE_FRAME))
201 {
202 // The ARM CORINFO_HELP_INIT_PINVOKE_FRAME helper uses a custom calling convention that returns with
203 // TCB in REG_PINVOKE_TCB. fgMorphCall() sets the correct argument registers.
204 dstCandidates = RBM_PINVOKE_TCB;
205 }
206 else
207#endif // _TARGET_ARM_
208 if (hasMultiRegRetVal)
209 {
210 assert(retTypeDesc != nullptr);
211 dstCandidates = retTypeDesc->GetABIReturnRegs();
212 }
213 else if (varTypeIsFloating(registerType))
214 {
215 dstCandidates = RBM_FLOATRET;
216 }
217 else if (registerType == TYP_LONG)
218 {
219 dstCandidates = RBM_LNGRET;
220 }
221 else
222 {
223 dstCandidates = RBM_INTRET;
224 }
225
226 // First, count reg args
227 // Each register argument corresponds to one source.
228 bool callHasFloatRegArgs = false;
229
230 for (GenTree* list = call->gtCallLateArgs; list; list = list->MoveNext())
231 {
232 assert(list->OperIsList());
233
234 GenTree* argNode = list->Current();
235
236#ifdef DEBUG
237 // During Build, we only use the ArgTabEntry for validation,
238 // as getting it is rather expensive.
239 fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, argNode);
240 regNumber argReg = curArgTabEntry->regNum;
241 assert(curArgTabEntry);
242#endif
243
244 if (argNode->gtOper == GT_PUTARG_STK)
245 {
246 // late arg that is not passed in a register
247 assert(curArgTabEntry->regNum == REG_STK);
248 // These should never be contained.
249 assert(!argNode->isContained());
250 continue;
251 }
252
253 // A GT_FIELD_LIST has a TYP_VOID, but is used to represent a multireg struct
254 if (argNode->OperGet() == GT_FIELD_LIST)
255 {
256 assert(argNode->isContained());
257
258 // There could be up to 2-4 PUTARG_REGs in the list (3 or 4 can only occur for HFAs)
259 for (GenTreeFieldList* entry = argNode->AsFieldList(); entry != nullptr; entry = entry->Rest())
260 {
261#ifdef DEBUG
262 assert(entry->Current()->OperIs(GT_PUTARG_REG));
263 assert(entry->Current()->gtRegNum == argReg);
264 // Update argReg for the next putarg_reg (if any)
265 argReg = genRegArgNext(argReg);
266
267#if defined(_TARGET_ARM_)
268 // A double register is modelled as an even-numbered single one
269 if (entry->Current()->TypeGet() == TYP_DOUBLE)
270 {
271 argReg = genRegArgNext(argReg);
272 }
273#endif // _TARGET_ARM_
274#endif
275 BuildUse(entry->Current(), genRegMask(entry->Current()->gtRegNum));
276 srcCount++;
277 }
278 }
279#if FEATURE_ARG_SPLIT
280 else if (argNode->OperGet() == GT_PUTARG_SPLIT)
281 {
282 unsigned regCount = argNode->AsPutArgSplit()->gtNumRegs;
283 assert(regCount == curArgTabEntry->numRegs);
284 for (unsigned int i = 0; i < regCount; i++)
285 {
286 BuildUse(argNode, genRegMask(argNode->AsPutArgSplit()->GetRegNumByIdx(i)), i);
287 }
288 srcCount += regCount;
289 }
290#endif // FEATURE_ARG_SPLIT
291 else
292 {
293 assert(argNode->OperIs(GT_PUTARG_REG));
294 assert(argNode->gtRegNum == argReg);
295 HandleFloatVarArgs(call, argNode, &callHasFloatRegArgs);
296#ifdef _TARGET_ARM_
297 // The `double` types have been transformed to `long` on armel,
298 // while the actual long types have been decomposed.
299 // On ARM we may have bitcasts from DOUBLE to LONG.
300 if (argNode->TypeGet() == TYP_LONG)
301 {
302 assert(argNode->IsMultiRegNode());
303 BuildUse(argNode, genRegMask(argNode->gtRegNum), 0);
304 BuildUse(argNode, genRegMask(genRegArgNext(argNode->gtRegNum)), 1);
305 srcCount += 2;
306 }
307 else
308#endif // _TARGET_ARM_
309 {
310 BuildUse(argNode, genRegMask(argNode->gtRegNum));
311 srcCount++;
312 }
313 }
314 }
315
316 // Now, count stack args
317 // Note that these need to be computed into a register, but then
318 // they're just stored to the stack - so the reg doesn't
319 // need to remain live until the call. In fact, it must not
320 // because the code generator doesn't actually consider it live,
321 // so it can't be spilled.
322
323 GenTree* args = call->gtCallArgs;
324 while (args)
325 {
326 GenTree* arg = args->gtGetOp1();
327
328 // Skip arguments that have been moved to the Late Arg list
329 if (!(args->gtFlags & GTF_LATE_ARG))
330 {
331#ifdef DEBUG
332 fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, arg);
333 assert(curArgTabEntry);
334#endif
335#if FEATURE_ARG_SPLIT
336 // PUTARG_SPLIT nodes must be in the gtCallLateArgs list, since they
337 // define registers used by the call.
338 assert(arg->OperGet() != GT_PUTARG_SPLIT);
339#endif // FEATURE_ARG_SPLIT
340 if (arg->gtOper == GT_PUTARG_STK)
341 {
342 assert(curArgTabEntry->regNum == REG_STK);
343 }
344 else
345 {
346 assert(!arg->IsValue() || arg->IsUnusedValue());
347 }
348 }
349 args = args->gtGetOp2();
350 }
351
352 // If it is a fast tail call, it is already preferenced to use IP0.
353 // Therefore, no need set src candidates on call tgt again.
354 if (call->IsVarargs() && callHasFloatRegArgs && !call->IsFastTailCall() && (ctrlExpr != nullptr))
355 {
356 NYI_ARM("float reg varargs");
357
358 // Don't assign the call target to any of the argument registers because
359 // we will use them to also pass floating point arguments as required
360 // by Arm64 ABI.
361 ctrlExprCandidates = allRegs(TYP_INT) & ~(RBM_ARG_REGS);
362 }
363
364 if (ctrlExpr != nullptr)
365 {
366 BuildUse(ctrlExpr, ctrlExprCandidates);
367 srcCount++;
368 }
369
370 buildInternalRegisterUses();
371
372 // Now generate defs and kills.
373 regMaskTP killMask = getKillSetForCall(call);
374 BuildDefsWithKills(call, dstCount, dstCandidates, killMask);
375 return srcCount;
376}
377
378//------------------------------------------------------------------------
379// BuildPutArgStk: Set the NodeInfo for a GT_PUTARG_STK node
380//
381// Arguments:
382// argNode - a GT_PUTARG_STK node
383//
384// Return Value:
385// The number of sources consumed by this node.
386//
387// Notes:
388// Set the child node(s) to be contained when we have a multireg arg
389//
390int LinearScan::BuildPutArgStk(GenTreePutArgStk* argNode)
391{
392 assert(argNode->gtOper == GT_PUTARG_STK);
393
394 GenTree* putArgChild = argNode->gtGetOp1();
395
396 int srcCount = 0;
397
398 // Do we have a TYP_STRUCT argument (or a GT_FIELD_LIST), if so it must be a multireg pass-by-value struct
399 if ((putArgChild->TypeGet() == TYP_STRUCT) || (putArgChild->OperGet() == GT_FIELD_LIST))
400 {
401 // We will use store instructions that each write a register sized value
402
403 if (putArgChild->OperGet() == GT_FIELD_LIST)
404 {
405 assert(putArgChild->isContained());
406 // We consume all of the items in the GT_FIELD_LIST
407 for (GenTreeFieldList* current = putArgChild->AsFieldList(); current != nullptr; current = current->Rest())
408 {
409 BuildUse(current->Current());
410 srcCount++;
411 }
412 }
413 else
414 {
415 // We can use a ldp/stp sequence so we need two internal registers for ARM64; one for ARM.
416 buildInternalIntRegisterDefForNode(argNode);
417#ifdef _TARGET_ARM64_
418 buildInternalIntRegisterDefForNode(argNode);
419#endif // _TARGET_ARM64_
420
421 if (putArgChild->OperGet() == GT_OBJ)
422 {
423 assert(putArgChild->isContained());
424 GenTree* objChild = putArgChild->gtGetOp1();
425 if (objChild->OperGet() == GT_LCL_VAR_ADDR)
426 {
427 // We will generate all of the code for the GT_PUTARG_STK, the GT_OBJ and the GT_LCL_VAR_ADDR
428 // as one contained operation, and there are no source registers.
429 //
430 assert(objChild->isContained());
431 }
432 else
433 {
434 // We will generate all of the code for the GT_PUTARG_STK and its child node
435 // as one contained operation
436 //
437 srcCount = BuildOperandUses(objChild);
438 }
439 }
440 else
441 {
442 // No source registers.
443 putArgChild->OperIs(GT_LCL_VAR);
444 }
445 }
446 }
447 else
448 {
449 assert(!putArgChild->isContained());
450 srcCount = BuildOperandUses(putArgChild);
451 }
452 buildInternalRegisterUses();
453 return srcCount;
454}
455
456#if FEATURE_ARG_SPLIT
457//------------------------------------------------------------------------
458// BuildPutArgSplit: Set the NodeInfo for a GT_PUTARG_SPLIT node
459//
460// Arguments:
461// argNode - a GT_PUTARG_SPLIT node
462//
463// Return Value:
464// The number of sources consumed by this node.
465//
466// Notes:
467// Set the child node(s) to be contained
468//
469int LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode)
470{
471 int srcCount = 0;
472 assert(argNode->gtOper == GT_PUTARG_SPLIT);
473
474 GenTree* putArgChild = argNode->gtGetOp1();
475
476 // Registers for split argument corresponds to source
477 int dstCount = argNode->gtNumRegs;
478
479 regNumber argReg = argNode->gtRegNum;
480 regMaskTP argMask = RBM_NONE;
481 for (unsigned i = 0; i < argNode->gtNumRegs; i++)
482 {
483 regNumber thisArgReg = (regNumber)((unsigned)argReg + i);
484 argMask |= genRegMask(thisArgReg);
485 argNode->SetRegNumByIdx(thisArgReg, i);
486 }
487
488 if (putArgChild->OperGet() == GT_FIELD_LIST)
489 {
490 // Generated code:
491 // 1. Consume all of the items in the GT_FIELD_LIST (source)
492 // 2. Store to target slot and move to target registers (destination) from source
493 //
494 unsigned sourceRegCount = 0;
495
496 // To avoid redundant moves, have the argument operand computed in the
497 // register in which the argument is passed to the call.
498
499 for (GenTreeFieldList* fieldListPtr = putArgChild->AsFieldList(); fieldListPtr != nullptr;
500 fieldListPtr = fieldListPtr->Rest())
501 {
502 GenTree* node = fieldListPtr->gtGetOp1();
503 assert(!node->isContained());
504 // The only multi-reg nodes we should see are OperIsMultiRegOp()
505 unsigned currentRegCount;
506#ifdef _TARGET_ARM_
507 if (node->OperIsMultiRegOp())
508 {
509 currentRegCount = node->AsMultiRegOp()->GetRegCount();
510 }
511 else
512#endif // _TARGET_ARM
513 {
514 assert(!node->IsMultiRegNode());
515 currentRegCount = 1;
516 }
517 // Consume all the registers, setting the appropriate register mask for the ones that
518 // go into registers.
519 for (unsigned regIndex = 0; regIndex < currentRegCount; regIndex++)
520 {
521 regMaskTP sourceMask = RBM_NONE;
522 if (sourceRegCount < argNode->gtNumRegs)
523 {
524 sourceMask = genRegMask((regNumber)((unsigned)argReg + sourceRegCount));
525 }
526 sourceRegCount++;
527 BuildUse(node, sourceMask, regIndex);
528 }
529 }
530 srcCount += sourceRegCount;
531 assert(putArgChild->isContained());
532 }
533 else
534 {
535 assert(putArgChild->TypeGet() == TYP_STRUCT);
536 assert(putArgChild->OperGet() == GT_OBJ);
537
538 // We can use a ldr/str sequence so we need an internal register
539 buildInternalIntRegisterDefForNode(argNode, allRegs(TYP_INT) & ~argMask);
540
541 GenTree* objChild = putArgChild->gtGetOp1();
542 if (objChild->OperGet() == GT_LCL_VAR_ADDR)
543 {
544 // We will generate all of the code for the GT_PUTARG_SPLIT, the GT_OBJ and the GT_LCL_VAR_ADDR
545 // as one contained operation
546 //
547 assert(objChild->isContained());
548 }
549 else
550 {
551 srcCount = BuildIndirUses(putArgChild->AsIndir());
552 }
553 assert(putArgChild->isContained());
554 }
555 buildInternalRegisterUses();
556 BuildDefs(argNode, dstCount, argMask);
557 return srcCount;
558}
559#endif // FEATURE_ARG_SPLIT
560
561//------------------------------------------------------------------------
562// BuildBlockStore: Set the NodeInfo for a block store.
563//
564// Arguments:
565// blkNode - The block store node of interest
566//
567// Return Value:
568// The number of sources consumed by this node.
569//
570int LinearScan::BuildBlockStore(GenTreeBlk* blkNode)
571{
572 GenTree* dstAddr = blkNode->Addr();
573 unsigned size = blkNode->gtBlkSize;
574 GenTree* source = blkNode->Data();
575 int srcCount = 0;
576
577 GenTree* srcAddrOrFill = nullptr;
578 bool isInitBlk = blkNode->OperIsInitBlkOp();
579
580 regMaskTP dstAddrRegMask = RBM_NONE;
581 regMaskTP sourceRegMask = RBM_NONE;
582 regMaskTP blkSizeRegMask = RBM_NONE;
583 regMaskTP internalIntCandidates = RBM_NONE;
584
585 if (isInitBlk)
586 {
587 GenTree* initVal = source;
588 if (initVal->OperIsInitVal())
589 {
590 assert(initVal->isContained());
591 initVal = initVal->gtGetOp1();
592 }
593 srcAddrOrFill = initVal;
594
595 if (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll)
596 {
597 // TODO-ARM-CQ: Currently we generate a helper call for every
598 // initblk we encounter. Later on we should implement loop unrolling
599 // code sequences to improve CQ.
600 // For reference see the code in lsraxarch.cpp.
601 NYI_ARM("initblk loop unrolling is currently not implemented.");
602 }
603 else
604 {
605 assert(blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper);
606 assert(!initVal->isContained());
607 // The helper follows the regular ABI.
608 dstAddrRegMask = RBM_ARG_0;
609 sourceRegMask = RBM_ARG_1;
610 blkSizeRegMask = RBM_ARG_2;
611 }
612 }
613 else
614 {
615 // CopyObj or CopyBlk
616 // Sources are src and dest and size if not constant.
617 if (source->gtOper == GT_IND)
618 {
619 assert(source->isContained());
620 srcAddrOrFill = source->gtGetOp1();
621 assert(!srcAddrOrFill->isContained());
622 }
623 if (blkNode->OperGet() == GT_STORE_OBJ)
624 {
625 // CopyObj
626 // We don't need to materialize the struct size but we still need
627 // a temporary register to perform the sequence of loads and stores.
628 // We can't use the special Write Barrier registers, so exclude them from the mask
629 internalIntCandidates = allRegs(TYP_INT) & ~(RBM_WRITE_BARRIER_DST_BYREF | RBM_WRITE_BARRIER_SRC_BYREF);
630 buildInternalIntRegisterDefForNode(blkNode, internalIntCandidates);
631
632 if (size >= 2 * REGSIZE_BYTES)
633 {
634 // We will use ldp/stp to reduce code size and improve performance
635 // so we need to reserve an extra internal register
636 buildInternalIntRegisterDefForNode(blkNode, internalIntCandidates);
637 }
638
639 // If we have a dest address we want it in RBM_WRITE_BARRIER_DST_BYREF.
640 dstAddrRegMask = RBM_WRITE_BARRIER_DST_BYREF;
641
642 // If we have a source address we want it in REG_WRITE_BARRIER_SRC_BYREF.
643 // Otherwise, if it is a local, codegen will put its address in REG_WRITE_BARRIER_SRC_BYREF,
644 // which is killed by a StoreObj (and thus needn't be reserved).
645 if (srcAddrOrFill != nullptr)
646 {
647 sourceRegMask = RBM_WRITE_BARRIER_SRC_BYREF;
648 }
649 }
650 else
651 {
652 // CopyBlk
653 if (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll)
654 {
655 // In case of a CpBlk with a constant size and less than CPBLK_UNROLL_LIMIT size
656 // we should unroll the loop to improve CQ.
657 // For reference see the code in lsraxarch.cpp.
658
659 buildInternalIntRegisterDefForNode(blkNode);
660
661#ifdef _TARGET_ARM64_
662 if (size >= 2 * REGSIZE_BYTES)
663 {
664 // We will use ldp/stp to reduce code size and improve performance
665 // so we need to reserve an extra internal register
666 buildInternalIntRegisterDefForNode(blkNode);
667 }
668#endif // _TARGET_ARM64_
669 }
670 else
671 {
672 assert(blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper);
673 dstAddrRegMask = RBM_ARG_0;
674 // The srcAddr goes in arg1.
675 if (srcAddrOrFill != nullptr)
676 {
677 sourceRegMask = RBM_ARG_1;
678 }
679 blkSizeRegMask = RBM_ARG_2;
680 }
681 }
682 }
683
684 if ((size != 0) && (blkSizeRegMask != RBM_NONE))
685 {
686 // Reserve a temp register for the block size argument.
687 buildInternalIntRegisterDefForNode(blkNode, blkSizeRegMask);
688 }
689
690 if (!dstAddr->isContained() && !blkNode->IsReverseOp())
691 {
692 srcCount++;
693 BuildUse(dstAddr, dstAddrRegMask);
694 }
695 if ((srcAddrOrFill != nullptr) && !srcAddrOrFill->isContained())
696 {
697 srcCount++;
698 BuildUse(srcAddrOrFill, sourceRegMask);
699 }
700 if (!dstAddr->isContained() && blkNode->IsReverseOp())
701 {
702 srcCount++;
703 BuildUse(dstAddr, dstAddrRegMask);
704 }
705
706 if (size == 0)
707 {
708 assert(blkNode->OperIs(GT_STORE_DYN_BLK));
709 // The block size argument is a third argument to GT_STORE_DYN_BLK
710 srcCount++;
711 GenTree* blockSize = blkNode->AsDynBlk()->gtDynamicSize;
712 BuildUse(blockSize, blkSizeRegMask);
713 }
714
715 buildInternalRegisterUses();
716 regMaskTP killMask = getKillSetForBlockStore(blkNode);
717 BuildDefsWithKills(blkNode, 0, RBM_NONE, killMask);
718 return srcCount;
719}
720
721//------------------------------------------------------------------------
722// BuildCast: Set the NodeInfo for a GT_CAST.
723//
724// Arguments:
725// cast - The GT_CAST node
726//
727// Return Value:
728// The number of sources consumed by this node.
729//
730int LinearScan::BuildCast(GenTreeCast* cast)
731{
732 GenTree* src = cast->gtGetOp1();
733
734 const var_types srcType = genActualType(src->TypeGet());
735 const var_types castType = cast->gtCastType;
736
737#ifdef _TARGET_ARM_
738 assert(!varTypeIsLong(srcType) || (src->OperIs(GT_LONG) && src->isContained()));
739
740 // Floating point to integer casts requires a temporary register.
741 if (varTypeIsFloating(srcType) && !varTypeIsFloating(castType))
742 {
743 buildInternalFloatRegisterDefForNode(cast, RBM_ALLFLOAT);
744 setInternalRegsDelayFree = true;
745 }
746#else
747 // Overflow checking cast from TYP_LONG to TYP_INT requires a temporary register to
748 // store the min and max immediate values that cannot be encoded in the CMP instruction.
749 if (cast->gtOverflow() && varTypeIsLong(srcType) && !cast->IsUnsigned() && (castType == TYP_INT))
750 {
751 buildInternalIntRegisterDefForNode(cast);
752 }
753#endif
754
755 int srcCount = BuildOperandUses(src);
756 buildInternalRegisterUses();
757 BuildDef(cast);
758 return srcCount;
759}
760
761#endif // _TARGET_ARMARCH_
762