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 Lowering for ARM and ARM64 common code 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
14 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
15 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
23 #ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator
25 #ifdef _TARGET_ARMARCH_ // This file is ONLY used for ARM and ARM64 architectures
28 #include "sideeffects.h"
32 #ifdef FEATURE_HW_INTRINSICS
33 #include "hwintrinsicArm64.h"
36 //------------------------------------------------------------------------
37 // IsCallTargetInRange: Can a call target address be encoded in-place?
40 // True if the addr fits into the range.
42 bool Lowering::IsCallTargetInRange(void* addr)
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.
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.
55 #elif defined(_TARGET_ARM_)
56 return comp->codeGen->validImmForBL((ssize_t)addr);
60 //------------------------------------------------------------------------
61 // IsContainableImmed: Is an immediate encodable in-place?
64 // True if the immediate can be folded into an instruction,
65 // for example small enough and non-relocatable.
67 // TODO-CQ: we can contain a floating point 0.0 constant in a compare instruction
68 // (vcmp on arm, fcmp on arm64).
70 bool Lowering::IsContainableImmed(GenTree* parentNode, GenTree* childNode)
72 if (!varTypeIsFloating(parentNode->TypeGet()))
74 // Make sure we have an actual immediate
75 if (!childNode->IsCnsIntOrI())
77 if (childNode->IsIconHandle() && comp->opts.compReloc)
80 ssize_t immVal = childNode->gtIntCon.gtIconVal;
81 emitAttr attr = emitActualTypeSize(childNode->TypeGet());
82 emitAttr size = EA_SIZE(attr);
84 insFlags flags = parentNode->gtSetFlags() ? INS_FLAGS_SET : INS_FLAGS_DONT_CARE;
87 switch (parentNode->OperGet())
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);
101 #ifdef _TARGET_ARM64_
108 return emitter::emitIns_valid_imm_for_cmp(immVal, size);
114 return emitter::emitIns_valid_imm_for_alu(immVal, size);
116 assert(((parentNode->gtFlags & GTF_JCMP_TST) == 0) ? (immVal == 0) : isPow2(immVal));
118 #elif defined(_TARGET_ARM_)
129 return emitter::emitIns_valid_imm_for_alu(immVal);
130 #endif // _TARGET_ARM_
132 #ifdef _TARGET_ARM64_
133 case GT_STORE_LCL_FLD:
134 case GT_STORE_LCL_VAR:
148 //------------------------------------------------------------------------
149 // LowerStoreLoc: Lower a store of a lclVar
152 // storeLoc - the local store (GT_STORE_LCL_FLD or GT_STORE_LCL_VAR)
156 // - Widening operations of unsigneds.
158 void Lowering::LowerStoreLoc(GenTreeLclVarCommon* storeLoc)
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))
164 GenTreeIntCon* con = op1->AsIntCon();
165 ssize_t ival = con->gtIconVal;
166 unsigned varNum = storeLoc->gtLclNum;
167 LclVarDsc* varDsc = comp->lvaTable + varNum;
169 if (varDsc->lvIsSIMDType())
171 noway_assert(storeLoc->gtType != TYP_STRUCT);
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))
178 if (!varTypeIsUnsigned(varDsc))
180 if (genTypeSize(storeLoc) == 1)
182 if ((ival & 0x7f) != ival)
184 ival = ival | 0xffffff00;
189 assert(genTypeSize(storeLoc) == 2);
190 if ((ival & 0x7fff) != ival)
192 ival = ival | 0xffff0000;
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)
203 storeLoc->gtType = TYP_INT;
204 con->SetIconValue(ival);
208 ContainCheckStoreLoc(storeLoc);
211 //------------------------------------------------------------------------
212 // LowerStoreIndir: Determine addressing mode for an indirection, and whether operands are contained.
215 // node - The indirect store node (GT_STORE_IND) of interest
220 void Lowering::LowerStoreIndir(GenTreeIndir* node)
222 ContainCheckStoreIndir(node);
225 //------------------------------------------------------------------------
226 // LowerBlockStore: Set block store type
229 // blkNode - The block store node of interest
234 void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
236 GenTree* dstAddr = blkNode->Addr();
237 unsigned size = blkNode->gtBlkSize;
238 GenTree* source = blkNode->Data();
239 Compiler* compiler = comp;
241 // Sources are dest address and initVal or source.
242 GenTree* srcAddrOrFill = nullptr;
243 bool isInitBlk = blkNode->OperIsInitBlkOp();
247 // CopyObj or CopyBlk
248 if ((blkNode->OperGet() == GT_STORE_OBJ) && ((blkNode->AsObj()->gtGcPtrCount == 0) || blkNode->gtBlkOpGcUnsafe))
250 blkNode->SetOper(GT_STORE_BLK);
252 if (source->gtOper == GT_IND)
254 srcAddrOrFill = blkNode->Data()->gtGetOp1();
260 GenTree* initVal = source;
261 if (initVal->OperIsInitVal())
263 initVal->SetContained();
264 initVal = initVal->gtGetOp1();
266 srcAddrOrFill = initVal;
268 #ifdef _TARGET_ARM64_
269 if ((size != 0) && (size <= INITBLK_UNROLL_LIMIT) && initVal->IsCnsIntOrI())
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.");
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.
284 ssize_t fill = initVal->gtIntCon.gtIconVal & 0xFF;
287 MakeSrcContained(blkNode, source);
289 else if (size < REGSIZE_BYTES)
291 initVal->gtIntCon.gtIconVal = 0x01010101 * fill;
295 initVal->gtIntCon.gtIconVal = 0x0101010101010101LL * fill;
296 initVal->gtType = TYP_LONG;
298 blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
301 #endif // _TARGET_ARM64_
303 blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper;
308 // CopyObj or CopyBlk
309 // Sources are src and dest and size if not constant.
311 if (blkNode->OperGet() == GT_STORE_OBJ)
314 GenTreeObj* objNode = blkNode->AsObj();
316 unsigned slots = objNode->gtSlots;
319 // CpObj must always have at least one GC-Pointer as a member.
320 assert(objNode->gtGcPtrCount > 0);
322 assert(dstAddr->gtType == TYP_BYREF || dstAddr->gtType == TYP_I_IMPL);
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);
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
333 assert(classSize == blkSize);
334 assert((blkSize / TARGET_POINTER_SIZE) == slots);
335 assert(objNode->HasGCPtr());
338 blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
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.
346 if ((size != 0) && (size <= CPBLK_UNROLL_LIMIT))
348 blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
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;
358 // CopyObj or CopyBlk
359 if (source->gtOper == GT_IND)
361 MakeSrcContained(blkNode, source);
362 GenTree* addr = source->AsIndir()->Addr();
363 if (!addr->OperIsLocalAddr())
365 addr->ClearContained();
368 else if (!source->IsMultiRegCall() && !source->OperIsSIMD())
370 assert(source->IsLocal());
371 MakeSrcContained(blkNode, source);
376 //------------------------------------------------------------------------
377 // LowerCast: Lower GT_CAST(srcType, DstType) nodes.
380 // tree - GT_CAST node to be lowered
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)
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)
396 void Lowering::LowerCast(GenTree* tree)
398 assert(tree->OperGet() == GT_CAST);
400 JITDUMP("LowerCast for: ");
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;
409 if (varTypeIsFloating(srcType))
411 noway_assert(!tree->gtOverflow());
412 assert(!varTypeIsSmall(dstType)); // fgMorphCast creates intermediate casts when converting from float to small
416 assert(!varTypeIsSmall(srcType));
418 if (tmpType != TYP_UNDEF)
420 GenTree* tmp = comp->gtNewCastNode(tmpType, op1, tree->IsUnsigned(), tmpType);
421 tmp->gtFlags |= (tree->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT));
423 tree->gtFlags &= ~GTF_UNSIGNED;
424 tree->gtOp.gtOp1 = tmp;
425 BlockRange().InsertAfter(op1, tmp);
428 // Now determine if we have operands that should be contained.
429 ContainCheckCast(tree->AsCast());
432 //------------------------------------------------------------------------
433 // LowerRotate: Lower GT_ROL and GT_ROR nodes.
436 // tree - the node to lower
441 void Lowering::LowerRotate(GenTree* tree)
443 if (tree->OperGet() == GT_ROL)
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;
450 if (rotateLeftIndexNode->IsCnsIntOrI())
452 ssize_t rotateLeftIndex = rotateLeftIndexNode->gtIntCon.gtIconVal;
453 ssize_t rotateRightIndex = rotatedValueBitSize - rotateLeftIndex;
454 rotateLeftIndexNode->gtIntCon.gtIconVal = rotateRightIndex;
458 GenTree* tmp = comp->gtNewOperNode(GT_NEG, genActualType(rotateLeftIndexNode->gtType), rotateLeftIndexNode);
459 BlockRange().InsertAfter(rotateLeftIndexNode, tmp);
460 tree->gtOp.gtOp2 = tmp;
462 tree->ChangeOper(GT_ROR);
464 ContainCheckShiftRotate(tree->AsOp());
468 //----------------------------------------------------------------------------------------------
469 // Lowering::LowerSIMD: Perform containment analysis for a SIMD intrinsic node.
472 // simdNode - The SIMD intrinsic node.
474 void Lowering::LowerSIMD(GenTreeSIMD* simdNode)
476 assert(simdNode->gtType != TYP_SIMD32);
478 if (simdNode->TypeGet() == TYP_SIMD12)
480 // GT_SIMD node requiring to produce TYP_SIMD12 in fact
481 // produces a TYP_SIMD16 result
482 simdNode->gtType = TYP_SIMD16;
485 ContainCheckSIMD(simdNode);
487 #endif // FEATURE_SIMD
489 #ifdef FEATURE_HW_INTRINSICS
490 //----------------------------------------------------------------------------------------------
491 // Lowering::LowerHWIntrinsic: Perform containment analysis for a hardware intrinsic node.
494 // node - The hardware intrinsic node.
496 void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
498 auto intrinsicID = node->gtHWIntrinsicId;
499 auto intrinsicInfo = comp->getHWIntrinsicInfo(node->gtHWIntrinsicId);
502 // Lower unsupported Unsigned Compare Zero intrinsics to their trivial transformations
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
507 if ((intrinsicInfo.flags & HWIntrinsicInfo::LowerCmpUZero) && varTypeIsUnsigned(node->gtSIMDBaseType))
509 auto setAllVector = node->gtSIMDSize > 8 ? NI_ARM64_SIMD_SetAllVector128 : NI_ARM64_SIMD_SetAllVector64;
511 auto origOp1 = node->gtOp.gtOp1;
515 case NI_ARM64_SIMD_GT_ZERO:
516 // Unsigned > 0 ==> !(Unsigned == 0)
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);
523 case NI_ARM64_SIMD_LE_ZERO:
524 // Unsigned <= 0 ==> Unsigned == 0
525 node->gtHWIntrinsicId = NI_ARM64_SIMD_EQ_ZERO;
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)
536 BlockRange().Remove(origOp1, true);
540 origOp1->SetUnusedValue();
544 assert(!"Unhandled LowerCmpUZero case");
548 ContainCheckHWIntrinsic(node);
550 #endif // FEATURE_HW_INTRINSICS
552 //------------------------------------------------------------------------
553 // Containment analysis
554 //------------------------------------------------------------------------
556 //------------------------------------------------------------------------
557 // ContainCheckCallOperands: Determine whether operands of a call should be contained.
560 // call - The call node of interest
565 void Lowering::ContainCheckCallOperands(GenTreeCall* call)
567 // There are no contained operands for arm.
570 //------------------------------------------------------------------------
571 // ContainCheckStoreIndir: determine whether the sources of a STOREIND node should be contained.
574 // node - pointer to the node
576 void Lowering::ContainCheckStoreIndir(GenTreeIndir* node)
578 #ifdef _TARGET_ARM64_
579 GenTree* src = node->gtOp.gtOp2;
580 if (!varTypeIsFloating(src->TypeGet()) && src->IsIntegralConst(0))
582 // an integer zero for 'src' can be contained.
583 MakeSrcContained(node, src);
585 #endif // _TARGET_ARM64_
586 ContainCheckIndir(node);
589 //------------------------------------------------------------------------
590 // ContainCheckIndir: Determine whether operands of an indir should be contained.
593 // indirNode - The indirection node of interest
596 // This is called for both store and load indirections.
601 void Lowering::ContainCheckIndir(GenTreeIndir* indirNode)
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)
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.
615 // TODO-ARM64-CQ: handle other addr mode expressions that could be marked
617 if (indirNode->TypeGet() == TYP_SIMD12)
621 #endif // FEATURE_SIMD
623 GenTree* addr = indirNode->Addr();
624 bool makeContained = true;
625 if ((addr->OperGet() == GT_LEA) && IsSafeToContainMem(indirNode, addr))
627 GenTreeAddrMode* lea = addr->AsAddrMode();
628 GenTree* base = lea->Base();
629 GenTree* index = lea->Index();
630 int cns = lea->Offset();
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))
638 if (indirNode->OperGet() == GT_STOREIND)
640 if (varTypeIsFloating(indirNode->AsStoreInd()->Data()))
642 makeContained = false;
645 else if (indirNode->OperGet() == GT_IND)
647 if (varTypeIsFloating(indirNode))
649 makeContained = false;
656 MakeSrcContained(indirNode, addr);
661 //------------------------------------------------------------------------
662 // ContainCheckBinary: Determine whether a binary op's operands should be contained.
665 // node - the node we care about
667 void Lowering::ContainCheckBinary(GenTreeOp* node)
669 // Check and make op2 contained (if it is a containable immediate)
670 CheckImmedAndMakeContained(node, node->gtOp2);
673 //------------------------------------------------------------------------
674 // ContainCheckMul: Determine whether a mul op's operands should be contained.
677 // node - the node we care about
679 void Lowering::ContainCheckMul(GenTreeOp* node)
681 ContainCheckBinary(node);
684 //------------------------------------------------------------------------
685 // ContainCheckShiftRotate: Determine whether a mul op's operands should be contained.
688 // node - the node we care about
690 void Lowering::ContainCheckShiftRotate(GenTreeOp* node)
692 GenTree* shiftBy = node->gtOp2;
695 GenTree* source = node->gtOp1;
696 if (node->OperIs(GT_LSH_HI, GT_RSH_LO))
698 assert(source->OperGet() == GT_LONG);
699 MakeSrcContained(node, source);
701 #else // !_TARGET_ARM_
702 assert(node->OperIsShiftOrRotate());
703 #endif // !_TARGET_ARM_
705 if (shiftBy->IsCnsIntOrI())
707 MakeSrcContained(node, shiftBy);
711 //------------------------------------------------------------------------
712 // ContainCheckStoreLoc: determine whether the source of a STORE_LCL* should be contained.
715 // node - pointer to the node
717 void Lowering::ContainCheckStoreLoc(GenTreeLclVarCommon* storeLoc)
719 assert(storeLoc->OperIsLocalStore());
720 GenTree* op1 = storeLoc->gtGetOp1();
723 if (varTypeIsSIMD(storeLoc))
725 if (op1->IsIntegralConst(0))
727 // For an InitBlk we want op1 to be contained
728 MakeSrcContained(storeLoc, op1);
732 #endif // FEATURE_SIMD
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)))
739 MakeSrcContained(storeLoc, op1);
742 else if (op1->OperGet() == GT_LONG)
744 MakeSrcContained(storeLoc, op1);
746 #endif // _TARGET_ARM_
749 //------------------------------------------------------------------------
750 // ContainCheckCast: determine whether the source of a CAST node should be contained.
753 // node - pointer to the node
755 void Lowering::ContainCheckCast(GenTreeCast* node)
758 GenTree* castOp = node->CastOp();
759 var_types castToType = node->CastToType();
760 var_types srcType = castOp->TypeGet();
762 if (varTypeIsLong(castOp))
764 assert(castOp->OperGet() == GT_LONG);
765 MakeSrcContained(node, castOp);
767 #endif // _TARGET_ARM_
770 //------------------------------------------------------------------------
771 // ContainCheckCompare: determine whether the sources of a compare node should be contained.
774 // node - pointer to the node
776 void Lowering::ContainCheckCompare(GenTreeOp* cmp)
778 CheckImmedAndMakeContained(cmp, cmp->gtOp2);
781 //------------------------------------------------------------------------
782 // ContainCheckBoundsChk: determine whether any source of a bounds check node should be contained.
785 // node - pointer to the node
787 void Lowering::ContainCheckBoundsChk(GenTreeBoundsChk* node)
789 assert(node->OperIsBoundsCheck());
791 if (!CheckImmedAndMakeContained(node, node->gtIndex))
793 CheckImmedAndMakeContained(node, node->gtArrLen);
798 //----------------------------------------------------------------------------------------------
799 // ContainCheckSIMD: Perform containment analysis for a SIMD intrinsic node.
802 // simdNode - The SIMD intrinsic node.
804 void Lowering::ContainCheckSIMD(GenTreeSIMD* simdNode)
806 switch (simdNode->gtSIMDIntrinsicID)
811 case SIMDIntrinsicInit:
812 op1 = simdNode->gtOp.gtOp1;
813 if (op1->IsIntegralConst(0))
815 MakeSrcContained(simdNode, op1);
819 case SIMDIntrinsicInitArray:
820 // We have an array and an index, which may be contained.
821 CheckImmedAndMakeContained(simdNode, simdNode->gtGetOp2());
824 case SIMDIntrinsicOpEquality:
825 case SIMDIntrinsicOpInEquality:
826 // TODO-ARM64-CQ Support containing 0
829 case SIMDIntrinsicGetItem:
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;
838 // If the index is a constant, mark it as contained.
839 if (op2->IsCnsIntOrI())
841 MakeSrcContained(simdNode, op2);
844 if (IsContainableMemoryOp(op1))
846 MakeSrcContained(simdNode, op1);
847 if (op1->OperGet() == GT_IND)
849 op1->AsIndir()->Addr()->ClearContained();
859 #endif // FEATURE_SIMD
861 #ifdef FEATURE_HW_INTRINSICS
862 //----------------------------------------------------------------------------------------------
863 // ContainCheckHWIntrinsic: Perform containment analysis for a hardware intrinsic node.
866 // node - The hardware intrinsic node.
868 void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
870 NamedIntrinsic intrinsicID = node->gtHWIntrinsicId;
871 GenTreeArgList* argList = nullptr;
872 GenTree* op1 = node->gtOp.gtOp1;
873 GenTree* op2 = node->gtOp.gtOp2;
875 if (op1->OperIs(GT_LIST))
877 argList = op1->AsArgList();
878 op1 = argList->Current();
879 op2 = argList->Rest()->Current();
882 switch (comp->getHWIntrinsicInfo(node->gtHWIntrinsicId).form)
884 case HWIntrinsicInfo::SimdExtractOp:
885 if (op2->IsCnsIntOrI())
887 MakeSrcContained(node, op2);
891 case HWIntrinsicInfo::SimdInsertOp:
892 if (op2->IsCnsIntOrI())
894 MakeSrcContained(node, op2);
896 GenTree* op3 = argList->Rest()->Rest()->Current();
898 // In the HW intrinsics C# API there is no direct way to specify a vector element to element mov
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))
906 ContainCheckHWIntrinsic(op3->AsHWIntrinsic());
908 if (op3->gtOp.gtOp2->isContained())
910 MakeSrcContained(node, op3);
920 #endif // FEATURE_HW_INTRINSICS
922 #endif // _TARGET_ARMARCH_
924 #endif // !LEGACY_BACKEND