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 ARM/ARM64 Code Generator Common Code XX
10 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
18 #ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator
20 #ifdef _TARGET_ARMARCH_ // This file is ONLY used for ARM and ARM64 architectures
27 //------------------------------------------------------------------------
28 // genCodeForTreeNode Generate code for a single node in the tree.
31 // All operands have been evaluated.
33 void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
35 regNumber targetReg = treeNode->gtRegNum;
36 var_types targetType = treeNode->TypeGet();
37 emitter* emit = getEmitter();
40 // Validate that all the operands for the current node are consumed in order.
41 // This is important because LSRA ensures that any necessary copies will be
43 lastConsumedNode = nullptr;
44 if (compiler->verbose)
46 unsigned seqNum = treeNode->gtSeqNum; // Useful for setting a conditional break in Visual Studio
47 compiler->gtDispLIRNode(treeNode, "Generating: ");
51 #ifdef _TARGET_ARM64_ // TODO-ARM: is this applicable to ARM32?
52 // Is this a node whose value is already in a register? LSRA denotes this by
53 // setting the GTF_REUSE_REG_VAL flag.
54 if (treeNode->IsReuseRegVal())
56 // For now, this is only used for constant nodes.
57 assert((treeNode->OperGet() == GT_CNS_INT) || (treeNode->OperGet() == GT_CNS_DBL));
58 JITDUMP(" TreeNode is marked ReuseReg\n");
61 #endif // _TARGET_ARM64_
63 // contained nodes are part of their parents for codegen purposes
64 // ex : immediates, most LEAs
65 if (treeNode->isContained())
70 switch (treeNode->gtOper)
75 getEmitter()->emitDisableGC();
79 // We should be seeing this only if profiler hook is needed
80 noway_assert(compiler->compIsProfilerHookNeeded());
82 #ifdef PROFILING_SUPPORTED
83 // Right now this node is used only for tail calls. In future if
84 // we intend to use it for Enter or Leave hooks, add a data member
85 // to this node indicating the kind of profiler hook. For example,
86 // helper number can be used.
87 genProfilingLeaveCallback(CORINFO_HELP_PROF_FCN_TAILCALL);
88 #endif // PROFILING_SUPPORTED
91 #endif // _TARGET_ARM64_
99 genSetRegToConst(targetReg, targetType, treeNode);
100 genProduceReg(treeNode);
105 genCodeForNegNot(treeNode);
112 genCodeForDivMod(treeNode->AsOp());
118 assert(varTypeIsIntegralOrI(treeNode));
127 #endif // _TARGET_ARM_
132 genConsumeOperands(treeNode->AsOp());
133 genCodeForBinary(treeNode);
140 genCodeForShift(treeNode);
147 genCodeForShiftLong(treeNode);
150 #endif // _TARGET_ARM_
153 genCodeForCast(treeNode->AsOp());
156 case GT_LCL_FLD_ADDR:
157 case GT_LCL_VAR_ADDR:
158 genCodeForLclAddr(treeNode);
162 genCodeForLclFld(treeNode->AsLclFld());
166 genCodeForLclVar(treeNode->AsLclVar());
169 case GT_STORE_LCL_FLD:
170 genCodeForStoreLclFld(treeNode->AsLclFld());
173 case GT_STORE_LCL_VAR:
174 genCodeForStoreLclVar(treeNode->AsLclVar());
183 // if we are here, it is the case where there is an LEA that cannot
184 // be folded into a parent instruction
185 genLeaInstruction(treeNode->AsAddrMode());
189 genCodeForIndir(treeNode->AsIndir());
192 #ifdef _TARGET_ARM64_
195 genCodeForMulHi(treeNode->AsOp());
199 genCodeForSwap(treeNode->AsOp());
203 genJmpMethod(treeNode);
206 #endif // _TARGET_ARM64_
209 genCkfinite(treeNode);
213 genIntrinsic(treeNode);
218 genSIMDIntrinsic(treeNode->AsSIMD());
220 #endif // FEATURE_SIMD
228 genCodeForCompare(treeNode->AsOp());
232 genCodeForJumpTrue(treeNode);
238 genCodeForJcc(treeNode->AsJumpCC());
241 #endif // _TARGET_ARM_
244 genCodeForReturnTrap(treeNode->AsOp());
248 genCodeForStoreInd(treeNode->AsStoreInd());
252 // This is handled at the time we call genConsumeReg() on the GT_COPY
262 genPutArgStk(treeNode->AsPutArgStk());
266 genPutArgReg(treeNode->AsOp());
270 genCallInstruction(treeNode->AsCall());
276 genLockedInstructions(treeNode->AsOp());
279 case GT_MEMORYBARRIER:
280 instGen_MemoryBarrier();
288 // do nothing - reload is just a marker.
289 // The parent node will call genConsumeReg on this which will trigger the unspill of this node's child
290 // into the register specified in this node.
300 case GT_ARR_BOUNDS_CHECK:
303 #endif // FEATURE_SIMD
304 genRangeCheck(treeNode);
308 genCodeForPhysReg(treeNode->AsPhysReg());
315 genCodeForNullCheck(treeNode->AsOp());
320 noway_assert(handlerGetsXcptnObj(compiler->compCurBB->bbCatchTyp));
322 /* Catch arguments get passed in a register. genCodeForBBlist()
323 would have marked it as holding a GC object, but not used. */
325 noway_assert(gcInfo.gcRegGCrefSetCur & RBM_EXCEPTION_OBJECT);
326 genConsumeReg(treeNode);
329 case GT_PINVOKE_PROLOG:
330 noway_assert(((gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & ~fullIntArgRegMask()) == 0);
332 // the runtime side requires the codegen here to be consistent
333 emit->emitDisableRandomNops();
337 genPendingCallLabel = genCreateTempLabel();
338 treeNode->gtLabel.gtLabBB = genPendingCallLabel;
339 #if defined(_TARGET_ARM_)
340 emit->emitIns_J_R(INS_adr, EA_PTRSIZE, genPendingCallLabel, targetReg);
341 #elif defined(_TARGET_ARM64_)
342 emit->emitIns_R_L(INS_adr, EA_PTRSIZE, genPendingCallLabel, targetReg);
347 case GT_STORE_DYN_BLK:
349 genCodeForStoreBlk(treeNode->AsBlk());
353 genJumpTable(treeNode);
356 case GT_SWITCH_TABLE:
357 genTableBasedSwitch(treeNode);
361 genCodeForArrIndex(treeNode->AsArrIndex());
365 genCodeForArrOffset(treeNode->AsArrOffs());
370 case GT_CLS_VAR_ADDR:
371 emit->emitIns_R_C(INS_lea, EA_PTRSIZE, targetReg, treeNode->gtClsVar.gtClsVarHnd, 0);
372 genProduceReg(treeNode);
376 assert(treeNode->isUsedFromReg());
377 genConsumeRegs(treeNode);
380 #endif // _TARGET_ARM_
383 // Do nothing; these nodes are simply markers for debug info.
390 _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
391 GenTree::NodeName(treeNode->OperGet()));
394 NYI("unimplemented node");
401 //------------------------------------------------------------------------
402 // genSetRegToIcon: Generate code that will set the given register to the integer constant.
404 void CodeGen::genSetRegToIcon(regNumber reg, ssize_t val, var_types type, insFlags flags)
406 // Reg cannot be a FP reg
407 assert(!genIsValidFloatReg(reg));
409 // The only TYP_REF constant that can come this path is a managed 'null' since it is not
410 // relocatable. Other ref type constants (e.g. string objects) go through a different
412 noway_assert(type != TYP_REF || val == 0);
414 instGen_Set_Reg_To_Imm(emitActualTypeSize(type), reg, val, flags);
417 //---------------------------------------------------------------------
418 // genIntrinsic - generate code for a given intrinsic
421 // treeNode - the GT_INTRINSIC node
426 void CodeGen::genIntrinsic(GenTreePtr treeNode)
428 assert(treeNode->OperIs(GT_INTRINSIC));
430 // Both operand and its result must be of the same floating point type.
431 GenTreePtr srcNode = treeNode->gtOp.gtOp1;
432 assert(varTypeIsFloating(srcNode));
433 assert(srcNode->TypeGet() == treeNode->TypeGet());
435 // Right now only Abs/Round/Sqrt are treated as math intrinsics.
437 switch (treeNode->gtIntrinsic.gtIntrinsicId)
439 case CORINFO_INTRINSIC_Abs:
440 genConsumeOperands(treeNode->AsOp());
441 getEmitter()->emitInsBinary(INS_ABS, emitTypeSize(treeNode), treeNode, srcNode);
444 case CORINFO_INTRINSIC_Round:
445 NYI_ARM("genIntrinsic for round - not implemented yet");
446 genConsumeOperands(treeNode->AsOp());
447 getEmitter()->emitInsBinary(INS_ROUND, emitTypeSize(treeNode), treeNode, srcNode);
450 case CORINFO_INTRINSIC_Sqrt:
451 genConsumeOperands(treeNode->AsOp());
452 getEmitter()->emitInsBinary(INS_SQRT, emitTypeSize(treeNode), treeNode, srcNode);
456 assert(!"genIntrinsic: Unsupported intrinsic");
460 genProduceReg(treeNode);
463 //---------------------------------------------------------------------
464 // genPutArgStk - generate code for a GT_PUTARG_STK node
467 // treeNode - the GT_PUTARG_STK node
472 void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
474 assert(treeNode->OperIs(GT_PUTARG_STK));
475 var_types targetType = treeNode->TypeGet();
476 GenTreePtr source = treeNode->gtOp1;
477 emitter* emit = getEmitter();
479 // This is the varNum for our store operations,
480 // typically this is the varNum for the Outgoing arg space
481 // When we are generating a tail call it will be the varNum for arg0
482 unsigned varNumOut = (unsigned)-1;
483 unsigned argOffsetMax = (unsigned)-1; // Records the maximum size of this area for assert checks
485 // Get argument offset to use with 'varNumOut'
486 // Here we cross check that argument offset hasn't changed from lowering to codegen since
487 // we are storing arg slot number in GT_PUTARG_STK node in lowering phase.
488 unsigned argOffsetOut = treeNode->gtSlotNum * TARGET_POINTER_SIZE;
491 fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(treeNode->gtCall, treeNode);
492 assert(curArgTabEntry);
493 assert(argOffsetOut == (curArgTabEntry->slotNum * TARGET_POINTER_SIZE));
496 // Whether to setup stk arg in incoming or out-going arg area?
497 // Fast tail calls implemented as epilog+jmp = stk arg is setup in incoming arg area.
498 // All other calls - stk arg is setup in out-going arg area.
499 if (treeNode->putInIncomingArgArea())
501 NYI_ARM("genPutArgStk: fast tail call");
503 #ifdef _TARGET_ARM64_
504 varNumOut = getFirstArgWithStackSlot();
505 argOffsetMax = compiler->compArgSize;
506 #if FEATURE_FASTTAILCALL
507 // This must be a fast tail call.
508 assert(treeNode->gtCall->IsFastTailCall());
510 // Since it is a fast tail call, the existence of first incoming arg is guaranteed
511 // because fast tail call requires that in-coming arg area of caller is >= out-going
512 // arg area required for tail call.
513 LclVarDsc* varDsc = &(compiler->lvaTable[varNumOut]);
514 assert(varDsc != nullptr);
515 #endif // FEATURE_FASTTAILCALL
516 #endif // _TARGET_ARM64_
520 varNumOut = compiler->lvaOutgoingArgSpaceVar;
521 argOffsetMax = compiler->lvaOutgoingArgSpaceSize;
524 bool isStruct = (targetType == TYP_STRUCT) || (source->OperGet() == GT_FIELD_LIST);
526 if (!isStruct) // a normal non-Struct argument
528 instruction storeIns = ins_Store(targetType);
529 emitAttr storeAttr = emitTypeSize(targetType);
531 // If it is contained then source must be the integer constant zero
532 if (source->isContained())
534 assert(source->OperGet() == GT_CNS_INT);
535 assert(source->AsIntConCommon()->IconValue() == 0);
536 NYI_ARM("genPutArgStk: contained zero source");
538 #ifdef _TARGET_ARM64_
539 emit->emitIns_S_R(storeIns, storeAttr, REG_ZR, varNumOut, argOffsetOut);
540 #endif // _TARGET_ARM64_
544 genConsumeReg(source);
545 emit->emitIns_S_R(storeIns, storeAttr, source->gtRegNum, varNumOut, argOffsetOut);
547 argOffsetOut += EA_SIZE_IN_BYTES(storeAttr);
548 assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
550 else // We have some kind of a struct argument
552 assert(source->isContained()); // We expect that this node was marked as contained in Lower
554 if (source->OperGet() == GT_FIELD_LIST)
556 // Deal with the multi register passed struct args.
557 GenTreeFieldList* fieldListPtr = source->AsFieldList();
559 // Evaluate each of the GT_FIELD_LIST items into their register
560 // and store their register into the outgoing argument area
561 for (; fieldListPtr != nullptr; fieldListPtr = fieldListPtr->Rest())
563 GenTreePtr nextArgNode = fieldListPtr->gtOp.gtOp1;
564 genConsumeReg(nextArgNode);
566 regNumber reg = nextArgNode->gtRegNum;
567 var_types type = nextArgNode->TypeGet();
568 emitAttr attr = emitTypeSize(type);
570 // Emit store instructions to store the registers produced by the GT_FIELD_LIST into the outgoing
572 emit->emitIns_S_R(ins_Store(type), attr, reg, varNumOut, argOffsetOut);
573 argOffsetOut += EA_SIZE_IN_BYTES(attr);
574 assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
577 else // We must have a GT_OBJ or a GT_LCL_VAR
579 noway_assert((source->OperGet() == GT_LCL_VAR) || (source->OperGet() == GT_OBJ));
581 NYI_ARM("genPutArgStk: GT_OBJ or GT_LCL_VAR source of struct type");
583 #ifdef _TARGET_ARM64_
585 var_types targetType = source->TypeGet();
586 noway_assert(varTypeIsStruct(targetType));
588 // We will copy this struct to the stack, possibly using a ldp instruction
589 // Setup loReg and hiReg from the internal registers that we reserved in lower.
591 regNumber loReg = treeNode->ExtractTempReg();
592 regNumber hiReg = treeNode->GetSingleTempReg();
593 regNumber addrReg = REG_NA;
595 GenTreeLclVarCommon* varNode = nullptr;
596 GenTreePtr addrNode = nullptr;
598 if (source->OperGet() == GT_LCL_VAR)
600 varNode = source->AsLclVarCommon();
602 else // we must have a GT_OBJ
604 assert(source->OperGet() == GT_OBJ);
606 addrNode = source->gtOp.gtOp1;
608 // addrNode can either be a GT_LCL_VAR_ADDR or an address expression
610 if (addrNode->OperGet() == GT_LCL_VAR_ADDR)
612 // We have a GT_OBJ(GT_LCL_VAR_ADDR)
614 // We will treat this case the same as above
615 // (i.e if we just had this GT_LCL_VAR directly as the source)
616 // so update 'source' to point this GT_LCL_VAR_ADDR node
617 // and continue to the codegen for the LCL_VAR node below
619 varNode = addrNode->AsLclVarCommon();
624 // Either varNode or addrNOde must have been setup above,
625 // the xor ensures that only one of the two is setup, not both
626 assert((varNode != nullptr) ^ (addrNode != nullptr));
628 BYTE gcPtrs[MAX_ARG_REG_COUNT] = {}; // TYPE_GC_NONE = 0
629 unsigned gcPtrCount; // The count of GC pointers in the struct
633 // This is the varNum for our load operations,
634 // only used when we have a multireg struct with a LclVar source
635 unsigned varNumInp = BAD_VAR_NUM;
637 // Setup the structSize, isHFa, and gcPtrCount
638 if (varNode != nullptr)
640 varNumInp = varNode->gtLclNum;
641 assert(varNumInp < compiler->lvaCount);
642 LclVarDsc* varDsc = &compiler->lvaTable[varNumInp];
644 assert(varDsc->lvType == TYP_STRUCT);
645 assert(varDsc->lvOnFrame); // This struct also must live in the stack frame
646 assert(!varDsc->lvRegister); // And it can't live in a register (SIMD)
648 structSize = varDsc->lvSize(); // This yields the roundUp size, but that is fine
649 // as that is how much stack is allocated for this LclVar
650 isHfa = varDsc->lvIsHfa();
651 gcPtrCount = varDsc->lvStructGcCount;
652 for (unsigned i = 0; i < gcPtrCount; ++i)
653 gcPtrs[i] = varDsc->lvGcLayout[i];
655 else // addrNode is used
657 assert(addrNode != nullptr);
659 // Generate code to load the address that we need into a register
660 genConsumeAddress(addrNode);
661 addrReg = addrNode->gtRegNum;
663 // If addrReg equal to loReg, swap(loReg, hiReg)
664 // This reduces code complexity by only supporting one addrReg overwrite case
665 if (loReg == addrReg)
671 CORINFO_CLASS_HANDLE objClass = source->gtObj.gtClass;
673 structSize = compiler->info.compCompHnd->getClassSize(objClass);
674 isHfa = compiler->IsHfa(objClass);
675 gcPtrCount = compiler->info.compCompHnd->getClassGClayout(objClass, &gcPtrs[0]);
678 // If we have an HFA we can't have any GC pointers,
679 // if not then the max size for the the struct is 16 bytes
682 noway_assert(gcPtrCount == 0);
686 noway_assert(structSize <= 2 * TARGET_POINTER_SIZE);
689 noway_assert(structSize <= MAX_PASS_MULTIREG_BYTES);
691 // For a >= 16-byte structSize we will generate a ldp and stp instruction each loop
693 // stp x2, x3, [sp, #16]
695 int remainingSize = structSize;
696 unsigned structOffset = 0;
697 unsigned nextIndex = 0;
699 while (remainingSize >= 2 * TARGET_POINTER_SIZE)
701 var_types type0 = compiler->getJitGCType(gcPtrs[nextIndex + 0]);
702 var_types type1 = compiler->getJitGCType(gcPtrs[nextIndex + 1]);
704 if (varNode != nullptr)
706 // Load from our varNumImp source
707 emit->emitIns_R_R_S_S(INS_ldp, emitTypeSize(type0), emitTypeSize(type1), loReg, hiReg, varNumInp,
712 // check for case of destroying the addrRegister while we still need it
713 assert(loReg != addrReg);
714 noway_assert((remainingSize == 2 * TARGET_POINTER_SIZE) || (hiReg != addrReg));
716 // Load from our address expression source
717 emit->emitIns_R_R_R_I(INS_ldp, emitTypeSize(type0), loReg, hiReg, addrReg, structOffset,
718 INS_OPTS_NONE, emitTypeSize(type0));
721 // Emit stp instruction to store the two registers into the outgoing argument area
722 emit->emitIns_S_S_R_R(INS_stp, emitTypeSize(type0), emitTypeSize(type1), loReg, hiReg, varNumOut,
724 argOffsetOut += (2 * TARGET_POINTER_SIZE); // We stored 16-bytes of the struct
725 assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
727 remainingSize -= (2 * TARGET_POINTER_SIZE); // We loaded 16-bytes of the struct
728 structOffset += (2 * TARGET_POINTER_SIZE);
732 // For a 12-byte structSize we will we will generate two load instructions
738 var_types nextType = compiler->getJitGCType(gcPtrs[nextIndex]);
739 emitAttr nextAttr = emitTypeSize(nextType);
741 while (remainingSize > 0)
743 if (remainingSize >= TARGET_POINTER_SIZE)
745 remainingSize -= TARGET_POINTER_SIZE;
747 if (varNode != nullptr)
749 // Load from our varNumImp source
750 emit->emitIns_R_S(ins_Load(nextType), nextAttr, loReg, varNumInp, structOffset);
754 assert(loReg != addrReg);
756 // Load from our address expression source
757 emit->emitIns_R_R_I(ins_Load(nextType), nextAttr, loReg, addrReg, structOffset);
759 // Emit a store instruction to store the register into the outgoing argument area
760 emit->emitIns_S_R(ins_Store(nextType), nextAttr, loReg, varNumOut, argOffsetOut);
761 argOffsetOut += EA_SIZE_IN_BYTES(nextAttr);
762 assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
764 structOffset += TARGET_POINTER_SIZE;
766 nextType = compiler->getJitGCType(gcPtrs[nextIndex]);
767 nextAttr = emitTypeSize(nextType);
769 else // (remainingSize < TARGET_POINTER_SIZE)
771 int loadSize = remainingSize;
774 // We should never have to do a non-pointer sized load when we have a LclVar source
775 assert(varNode == nullptr);
777 // the left over size is smaller than a pointer and thus can never be a GC type
778 assert(varTypeIsGC(nextType) == false);
780 var_types loadType = TYP_UINT;
783 loadType = TYP_UBYTE;
785 else if (loadSize == 2)
787 loadType = TYP_USHORT;
791 // Need to handle additional loadSize cases here
792 noway_assert(loadSize == 4);
795 instruction loadIns = ins_Load(loadType);
796 emitAttr loadAttr = emitAttr(loadSize);
798 assert(loReg != addrReg);
800 emit->emitIns_R_R_I(loadIns, loadAttr, loReg, addrReg, structOffset);
802 // Emit a store instruction to store the register into the outgoing argument area
803 emit->emitIns_S_R(ins_Store(loadType), loadAttr, loReg, varNumOut, argOffsetOut);
804 argOffsetOut += EA_SIZE_IN_BYTES(loadAttr);
805 assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
809 #endif // _TARGET_ARM64_
814 //---------------------------------------------------------------------
815 // genPutArgReg - generate code for a GT_PUTARG_REG node
818 // tree - the GT_PUTARG_REG node
823 void CodeGen::genPutArgReg(GenTreeOp* tree)
825 assert(tree->OperIs(GT_PUTARG_REG));
826 var_types targetType = tree->TypeGet();
827 regNumber targetReg = tree->gtRegNum;
829 // Any TYP_STRUCT register args should have been removed by fgMorphMultiregStructArg
830 assert(targetType != TYP_STRUCT);
832 // We have a normal non-Struct targetType
834 GenTree* op1 = tree->gtOp1;
837 // If child node is not already in the register we need, move it
838 if (targetReg != op1->gtRegNum)
840 inst_RV_RV(ins_Copy(targetType), targetReg, op1->gtRegNum, targetType);
846 //----------------------------------------------------------------------------------
847 // genMultiRegCallStoreToLocal: store multi-reg return value of a call node to a local
850 // treeNode - Gentree of GT_STORE_LCL_VAR
856 // The child of store is a multi-reg call node.
857 // genProduceReg() on treeNode is made by caller of this routine.
859 void CodeGen::genMultiRegCallStoreToLocal(GenTreePtr treeNode)
861 assert(treeNode->OperGet() == GT_STORE_LCL_VAR);
863 #if defined(_TARGET_ARM_)
864 // Longs are returned in two return registers on Arm32.
865 assert(varTypeIsLong(treeNode));
866 #elif defined(_TARGET_ARM64_)
867 // Structs of size >=9 and <=16 are returned in two return registers on ARM64 and HFAs.
868 assert(varTypeIsStruct(treeNode));
871 // Assumption: current implementation requires that a multi-reg
872 // var in 'var = call' is flagged as lvIsMultiRegRet to prevent it from
874 unsigned lclNum = treeNode->AsLclVarCommon()->gtLclNum;
875 LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]);
876 noway_assert(varDsc->lvIsMultiRegRet);
878 GenTree* op1 = treeNode->gtGetOp1();
879 GenTree* actualOp1 = op1->gtSkipReloadOrCopy();
880 GenTreeCall* call = actualOp1->AsCall();
881 assert(call->HasMultiRegRetVal());
885 ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc();
886 unsigned regCount = pRetTypeDesc->GetReturnRegCount();
888 if (treeNode->gtRegNum != REG_NA)
890 // Right now the only enregistrable multi-reg return types supported are SIMD types.
891 assert(varTypeIsSIMD(treeNode));
892 NYI("GT_STORE_LCL_VAR of a SIMD enregisterable struct");
898 for (unsigned i = 0; i < regCount; ++i)
900 var_types type = pRetTypeDesc->GetReturnRegType(i);
901 regNumber reg = call->GetRegNumByIdx(i);
902 if (op1->IsCopyOrReload())
904 // GT_COPY/GT_RELOAD will have valid reg for those positions
905 // that need to be copied or reloaded.
906 regNumber reloadReg = op1->AsCopyOrReload()->GetRegNumByIdx(i);
907 if (reloadReg != REG_NA)
913 assert(reg != REG_NA);
914 getEmitter()->emitIns_S_R(ins_Store(type), emitTypeSize(type), reg, lclNum, offset);
915 offset += genTypeSize(type);
918 varDsc->lvRegNum = REG_STK;
922 //------------------------------------------------------------------------
923 // genRangeCheck: generate code for GT_ARR_BOUNDS_CHECK node.
925 void CodeGen::genRangeCheck(GenTreePtr oper)
928 noway_assert(oper->OperGet() == GT_ARR_BOUNDS_CHECK || oper->OperGet() == GT_SIMD_CHK);
929 #else // !FEATURE_SIMD
930 noway_assert(oper->OperGet() == GT_ARR_BOUNDS_CHECK);
931 #endif // !FEATURE_SIMD
933 GenTreeBoundsChk* bndsChk = oper->AsBoundsChk();
935 GenTreePtr arrLen = bndsChk->gtArrLen;
936 GenTreePtr arrIndex = bndsChk->gtIndex;
937 GenTreePtr arrRef = NULL;
942 emitJumpKind jmpKind;
944 genConsumeRegs(arrIndex);
945 genConsumeRegs(arrLen);
947 if (arrIndex->isContainedIntOrIImmed())
949 // To encode using a cmp immediate, we place the
950 // constant operand in the second position
953 jmpKind = genJumpKindForOper(GT_LE, CK_UNSIGNED);
959 jmpKind = genJumpKindForOper(GT_GE, CK_UNSIGNED);
962 getEmitter()->emitInsBinary(INS_cmp, EA_4BYTE, src1, src2);
963 genJumpToThrowHlpBlk(jmpKind, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
966 //---------------------------------------------------------------------
967 // genCodeForPhysReg - generate code for a GT_PHYSREG node
970 // tree - the GT_PHYSREG node
975 void CodeGen::genCodeForPhysReg(GenTreePhysReg* tree)
977 assert(tree->OperIs(GT_PHYSREG));
978 var_types targetType = tree->TypeGet();
979 regNumber targetReg = tree->gtRegNum;
981 if (targetReg != tree->gtSrcReg)
983 inst_RV_RV(ins_Copy(targetType), targetReg, tree->gtSrcReg, targetType);
984 genTransferRegGCState(targetReg, tree->gtSrcReg);
990 //---------------------------------------------------------------------
991 // genCodeForNullCheck - generate code for a GT_NULLCHECK node
994 // tree - the GT_NULLCHECK node
999 void CodeGen::genCodeForNullCheck(GenTreeOp* tree)
1001 assert(tree->OperIs(GT_NULLCHECK));
1002 assert(!tree->gtOp1->isContained());
1003 regNumber addrReg = genConsumeReg(tree->gtOp1);
1005 #ifdef _TARGET_ARM64_
1006 regNumber targetReg = REG_ZR;
1008 regNumber targetReg = tree->gtRegNum;
1011 getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, targetReg, addrReg, 0);
1014 //------------------------------------------------------------------------
1015 // genOffsetOfMDArrayLowerBound: Returns the offset from the Array object to the
1016 // lower bound for the given dimension.
1019 // elemType - the element type of the array
1020 // rank - the rank of the array
1021 // dimension - the dimension for which the lower bound offset will be returned.
1025 // TODO-Cleanup: move to CodeGenCommon.cpp
1028 unsigned CodeGen::genOffsetOfMDArrayLowerBound(var_types elemType, unsigned rank, unsigned dimension)
1030 // Note that the lower bound and length fields of the Array object are always TYP_INT
1031 return compiler->eeGetArrayDataOffset(elemType) + genTypeSize(TYP_INT) * (dimension + rank);
1034 //------------------------------------------------------------------------
1035 // genOffsetOfMDArrayLength: Returns the offset from the Array object to the
1036 // size for the given dimension.
1039 // elemType - the element type of the array
1040 // rank - the rank of the array
1041 // dimension - the dimension for which the lower bound offset will be returned.
1045 // TODO-Cleanup: move to CodeGenCommon.cpp
1048 unsigned CodeGen::genOffsetOfMDArrayDimensionSize(var_types elemType, unsigned rank, unsigned dimension)
1050 // Note that the lower bound and length fields of the Array object are always TYP_INT
1051 return compiler->eeGetArrayDataOffset(elemType) + genTypeSize(TYP_INT) * dimension;
1054 //------------------------------------------------------------------------
1055 // genCodeForArrIndex: Generates code to bounds check the index for one dimension of an array reference,
1056 // producing the effective index by subtracting the lower bound.
1059 // arrIndex - the node for which we're generating code
1064 void CodeGen::genCodeForArrIndex(GenTreeArrIndex* arrIndex)
1066 emitter* emit = getEmitter();
1067 GenTreePtr arrObj = arrIndex->ArrObj();
1068 GenTreePtr indexNode = arrIndex->IndexExpr();
1069 regNumber arrReg = genConsumeReg(arrObj);
1070 regNumber indexReg = genConsumeReg(indexNode);
1071 regNumber tgtReg = arrIndex->gtRegNum;
1072 noway_assert(tgtReg != REG_NA);
1074 // We will use a temp register to load the lower bound and dimension size values.
1076 regNumber tmpReg = arrIndex->GetSingleTempReg();
1077 assert(tgtReg != tmpReg);
1079 unsigned dim = arrIndex->gtCurrDim;
1080 unsigned rank = arrIndex->gtArrRank;
1081 var_types elemType = arrIndex->gtArrElemType;
1084 offset = genOffsetOfMDArrayLowerBound(elemType, rank, dim);
1085 emit->emitIns_R_R_I(ins_Load(TYP_INT), EA_PTRSIZE, tmpReg, arrReg, offset); // a 4 BYTE sign extending load
1086 emit->emitIns_R_R_R(INS_sub, EA_4BYTE, tgtReg, indexReg, tmpReg);
1088 offset = genOffsetOfMDArrayDimensionSize(elemType, rank, dim);
1089 emit->emitIns_R_R_I(ins_Load(TYP_INT), EA_PTRSIZE, tmpReg, arrReg, offset); // a 4 BYTE sign extending load
1090 emit->emitIns_R_R(INS_cmp, EA_4BYTE, tgtReg, tmpReg);
1092 emitJumpKind jmpGEU = genJumpKindForOper(GT_GE, CK_UNSIGNED);
1093 genJumpToThrowHlpBlk(jmpGEU, SCK_RNGCHK_FAIL);
1095 genProduceReg(arrIndex);
1098 //------------------------------------------------------------------------
1099 // genCodeForArrOffset: Generates code to compute the flattened array offset for
1100 // one dimension of an array reference:
1101 // result = (prevDimOffset * dimSize) + effectiveIndex
1102 // where dimSize is obtained from the arrObj operand
1105 // arrOffset - the node for which we're generating code
1111 // dimSize and effectiveIndex are always non-negative, the former by design,
1112 // and the latter because it has been normalized to be zero-based.
1114 void CodeGen::genCodeForArrOffset(GenTreeArrOffs* arrOffset)
1116 GenTreePtr offsetNode = arrOffset->gtOffset;
1117 GenTreePtr indexNode = arrOffset->gtIndex;
1118 regNumber tgtReg = arrOffset->gtRegNum;
1120 noway_assert(tgtReg != REG_NA);
1122 if (!offsetNode->IsIntegralConst(0))
1124 emitter* emit = getEmitter();
1125 regNumber offsetReg = genConsumeReg(offsetNode);
1126 regNumber indexReg = genConsumeReg(indexNode);
1127 regNumber arrReg = genConsumeReg(arrOffset->gtArrObj);
1128 noway_assert(offsetReg != REG_NA);
1129 noway_assert(indexReg != REG_NA);
1130 noway_assert(arrReg != REG_NA);
1132 regNumber tmpReg = arrOffset->GetSingleTempReg();
1134 unsigned dim = arrOffset->gtCurrDim;
1135 unsigned rank = arrOffset->gtArrRank;
1136 var_types elemType = arrOffset->gtArrElemType;
1137 unsigned offset = genOffsetOfMDArrayDimensionSize(elemType, rank, dim);
1139 // Load tmpReg with the dimension size and evaluate
1140 // tgtReg = offsetReg*tmpReg + indexReg.
1141 emit->emitIns_R_R_I(ins_Load(TYP_INT), EA_PTRSIZE, tmpReg, arrReg, offset);
1142 emit->emitIns_R_R_R_R(INS_MULADD, EA_PTRSIZE, tgtReg, tmpReg, offsetReg, indexReg);
1146 regNumber indexReg = genConsumeReg(indexNode);
1147 if (indexReg != tgtReg)
1149 inst_RV_RV(INS_mov, tgtReg, indexReg, TYP_INT);
1152 genProduceReg(arrOffset);
1155 //------------------------------------------------------------------------
1156 // indirForm: Make a temporary indir we can feed to pattern matching routines
1157 // in cases where we don't want to instantiate all the indirs that happen.
1159 GenTreeIndir CodeGen::indirForm(var_types type, GenTree* base)
1161 GenTreeIndir i(GT_IND, type, base, nullptr);
1162 i.gtRegNum = REG_NA;
1163 // has to be nonnull (because contained nodes can't be the last in block)
1164 // but don't want it to be a valid pointer
1165 i.gtNext = (GenTree*)(-1);
1169 //------------------------------------------------------------------------
1170 // intForm: Make a temporary int we can feed to pattern matching routines
1171 // in cases where we don't want to instantiate.
1173 GenTreeIntCon CodeGen::intForm(var_types type, ssize_t value)
1175 GenTreeIntCon i(type, value);
1176 i.gtRegNum = REG_NA;
1177 // has to be nonnull (because contained nodes can't be the last in block)
1178 // but don't want it to be a valid pointer
1179 i.gtNext = (GenTree*)(-1);
1183 //------------------------------------------------------------------------
1184 // genCodeForShift: Generates the code sequence for a GenTree node that
1185 // represents a bit shift or rotate operation (<<, >>, >>>, rol, ror).
1188 // tree - the bit shift node (that specifies the type of bit shift to perform).
1191 // a) All GenTrees are register allocated.
1193 void CodeGen::genCodeForShift(GenTreePtr tree)
1195 var_types targetType = tree->TypeGet();
1196 genTreeOps oper = tree->OperGet();
1197 instruction ins = genGetInsForOper(oper, targetType);
1198 emitAttr size = emitTypeSize(tree);
1200 assert(tree->gtRegNum != REG_NA);
1202 genConsumeOperands(tree->AsOp());
1204 GenTreePtr operand = tree->gtGetOp1();
1205 GenTreePtr shiftBy = tree->gtGetOp2();
1206 if (!shiftBy->IsCnsIntOrI())
1208 getEmitter()->emitIns_R_R_R(ins, size, tree->gtRegNum, operand->gtRegNum, shiftBy->gtRegNum);
1212 unsigned immWidth = emitter::getBitWidth(size); // For ARM64, immWidth will be set to 32 or 64
1213 ssize_t shiftByImm = shiftBy->gtIntCon.gtIconVal & (immWidth - 1);
1215 getEmitter()->emitIns_R_R_I(ins, size, tree->gtRegNum, operand->gtRegNum, shiftByImm);
1218 genProduceReg(tree);
1221 //------------------------------------------------------------------------
1222 // genCodeForCast: Generates the code for GT_CAST.
1225 // tree - the GT_CAST node.
1227 void CodeGen::genCodeForCast(GenTreeOp* tree)
1229 assert(tree->OperIs(GT_CAST));
1231 var_types targetType = tree->TypeGet();
1232 regNumber targetReg = tree->gtRegNum;
1234 // Cast is never contained (?)
1235 noway_assert(targetReg != REG_NA);
1237 if (varTypeIsFloating(targetType) && varTypeIsFloating(tree->gtOp1))
1239 // Casts float/double <--> double/float
1240 genFloatToFloatCast(tree);
1242 else if (varTypeIsFloating(tree->gtOp1))
1244 // Casts float/double --> int32/int64
1245 genFloatToIntCast(tree);
1247 else if (varTypeIsFloating(targetType))
1249 // Casts int32/uint32/int64/uint64 --> float/double
1250 genIntToFloatCast(tree);
1254 // Casts int <--> int
1255 genIntToIntCast(tree);
1257 // The per-case functions call genProduceReg()
1260 //------------------------------------------------------------------------
1261 // genCodeForLclAddr: Generates the code for GT_LCL_FLD_ADDR/GT_LCL_VAR_ADDR.
1266 void CodeGen::genCodeForLclAddr(GenTree* tree)
1268 assert(tree->OperIs(GT_LCL_FLD_ADDR, GT_LCL_VAR_ADDR));
1270 var_types targetType = tree->TypeGet();
1271 regNumber targetReg = tree->gtRegNum;
1273 // Address of a local var. This by itself should never be allocated a register.
1274 // If it is worth storing the address in a register then it should be cse'ed into
1275 // a temp and that would be allocated a register.
1276 noway_assert(targetType == TYP_BYREF);
1277 noway_assert(!tree->InReg());
1279 inst_RV_TT(INS_lea, targetReg, tree, 0, EA_BYREF);
1280 genProduceReg(tree);
1283 //------------------------------------------------------------------------
1284 // genCodeForLclFld: Produce code for a GT_LCL_FLD node.
1287 // tree - the GT_LCL_FLD node
1289 void CodeGen::genCodeForLclFld(GenTreeLclFld* tree)
1291 assert(tree->OperIs(GT_LCL_FLD));
1293 var_types targetType = tree->TypeGet();
1294 regNumber targetReg = tree->gtRegNum;
1295 emitter* emit = getEmitter();
1297 NYI_IF(targetType == TYP_STRUCT, "GT_LCL_FLD: struct load local field not supported");
1298 NYI_IF(targetReg == REG_NA, "GT_LCL_FLD: load local field not into a register is not supported");
1300 emitAttr size = emitTypeSize(targetType);
1301 unsigned offs = tree->gtLclOffs;
1302 unsigned varNum = tree->gtLclNum;
1303 assert(varNum < compiler->lvaCount);
1305 if (varTypeIsFloating(targetType))
1309 NYI("GT_LCL_FLD with register to register Floating point move");
1313 emit->emitIns_R_S(ins_Load(targetType), size, targetReg, varNum, offs);
1318 #ifdef _TARGET_ARM64_
1319 size = EA_SET_SIZE(size, EA_8BYTE);
1320 #endif // _TARGET_ARM64_
1321 emit->emitIns_R_S(ins_Move_Extend(targetType, tree->InReg()), size, targetReg, varNum, offs);
1324 genProduceReg(tree);
1327 //------------------------------------------------------------------------
1328 // genCodeForIndir: Produce code for a GT_IND node.
1331 // tree - the GT_IND node
1333 void CodeGen::genCodeForIndir(GenTreeIndir* tree)
1335 assert(tree->OperIs(GT_IND));
1337 var_types targetType = tree->TypeGet();
1338 regNumber targetReg = tree->gtRegNum;
1339 emitter* emit = getEmitter();
1341 genConsumeAddress(tree->Addr());
1342 emit->emitInsLoadStoreOp(ins_Load(targetType), emitTypeSize(tree), targetReg, tree);
1343 genProduceReg(tree);
1345 if (tree->gtFlags & GTF_IND_VOLATILE)
1347 // issue a full memory barrier after a volatile LdInd operation
1348 instGen_MemoryBarrier();
1352 // Generate code for a CpBlk node by the means of the VM memcpy helper call
1354 // a) The size argument of the CpBlk is not an integer constant
1355 // b) The size argument is a constant but is larger than CPBLK_MOVS_LIMIT bytes.
1356 void CodeGen::genCodeForCpBlk(GenTreeBlk* cpBlkNode)
1358 // Make sure we got the arguments of the cpblk operation in the right registers
1359 unsigned blockSize = cpBlkNode->Size();
1360 GenTreePtr dstAddr = cpBlkNode->Addr();
1361 assert(!dstAddr->isContained());
1363 genConsumeBlockOp(cpBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2);
1365 #ifdef _TARGET_ARM64_
1368 assert(blockSize > CPBLK_UNROLL_LIMIT);
1370 #endif // _TARGET_ARM64_
1372 if (cpBlkNode->gtFlags & GTF_BLK_VOLATILE)
1374 // issue a full memory barrier before & after a volatile CpBlkUnroll operation
1375 instGen_MemoryBarrier();
1378 genEmitHelperCall(CORINFO_HELP_MEMCPY, 0, EA_UNKNOWN);
1380 if (cpBlkNode->gtFlags & GTF_BLK_VOLATILE)
1382 // issue a full memory barrier before & after a volatile CpBlkUnroll operation
1383 instGen_MemoryBarrier();
1387 // Generates code for InitBlk by calling the VM memset helper function.
1389 // a) The size argument of the InitBlk is not an integer constant.
1390 // b) The size argument of the InitBlk is >= INITBLK_STOS_LIMIT bytes.
1391 void CodeGen::genCodeForInitBlk(GenTreeBlk* initBlkNode)
1393 // Make sure we got the arguments of the initblk operation in the right registers
1394 unsigned size = initBlkNode->Size();
1395 GenTreePtr dstAddr = initBlkNode->Addr();
1396 GenTreePtr initVal = initBlkNode->Data();
1397 if (initVal->OperIsInitVal())
1399 initVal = initVal->gtGetOp1();
1402 assert(!dstAddr->isContained());
1403 assert(!initVal->isContained());
1404 if (initBlkNode->gtOper == GT_STORE_DYN_BLK)
1406 assert(initBlkNode->AsDynBlk()->gtDynamicSize->gtRegNum == REG_ARG_2);
1410 assert(initBlkNode->gtRsvdRegs == RBM_ARG_2);
1413 #ifdef _TARGET_ARM64_
1416 assert(size > INITBLK_UNROLL_LIMIT);
1418 #endif // _TARGET_ARM64_
1420 genConsumeBlockOp(initBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2);
1422 if (initBlkNode->gtFlags & GTF_BLK_VOLATILE)
1424 // issue a full memory barrier before a volatile initBlock Operation
1425 instGen_MemoryBarrier();
1428 genEmitHelperCall(CORINFO_HELP_MEMSET, 0, EA_UNKNOWN);
1431 //------------------------------------------------------------------------
1432 // genRegCopy: Generate a register copy.
1434 void CodeGen::genRegCopy(GenTree* treeNode)
1436 assert(treeNode->OperGet() == GT_COPY);
1438 var_types targetType = treeNode->TypeGet();
1439 regNumber targetReg = treeNode->gtRegNum;
1440 assert(targetReg != REG_NA);
1442 GenTree* op1 = treeNode->gtOp.gtOp1;
1444 // Check whether this node and the node from which we're copying the value have the same
1446 // This can happen if (currently iff) we have a SIMD vector type that fits in an integer
1447 // register, in which case it is passed as an argument, or returned from a call,
1448 // in an integer register and must be copied if it's in an xmm register.
1450 if (varTypeIsFloating(treeNode) != varTypeIsFloating(op1))
1452 NYI_ARM("genRegCopy floating point");
1453 #ifdef _TARGET_ARM64_
1454 inst_RV_RV(INS_fmov, targetReg, genConsumeReg(op1), targetType);
1455 #endif // _TARGET_ARM64_
1459 inst_RV_RV(ins_Copy(targetType), targetReg, genConsumeReg(op1), targetType);
1464 // The lclVar will never be a def.
1465 // If it is a last use, the lclVar will be killed by genConsumeReg(), as usual, and genProduceReg will
1466 // appropriately set the gcInfo for the copied value.
1467 // If not, there are two cases we need to handle:
1468 // - If this is a TEMPORARY copy (indicated by the GTF_VAR_DEATH flag) the variable
1469 // will remain live in its original register.
1470 // genProduceReg() will appropriately set the gcInfo for the copied value,
1471 // and genConsumeReg will reset it.
1472 // - Otherwise, we need to update register info for the lclVar.
1474 GenTreeLclVarCommon* lcl = op1->AsLclVarCommon();
1475 assert((lcl->gtFlags & GTF_VAR_DEF) == 0);
1477 if ((lcl->gtFlags & GTF_VAR_DEATH) == 0 && (treeNode->gtFlags & GTF_VAR_DEATH) == 0)
1479 LclVarDsc* varDsc = &compiler->lvaTable[lcl->gtLclNum];
1481 // If we didn't just spill it (in genConsumeReg, above), then update the register info
1482 if (varDsc->lvRegNum != REG_STK)
1484 // The old location is dying
1485 genUpdateRegLife(varDsc, /*isBorn*/ false, /*isDying*/ true DEBUGARG(op1));
1487 gcInfo.gcMarkRegSetNpt(genRegMask(op1->gtRegNum));
1489 genUpdateVarReg(varDsc, treeNode);
1491 // The new location is going live
1492 genUpdateRegLife(varDsc, /*isBorn*/ true, /*isDying*/ false DEBUGARG(treeNode));
1497 genProduceReg(treeNode);
1500 //------------------------------------------------------------------------
1501 // genCallInstruction: Produce code for a GT_CALL node
1503 void CodeGen::genCallInstruction(GenTreeCall* call)
1505 gtCallTypes callType = (gtCallTypes)call->gtCallType;
1507 IL_OFFSETX ilOffset = BAD_IL_OFFSET;
1509 // all virtuals should have been expanded into a control expression
1510 assert(!call->IsVirtual() || call->gtControlExpr || call->gtCallAddr);
1512 // Consume all the arg regs
1513 for (GenTreePtr list = call->gtCallLateArgs; list; list = list->MoveNext())
1515 assert(list->OperIsList());
1517 GenTreePtr argNode = list->Current();
1519 fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, argNode->gtSkipReloadOrCopy());
1520 assert(curArgTabEntry);
1522 if (curArgTabEntry->regNum == REG_STK)
1525 // Deal with multi register passed struct args.
1526 if (argNode->OperGet() == GT_FIELD_LIST)
1528 GenTreeArgList* argListPtr = argNode->AsArgList();
1529 unsigned iterationNum = 0;
1530 regNumber argReg = curArgTabEntry->regNum;
1531 for (; argListPtr != nullptr; argListPtr = argListPtr->Rest(), iterationNum++)
1533 GenTreePtr putArgRegNode = argListPtr->gtOp.gtOp1;
1534 assert(putArgRegNode->gtOper == GT_PUTARG_REG);
1536 genConsumeReg(putArgRegNode);
1538 if (putArgRegNode->gtRegNum != argReg)
1540 inst_RV_RV(ins_Move_Extend(putArgRegNode->TypeGet(), putArgRegNode->InReg()), argReg,
1541 putArgRegNode->gtRegNum);
1544 argReg = genRegArgNext(argReg);
1549 regNumber argReg = curArgTabEntry->regNum;
1550 genConsumeReg(argNode);
1551 if (argNode->gtRegNum != argReg)
1553 inst_RV_RV(ins_Move_Extend(argNode->TypeGet(), argNode->InReg()), argReg, argNode->gtRegNum);
1557 // In the case of a varargs call,
1558 // the ABI dictates that if we have floating point args,
1559 // we must pass the enregistered arguments in both the
1560 // integer and floating point registers so, let's do that.
1561 if (call->IsVarargs() && varTypeIsFloating(argNode))
1563 NYI_ARM("CodeGen - IsVarargs");
1564 NYI_ARM64("CodeGen - IsVarargs");
1568 // Insert a null check on "this" pointer if asked.
1569 if (call->NeedsNullCheck())
1571 const regNumber regThis = genGetThisArgReg(call);
1573 #if defined(_TARGET_ARM_)
1574 const regNumber tmpReg = call->ExtractTempReg();
1575 getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, tmpReg, regThis, 0);
1576 #elif defined(_TARGET_ARM64_)
1577 getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, REG_ZR, regThis, 0);
1581 // Either gtControlExpr != null or gtCallAddr != null or it is a direct non-virtual call to a user or helper method.
1582 CORINFO_METHOD_HANDLE methHnd;
1583 GenTree* target = call->gtControlExpr;
1584 if (callType == CT_INDIRECT)
1586 assert(target == nullptr);
1587 target = call->gtCallAddr;
1592 methHnd = call->gtCallMethHnd;
1595 CORINFO_SIG_INFO* sigInfo = nullptr;
1597 // Pass the call signature information down into the emitter so the emitter can associate
1598 // native call sites with the signatures they were generated from.
1599 if (callType != CT_HELPER)
1601 sigInfo = call->callSig;
1605 // If fast tail call, then we are done. In this case we setup the args (both reg args
1606 // and stack args in incoming arg area) and call target. Epilog sequence would
1607 // generate "br <reg>".
1608 if (call->IsFastTailCall())
1610 // Don't support fast tail calling JIT helpers
1611 assert(callType != CT_HELPER);
1613 // Fast tail calls materialize call target either in gtControlExpr or in gtCallAddr.
1614 assert(target != nullptr);
1616 genConsumeReg(target);
1618 NYI_ARM("fast tail call");
1620 #ifdef _TARGET_ARM64_
1621 // Use IP0 as the call target register.
1622 if (target->gtRegNum != REG_IP0)
1624 inst_RV_RV(INS_mov, REG_IP0, target->gtRegNum);
1626 #endif // _TARGET_ARM64_
1631 // For a pinvoke to unmanaged code we emit a label to clear
1632 // the GC pointer state before the callsite.
1633 // We can't utilize the typical lazy killing of GC pointers
1634 // at (or inside) the callsite.
1635 if (call->IsUnmanaged())
1637 genDefineTempLabel(genCreateTempLabel());
1640 // Determine return value size(s).
1641 ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc();
1642 emitAttr retSize = EA_PTRSIZE;
1643 emitAttr secondRetSize = EA_UNKNOWN;
1645 if (call->HasMultiRegRetVal())
1647 retSize = emitTypeSize(pRetTypeDesc->GetReturnRegType(0));
1648 secondRetSize = emitTypeSize(pRetTypeDesc->GetReturnRegType(1));
1652 assert(!varTypeIsStruct(call));
1654 if (call->gtType == TYP_REF || call->gtType == TYP_ARRAY)
1658 else if (call->gtType == TYP_BYREF)
1664 // We need to propagate the IL offset information to the call instruction, so we can emit
1665 // an IL to native mapping record for the call, to support managed return value debugging.
1666 // We don't want tail call helper calls that were converted from normal calls to get a record,
1667 // so we skip this hash table lookup logic in that case.
1668 if (compiler->opts.compDbgInfo && compiler->genCallSite2ILOffsetMap != nullptr && !call->IsTailCall())
1670 (void)compiler->genCallSite2ILOffsetMap->Lookup(call, &ilOffset);
1673 if (target != nullptr)
1675 // A call target can not be a contained indirection
1676 assert(!target->isContainedIndir());
1678 genConsumeReg(target);
1680 // We have already generated code for gtControlExpr evaluating it into a register.
1681 // We just need to emit "call reg" in this case.
1683 assert(genIsValidIntReg(target->gtRegNum));
1685 genEmitCall(emitter::EC_INDIR_R, methHnd,
1686 INDEBUG_LDISASM_COMMA(sigInfo) nullptr, // addr
1687 retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), ilOffset, target->gtRegNum);
1691 // Generate a direct call to a non-virtual user defined or helper method
1692 assert(callType == CT_HELPER || callType == CT_USER_FUNC);
1694 void* addr = nullptr;
1695 if (callType == CT_HELPER)
1697 // Direct call to a helper method.
1698 CorInfoHelpFunc helperNum = compiler->eeGetHelperNum(methHnd);
1699 noway_assert(helperNum != CORINFO_HELP_UNDEF);
1701 void* pAddr = nullptr;
1702 addr = compiler->compGetHelperFtn(helperNum, (void**)&pAddr);
1704 if (addr == nullptr)
1711 // Direct call to a non-virtual user function.
1712 CORINFO_ACCESS_FLAGS aflags = CORINFO_ACCESS_ANY;
1713 if (call->IsSameThis())
1715 aflags = (CORINFO_ACCESS_FLAGS)(aflags | CORINFO_ACCESS_THIS);
1718 if ((call->NeedsNullCheck()) == 0)
1720 aflags = (CORINFO_ACCESS_FLAGS)(aflags | CORINFO_ACCESS_NONNULL);
1723 CORINFO_CONST_LOOKUP addrInfo;
1724 compiler->info.compCompHnd->getFunctionEntryPoint(methHnd, &addrInfo, aflags);
1726 addr = addrInfo.addr;
1729 assert(addr != nullptr);
1731 // Non-virtual direct call to known addresses
1733 if (!arm_Valid_Imm_For_BL((ssize_t)addr))
1735 regNumber tmpReg = call->GetSingleTempReg();
1736 instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, tmpReg, (ssize_t)addr);
1737 genEmitCall(emitter::EC_INDIR_R, methHnd, INDEBUG_LDISASM_COMMA(sigInfo) NULL, retSize, ilOffset, tmpReg);
1740 #endif // _TARGET_ARM_
1742 genEmitCall(emitter::EC_FUNC_TOKEN, methHnd, INDEBUG_LDISASM_COMMA(sigInfo) addr,
1743 retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), ilOffset);
1746 #if 0 && defined(_TARGET_ARM64_)
1747 // Use this path if you want to load an absolute call target using
1748 // a sequence of movs followed by an indirect call (blr instruction)
1750 // Load the call target address in x16
1751 instGen_Set_Reg_To_Imm(EA_8BYTE, REG_IP0, (ssize_t) addr);
1753 // indirect call to constant address in IP0
1754 genEmitCall(emitter::EC_INDIR_R,
1756 INDEBUG_LDISASM_COMMA(sigInfo)
1765 // if it was a pinvoke we may have needed to get the address of a label
1766 if (genPendingCallLabel)
1768 assert(call->IsUnmanaged());
1769 genDefineTempLabel(genPendingCallLabel);
1770 genPendingCallLabel = nullptr;
1774 // All Callee arg registers are trashed and no longer contain any GC pointers.
1775 // TODO-Bug?: As a matter of fact shouldn't we be killing all of callee trashed regs here?
1776 // For now we will assert that other than arg regs gc ref/byref set doesn't contain any other
1777 // registers from RBM_CALLEE_TRASH
1778 assert((gcInfo.gcRegGCrefSetCur & (RBM_CALLEE_TRASH & ~RBM_ARG_REGS)) == 0);
1779 assert((gcInfo.gcRegByrefSetCur & (RBM_CALLEE_TRASH & ~RBM_ARG_REGS)) == 0);
1780 gcInfo.gcRegGCrefSetCur &= ~RBM_ARG_REGS;
1781 gcInfo.gcRegByrefSetCur &= ~RBM_ARG_REGS;
1783 var_types returnType = call->TypeGet();
1784 if (returnType != TYP_VOID)
1786 regNumber returnReg;
1788 if (call->HasMultiRegRetVal())
1790 assert(pRetTypeDesc != nullptr);
1791 unsigned regCount = pRetTypeDesc->GetReturnRegCount();
1793 // If regs allocated to call node are different from ABI return
1794 // regs in which the call has returned its result, move the result
1795 // to regs allocated to call node.
1796 for (unsigned i = 0; i < regCount; ++i)
1798 var_types regType = pRetTypeDesc->GetReturnRegType(i);
1799 returnReg = pRetTypeDesc->GetABIReturnReg(i);
1800 regNumber allocatedReg = call->GetRegNumByIdx(i);
1801 if (returnReg != allocatedReg)
1803 inst_RV_RV(ins_Copy(regType), allocatedReg, returnReg, regType);
1810 if (call->IsHelperCall(compiler, CORINFO_HELP_INIT_PINVOKE_FRAME))
1812 // The CORINFO_HELP_INIT_PINVOKE_FRAME helper uses a custom calling convention that returns with
1813 // TCB in REG_PINVOKE_TCB. fgMorphCall() sets the correct argument registers.
1814 returnReg = REG_PINVOKE_TCB;
1817 #endif // _TARGET_ARM_
1818 if (varTypeIsFloating(returnType))
1820 returnReg = REG_FLOATRET;
1824 returnReg = REG_INTRET;
1827 if (call->gtRegNum != returnReg)
1829 inst_RV_RV(ins_Copy(returnType), call->gtRegNum, returnReg, returnType);
1833 genProduceReg(call);
1836 // If there is nothing next, that means the result is thrown away, so this value is not live.
1837 // However, for minopts or debuggable code, we keep it live to support managed return value debugging.
1838 if ((call->gtNext == nullptr) && !compiler->opts.MinOpts() && !compiler->opts.compDbgCode)
1840 gcInfo.gcMarkRegSetNpt(RBM_INTRET);
1844 //------------------------------------------------------------------------
1845 // genIntToIntCast: Generate code for an integer cast
1848 // treeNode - The GT_CAST node
1854 // The treeNode must have an assigned register.
1855 // For a signed convert from byte, the source must be in a byte-addressable register.
1856 // Neither the source nor target type can be a floating point type.
1858 // TODO-ARM64-CQ: Allow castOp to be a contained node without an assigned register.
1860 void CodeGen::genIntToIntCast(GenTreePtr treeNode)
1862 assert(treeNode->OperGet() == GT_CAST);
1864 GenTreePtr castOp = treeNode->gtCast.CastOp();
1865 emitter* emit = getEmitter();
1867 var_types dstType = treeNode->CastToType();
1868 var_types srcType = genActualType(castOp->TypeGet());
1869 emitAttr movSize = emitActualTypeSize(dstType);
1870 bool movRequired = false;
1873 if (varTypeIsLong(srcType))
1875 genLongToIntCast(treeNode);
1878 #endif // _TARGET_ARM_
1880 regNumber targetReg = treeNode->gtRegNum;
1881 regNumber sourceReg = castOp->gtRegNum;
1883 // For Long to Int conversion we will have a reserved integer register to hold the immediate mask
1884 regNumber tmpReg = (treeNode->AvailableTempRegCount() == 0) ? REG_NA : treeNode->GetSingleTempReg();
1886 assert(genIsValidIntReg(targetReg));
1887 assert(genIsValidIntReg(sourceReg));
1889 instruction ins = INS_invalid;
1891 genConsumeReg(castOp);
1892 Lowering::CastInfo castInfo;
1894 // Get information about the cast.
1895 Lowering::getCastDescription(treeNode, &castInfo);
1897 if (castInfo.requiresOverflowCheck)
1899 emitAttr cmpSize = EA_ATTR(genTypeSize(srcType));
1901 if (castInfo.signCheckOnly)
1903 // We only need to check for a negative value in sourceReg
1904 emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, 0);
1905 emitJumpKind jmpLT = genJumpKindForOper(GT_LT, CK_SIGNED);
1906 genJumpToThrowHlpBlk(jmpLT, SCK_OVERFLOW);
1907 noway_assert(genTypeSize(srcType) == 4 || genTypeSize(srcType) == 8);
1908 // This is only interesting case to ensure zero-upper bits.
1909 if ((srcType == TYP_INT) && (dstType == TYP_ULONG))
1911 // cast to TYP_ULONG:
1912 // We use a mov with size=EA_4BYTE
1913 // which will zero out the upper bits
1918 else if (castInfo.unsignedSource || castInfo.unsignedDest)
1920 // When we are converting from/to unsigned,
1921 // we only have to check for any bits set in 'typeMask'
1923 noway_assert(castInfo.typeMask != 0);
1924 #if defined(_TARGET_ARM_)
1925 if (arm_Valid_Imm_For_Instr(INS_tst, castInfo.typeMask, INS_FLAGS_DONT_CARE))
1927 emit->emitIns_R_I(INS_tst, cmpSize, sourceReg, castInfo.typeMask);
1931 noway_assert(tmpReg != REG_NA);
1932 instGen_Set_Reg_To_Imm(cmpSize, tmpReg, castInfo.typeMask);
1933 emit->emitIns_R_R(INS_tst, cmpSize, sourceReg, tmpReg);
1935 #elif defined(_TARGET_ARM64_)
1936 emit->emitIns_R_I(INS_tst, cmpSize, sourceReg, castInfo.typeMask);
1937 #endif // _TARGET_ARM*
1938 emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED);
1939 genJumpToThrowHlpBlk(jmpNotEqual, SCK_OVERFLOW);
1943 // For a narrowing signed cast
1945 // We must check the value is in a signed range.
1947 // Compare with the MAX
1949 noway_assert((castInfo.typeMin != 0) && (castInfo.typeMax != 0));
1951 #if defined(_TARGET_ARM_)
1952 if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, INS_FLAGS_DONT_CARE))
1953 #elif defined(_TARGET_ARM64_)
1954 if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, cmpSize))
1957 emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, castInfo.typeMax);
1961 noway_assert(tmpReg != REG_NA);
1962 instGen_Set_Reg_To_Imm(cmpSize, tmpReg, castInfo.typeMax);
1963 emit->emitIns_R_R(INS_cmp, cmpSize, sourceReg, tmpReg);
1966 emitJumpKind jmpGT = genJumpKindForOper(GT_GT, CK_SIGNED);
1967 genJumpToThrowHlpBlk(jmpGT, SCK_OVERFLOW);
1969 // Compare with the MIN
1971 #if defined(_TARGET_ARM_)
1972 if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, INS_FLAGS_DONT_CARE))
1973 #elif defined(_TARGET_ARM64_)
1974 if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, cmpSize))
1977 emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, castInfo.typeMin);
1981 noway_assert(tmpReg != REG_NA);
1982 instGen_Set_Reg_To_Imm(cmpSize, tmpReg, castInfo.typeMin);
1983 emit->emitIns_R_R(INS_cmp, cmpSize, sourceReg, tmpReg);
1986 emitJumpKind jmpLT = genJumpKindForOper(GT_LT, CK_SIGNED);
1987 genJumpToThrowHlpBlk(jmpLT, SCK_OVERFLOW);
1991 else // Non-overflow checking cast.
1993 if (genTypeSize(srcType) == genTypeSize(dstType))
1999 var_types extendType = TYP_UNKNOWN;
2001 // If we need to treat a signed type as unsigned
2002 if ((treeNode->gtFlags & GTF_UNSIGNED) != 0)
2004 extendType = genUnsignedType(srcType);
2005 movSize = emitTypeSize(extendType);
2010 if (genTypeSize(srcType) < genTypeSize(dstType))
2012 extendType = srcType;
2014 movSize = emitTypeSize(srcType);
2015 #endif // _TARGET_ARM_
2016 if (srcType == TYP_UINT)
2018 #ifdef _TARGET_ARM64_
2019 // If we are casting from a smaller type to
2020 // a larger type, then we need to make sure the
2021 // higher 4 bytes are zero to gaurentee the correct value.
2022 // Therefore using a mov with EA_4BYTE in place of EA_8BYTE
2023 // will zero the upper bits
2025 #endif // _TARGET_ARM64_
2029 else // (genTypeSize(srcType) > genTypeSize(dstType))
2031 extendType = dstType;
2032 #if defined(_TARGET_ARM_)
2033 movSize = emitTypeSize(dstType);
2034 #elif defined(_TARGET_ARM64_)
2035 if (dstType == TYP_INT)
2037 movSize = EA_8BYTE; // a sxtw instruction requires EA_8BYTE
2043 ins = ins_Move_Extend(extendType, castOp->InReg());
2047 // We should never be generating a load from memory instruction here!
2048 assert(!emit->emitInsIsLoad(ins));
2050 if ((ins != INS_mov) || movRequired || (targetReg != sourceReg))
2052 emit->emitIns_R_R(ins, movSize, targetReg, sourceReg);
2055 genProduceReg(treeNode);
2058 //------------------------------------------------------------------------
2059 // genFloatToFloatCast: Generate code for a cast between float and double
2062 // treeNode - The GT_CAST node
2068 // Cast is a non-overflow conversion.
2069 // The treeNode must have an assigned register.
2070 // The cast is between float and double.
2072 void CodeGen::genFloatToFloatCast(GenTreePtr treeNode)
2074 // float <--> double conversions are always non-overflow ones
2075 assert(treeNode->OperGet() == GT_CAST);
2076 assert(!treeNode->gtOverflow());
2078 regNumber targetReg = treeNode->gtRegNum;
2079 assert(genIsValidFloatReg(targetReg));
2081 GenTreePtr op1 = treeNode->gtOp.gtOp1;
2082 assert(!op1->isContained()); // Cannot be contained
2083 assert(genIsValidFloatReg(op1->gtRegNum)); // Must be a valid float reg.
2085 var_types dstType = treeNode->CastToType();
2086 var_types srcType = op1->TypeGet();
2087 assert(varTypeIsFloating(srcType) && varTypeIsFloating(dstType));
2089 genConsumeOperands(treeNode->AsOp());
2091 // treeNode must be a reg
2092 assert(!treeNode->isContained());
2094 #if defined(_TARGET_ARM_)
2096 if (srcType != dstType)
2098 instruction insVcvt = (srcType == TYP_FLOAT) ? INS_vcvt_f2d // convert Float to Double
2099 : INS_vcvt_d2f; // convert Double to Float
2101 getEmitter()->emitIns_R_R(insVcvt, emitTypeSize(treeNode), treeNode->gtRegNum, op1->gtRegNum);
2103 else if (treeNode->gtRegNum != op1->gtRegNum)
2105 getEmitter()->emitIns_R_R(INS_vmov, emitTypeSize(treeNode), treeNode->gtRegNum, op1->gtRegNum);
2108 #elif defined(_TARGET_ARM64_)
2110 if (srcType != dstType)
2112 insOpts cvtOption = (srcType == TYP_FLOAT) ? INS_OPTS_S_TO_D // convert Single to Double
2113 : INS_OPTS_D_TO_S; // convert Double to Single
2115 getEmitter()->emitIns_R_R(INS_fcvt, emitTypeSize(treeNode), treeNode->gtRegNum, op1->gtRegNum, cvtOption);
2117 else if (treeNode->gtRegNum != op1->gtRegNum)
2119 // If double to double cast or float to float cast. Emit a move instruction.
2120 getEmitter()->emitIns_R_R(INS_mov, emitTypeSize(treeNode), treeNode->gtRegNum, op1->gtRegNum);
2125 genProduceReg(treeNode);
2128 //------------------------------------------------------------------------
2129 // genCreateAndStoreGCInfo: Create and record GC Info for the function.
2131 void CodeGen::genCreateAndStoreGCInfo(unsigned codeSize,
2132 unsigned prologSize,
2133 unsigned epilogSize DEBUGARG(void* codePtr))
2135 IAllocator* allowZeroAlloc = new (compiler, CMK_GC) AllowZeroAllocator(compiler->getAllocatorGC());
2136 GcInfoEncoder* gcInfoEncoder = new (compiler, CMK_GC)
2137 GcInfoEncoder(compiler->info.compCompHnd, compiler->info.compMethodInfo, allowZeroAlloc, NOMEM);
2138 assert(gcInfoEncoder != nullptr);
2140 // Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32).
2141 gcInfo.gcInfoBlockHdrSave(gcInfoEncoder, codeSize, prologSize);
2143 // We keep the call count for the second call to gcMakeRegPtrTable() below.
2144 unsigned callCnt = 0;
2146 // First we figure out the encoder ID's for the stack slots and registers.
2147 gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS, &callCnt);
2149 // Now we've requested all the slots we'll need; "finalize" these (make more compact data structures for them).
2150 gcInfoEncoder->FinalizeSlotIds();
2152 // Now we can actually use those slot ID's to declare live ranges.
2153 gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt);
2155 #ifdef _TARGET_ARM64_
2157 if (compiler->opts.compDbgEnC)
2159 // what we have to preserve is called the "frame header" (see comments in VM\eetwain.cpp)
2163 // -saved 'this' pointer and bool for synchronized methods
2165 // 4 slots for RBP + return address + RSI + RDI
2166 int preservedAreaSize = 4 * REGSIZE_BYTES;
2168 if (compiler->info.compFlags & CORINFO_FLG_SYNCH)
2170 if (!(compiler->info.compFlags & CORINFO_FLG_STATIC))
2171 preservedAreaSize += REGSIZE_BYTES;
2173 preservedAreaSize += 1; // bool for synchronized methods
2176 // Used to signal both that the method is compiled for EnC, and also the size of the block at the top of the
2178 gcInfoEncoder->SetSizeOfEditAndContinuePreservedArea(preservedAreaSize);
2181 #endif // _TARGET_ARM64_
2183 gcInfoEncoder->Build();
2185 // GC Encoder automatically puts the GC info in the right spot using ICorJitInfo::allocGCInfo(size_t)
2186 // let's save the values anyway for debugging purposes
2187 compiler->compInfoBlkAddr = gcInfoEncoder->Emit();
2188 compiler->compInfoBlkSize = 0; // not exposed by the GCEncoder interface
2191 //-------------------------------------------------------------------------------------------
2192 // genJumpKindsForTree: Determine the number and kinds of conditional branches
2193 // necessary to implement the given GT_CMP node
2196 // cmpTree - (input) The GenTree node that is used to set the Condition codes
2197 // - The GenTree Relop node that was used to set the Condition codes
2198 // jmpKind[2] - (output) One or two conditional branch instructions
2199 // jmpToTrueLabel[2] - (output) On Arm64 both branches will always branch to the true label
2202 // Sets the proper values into the array elements of jmpKind[] and jmpToTrueLabel[]
2205 // At least one conditional branch instruction will be returned.
2206 // Typically only one conditional branch is needed
2207 // and the second jmpKind[] value is set to EJ_NONE
2209 void CodeGen::genJumpKindsForTree(GenTreePtr cmpTree, emitJumpKind jmpKind[2], bool jmpToTrueLabel[2])
2211 // On ARM both branches will always branch to the true label
2212 jmpToTrueLabel[0] = true;
2213 jmpToTrueLabel[1] = true;
2215 // For integer comparisons just use genJumpKindForOper
2216 if (!varTypeIsFloating(cmpTree->gtOp.gtOp1->gtEffectiveVal()))
2218 CompareKind compareKind = ((cmpTree->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
2219 jmpKind[0] = genJumpKindForOper(cmpTree->gtOper, compareKind);
2220 jmpKind[1] = EJ_NONE;
2222 else // We have a Floating Point Compare operation
2224 assert(cmpTree->OperIsCompare());
2226 // For details on this mapping, see the ARM Condition Code table
2227 // at section A8.3 in the ARMv7 architecture manual or
2228 // at section C1.2.3 in the ARMV8 architecture manual.
2230 // We must check the GTF_RELOP_NAN_UN to find out
2231 // if we need to branch when we have a NaN operand.
2233 if ((cmpTree->gtFlags & GTF_RELOP_NAN_UN) != 0)
2235 // Must branch if we have an NaN, unordered
2236 switch (cmpTree->gtOper)
2239 jmpKind[0] = EJ_eq; // branch or set when equal (and no NaN's)
2240 jmpKind[1] = EJ_vs; // branch or set when we have a NaN
2244 jmpKind[0] = EJ_ne; // branch or set when not equal (or have NaN's)
2245 jmpKind[1] = EJ_NONE;
2249 jmpKind[0] = EJ_lt; // branch or set when less than (or have NaN's)
2250 jmpKind[1] = EJ_NONE;
2254 jmpKind[0] = EJ_le; // branch or set when less than or equal (or have NaN's)
2255 jmpKind[1] = EJ_NONE;
2259 jmpKind[0] = EJ_hi; // branch or set when greater than (or have NaN's)
2260 jmpKind[1] = EJ_NONE;
2264 jmpKind[0] = EJ_hs; // branch or set when greater than or equal (or have NaN's)
2265 jmpKind[1] = EJ_NONE;
2272 else // ((cmpTree->gtFlags & GTF_RELOP_NAN_UN) == 0)
2274 // Do not branch if we have an NaN, unordered
2275 switch (cmpTree->gtOper)
2278 jmpKind[0] = EJ_eq; // branch or set when equal (and no NaN's)
2279 jmpKind[1] = EJ_NONE;
2283 jmpKind[0] = EJ_gt; // branch or set when greater than (and no NaN's)
2284 jmpKind[1] = EJ_lo; // branch or set when less than (and no NaN's)
2288 jmpKind[0] = EJ_lo; // branch or set when less than (and no NaN's)
2289 jmpKind[1] = EJ_NONE;
2293 jmpKind[0] = EJ_ls; // branch or set when less than or equal (and no NaN's)
2294 jmpKind[1] = EJ_NONE;
2298 jmpKind[0] = EJ_gt; // branch or set when greater than (and no NaN's)
2299 jmpKind[1] = EJ_NONE;
2303 jmpKind[0] = EJ_ge; // branch or set when greater than or equal (and no NaN's)
2304 jmpKind[1] = EJ_NONE;
2314 //------------------------------------------------------------------------
2315 // genCodeForJumpTrue: Generates code for jmpTrue statement.
2318 // tree - The GT_JTRUE tree node.
2323 void CodeGen::genCodeForJumpTrue(GenTreePtr tree)
2325 GenTree* cmp = tree->gtOp.gtOp1->gtEffectiveVal();
2326 assert(cmp->OperIsCompare());
2327 assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
2329 // Get the "kind" and type of the comparison. Note that whether it is an unsigned cmp
2330 // is governed by a flag NOT by the inherent type of the node
2331 emitJumpKind jumpKind[2];
2332 bool branchToTrueLabel[2];
2333 genJumpKindsForTree(cmp, jumpKind, branchToTrueLabel);
2334 assert(jumpKind[0] != EJ_NONE);
2336 // On ARM the branches will always branch to the true label
2337 assert(branchToTrueLabel[0]);
2338 inst_JMP(jumpKind[0], compiler->compCurBB->bbJumpDest);
2340 if (jumpKind[1] != EJ_NONE)
2342 // the second conditional branch always has to be to the true label
2343 assert(branchToTrueLabel[1]);
2344 inst_JMP(jumpKind[1], compiler->compCurBB->bbJumpDest);
2348 //------------------------------------------------------------------------
2349 // genCodeForStoreBlk: Produce code for a GT_STORE_OBJ/GT_STORE_DYN_BLK/GT_STORE_BLK node.
2354 void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp)
2356 assert(blkOp->OperIs(GT_STORE_OBJ, GT_STORE_DYN_BLK, GT_STORE_BLK));
2358 if (blkOp->OperIs(GT_STORE_OBJ) && blkOp->OperIsCopyBlkOp())
2360 assert(blkOp->AsObj()->gtGcPtrCount != 0);
2361 genCodeForCpObj(blkOp->AsObj());
2365 if (blkOp->gtBlkOpGcUnsafe)
2367 getEmitter()->emitDisableGC();
2369 bool isCopyBlk = blkOp->OperIsCopyBlkOp();
2371 switch (blkOp->gtBlkOpKind)
2373 case GenTreeBlk::BlkOpKindHelper:
2376 genCodeForCpBlk(blkOp);
2380 genCodeForInitBlk(blkOp);
2384 case GenTreeBlk::BlkOpKindUnroll:
2387 genCodeForCpBlkUnroll(blkOp);
2391 genCodeForInitBlkUnroll(blkOp);
2399 if (blkOp->gtBlkOpGcUnsafe)
2401 getEmitter()->emitEnableGC();
2405 #endif // _TARGET_ARMARCH_
2407 #endif // !LEGACY_BACKEND