Fix reading Time zone rules using Julian days (#17672)
[platform/upstream/coreclr.git] / src / jit / lowerarmarch.cpp
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
6 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7 XX                                                                           XX
8 XX             Lowering for ARM and ARM64 common code                        XX
9 XX                                                                           XX
10 XX  This encapsulates common logic for lowering trees for the ARM and ARM64  XX
11 XX  architectures.  For a more detailed view of what is lowering, please     XX
12 XX  take a look at Lower.cpp                                                 XX
13 XX                                                                           XX
14 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
15 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
16 */
17
18 #include "jitpch.h"
19 #ifdef _MSC_VER
20 #pragma hdrstop
21 #endif
22
23 #ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator
24
25 #ifdef _TARGET_ARMARCH_ // This file is ONLY used for ARM and ARM64 architectures
26
27 #include "jit.h"
28 #include "sideeffects.h"
29 #include "lower.h"
30 #include "lsra.h"
31
32 #ifdef FEATURE_HW_INTRINSICS
33 #include "hwintrinsicArm64.h"
34 #endif
35
36 //------------------------------------------------------------------------
37 // IsCallTargetInRange: Can a call target address be encoded in-place?
38 //
39 // Return Value:
40 //    True if the addr fits into the range.
41 //
42 bool Lowering::IsCallTargetInRange(void* addr)
43 {
44 #ifdef _TARGET_ARM64_
45     // TODO-ARM64-CQ:  This is a workaround to unblock the JIT from getting calls working.
46     // Currently, we'll be generating calls using blr and manually loading an absolute
47     // call target in a register using a sequence of load immediate instructions.
48     //
49     // As you can expect, this is inefficient and it's not the recommended way as per the
50     // ARM64 ABI Manual but will get us getting things done for now.
51     // The work to get this right would be to implement PC-relative calls, the bl instruction
52     // can only address things -128 + 128MB away, so this will require getting some additional
53     // code to get jump thunks working.
54     return true;
55 #elif defined(_TARGET_ARM_)
56     return comp->codeGen->validImmForBL((ssize_t)addr);
57 #endif
58 }
59
60 //------------------------------------------------------------------------
61 // IsContainableImmed: Is an immediate encodable in-place?
62 //
63 // Return Value:
64 //    True if the immediate can be folded into an instruction,
65 //    for example small enough and non-relocatable.
66 //
67 // TODO-CQ: we can contain a floating point 0.0 constant in a compare instruction
68 // (vcmp on arm, fcmp on arm64).
69 //
70 bool Lowering::IsContainableImmed(GenTree* parentNode, GenTree* childNode)
71 {
72     if (!varTypeIsFloating(parentNode->TypeGet()))
73     {
74         // Make sure we have an actual immediate
75         if (!childNode->IsCnsIntOrI())
76             return false;
77         if (childNode->IsIconHandle() && comp->opts.compReloc)
78             return false;
79
80         ssize_t  immVal = childNode->gtIntCon.gtIconVal;
81         emitAttr attr   = emitActualTypeSize(childNode->TypeGet());
82         emitAttr size   = EA_SIZE(attr);
83 #ifdef _TARGET_ARM_
84         insFlags flags = parentNode->gtSetFlags() ? INS_FLAGS_SET : INS_FLAGS_DONT_CARE;
85 #endif
86
87         switch (parentNode->OperGet())
88         {
89             case GT_ADD:
90             case GT_SUB:
91 #ifdef _TARGET_ARM64_
92             case GT_CMPXCHG:
93             case GT_LOCKADD:
94             case GT_XADD:
95                 return emitter::emitIns_valid_imm_for_add(immVal, size);
96 #elif defined(_TARGET_ARM_)
97                 return emitter::emitIns_valid_imm_for_add(immVal, flags);
98 #endif
99                 break;
100
101 #ifdef _TARGET_ARM64_
102             case GT_EQ:
103             case GT_NE:
104             case GT_LT:
105             case GT_LE:
106             case GT_GE:
107             case GT_GT:
108                 return emitter::emitIns_valid_imm_for_cmp(immVal, size);
109             case GT_AND:
110             case GT_OR:
111             case GT_XOR:
112             case GT_TEST_EQ:
113             case GT_TEST_NE:
114                 return emitter::emitIns_valid_imm_for_alu(immVal, size);
115             case GT_JCMP:
116                 assert(((parentNode->gtFlags & GTF_JCMP_TST) == 0) ? (immVal == 0) : isPow2(immVal));
117                 return true;
118 #elif defined(_TARGET_ARM_)
119             case GT_EQ:
120             case GT_NE:
121             case GT_LT:
122             case GT_LE:
123             case GT_GE:
124             case GT_GT:
125             case GT_CMP:
126             case GT_AND:
127             case GT_OR:
128             case GT_XOR:
129                 return emitter::emitIns_valid_imm_for_alu(immVal);
130 #endif // _TARGET_ARM_
131
132 #ifdef _TARGET_ARM64_
133             case GT_STORE_LCL_FLD:
134             case GT_STORE_LCL_VAR:
135                 if (immVal == 0)
136                     return true;
137                 break;
138 #endif
139
140             default:
141                 break;
142         }
143     }
144
145     return false;
146 }
147
148 //------------------------------------------------------------------------
149 // LowerStoreLoc: Lower a store of a lclVar
150 //
151 // Arguments:
152 //    storeLoc - the local store (GT_STORE_LCL_FLD or GT_STORE_LCL_VAR)
153 //
154 // Notes:
155 //    This involves:
156 //    - Widening operations of unsigneds.
157 //
158 void Lowering::LowerStoreLoc(GenTreeLclVarCommon* storeLoc)
159 {
160     // Try to widen the ops if they are going into a local var.
161     GenTree* op1 = storeLoc->gtGetOp1();
162     if ((storeLoc->gtOper == GT_STORE_LCL_VAR) && (op1->gtOper == GT_CNS_INT))
163     {
164         GenTreeIntCon* con    = op1->AsIntCon();
165         ssize_t        ival   = con->gtIconVal;
166         unsigned       varNum = storeLoc->gtLclNum;
167         LclVarDsc*     varDsc = comp->lvaTable + varNum;
168
169         if (varDsc->lvIsSIMDType())
170         {
171             noway_assert(storeLoc->gtType != TYP_STRUCT);
172         }
173         unsigned size = genTypeSize(storeLoc);
174         // If we are storing a constant into a local variable
175         // we extend the size of the store here
176         if ((size < 4) && !varTypeIsStruct(varDsc))
177         {
178             if (!varTypeIsUnsigned(varDsc))
179             {
180                 if (genTypeSize(storeLoc) == 1)
181                 {
182                     if ((ival & 0x7f) != ival)
183                     {
184                         ival = ival | 0xffffff00;
185                     }
186                 }
187                 else
188                 {
189                     assert(genTypeSize(storeLoc) == 2);
190                     if ((ival & 0x7fff) != ival)
191                     {
192                         ival = ival | 0xffff0000;
193                     }
194                 }
195             }
196
197             // A local stack slot is at least 4 bytes in size, regardless of
198             // what the local var is typed as, so auto-promote it here
199             // unless it is a field of a promoted struct
200             // TODO-CQ: if the field is promoted shouldn't we also be able to do this?
201             if (!varDsc->lvIsStructField)
202             {
203                 storeLoc->gtType = TYP_INT;
204                 con->SetIconValue(ival);
205             }
206         }
207     }
208     ContainCheckStoreLoc(storeLoc);
209 }
210
211 //------------------------------------------------------------------------
212 // LowerStoreIndir: Determine addressing mode for an indirection, and whether operands are contained.
213 //
214 // Arguments:
215 //    node       - The indirect store node (GT_STORE_IND) of interest
216 //
217 // Return Value:
218 //    None.
219 //
220 void Lowering::LowerStoreIndir(GenTreeIndir* node)
221 {
222     ContainCheckStoreIndir(node);
223 }
224
225 //------------------------------------------------------------------------
226 // LowerBlockStore: Set block store type
227 //
228 // Arguments:
229 //    blkNode       - The block store node of interest
230 //
231 // Return Value:
232 //    None.
233 //
234 void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
235 {
236     GenTree*  dstAddr  = blkNode->Addr();
237     unsigned  size     = blkNode->gtBlkSize;
238     GenTree*  source   = blkNode->Data();
239     Compiler* compiler = comp;
240
241     // Sources are dest address and initVal or source.
242     GenTree* srcAddrOrFill = nullptr;
243     bool     isInitBlk     = blkNode->OperIsInitBlkOp();
244
245     if (!isInitBlk)
246     {
247         // CopyObj or CopyBlk
248         if ((blkNode->OperGet() == GT_STORE_OBJ) && ((blkNode->AsObj()->gtGcPtrCount == 0) || blkNode->gtBlkOpGcUnsafe))
249         {
250             blkNode->SetOper(GT_STORE_BLK);
251         }
252         if (source->gtOper == GT_IND)
253         {
254             srcAddrOrFill = blkNode->Data()->gtGetOp1();
255         }
256     }
257
258     if (isInitBlk)
259     {
260         GenTree* initVal = source;
261         if (initVal->OperIsInitVal())
262         {
263             initVal->SetContained();
264             initVal = initVal->gtGetOp1();
265         }
266         srcAddrOrFill = initVal;
267
268 #ifdef _TARGET_ARM64_
269         if ((size != 0) && (size <= INITBLK_UNROLL_LIMIT) && initVal->IsCnsIntOrI())
270         {
271             // TODO-ARM-CQ: Currently we generate a helper call for every
272             // initblk we encounter.  Later on we should implement loop unrolling
273             // code sequences to improve CQ.
274             // For reference see the code in LowerXArch.cpp.
275             NYI_ARM("initblk loop unrolling is currently not implemented.");
276
277             // The fill value of an initblk is interpreted to hold a
278             // value of (unsigned int8) however a constant of any size
279             // may practically reside on the evaluation stack. So extract
280             // the lower byte out of the initVal constant and replicate
281             // it to a larger constant whose size is sufficient to support
282             // the largest width store of the desired inline expansion.
283
284             ssize_t fill = initVal->gtIntCon.gtIconVal & 0xFF;
285             if (fill == 0)
286             {
287                 MakeSrcContained(blkNode, source);
288             }
289             else if (size < REGSIZE_BYTES)
290             {
291                 initVal->gtIntCon.gtIconVal = 0x01010101 * fill;
292             }
293             else
294             {
295                 initVal->gtIntCon.gtIconVal = 0x0101010101010101LL * fill;
296                 initVal->gtType             = TYP_LONG;
297             }
298             blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
299         }
300         else
301 #endif // _TARGET_ARM64_
302         {
303             blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper;
304         }
305     }
306     else
307     {
308         // CopyObj or CopyBlk
309         // Sources are src and dest and size if not constant.
310
311         if (blkNode->OperGet() == GT_STORE_OBJ)
312         {
313             // CopyObj
314             GenTreeObj* objNode = blkNode->AsObj();
315
316             unsigned slots = objNode->gtSlots;
317
318 #ifdef DEBUG
319             // CpObj must always have at least one GC-Pointer as a member.
320             assert(objNode->gtGcPtrCount > 0);
321
322             assert(dstAddr->gtType == TYP_BYREF || dstAddr->gtType == TYP_I_IMPL);
323
324             CORINFO_CLASS_HANDLE clsHnd    = objNode->gtClass;
325             size_t               classSize = compiler->info.compCompHnd->getClassSize(clsHnd);
326             size_t               blkSize   = roundUp(classSize, TARGET_POINTER_SIZE);
327
328             // Currently, the EE always round up a class data structure so
329             // we are not handling the case where we have a non multiple of pointer sized
330             // struct. This behavior may change in the future so in order to keeps things correct
331             // let's assert it just to be safe. Going forward we should simply
332             // handle this case.
333             assert(classSize == blkSize);
334             assert((blkSize / TARGET_POINTER_SIZE) == slots);
335             assert(objNode->HasGCPtr());
336 #endif
337
338             blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
339         }
340         else // CopyBlk
341         {
342             // In case of a CpBlk with a constant size and less than CPBLK_UNROLL_LIMIT size
343             // we should unroll the loop to improve CQ.
344             // For reference see the code in lowerxarch.cpp.
345
346             if ((size != 0) && (size <= CPBLK_UNROLL_LIMIT))
347             {
348                 blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
349             }
350             else
351             {
352                 // In case we have a constant integer this means we went beyond
353                 // CPBLK_UNROLL_LIMIT bytes of size, still we should never have the case of
354                 // any GC-Pointers in the src struct.
355                 blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper;
356             }
357         }
358         // CopyObj or CopyBlk
359         if (source->gtOper == GT_IND)
360         {
361             MakeSrcContained(blkNode, source);
362             GenTree* addr = source->AsIndir()->Addr();
363             if (!addr->OperIsLocalAddr())
364             {
365                 addr->ClearContained();
366             }
367         }
368         else if (!source->IsMultiRegCall() && !source->OperIsSIMD())
369         {
370             assert(source->IsLocal());
371             MakeSrcContained(blkNode, source);
372         }
373     }
374 }
375
376 //------------------------------------------------------------------------
377 // LowerCast: Lower GT_CAST(srcType, DstType) nodes.
378 //
379 // Arguments:
380 //    tree - GT_CAST node to be lowered
381 //
382 // Return Value:
383 //    None.
384 //
385 // Notes:
386 //    Casts from float/double to a smaller int type are transformed as follows:
387 //    GT_CAST(float/double, byte)     =   GT_CAST(GT_CAST(float/double, int32), byte)
388 //    GT_CAST(float/double, sbyte)    =   GT_CAST(GT_CAST(float/double, int32), sbyte)
389 //    GT_CAST(float/double, int16)    =   GT_CAST(GT_CAST(double/double, int32), int16)
390 //    GT_CAST(float/double, uint16)   =   GT_CAST(GT_CAST(double/double, int32), uint16)
391 //
392 //    Note that for the overflow conversions we still depend on helper calls and
393 //    don't expect to see them here.
394 //    i) GT_CAST(float/double, int type with overflow detection)
395 //
396 void Lowering::LowerCast(GenTree* tree)
397 {
398     assert(tree->OperGet() == GT_CAST);
399
400     JITDUMP("LowerCast for: ");
401     DISPNODE(tree);
402     JITDUMP("\n");
403
404     GenTree*  op1     = tree->gtOp.gtOp1;
405     var_types dstType = tree->CastToType();
406     var_types srcType = genActualType(op1->TypeGet());
407     var_types tmpType = TYP_UNDEF;
408
409     if (varTypeIsFloating(srcType))
410     {
411         noway_assert(!tree->gtOverflow());
412         assert(!varTypeIsSmall(dstType)); // fgMorphCast creates intermediate casts when converting from float to small
413                                           // int.
414     }
415
416     assert(!varTypeIsSmall(srcType));
417
418     if (tmpType != TYP_UNDEF)
419     {
420         GenTree* tmp = comp->gtNewCastNode(tmpType, op1, tree->IsUnsigned(), tmpType);
421         tmp->gtFlags |= (tree->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT));
422
423         tree->gtFlags &= ~GTF_UNSIGNED;
424         tree->gtOp.gtOp1 = tmp;
425         BlockRange().InsertAfter(op1, tmp);
426     }
427
428     // Now determine if we have operands that should be contained.
429     ContainCheckCast(tree->AsCast());
430 }
431
432 //------------------------------------------------------------------------
433 // LowerRotate: Lower GT_ROL and GT_ROR nodes.
434 //
435 // Arguments:
436 //    tree - the node to lower
437 //
438 // Return Value:
439 //    None.
440 //
441 void Lowering::LowerRotate(GenTree* tree)
442 {
443     if (tree->OperGet() == GT_ROL)
444     {
445         // There is no ROL instruction on ARM. Convert ROL into ROR.
446         GenTree* rotatedValue        = tree->gtOp.gtOp1;
447         unsigned rotatedValueBitSize = genTypeSize(rotatedValue->gtType) * 8;
448         GenTree* rotateLeftIndexNode = tree->gtOp.gtOp2;
449
450         if (rotateLeftIndexNode->IsCnsIntOrI())
451         {
452             ssize_t rotateLeftIndex                 = rotateLeftIndexNode->gtIntCon.gtIconVal;
453             ssize_t rotateRightIndex                = rotatedValueBitSize - rotateLeftIndex;
454             rotateLeftIndexNode->gtIntCon.gtIconVal = rotateRightIndex;
455         }
456         else
457         {
458             GenTree* tmp = comp->gtNewOperNode(GT_NEG, genActualType(rotateLeftIndexNode->gtType), rotateLeftIndexNode);
459             BlockRange().InsertAfter(rotateLeftIndexNode, tmp);
460             tree->gtOp.gtOp2 = tmp;
461         }
462         tree->ChangeOper(GT_ROR);
463     }
464     ContainCheckShiftRotate(tree->AsOp());
465 }
466
467 #ifdef FEATURE_SIMD
468 //----------------------------------------------------------------------------------------------
469 // Lowering::LowerSIMD: Perform containment analysis for a SIMD intrinsic node.
470 //
471 //  Arguments:
472 //     simdNode - The SIMD intrinsic node.
473 //
474 void Lowering::LowerSIMD(GenTreeSIMD* simdNode)
475 {
476     assert(simdNode->gtType != TYP_SIMD32);
477
478     if (simdNode->TypeGet() == TYP_SIMD12)
479     {
480         // GT_SIMD node requiring to produce TYP_SIMD12 in fact
481         // produces a TYP_SIMD16 result
482         simdNode->gtType = TYP_SIMD16;
483     }
484
485     ContainCheckSIMD(simdNode);
486 }
487 #endif // FEATURE_SIMD
488
489 #ifdef FEATURE_HW_INTRINSICS
490 //----------------------------------------------------------------------------------------------
491 // Lowering::LowerHWIntrinsic: Perform containment analysis for a hardware intrinsic node.
492 //
493 //  Arguments:
494 //     node - The hardware intrinsic node.
495 //
496 void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
497 {
498     auto intrinsicID   = node->gtHWIntrinsicId;
499     auto intrinsicInfo = comp->getHWIntrinsicInfo(node->gtHWIntrinsicId);
500
501     //
502     // Lower unsupported Unsigned Compare Zero intrinsics to their trivial transformations
503     //
504     // ARM64 does not support most forms of compare zero for Unsigned values
505     // This is because some are non-sensical, and the rest are trivial transformations of other operators
506     //
507     if ((intrinsicInfo.flags & HWIntrinsicInfo::LowerCmpUZero) && varTypeIsUnsigned(node->gtSIMDBaseType))
508     {
509         auto setAllVector = node->gtSIMDSize > 8 ? NI_ARM64_SIMD_SetAllVector128 : NI_ARM64_SIMD_SetAllVector64;
510
511         auto origOp1 = node->gtOp.gtOp1;
512
513         switch (intrinsicID)
514         {
515             case NI_ARM64_SIMD_GT_ZERO:
516                 // Unsigned > 0 ==> !(Unsigned == 0)
517                 node->gtOp.gtOp1 =
518                     comp->gtNewSimdHWIntrinsicNode(node->TypeGet(), node->gtOp.gtOp1, NI_ARM64_SIMD_EQ_ZERO,
519                                                    node->gtSIMDBaseType, node->gtSIMDSize);
520                 node->gtHWIntrinsicId = NI_ARM64_SIMD_BitwiseNot;
521                 BlockRange().InsertBefore(node, node->gtOp.gtOp1);
522                 break;
523             case NI_ARM64_SIMD_LE_ZERO:
524                 // Unsigned <= 0 ==> Unsigned == 0
525                 node->gtHWIntrinsicId = NI_ARM64_SIMD_EQ_ZERO;
526                 break;
527             case NI_ARM64_SIMD_GE_ZERO:
528             case NI_ARM64_SIMD_LT_ZERO:
529                 // Unsigned >= 0 ==> Always true
530                 // Unsigned < 0 ==> Always false
531                 node->gtHWIntrinsicId = setAllVector;
532                 node->gtOp.gtOp1      = comp->gtNewLconNode((intrinsicID == NI_ARM64_SIMD_GE_ZERO) ? ~0ULL : 0ULL);
533                 BlockRange().InsertBefore(node, node->gtOp.gtOp1);
534                 if ((origOp1->gtFlags & GTF_ALL_EFFECT) == 0)
535                 {
536                     BlockRange().Remove(origOp1, true);
537                 }
538                 else
539                 {
540                     origOp1->SetUnusedValue();
541                 }
542                 break;
543             default:
544                 assert(!"Unhandled LowerCmpUZero case");
545         }
546     }
547
548     ContainCheckHWIntrinsic(node);
549 }
550 #endif // FEATURE_HW_INTRINSICS
551
552 //------------------------------------------------------------------------
553 // Containment analysis
554 //------------------------------------------------------------------------
555
556 //------------------------------------------------------------------------
557 // ContainCheckCallOperands: Determine whether operands of a call should be contained.
558 //
559 // Arguments:
560 //    call       - The call node of interest
561 //
562 // Return Value:
563 //    None.
564 //
565 void Lowering::ContainCheckCallOperands(GenTreeCall* call)
566 {
567     // There are no contained operands for arm.
568 }
569
570 //------------------------------------------------------------------------
571 // ContainCheckStoreIndir: determine whether the sources of a STOREIND node should be contained.
572 //
573 // Arguments:
574 //    node - pointer to the node
575 //
576 void Lowering::ContainCheckStoreIndir(GenTreeIndir* node)
577 {
578 #ifdef _TARGET_ARM64_
579     GenTree* src = node->gtOp.gtOp2;
580     if (!varTypeIsFloating(src->TypeGet()) && src->IsIntegralConst(0))
581     {
582         // an integer zero for 'src' can be contained.
583         MakeSrcContained(node, src);
584     }
585 #endif // _TARGET_ARM64_
586     ContainCheckIndir(node);
587 }
588
589 //------------------------------------------------------------------------
590 // ContainCheckIndir: Determine whether operands of an indir should be contained.
591 //
592 // Arguments:
593 //    indirNode - The indirection node of interest
594 //
595 // Notes:
596 //    This is called for both store and load indirections.
597 //
598 // Return Value:
599 //    None.
600 //
601 void Lowering::ContainCheckIndir(GenTreeIndir* indirNode)
602 {
603     // If this is the rhs of a block copy it will be handled when we handle the store.
604     if (indirNode->TypeGet() == TYP_STRUCT)
605     {
606         return;
607     }
608
609 #ifdef FEATURE_SIMD
610     // If indirTree is of TYP_SIMD12, don't mark addr as contained
611     // so that it always get computed to a register.  This would
612     // mean codegen side logic doesn't need to handle all possible
613     // addr expressions that could be contained.
614     //
615     // TODO-ARM64-CQ: handle other addr mode expressions that could be marked
616     // as contained.
617     if (indirNode->TypeGet() == TYP_SIMD12)
618     {
619         return;
620     }
621 #endif // FEATURE_SIMD
622
623     GenTree* addr          = indirNode->Addr();
624     bool     makeContained = true;
625     if ((addr->OperGet() == GT_LEA) && IsSafeToContainMem(indirNode, addr))
626     {
627         GenTreeAddrMode* lea   = addr->AsAddrMode();
628         GenTree*         base  = lea->Base();
629         GenTree*         index = lea->Index();
630         int              cns   = lea->Offset();
631
632 #ifdef _TARGET_ARM_
633         // ARM floating-point load/store doesn't support a form similar to integer
634         // ldr Rdst, [Rbase + Roffset] with offset in a register. The only supported
635         // form is vldr Rdst, [Rbase + imm] with a more limited constraint on the imm.
636         if (lea->HasIndex() || !emitter::emitIns_valid_imm_for_vldst_offset(cns))
637         {
638             if (indirNode->OperGet() == GT_STOREIND)
639             {
640                 if (varTypeIsFloating(indirNode->AsStoreInd()->Data()))
641                 {
642                     makeContained = false;
643                 }
644             }
645             else if (indirNode->OperGet() == GT_IND)
646             {
647                 if (varTypeIsFloating(indirNode))
648                 {
649                     makeContained = false;
650                 }
651             }
652         }
653 #endif
654         if (makeContained)
655         {
656             MakeSrcContained(indirNode, addr);
657         }
658     }
659 }
660
661 //------------------------------------------------------------------------
662 // ContainCheckBinary: Determine whether a binary op's operands should be contained.
663 //
664 // Arguments:
665 //    node - the node we care about
666 //
667 void Lowering::ContainCheckBinary(GenTreeOp* node)
668 {
669     // Check and make op2 contained (if it is a containable immediate)
670     CheckImmedAndMakeContained(node, node->gtOp2);
671 }
672
673 //------------------------------------------------------------------------
674 // ContainCheckMul: Determine whether a mul op's operands should be contained.
675 //
676 // Arguments:
677 //    node - the node we care about
678 //
679 void Lowering::ContainCheckMul(GenTreeOp* node)
680 {
681     ContainCheckBinary(node);
682 }
683
684 //------------------------------------------------------------------------
685 // ContainCheckShiftRotate: Determine whether a mul op's operands should be contained.
686 //
687 // Arguments:
688 //    node - the node we care about
689 //
690 void Lowering::ContainCheckShiftRotate(GenTreeOp* node)
691 {
692     GenTree* shiftBy = node->gtOp2;
693
694 #ifdef _TARGET_ARM_
695     GenTree* source = node->gtOp1;
696     if (node->OperIs(GT_LSH_HI, GT_RSH_LO))
697     {
698         assert(source->OperGet() == GT_LONG);
699         MakeSrcContained(node, source);
700     }
701 #else  // !_TARGET_ARM_
702     assert(node->OperIsShiftOrRotate());
703 #endif // !_TARGET_ARM_
704
705     if (shiftBy->IsCnsIntOrI())
706     {
707         MakeSrcContained(node, shiftBy);
708     }
709 }
710
711 //------------------------------------------------------------------------
712 // ContainCheckStoreLoc: determine whether the source of a STORE_LCL* should be contained.
713 //
714 // Arguments:
715 //    node - pointer to the node
716 //
717 void Lowering::ContainCheckStoreLoc(GenTreeLclVarCommon* storeLoc)
718 {
719     assert(storeLoc->OperIsLocalStore());
720     GenTree* op1 = storeLoc->gtGetOp1();
721
722 #ifdef FEATURE_SIMD
723     if (varTypeIsSIMD(storeLoc))
724     {
725         if (op1->IsIntegralConst(0))
726         {
727             // For an InitBlk we want op1 to be contained
728             MakeSrcContained(storeLoc, op1);
729         }
730         return;
731     }
732 #endif // FEATURE_SIMD
733
734     // If the source is a containable immediate, make it contained, unless it is
735     // an int-size or larger store of zero to memory, because we can generate smaller code
736     // by zeroing a register and then storing it.
737     if (IsContainableImmed(storeLoc, op1) && (!op1->IsIntegralConst(0) || varTypeIsSmall(storeLoc)))
738     {
739         MakeSrcContained(storeLoc, op1);
740     }
741 #ifdef _TARGET_ARM_
742     else if (op1->OperGet() == GT_LONG)
743     {
744         MakeSrcContained(storeLoc, op1);
745     }
746 #endif // _TARGET_ARM_
747 }
748
749 //------------------------------------------------------------------------
750 // ContainCheckCast: determine whether the source of a CAST node should be contained.
751 //
752 // Arguments:
753 //    node - pointer to the node
754 //
755 void Lowering::ContainCheckCast(GenTreeCast* node)
756 {
757 #ifdef _TARGET_ARM_
758     GenTree*  castOp     = node->CastOp();
759     var_types castToType = node->CastToType();
760     var_types srcType    = castOp->TypeGet();
761
762     if (varTypeIsLong(castOp))
763     {
764         assert(castOp->OperGet() == GT_LONG);
765         MakeSrcContained(node, castOp);
766     }
767 #endif // _TARGET_ARM_
768 }
769
770 //------------------------------------------------------------------------
771 // ContainCheckCompare: determine whether the sources of a compare node should be contained.
772 //
773 // Arguments:
774 //    node - pointer to the node
775 //
776 void Lowering::ContainCheckCompare(GenTreeOp* cmp)
777 {
778     CheckImmedAndMakeContained(cmp, cmp->gtOp2);
779 }
780
781 //------------------------------------------------------------------------
782 // ContainCheckBoundsChk: determine whether any source of a bounds check node should be contained.
783 //
784 // Arguments:
785 //    node - pointer to the node
786 //
787 void Lowering::ContainCheckBoundsChk(GenTreeBoundsChk* node)
788 {
789     assert(node->OperIsBoundsCheck());
790     GenTree* other;
791     if (!CheckImmedAndMakeContained(node, node->gtIndex))
792     {
793         CheckImmedAndMakeContained(node, node->gtArrLen);
794     }
795 }
796
797 #ifdef FEATURE_SIMD
798 //----------------------------------------------------------------------------------------------
799 // ContainCheckSIMD: Perform containment analysis for a SIMD intrinsic node.
800 //
801 //  Arguments:
802 //     simdNode - The SIMD intrinsic node.
803 //
804 void Lowering::ContainCheckSIMD(GenTreeSIMD* simdNode)
805 {
806     switch (simdNode->gtSIMDIntrinsicID)
807     {
808         GenTree* op1;
809         GenTree* op2;
810
811         case SIMDIntrinsicInit:
812             op1 = simdNode->gtOp.gtOp1;
813             if (op1->IsIntegralConst(0))
814             {
815                 MakeSrcContained(simdNode, op1);
816             }
817             break;
818
819         case SIMDIntrinsicInitArray:
820             // We have an array and an index, which may be contained.
821             CheckImmedAndMakeContained(simdNode, simdNode->gtGetOp2());
822             break;
823
824         case SIMDIntrinsicOpEquality:
825         case SIMDIntrinsicOpInEquality:
826             // TODO-ARM64-CQ Support containing 0
827             break;
828
829         case SIMDIntrinsicGetItem:
830         {
831             // This implements get_Item method. The sources are:
832             //  - the source SIMD struct
833             //  - index (which element to get)
834             // The result is baseType of SIMD struct.
835             op1 = simdNode->gtOp.gtOp1;
836             op2 = simdNode->gtOp.gtOp2;
837
838             // If the index is a constant, mark it as contained.
839             if (op2->IsCnsIntOrI())
840             {
841                 MakeSrcContained(simdNode, op2);
842             }
843
844             if (IsContainableMemoryOp(op1))
845             {
846                 MakeSrcContained(simdNode, op1);
847                 if (op1->OperGet() == GT_IND)
848                 {
849                     op1->AsIndir()->Addr()->ClearContained();
850                 }
851             }
852             break;
853         }
854
855         default:
856             break;
857     }
858 }
859 #endif // FEATURE_SIMD
860
861 #ifdef FEATURE_HW_INTRINSICS
862 //----------------------------------------------------------------------------------------------
863 // ContainCheckHWIntrinsic: Perform containment analysis for a hardware intrinsic node.
864 //
865 //  Arguments:
866 //     node - The hardware intrinsic node.
867 //
868 void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
869 {
870     NamedIntrinsic  intrinsicID = node->gtHWIntrinsicId;
871     GenTreeArgList* argList     = nullptr;
872     GenTree*        op1         = node->gtOp.gtOp1;
873     GenTree*        op2         = node->gtOp.gtOp2;
874
875     if (op1->OperIs(GT_LIST))
876     {
877         argList = op1->AsArgList();
878         op1     = argList->Current();
879         op2     = argList->Rest()->Current();
880     }
881
882     switch (comp->getHWIntrinsicInfo(node->gtHWIntrinsicId).form)
883     {
884         case HWIntrinsicInfo::SimdExtractOp:
885             if (op2->IsCnsIntOrI())
886             {
887                 MakeSrcContained(node, op2);
888             }
889             break;
890
891         case HWIntrinsicInfo::SimdInsertOp:
892             if (op2->IsCnsIntOrI())
893             {
894                 MakeSrcContained(node, op2);
895
896                 GenTree* op3 = argList->Rest()->Rest()->Current();
897
898                 // In the HW intrinsics C# API there is no direct way to specify a vector element to element mov
899                 //   VX[a] = VY[b]
900                 // In C# this would naturally be expressed by
901                 //   Insert(VX, a, Extract(VY, b))
902                 // If both a & b are immediate constants contain the extract/getItem so that we can emit
903                 //   the single instruction mov Vx[a], Vy[b]
904                 if (op3->OperIs(GT_HWIntrinsic) && (op3->AsHWIntrinsic()->gtHWIntrinsicId == NI_ARM64_SIMD_GetItem))
905                 {
906                     ContainCheckHWIntrinsic(op3->AsHWIntrinsic());
907
908                     if (op3->gtOp.gtOp2->isContained())
909                     {
910                         MakeSrcContained(node, op3);
911                     }
912                 }
913             }
914             break;
915
916         default:
917             break;
918     }
919 }
920 #endif // FEATURE_HW_INTRINSICS
921
922 #endif // _TARGET_ARMARCH_
923
924 #endif // !LEGACY_BACKEND