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 ARM64 XX
10 XX This encapsulates all the logic for setting register requirements for XX
11 XX the ARM64 architecture. 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
28 #include "sideeffects.h"
31 //------------------------------------------------------------------------
32 // BuildNode: Set the register requirements for RA.
35 // Takes care of annotating the register requirements
36 // for every TreeNodeInfo struct that maps to each tree node.
39 // LSRA has been initialized and there is a TreeNodeInfo node
40 // already allocated and initialized for every tree in the IR.
43 // Every TreeNodeInfo instance has the right annotations on register
44 // requirements needed by LSRA to build the Interval Table (source,
45 // destination and internal [temp] register counts).
47 void LinearScan::BuildNode(GenTree* tree)
49 TreeNodeInfo* info = currentNodeInfo;
50 unsigned kind = tree->OperKind();
51 RegisterType registerType = TypeGet(tree);
53 if (tree->isContained())
56 assert(info->srcCount == 0);
60 // Set the default dstCount. This may be modified below.
64 if (tree->IsUnusedValue())
66 info->isLocalDefUse = true;
74 switch (tree->OperGet())
83 case GT_STORE_LCL_FLD:
84 case GT_STORE_LCL_VAR:
86 assert(info->dstCount == 0);
87 BuildStoreLoc(tree->AsLclVarCommon());
91 // These should always be contained. We don't correctly allocate or
92 // generate code for a non-contained GT_FIELD_LIST.
93 noway_assert(!"Non-contained GT_FIELD_LIST");
102 assert(info->dstCount == 0);
107 assert(info->dstCount == 1);
109 GenTreeDblCon* dblConst = tree->AsDblCon();
110 double constValue = dblConst->gtDblCon.gtDconVal;
112 if (emitter::emitIns_valid_imm_for_fmov(constValue))
114 // Directly encode constant to instructions.
118 // Reserve int to load constant from memory (IF_LARGELDC)
119 info->internalIntCount = 1;
129 assert(info->dstCount == 0);
138 if (tree->TypeGet() == TYP_VOID)
141 assert(info->dstCount == 0);
145 assert(tree->TypeGet() == TYP_INT);
148 assert(info->dstCount == 0);
150 info->setSrcCandidates(this, RBM_INTRET);
151 LocationInfoListNode* locationInfo = getLocationInfo(tree->gtOp.gtOp1);
152 locationInfo->info.setSrcCandidates(this, RBM_INTRET);
153 useList.Append(locationInfo);
158 // A GT_NOP is either a passthrough (if it is void, or if it has
159 // a child), but must be considered to produce a dummy value if it
160 // has a type but no child
162 if (tree->TypeGet() != TYP_VOID && tree->gtOp.gtOp1 == nullptr)
164 assert(info->dstCount == 1);
168 assert(info->dstCount == 0);
174 assert(info->dstCount == 0);
179 assert(info->dstCount == 0);
183 // This should never occur since switch nodes must not be visible at this
186 noway_assert(!"Switch must be lowered at this point");
191 assert(info->dstCount == 1);
194 case GT_SWITCH_TABLE:
195 info->srcCount = appendBinaryLocationInfoToList(tree->AsOp());
196 info->internalIntCount = 1;
197 assert(info->dstCount == 0);
201 noway_assert(!"We should never hit any assignment operator in lowering");
207 if (varTypeIsFloating(tree->TypeGet()))
209 // overflow operations aren't supported on float/double types.
210 assert(!tree->gtOverflow());
212 // No implicit conversions at this stage as the expectation is that
213 // everything is made explicit by adding casts.
214 assert(tree->gtOp.gtOp1->TypeGet() == tree->gtOp.gtOp2->TypeGet());
226 info->srcCount = appendBinaryLocationInfoToList(tree->AsOp());
227 assert(info->dstCount == 1);
231 // this just turns into a compare of its child with an int
232 // + a conditional call
233 appendLocationInfoToList(tree->gtGetOp1());
235 assert(info->dstCount == 0);
240 NYI_IF(varTypeIsFloating(tree->TypeGet()), "FP Remainder in ARM64");
241 assert(!"Shouldn't see an integer typed GT_MOD node in ARM64");
245 if (tree->gtOverflow())
247 // Need a register different from target reg to check for overflow.
248 info->internalIntCount = 1;
249 info->isInternalRegDelayFree = true;
257 info->srcCount = appendBinaryLocationInfoToList(tree->AsOp());
258 assert(info->dstCount == 1);
264 noway_assert((tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Abs) ||
265 (tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Ceiling) ||
266 (tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Floor) ||
267 (tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Round) ||
268 (tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Sqrt));
270 // Both operand and its result must be of the same floating point type.
271 op1 = tree->gtOp.gtOp1;
272 assert(varTypeIsFloating(op1));
273 assert(op1->TypeGet() == tree->TypeGet());
275 appendLocationInfoToList(op1);
277 assert(info->dstCount == 1);
283 BuildSIMD(tree->AsSIMD());
285 #endif // FEATURE_SIMD
287 #ifdef FEATURE_HW_INTRINSICS
289 BuildHWIntrinsic(tree->AsHWIntrinsic());
291 #endif // FEATURE_HW_INTRINSICS
295 // TODO-ARM64-CQ: Int-To-Int conversions - castOp cannot be a memory op and must have an assigned
297 // see CodeGen::genIntToIntCast()
299 appendLocationInfoToList(tree->gtGetOp1());
301 assert(info->dstCount == 1);
303 // Non-overflow casts to/from float/double are done using SSE2 instructions
304 // and that allow the source operand to be either a reg or memop. Given the
305 // fact that casts from small int to float/double are done as two-level casts,
306 // the source operand is always guaranteed to be of size 4 or 8 bytes.
307 var_types castToType = tree->CastToType();
308 GenTree* castOp = tree->gtCast.CastOp();
309 var_types castOpType = castOp->TypeGet();
310 if (tree->gtFlags & GTF_UNSIGNED)
312 castOpType = genUnsignedType(castOpType);
315 // Some overflow checks need a temp reg
317 Lowering::CastInfo castInfo;
318 // Get information about the cast.
319 Lowering::getCastDescription(tree, &castInfo);
321 if (castInfo.requiresOverflowCheck)
323 var_types srcType = castOp->TypeGet();
324 emitAttr cmpSize = EA_ATTR(genTypeSize(srcType));
326 // If we cannot store the comparisons in an immediate for either
327 // comparing against the max or min value, then we will need to
328 // reserve a temporary register.
330 bool canStoreMaxValue = emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, cmpSize);
331 bool canStoreMinValue = emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, cmpSize);
333 if (!canStoreMaxValue || !canStoreMinValue)
335 info->internalIntCount = 1;
343 appendLocationInfoToList(tree->gtGetOp1());
345 assert(info->dstCount == 1);
361 appendLocationInfoToList(tree->gtOp.gtOp1);
363 assert(info->dstCount == 1);
364 info->internalIntCount = 1;
369 GenTreeCmpXchg* cmpXchgNode = tree->AsCmpXchg();
370 info->srcCount = cmpXchgNode->gtOpComparand->isContained() ? 2 : 3;
371 assert(info->dstCount == 1);
373 info->internalIntCount = 1;
375 // For ARMv8 exclusives the lifetime of the addr and data must be extended because
376 // it may be used used multiple during retries
377 LocationInfoListNode* locationInfo = getLocationInfo(tree->gtCmpXchg.gtOpLocation);
378 locationInfo->info.isDelayFree = true;
379 useList.Append(locationInfo);
380 LocationInfoListNode* valueInfo = getLocationInfo(tree->gtCmpXchg.gtOpValue);
381 valueInfo->info.isDelayFree = true;
382 useList.Append(valueInfo);
383 if (!cmpXchgNode->gtOpComparand->isContained())
385 LocationInfoListNode* comparandInfo = getLocationInfo(tree->gtCmpXchg.gtOpComparand);
386 comparandInfo->info.isDelayFree = true;
387 useList.Append(comparandInfo);
389 info->hasDelayFreeSrc = true;
391 // Internals may not collide with target
392 info->isInternalRegDelayFree = true;
400 assert(info->dstCount == (tree->TypeGet() == TYP_VOID) ? 0 : 1);
401 info->srcCount = tree->gtOp.gtOp2->isContained() ? 1 : 2;
402 info->internalIntCount = (tree->OperGet() == GT_XCHG) ? 1 : 2;
404 // For ARMv8 exclusives the lifetime of the addr and data must be extended because
405 // it may be used used multiple during retries
406 assert(!tree->gtOp.gtOp1->isContained());
407 LocationInfoListNode* op1Info = getLocationInfo(tree->gtOp.gtOp1);
408 useList.Append(op1Info);
409 LocationInfoListNode* op2Info = nullptr;
410 if (!tree->gtOp.gtOp2->isContained())
412 op2Info = getLocationInfo(tree->gtOp.gtOp2);
413 useList.Append(op2Info);
415 if (info->dstCount != 0)
417 op1Info->info.isDelayFree = true;
418 if (op2Info != nullptr)
420 op2Info->info.isDelayFree = true;
422 // Internals may not collide with target
423 info->isInternalRegDelayFree = true;
424 info->hasDelayFreeSrc = true;
430 BuildPutArgStk(tree->AsPutArgStk());
434 BuildPutArgReg(tree->AsUnOp());
438 BuildCall(tree->AsCall());
443 // For a GT_ADDR, the child node should not be evaluated into a register
444 GenTree* child = tree->gtOp.gtOp1;
445 assert(!isCandidateLocalRef(child));
446 assert(child->isContained());
447 assert(info->dstCount == 1);
454 // These should all be eliminated prior to Lowering.
455 assert(!"Non-store block node in Lowering");
461 case GT_STORE_DYN_BLK:
462 BuildBlockStore(tree->AsBlk());
466 // Always a passthrough of its child's value.
467 assert(!"INIT_VAL should always be contained");
472 assert(info->dstCount == 1);
474 // Need a variable number of temp regs (see genLclHeap() in codegenamd64.cpp):
475 // Here '-' means don't care.
477 // Size? Init Memory? # temp regs
479 // const and <=6 ptr words - 0
480 // const and <PageSize No 0
481 // >6 ptr words Yes hasPspSym ? 1 : 0
482 // Non-const Yes hasPspSym ? 1 : 0
485 // PSPSym - If the method has PSPSym increment internalIntCount by 1.
488 #if FEATURE_EH_FUNCLETS
489 hasPspSym = (compiler->lvaPSPSym != BAD_VAR_NUM);
494 GenTree* size = tree->gtOp.gtOp1;
495 if (size->IsCnsIntOrI())
497 assert(size->isContained());
500 size_t sizeVal = size->gtIntCon.gtIconVal;
504 info->internalIntCount = 0;
508 // Compute the amount of memory to properly STACK_ALIGN.
509 // Note: The Gentree node is not updated here as it is cheap to recompute stack aligned size.
510 // This should also help in debugging as we can examine the original size specified with
512 sizeVal = AlignUp(sizeVal, STACK_ALIGN);
513 size_t cntStackAlignedWidthItems = (sizeVal >> STACK_ALIGN_SHIFT);
515 // For small allocations upto 4 'stp' instructions (i.e. 64 bytes of localloc)
517 if (cntStackAlignedWidthItems <= 4)
519 info->internalIntCount = 0;
521 else if (!compiler->info.compInitMem)
523 // No need to initialize allocated stack space.
524 if (sizeVal < compiler->eeGetPageSize())
526 info->internalIntCount = 0;
530 // We need two registers: regCnt and RegTmp
531 info->internalIntCount = 2;
536 // greater than 4 and need to zero initialize allocated stack space.
537 // If the method has PSPSym, we need an internal register to hold regCnt
538 // since targetReg allocated to GT_LCLHEAP node could be the same as one of
539 // the the internal registers.
540 info->internalIntCount = hasPspSym ? 1 : 0;
546 appendLocationInfoToList(size);
548 if (!compiler->info.compInitMem)
550 info->internalIntCount = 2;
554 // If the method has PSPSym, we need an internal register to hold regCnt
555 // since targetReg allocated to GT_LCLHEAP node could be the same as one of
556 // the the internal registers.
557 info->internalIntCount = hasPspSym ? 1 : 0;
561 // If the method has PSPSym, we would need an addtional register to relocate it on stack.
564 // Exclude const size 0
565 if (!size->IsCnsIntOrI() || (size->gtIntCon.gtIconVal > 0))
566 info->internalIntCount++;
571 case GT_ARR_BOUNDS_CHECK:
574 #endif // FEATURE_SIMD
576 GenTreeBoundsChk* node = tree->AsBoundsChk();
577 // Consumes arrLen & index - has no result
578 assert(info->dstCount == 0);
580 GenTree* intCns = nullptr;
581 GenTree* other = nullptr;
582 info->srcCount = GetOperandInfo(tree->AsBoundsChk()->gtIndex);
583 info->srcCount += GetOperandInfo(tree->AsBoundsChk()->gtArrLen);
588 // These must have been lowered to GT_ARR_INDEX
589 noway_assert(!"We should never see a GT_ARR_ELEM in lowering");
591 assert(info->dstCount == 0);
597 assert(info->dstCount == 1);
598 info->internalIntCount = 1;
599 info->isInternalRegDelayFree = true;
601 // For GT_ARR_INDEX, the lifetime of the arrObj must be extended because it is actually used multiple
602 // times while the result is being computed.
603 LocationInfoListNode* arrObjInfo = getLocationInfo(tree->AsArrIndex()->ArrObj());
604 arrObjInfo->info.isDelayFree = true;
605 useList.Append(arrObjInfo);
606 useList.Append(getLocationInfo(tree->AsArrIndex()->IndexExpr()));
607 info->hasDelayFreeSrc = true;
612 // This consumes the offset, if any, the arrObj and the effective index,
613 // and produces the flattened offset for this dimension.
615 if (!tree->gtArrOffs.gtOffset->isContained())
617 appendLocationInfoToList(tree->AsArrOffs()->gtOffset);
620 appendLocationInfoToList(tree->AsArrOffs()->gtIndex);
621 appendLocationInfoToList(tree->AsArrOffs()->gtArrObj);
622 assert(info->dstCount == 1);
623 info->internalIntCount = 1;
628 GenTreeAddrMode* lea = tree->AsAddrMode();
630 GenTree* base = lea->Base();
631 GenTree* index = lea->Index();
632 int cns = lea->Offset();
634 // This LEA is instantiating an address, so we set up the srcCount here.
639 appendLocationInfoToList(base);
641 if (index != nullptr)
644 appendLocationInfoToList(index);
646 assert(info->dstCount == 1);
648 // On ARM64 we may need a single internal register
649 // (when both conditions are true then we still only need a single internal register)
650 if ((index != nullptr) && (cns != 0))
652 // ARM64 does not support both Index and offset so we need an internal register
653 info->internalIntCount = 1;
655 else if (!emitter::emitIns_valid_imm_for_add(cns, EA_8BYTE))
657 // This offset can't be contained in the add instruction, so we need an internal register
658 info->internalIntCount = 1;
665 assert(info->dstCount == 0);
667 if (compiler->codeGen->gcInfo.gcIsWriteBarrierAsgNode(tree))
670 BuildGCWriteBarrier(tree);
674 BuildIndir(tree->AsIndir());
675 if (!tree->gtGetOp2()->isContained())
677 appendLocationInfoToList(tree->gtGetOp2());
684 // Unlike ARM, ARM64 implements NULLCHECK as a load to REG_ZR, so no internal register
685 // is required, and it is not a localDefUse.
686 assert(info->dstCount == 0);
687 assert(!tree->gtGetOp1()->isContained());
688 appendLocationInfoToList(tree->gtOp.gtOp1);
693 assert(info->dstCount == 1);
694 BuildIndir(tree->AsIndir());
699 assert(info->dstCount == 1);
700 info->setDstCandidates(this, RBM_EXCEPTION_OBJECT);
705 // GT_CLS_VAR, by the time we reach the backend, must always
707 // It will produce a result of the type of the
708 // node, and use an internal register for the address.
710 assert(info->dstCount == 1);
711 assert((tree->gtFlags & (GTF_VAR_DEF | GTF_VAR_USEASG)) == 0);
712 info->internalIntCount = 1;
716 assert(info->dstCount == 1);
717 info->srcCount = appendBinaryLocationInfoToList(tree->AsOp());
718 info->internalIntCount = 1;
720 } // end switch (tree->OperGet())
722 if (tree->IsUnusedValue() && (info->dstCount != 0))
724 info->isLocalDefUse = true;
726 // We need to be sure that we've set info->srcCount and info->dstCount appropriately
727 assert((info->dstCount < 2) || tree->IsMultiRegCall());
728 assert(info->isLocalDefUse == (tree->IsValue() && tree->IsUnusedValue()));
729 assert(!tree->IsUnusedValue() || (info->dstCount != 0));
730 assert(info->dstCount == tree->GetRegisterDstCount());
734 //------------------------------------------------------------------------
735 // BuildSIMD: Set the NodeInfo for a GT_SIMD tree.
738 // tree - The GT_SIMD node of interest
743 void LinearScan::BuildSIMD(GenTreeSIMD* simdTree)
745 TreeNodeInfo* info = currentNodeInfo;
746 // Only SIMDIntrinsicInit can be contained
747 if (simdTree->isContained())
749 assert(simdTree->gtSIMDIntrinsicID == SIMDIntrinsicInit);
751 assert(info->dstCount == 1);
753 GenTree* op1 = simdTree->gtOp.gtOp1;
754 GenTree* op2 = simdTree->gtOp.gtOp2;
755 if (!op1->OperIs(GT_LIST))
757 info->srcCount += GetOperandInfo(op1);
759 if ((op2 != nullptr) && !op2->isContained())
761 info->srcCount += GetOperandInfo(op2);
764 switch (simdTree->gtSIMDIntrinsicID)
766 case SIMDIntrinsicInit:
767 assert(info->srcCount == (simdTree->gtGetOp1()->isContained() ? 0 : 1));
770 case SIMDIntrinsicCast:
771 case SIMDIntrinsicSqrt:
772 case SIMDIntrinsicAbs:
773 case SIMDIntrinsicConvertToSingle:
774 case SIMDIntrinsicConvertToInt32:
775 case SIMDIntrinsicConvertToDouble:
776 case SIMDIntrinsicConvertToInt64:
777 case SIMDIntrinsicWidenLo:
778 case SIMDIntrinsicWidenHi:
779 assert(info->srcCount == 1);
782 case SIMDIntrinsicGetItem:
784 op1 = simdTree->gtGetOp1();
785 op2 = simdTree->gtGetOp2();
787 // We have an object and an index, either of which may be contained.
788 if (!op2->IsCnsIntOrI() && (!op1->isContained() || op1->OperIsLocal()))
790 // If the index is not a constant and not contained or is a local
791 // we will need a general purpose register to calculate the address
792 info->internalIntCount = 1;
794 // internal register must not clobber input index
795 LocationInfoListNode* op2Info =
796 (op1->isContained()) ? useList.Begin() : useList.GetSecond(INDEBUG(op2));
797 op2Info->info.isDelayFree = true;
798 info->hasDelayFreeSrc = true;
801 if (!op2->IsCnsIntOrI() && (!op1->isContained()))
803 // If vector is not already in memory (contained) and the index is not a constant,
804 // we will use the SIMD temp location to store the vector.
805 compiler->getSIMDInitTempVarNum();
810 case SIMDIntrinsicAdd:
811 case SIMDIntrinsicSub:
812 case SIMDIntrinsicMul:
813 case SIMDIntrinsicDiv:
814 case SIMDIntrinsicBitwiseAnd:
815 case SIMDIntrinsicBitwiseAndNot:
816 case SIMDIntrinsicBitwiseOr:
817 case SIMDIntrinsicBitwiseXor:
818 case SIMDIntrinsicMin:
819 case SIMDIntrinsicMax:
820 case SIMDIntrinsicEqual:
821 case SIMDIntrinsicLessThan:
822 case SIMDIntrinsicGreaterThan:
823 case SIMDIntrinsicLessThanOrEqual:
824 case SIMDIntrinsicGreaterThanOrEqual:
825 assert(info->srcCount == 2);
828 case SIMDIntrinsicSetX:
829 case SIMDIntrinsicSetY:
830 case SIMDIntrinsicSetZ:
831 case SIMDIntrinsicSetW:
832 case SIMDIntrinsicNarrow:
833 assert(info->srcCount == 2);
835 // Op1 will write to dst before Op2 is free
836 useList.GetSecond(INDEBUG(simdTree->gtGetOp2()))->info.isDelayFree = true;
837 info->hasDelayFreeSrc = true;
840 case SIMDIntrinsicInitN:
842 var_types baseType = simdTree->gtSIMDBaseType;
843 info->srcCount = (short)(simdTree->gtSIMDSize / genTypeSize(baseType));
845 for (GenTree* list = op1; list != nullptr; list = list->gtGetOp2())
847 assert(list->OperGet() == GT_LIST);
848 GenTree* listItem = list->gtGetOp1();
849 assert(listItem->TypeGet() == baseType);
850 assert(!listItem->isContained());
851 appendLocationInfoToList(listItem);
854 assert(initCount == info->srcCount);
856 if (varTypeIsFloating(simdTree->gtSIMDBaseType))
858 // Need an internal register to stitch together all the values into a single vector in a SIMD reg.
859 info->setInternalCandidates(this, RBM_ALLFLOAT);
860 info->internalFloatCount = 1;
865 case SIMDIntrinsicInitArray:
866 // We have an array and an index, which may be contained.
867 assert(info->srcCount == (simdTree->gtGetOp2()->isContained() ? 1 : 2));
870 case SIMDIntrinsicOpEquality:
871 case SIMDIntrinsicOpInEquality:
872 assert(info->srcCount == (simdTree->gtGetOp2()->isContained() ? 1 : 2));
873 info->setInternalCandidates(this, RBM_ALLFLOAT);
874 info->internalFloatCount = 1;
877 case SIMDIntrinsicDotProduct:
878 assert(info->srcCount == 2);
879 info->setInternalCandidates(this, RBM_ALLFLOAT);
880 info->internalFloatCount = 1;
883 case SIMDIntrinsicSelect:
884 // TODO-ARM64-CQ Allow lowering to see SIMDIntrinsicSelect so we can generate BSL VC, VA, VB
885 // bsl target register must be VC. Reserve a temp in case we need to shuffle things.
886 // This will require a different approach, as GenTreeSIMD has only two operands.
887 assert(!"SIMDIntrinsicSelect not yet supported");
888 assert(info->srcCount == 3);
889 info->setInternalCandidates(this, RBM_ALLFLOAT);
890 info->internalFloatCount = 1;
893 case SIMDIntrinsicInitArrayX:
894 case SIMDIntrinsicInitFixed:
895 case SIMDIntrinsicCopyToArray:
896 case SIMDIntrinsicCopyToArrayX:
897 case SIMDIntrinsicNone:
898 case SIMDIntrinsicGetCount:
899 case SIMDIntrinsicGetOne:
900 case SIMDIntrinsicGetZero:
901 case SIMDIntrinsicGetAllOnes:
902 case SIMDIntrinsicGetX:
903 case SIMDIntrinsicGetY:
904 case SIMDIntrinsicGetZ:
905 case SIMDIntrinsicGetW:
906 case SIMDIntrinsicInstEquals:
907 case SIMDIntrinsicHWAccel:
908 case SIMDIntrinsicWiden:
909 case SIMDIntrinsicInvalid:
910 assert(!"These intrinsics should not be seen during register allocation");
914 noway_assert(!"Unimplemented SIMD node type.");
918 #endif // FEATURE_SIMD
920 #ifdef FEATURE_HW_INTRINSICS
921 #include "hwintrinsicArm64.h"
922 //------------------------------------------------------------------------
923 // BuildHWIntrinsic: Set the NodeInfo for a GT_HWIntrinsic tree.
926 // tree - The GT_HWIntrinsic node of interest
931 void LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree)
933 TreeNodeInfo* info = currentNodeInfo;
934 NamedIntrinsic intrinsicID = intrinsicTree->gtHWIntrinsicId;
936 GenTreeArgList* argList = nullptr;
937 GenTree* op1 = intrinsicTree->gtOp.gtOp1;
938 GenTree* op2 = intrinsicTree->gtOp.gtOp2;
940 if (op1->OperIs(GT_LIST))
942 argList = op1->AsArgList();
943 op1 = argList->Current();
944 op2 = argList->Rest()->Current();
946 for (GenTreeArgList* list = argList; list != nullptr; list = list->Rest())
948 info->srcCount += GetOperandInfo(list->Current());
953 info->srcCount += GetOperandInfo(op1);
956 info->srcCount += GetOperandInfo(op2);
960 switch (compiler->getHWIntrinsicInfo(intrinsicID).form)
962 case HWIntrinsicInfo::Sha1HashOp:
963 info->setInternalCandidates(this, RBM_ALLFLOAT);
964 info->internalFloatCount = 1;
965 if (!op2->isContained())
967 LocationInfoListNode* op2Info = useList.Begin()->Next();
968 op2Info->info.isDelayFree = true;
969 GenTree* op3 = intrinsicTree->gtOp.gtOp1->AsArgList()->Rest()->Rest()->Current();
970 assert(!op3->isContained());
971 LocationInfoListNode* op3Info = op2Info->Next();
972 op3Info->info.isDelayFree = true;
973 info->hasDelayFreeSrc = true;
974 info->isInternalRegDelayFree = true;
977 case HWIntrinsicInfo::SimdTernaryRMWOp:
978 if (!op2->isContained())
980 LocationInfoListNode* op2Info = useList.Begin()->Next();
981 op2Info->info.isDelayFree = true;
982 GenTree* op3 = intrinsicTree->gtOp.gtOp1->AsArgList()->Rest()->Rest()->Current();
983 assert(!op3->isContained());
984 LocationInfoListNode* op3Info = op2Info->Next();
985 op3Info->info.isDelayFree = true;
986 info->hasDelayFreeSrc = true;
989 case HWIntrinsicInfo::Sha1RotateOp:
990 info->setInternalCandidates(this, RBM_ALLFLOAT);
991 info->internalFloatCount = 1;
994 case HWIntrinsicInfo::SimdExtractOp:
995 case HWIntrinsicInfo::SimdInsertOp:
996 if (!op2->isContained())
998 // We need a temp to create a switch table
999 info->internalIntCount = 1;
1000 info->setInternalCandidates(this, allRegs(TYP_INT));
1010 #endif // _TARGET_ARM64_
1012 #endif // !LEGACY_BACKEND