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));
122 #if !defined(_TARGET_64BIT_)
127 #endif // !defined(_TARGET_64BIT_)
132 genConsumeOperands(treeNode->AsOp());
133 genCodeForBinary(treeNode);
139 // case GT_ROL: // No ROL instruction on ARM; it has been lowered to ROR.
141 genCodeForShift(treeNode);
144 #if !defined(_TARGET_64BIT_)
148 genCodeForShiftLong(treeNode);
151 #endif // !defined(_TARGET_64BIT_)
154 genCodeForCast(treeNode->AsOp());
157 case GT_LCL_FLD_ADDR:
158 case GT_LCL_VAR_ADDR:
159 genCodeForLclAddr(treeNode);
163 genCodeForLclFld(treeNode->AsLclFld());
167 genCodeForLclVar(treeNode->AsLclVar());
170 case GT_STORE_LCL_FLD:
171 genCodeForStoreLclFld(treeNode->AsLclFld());
174 case GT_STORE_LCL_VAR:
175 genCodeForStoreLclVar(treeNode->AsLclVar());
184 // If we are here, it is the case where there is an LEA that cannot be folded into a parent instruction.
185 genLeaInstruction(treeNode->AsAddrMode());
189 genCodeForIndir(treeNode->AsIndir());
194 genCodeForMulLong(treeNode->AsMulLong());
196 #endif // _TARGET_ARM_
198 #ifdef _TARGET_ARM64_
201 genCodeForMulHi(treeNode->AsOp());
205 genCodeForSwap(treeNode->AsOp());
207 #endif // _TARGET_ARM64_
210 genJmpMethod(treeNode);
214 genCkfinite(treeNode);
218 genIntrinsic(treeNode);
223 genSIMDIntrinsic(treeNode->AsSIMD());
225 #endif // FEATURE_SIMD
233 genCodeForCompare(treeNode->AsOp());
237 genCodeForJumpTrue(treeNode);
243 genCodeForJcc(treeNode->AsCC());
246 #endif // _TARGET_ARM_
249 genCodeForReturnTrap(treeNode->AsOp());
253 genCodeForStoreInd(treeNode->AsStoreInd());
257 // This is handled at the time we call genConsumeReg() on the GT_COPY
267 genPutArgStk(treeNode->AsPutArgStk());
271 genPutArgReg(treeNode->AsOp());
275 genCallInstruction(treeNode->AsCall());
281 genLockedInstructions(treeNode->AsOp());
284 case GT_MEMORYBARRIER:
285 instGen_MemoryBarrier();
293 // do nothing - reload is just a marker.
294 // The parent node will call genConsumeReg on this which will trigger the unspill of this node's child
295 // into the register specified in this node.
305 case GT_ARR_BOUNDS_CHECK:
308 #endif // FEATURE_SIMD
309 genRangeCheck(treeNode);
313 genCodeForPhysReg(treeNode->AsPhysReg());
320 genCodeForNullCheck(treeNode->AsOp());
325 noway_assert(handlerGetsXcptnObj(compiler->compCurBB->bbCatchTyp));
327 /* Catch arguments get passed in a register. genCodeForBBlist()
328 would have marked it as holding a GC object, but not used. */
330 noway_assert(gcInfo.gcRegGCrefSetCur & RBM_EXCEPTION_OBJECT);
331 genConsumeReg(treeNode);
334 case GT_PINVOKE_PROLOG:
335 noway_assert(((gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & ~fullIntArgRegMask()) == 0);
337 // the runtime side requires the codegen here to be consistent
338 emit->emitDisableRandomNops();
342 genPendingCallLabel = genCreateTempLabel();
343 treeNode->gtLabel.gtLabBB = genPendingCallLabel;
344 emit->emitIns_R_L(INS_adr, EA_PTRSIZE, genPendingCallLabel, targetReg);
348 case GT_STORE_DYN_BLK:
350 genCodeForStoreBlk(treeNode->AsBlk());
354 genJumpTable(treeNode);
357 case GT_SWITCH_TABLE:
358 genTableBasedSwitch(treeNode);
362 genCodeForArrIndex(treeNode->AsArrIndex());
366 genCodeForArrOffset(treeNode->AsArrOffs());
371 case GT_CLS_VAR_ADDR:
372 emit->emitIns_R_C(INS_lea, EA_PTRSIZE, targetReg, treeNode->gtClsVar.gtClsVarHnd, 0);
373 genProduceReg(treeNode);
377 assert(treeNode->isUsedFromReg());
378 genConsumeRegs(treeNode);
381 #endif // _TARGET_ARM_
384 // Do nothing; these nodes are simply markers for debug info.
391 _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
392 GenTree::NodeName(treeNode->OperGet()));
395 NYI("unimplemented node");
402 //------------------------------------------------------------------------
403 // genSetRegToIcon: Generate code that will set the given register to the integer constant.
405 void CodeGen::genSetRegToIcon(regNumber reg, ssize_t val, var_types type, insFlags flags)
407 // Reg cannot be a FP reg
408 assert(!genIsValidFloatReg(reg));
410 // The only TYP_REF constant that can come this path is a managed 'null' since it is not
411 // relocatable. Other ref type constants (e.g. string objects) go through a different
413 noway_assert(type != TYP_REF || val == 0);
415 instGen_Set_Reg_To_Imm(emitActualTypeSize(type), reg, val, flags);
418 //---------------------------------------------------------------------
419 // genIntrinsic - generate code for a given intrinsic
422 // treeNode - the GT_INTRINSIC node
427 void CodeGen::genIntrinsic(GenTreePtr treeNode)
429 assert(treeNode->OperIs(GT_INTRINSIC));
431 // Both operand and its result must be of the same floating point type.
432 GenTreePtr srcNode = treeNode->gtOp.gtOp1;
433 assert(varTypeIsFloating(srcNode));
434 assert(srcNode->TypeGet() == treeNode->TypeGet());
436 // Right now only Abs/Round/Sqrt are treated as math intrinsics.
438 switch (treeNode->gtIntrinsic.gtIntrinsicId)
440 case CORINFO_INTRINSIC_Abs:
441 genConsumeOperands(treeNode->AsOp());
442 getEmitter()->emitInsBinary(INS_ABS, emitTypeSize(treeNode), treeNode, srcNode);
445 case CORINFO_INTRINSIC_Round:
446 NYI_ARM("genIntrinsic for round - not implemented yet");
447 genConsumeOperands(treeNode->AsOp());
448 getEmitter()->emitInsBinary(INS_ROUND, emitTypeSize(treeNode), treeNode, srcNode);
451 case CORINFO_INTRINSIC_Sqrt:
452 genConsumeOperands(treeNode->AsOp());
453 getEmitter()->emitInsBinary(INS_SQRT, emitTypeSize(treeNode), treeNode, srcNode);
457 assert(!"genIntrinsic: Unsupported intrinsic");
461 genProduceReg(treeNode);
464 //---------------------------------------------------------------------
465 // genPutArgStk - generate code for a GT_PUTARG_STK node
468 // treeNode - the GT_PUTARG_STK node
473 void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
475 assert(treeNode->OperIs(GT_PUTARG_STK));
476 var_types targetType = treeNode->TypeGet();
477 GenTreePtr source = treeNode->gtOp1;
478 emitter* emit = getEmitter();
480 // This is the varNum for our store operations,
481 // typically this is the varNum for the Outgoing arg space
482 // When we are generating a tail call it will be the varNum for arg0
483 unsigned varNumOut = (unsigned)-1;
484 unsigned argOffsetMax = (unsigned)-1; // Records the maximum size of this area for assert checks
486 // Get argument offset to use with 'varNumOut'
487 // Here we cross check that argument offset hasn't changed from lowering to codegen since
488 // we are storing arg slot number in GT_PUTARG_STK node in lowering phase.
489 unsigned argOffsetOut = treeNode->gtSlotNum * TARGET_POINTER_SIZE;
492 fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(treeNode->gtCall, treeNode);
493 assert(curArgTabEntry);
494 assert(argOffsetOut == (curArgTabEntry->slotNum * TARGET_POINTER_SIZE));
497 // Whether to setup stk arg in incoming or out-going arg area?
498 // Fast tail calls implemented as epilog+jmp = stk arg is setup in incoming arg area.
499 // All other calls - stk arg is setup in out-going arg area.
500 if (treeNode->putInIncomingArgArea())
502 NYI_ARM("genPutArgStk: fast tail call");
504 #ifdef _TARGET_ARM64_
505 varNumOut = getFirstArgWithStackSlot();
506 argOffsetMax = compiler->compArgSize;
507 #if FEATURE_FASTTAILCALL
508 // This must be a fast tail call.
509 assert(treeNode->gtCall->IsFastTailCall());
511 // Since it is a fast tail call, the existence of first incoming arg is guaranteed
512 // because fast tail call requires that in-coming arg area of caller is >= out-going
513 // arg area required for tail call.
514 LclVarDsc* varDsc = &(compiler->lvaTable[varNumOut]);
515 assert(varDsc != nullptr);
516 #endif // FEATURE_FASTTAILCALL
517 #endif // _TARGET_ARM64_
521 varNumOut = compiler->lvaOutgoingArgSpaceVar;
522 argOffsetMax = compiler->lvaOutgoingArgSpaceSize;
525 bool isStruct = (targetType == TYP_STRUCT) || (source->OperGet() == GT_FIELD_LIST);
527 if (!isStruct) // a normal non-Struct argument
529 instruction storeIns = ins_Store(targetType);
530 emitAttr storeAttr = emitTypeSize(targetType);
532 // If it is contained then source must be the integer constant zero
533 if (source->isContained())
535 assert(source->OperGet() == GT_CNS_INT);
536 assert(source->AsIntConCommon()->IconValue() == 0);
537 NYI_ARM("genPutArgStk: contained zero source");
539 #ifdef _TARGET_ARM64_
540 emit->emitIns_S_R(storeIns, storeAttr, REG_ZR, varNumOut, argOffsetOut);
541 #endif // _TARGET_ARM64_
545 genConsumeReg(source);
546 emit->emitIns_S_R(storeIns, storeAttr, source->gtRegNum, varNumOut, argOffsetOut);
548 argOffsetOut += EA_SIZE_IN_BYTES(storeAttr);
549 assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
551 else // We have some kind of a struct argument
553 assert(source->isContained()); // We expect that this node was marked as contained in Lower
555 if (source->OperGet() == GT_FIELD_LIST)
557 // Deal with the multi register passed struct args.
558 GenTreeFieldList* fieldListPtr = source->AsFieldList();
560 // Evaluate each of the GT_FIELD_LIST items into their register
561 // and store their register into the outgoing argument area
562 for (; fieldListPtr != nullptr; fieldListPtr = fieldListPtr->Rest())
564 GenTreePtr nextArgNode = fieldListPtr->gtOp.gtOp1;
565 genConsumeReg(nextArgNode);
567 regNumber reg = nextArgNode->gtRegNum;
568 var_types type = nextArgNode->TypeGet();
569 emitAttr attr = emitTypeSize(type);
571 // Emit store instructions to store the registers produced by the GT_FIELD_LIST into the outgoing
573 emit->emitIns_S_R(ins_Store(type), attr, reg, varNumOut, argOffsetOut);
574 argOffsetOut += EA_SIZE_IN_BYTES(attr);
575 assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
578 else // We must have a GT_OBJ or a GT_LCL_VAR
580 noway_assert((source->OperGet() == GT_LCL_VAR) || (source->OperGet() == GT_OBJ));
582 var_types targetType = source->TypeGet();
583 noway_assert(varTypeIsStruct(targetType));
585 // We will copy this struct to the stack, possibly using a ldp/ldr instruction
587 // Setup loReg (and hiReg) from the internal registers that we reserved in lower.
589 regNumber loReg = treeNode->ExtractTempReg();
590 #ifdef _TARGET_ARM64_
591 regNumber hiReg = treeNode->GetSingleTempReg();
592 #endif // _TARGET_ARM64_
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 gcPtrArray[MAX_ARG_REG_COUNT] = {}; // TYPE_GC_NONE = 0
629 BYTE* gcPtrs = gcPtrArray;
631 unsigned gcPtrCount; // The count of GC pointers in the struct
635 // This is the varNum for our load operations,
636 // only used when we have a multireg struct with a LclVar source
637 unsigned varNumInp = BAD_VAR_NUM;
639 // Setup the structSize, isHFa, and gcPtrCount
640 if (varNode != nullptr)
642 varNumInp = varNode->gtLclNum;
643 assert(varNumInp < compiler->lvaCount);
644 LclVarDsc* varDsc = &compiler->lvaTable[varNumInp];
646 assert(varDsc->lvType == TYP_STRUCT);
647 assert(varDsc->lvOnFrame); // This struct also must live in the stack frame
648 assert(!varDsc->lvRegister); // And it can't live in a register (SIMD)
650 structSize = varDsc->lvSize(); // This yields the roundUp size, but that is fine
651 // as that is how much stack is allocated for this LclVar
652 isHfa = varDsc->lvIsHfa();
653 #ifdef _TARGET_ARM64_
654 gcPtrCount = varDsc->lvStructGcCount;
655 for (unsigned i = 0; i < gcPtrCount; ++i)
656 gcPtrs[i] = varDsc->lvGcLayout[i];
657 #else // _TARGET_ARM_
658 gcPtrs = treeNode->gtGcPtrs;
659 gcPtrCount = treeNode->gtNumSlots;
660 #endif // _TARGET_ARM_
662 else // addrNode is used
664 assert(addrNode != nullptr);
666 // Generate code to load the address that we need into a register
667 genConsumeAddress(addrNode);
668 addrReg = addrNode->gtRegNum;
670 #ifdef _TARGET_ARM64_
671 // If addrReg equal to loReg, swap(loReg, hiReg)
672 // This reduces code complexity by only supporting one addrReg overwrite case
673 if (loReg == addrReg)
678 #endif // _TARGET_ARM64_
680 CORINFO_CLASS_HANDLE objClass = source->gtObj.gtClass;
682 structSize = compiler->info.compCompHnd->getClassSize(objClass);
683 isHfa = compiler->IsHfa(objClass);
684 gcPtrCount = compiler->info.compCompHnd->getClassGClayout(objClass, &gcPtrs[0]);
687 // If we have an HFA we can't have any GC pointers,
688 // if not then the max size for the the struct is 16 bytes
691 noway_assert(gcPtrCount == 0);
693 #ifdef _TARGET_ARM64_
696 noway_assert(structSize <= 2 * TARGET_POINTER_SIZE);
699 noway_assert(structSize <= MAX_PASS_MULTIREG_BYTES);
700 #endif // _TARGET_ARM64_
702 int remainingSize = structSize;
703 unsigned structOffset = 0;
704 unsigned nextIndex = 0;
706 #ifdef _TARGET_ARM64_
707 // For a >= 16-byte structSize we will generate a ldp and stp instruction each loop
709 // stp x2, x3, [sp, #16]
711 while (remainingSize >= 2 * TARGET_POINTER_SIZE)
713 var_types type0 = compiler->getJitGCType(gcPtrs[nextIndex + 0]);
714 var_types type1 = compiler->getJitGCType(gcPtrs[nextIndex + 1]);
716 if (varNode != nullptr)
718 // Load from our varNumImp source
719 emit->emitIns_R_R_S_S(INS_ldp, emitTypeSize(type0), emitTypeSize(type1), loReg, hiReg, varNumInp,
724 // check for case of destroying the addrRegister while we still need it
725 assert(loReg != addrReg);
726 noway_assert((remainingSize == 2 * TARGET_POINTER_SIZE) || (hiReg != addrReg));
728 // Load from our address expression source
729 emit->emitIns_R_R_R_I(INS_ldp, emitTypeSize(type0), loReg, hiReg, addrReg, structOffset,
730 INS_OPTS_NONE, emitTypeSize(type0));
733 // Emit stp instruction to store the two registers into the outgoing argument area
734 emit->emitIns_S_S_R_R(INS_stp, emitTypeSize(type0), emitTypeSize(type1), loReg, hiReg, varNumOut,
736 argOffsetOut += (2 * TARGET_POINTER_SIZE); // We stored 16-bytes of the struct
737 assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
739 remainingSize -= (2 * TARGET_POINTER_SIZE); // We loaded 16-bytes of the struct
740 structOffset += (2 * TARGET_POINTER_SIZE);
743 #else // _TARGET_ARM_
744 // For a >= 4 byte structSize we will generate a ldr and str instruction each loop
747 while (remainingSize >= TARGET_POINTER_SIZE)
749 var_types type = compiler->getJitGCType(gcPtrs[nextIndex]);
751 if (varNode != nullptr)
753 // Load from our varNumImp source
754 emit->emitIns_R_S(INS_ldr, emitTypeSize(type), loReg, varNumInp, structOffset);
758 // check for case of destroying the addrRegister while we still need it
759 assert(loReg != addrReg || remainingSize == TARGET_POINTER_SIZE);
761 // Load from our address expression source
762 emit->emitIns_R_R_I(INS_ldr, emitTypeSize(type), loReg, addrReg, structOffset);
765 // Emit str instruction to store the register into the outgoing argument area
766 emit->emitIns_S_R(INS_str, emitTypeSize(type), loReg, varNumOut, argOffsetOut);
767 argOffsetOut += TARGET_POINTER_SIZE; // We stored 4-bytes of the struct
768 assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
770 remainingSize -= TARGET_POINTER_SIZE; // We loaded 4-bytes of the struct
771 structOffset += TARGET_POINTER_SIZE;
774 #endif // _TARGET_ARM_
776 // For a 12-byte structSize we will we will generate two load instructions
782 while (remainingSize > 0)
784 if (remainingSize >= TARGET_POINTER_SIZE)
786 var_types nextType = compiler->getJitGCType(gcPtrs[nextIndex]);
787 emitAttr nextAttr = emitTypeSize(nextType);
788 remainingSize -= TARGET_POINTER_SIZE;
790 if (varNode != nullptr)
792 // Load from our varNumImp source
793 emit->emitIns_R_S(ins_Load(nextType), nextAttr, loReg, varNumInp, structOffset);
797 assert(loReg != addrReg);
799 // Load from our address expression source
800 emit->emitIns_R_R_I(ins_Load(nextType), nextAttr, loReg, addrReg, structOffset);
802 // Emit a store instruction to store the register into the outgoing argument area
803 emit->emitIns_S_R(ins_Store(nextType), nextAttr, loReg, varNumOut, argOffsetOut);
804 argOffsetOut += EA_SIZE_IN_BYTES(nextAttr);
805 assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
807 structOffset += TARGET_POINTER_SIZE;
810 else // (remainingSize < TARGET_POINTER_SIZE)
812 int loadSize = remainingSize;
815 // We should never have to do a non-pointer sized load when we have a LclVar source
816 assert(varNode == nullptr);
818 // the left over size is smaller than a pointer and thus can never be a GC type
819 assert(varTypeIsGC(compiler->getJitGCType(gcPtrs[nextIndex])) == false);
821 var_types loadType = TYP_UINT;
824 loadType = TYP_UBYTE;
826 else if (loadSize == 2)
828 loadType = TYP_USHORT;
832 // Need to handle additional loadSize cases here
833 noway_assert(loadSize == 4);
836 instruction loadIns = ins_Load(loadType);
837 emitAttr loadAttr = emitAttr(loadSize);
839 assert(loReg != addrReg);
841 emit->emitIns_R_R_I(loadIns, loadAttr, loReg, addrReg, structOffset);
843 // Emit a store instruction to store the register into the outgoing argument area
844 emit->emitIns_S_R(ins_Store(loadType), loadAttr, loReg, varNumOut, argOffsetOut);
845 argOffsetOut += EA_SIZE_IN_BYTES(loadAttr);
846 assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
853 //---------------------------------------------------------------------
854 // genPutArgReg - generate code for a GT_PUTARG_REG node
857 // tree - the GT_PUTARG_REG node
862 void CodeGen::genPutArgReg(GenTreeOp* tree)
864 assert(tree->OperIs(GT_PUTARG_REG));
866 var_types targetType = tree->TypeGet();
867 regNumber targetReg = tree->gtRegNum;
869 assert(targetType != TYP_STRUCT);
871 GenTree* op1 = tree->gtOp1;
874 // If child node is not already in the register we need, move it
875 if (targetReg != op1->gtRegNum)
877 inst_RV_RV(ins_Copy(targetType), targetReg, op1->gtRegNum, targetType);
883 //----------------------------------------------------------------------------------
884 // genMultiRegCallStoreToLocal: store multi-reg return value of a call node to a local
887 // treeNode - Gentree of GT_STORE_LCL_VAR
893 // The child of store is a multi-reg call node.
894 // genProduceReg() on treeNode is made by caller of this routine.
896 void CodeGen::genMultiRegCallStoreToLocal(GenTreePtr treeNode)
898 assert(treeNode->OperGet() == GT_STORE_LCL_VAR);
900 #if defined(_TARGET_ARM_)
901 // Longs are returned in two return registers on Arm32.
902 assert(varTypeIsLong(treeNode));
903 #elif defined(_TARGET_ARM64_)
904 // Structs of size >=9 and <=16 are returned in two return registers on ARM64 and HFAs.
905 assert(varTypeIsStruct(treeNode));
908 // Assumption: current implementation requires that a multi-reg
909 // var in 'var = call' is flagged as lvIsMultiRegRet to prevent it from
911 unsigned lclNum = treeNode->AsLclVarCommon()->gtLclNum;
912 LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]);
913 noway_assert(varDsc->lvIsMultiRegRet);
915 GenTree* op1 = treeNode->gtGetOp1();
916 GenTree* actualOp1 = op1->gtSkipReloadOrCopy();
917 GenTreeCall* call = actualOp1->AsCall();
918 assert(call->HasMultiRegRetVal());
922 ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc();
923 unsigned regCount = pRetTypeDesc->GetReturnRegCount();
925 if (treeNode->gtRegNum != REG_NA)
927 // Right now the only enregistrable multi-reg return types supported are SIMD types.
928 assert(varTypeIsSIMD(treeNode));
929 NYI("GT_STORE_LCL_VAR of a SIMD enregisterable struct");
935 for (unsigned i = 0; i < regCount; ++i)
937 var_types type = pRetTypeDesc->GetReturnRegType(i);
938 regNumber reg = call->GetRegNumByIdx(i);
939 if (op1->IsCopyOrReload())
941 // GT_COPY/GT_RELOAD will have valid reg for those positions
942 // that need to be copied or reloaded.
943 regNumber reloadReg = op1->AsCopyOrReload()->GetRegNumByIdx(i);
944 if (reloadReg != REG_NA)
950 assert(reg != REG_NA);
951 getEmitter()->emitIns_S_R(ins_Store(type), emitTypeSize(type), reg, lclNum, offset);
952 offset += genTypeSize(type);
955 varDsc->lvRegNum = REG_STK;
959 //------------------------------------------------------------------------
960 // genRangeCheck: generate code for GT_ARR_BOUNDS_CHECK node.
962 void CodeGen::genRangeCheck(GenTreePtr oper)
965 noway_assert(oper->OperGet() == GT_ARR_BOUNDS_CHECK || oper->OperGet() == GT_SIMD_CHK);
966 #else // !FEATURE_SIMD
967 noway_assert(oper->OperGet() == GT_ARR_BOUNDS_CHECK);
968 #endif // !FEATURE_SIMD
970 GenTreeBoundsChk* bndsChk = oper->AsBoundsChk();
972 GenTreePtr arrLen = bndsChk->gtArrLen;
973 GenTreePtr arrIndex = bndsChk->gtIndex;
974 GenTreePtr arrRef = NULL;
979 emitJumpKind jmpKind;
981 genConsumeRegs(arrIndex);
982 genConsumeRegs(arrLen);
984 if (arrIndex->isContainedIntOrIImmed())
986 // To encode using a cmp immediate, we place the
987 // constant operand in the second position
990 jmpKind = genJumpKindForOper(GT_LE, CK_UNSIGNED);
996 jmpKind = genJumpKindForOper(GT_GE, CK_UNSIGNED);
999 getEmitter()->emitInsBinary(INS_cmp, EA_4BYTE, src1, src2);
1000 genJumpToThrowHlpBlk(jmpKind, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
1003 //---------------------------------------------------------------------
1004 // genCodeForPhysReg - generate code for a GT_PHYSREG node
1007 // tree - the GT_PHYSREG node
1012 void CodeGen::genCodeForPhysReg(GenTreePhysReg* tree)
1014 assert(tree->OperIs(GT_PHYSREG));
1016 var_types targetType = tree->TypeGet();
1017 regNumber targetReg = tree->gtRegNum;
1019 if (targetReg != tree->gtSrcReg)
1021 inst_RV_RV(ins_Copy(targetType), targetReg, tree->gtSrcReg, targetType);
1022 genTransferRegGCState(targetReg, tree->gtSrcReg);
1025 genProduceReg(tree);
1028 //---------------------------------------------------------------------
1029 // genCodeForNullCheck - generate code for a GT_NULLCHECK node
1032 // tree - the GT_NULLCHECK node
1037 void CodeGen::genCodeForNullCheck(GenTreeOp* tree)
1039 assert(tree->OperIs(GT_NULLCHECK));
1040 assert(!tree->gtOp1->isContained());
1041 regNumber addrReg = genConsumeReg(tree->gtOp1);
1043 #ifdef _TARGET_ARM64_
1044 regNumber targetReg = REG_ZR;
1046 regNumber targetReg = tree->gtRegNum;
1049 getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, targetReg, addrReg, 0);
1052 //------------------------------------------------------------------------
1053 // genOffsetOfMDArrayLowerBound: Returns the offset from the Array object to the
1054 // lower bound for the given dimension.
1057 // elemType - the element type of the array
1058 // rank - the rank of the array
1059 // dimension - the dimension for which the lower bound offset will be returned.
1063 // TODO-Cleanup: move to CodeGenCommon.cpp
1066 unsigned CodeGen::genOffsetOfMDArrayLowerBound(var_types elemType, unsigned rank, unsigned dimension)
1068 // Note that the lower bound and length fields of the Array object are always TYP_INT
1069 return compiler->eeGetArrayDataOffset(elemType) + genTypeSize(TYP_INT) * (dimension + rank);
1072 //------------------------------------------------------------------------
1073 // genOffsetOfMDArrayLength: Returns the offset from the Array object to the
1074 // size for the given dimension.
1077 // elemType - the element type of the array
1078 // rank - the rank of the array
1079 // dimension - the dimension for which the lower bound offset will be returned.
1083 // TODO-Cleanup: move to CodeGenCommon.cpp
1086 unsigned CodeGen::genOffsetOfMDArrayDimensionSize(var_types elemType, unsigned rank, unsigned dimension)
1088 // Note that the lower bound and length fields of the Array object are always TYP_INT
1089 return compiler->eeGetArrayDataOffset(elemType) + genTypeSize(TYP_INT) * dimension;
1092 //------------------------------------------------------------------------
1093 // genCodeForArrIndex: Generates code to bounds check the index for one dimension of an array reference,
1094 // producing the effective index by subtracting the lower bound.
1097 // arrIndex - the node for which we're generating code
1102 void CodeGen::genCodeForArrIndex(GenTreeArrIndex* arrIndex)
1104 emitter* emit = getEmitter();
1105 GenTreePtr arrObj = arrIndex->ArrObj();
1106 GenTreePtr indexNode = arrIndex->IndexExpr();
1107 regNumber arrReg = genConsumeReg(arrObj);
1108 regNumber indexReg = genConsumeReg(indexNode);
1109 regNumber tgtReg = arrIndex->gtRegNum;
1110 noway_assert(tgtReg != REG_NA);
1112 // We will use a temp register to load the lower bound and dimension size values.
1114 regNumber tmpReg = arrIndex->GetSingleTempReg();
1115 assert(tgtReg != tmpReg);
1117 unsigned dim = arrIndex->gtCurrDim;
1118 unsigned rank = arrIndex->gtArrRank;
1119 var_types elemType = arrIndex->gtArrElemType;
1122 offset = genOffsetOfMDArrayLowerBound(elemType, rank, dim);
1123 emit->emitIns_R_R_I(ins_Load(TYP_INT), EA_PTRSIZE, tmpReg, arrReg, offset); // a 4 BYTE sign extending load
1124 emit->emitIns_R_R_R(INS_sub, EA_4BYTE, tgtReg, indexReg, tmpReg);
1126 offset = genOffsetOfMDArrayDimensionSize(elemType, rank, dim);
1127 emit->emitIns_R_R_I(ins_Load(TYP_INT), EA_PTRSIZE, tmpReg, arrReg, offset); // a 4 BYTE sign extending load
1128 emit->emitIns_R_R(INS_cmp, EA_4BYTE, tgtReg, tmpReg);
1130 emitJumpKind jmpGEU = genJumpKindForOper(GT_GE, CK_UNSIGNED);
1131 genJumpToThrowHlpBlk(jmpGEU, SCK_RNGCHK_FAIL);
1133 genProduceReg(arrIndex);
1136 //------------------------------------------------------------------------
1137 // genCodeForArrOffset: Generates code to compute the flattened array offset for
1138 // one dimension of an array reference:
1139 // result = (prevDimOffset * dimSize) + effectiveIndex
1140 // where dimSize is obtained from the arrObj operand
1143 // arrOffset - the node for which we're generating code
1149 // dimSize and effectiveIndex are always non-negative, the former by design,
1150 // and the latter because it has been normalized to be zero-based.
1152 void CodeGen::genCodeForArrOffset(GenTreeArrOffs* arrOffset)
1154 GenTreePtr offsetNode = arrOffset->gtOffset;
1155 GenTreePtr indexNode = arrOffset->gtIndex;
1156 regNumber tgtReg = arrOffset->gtRegNum;
1158 noway_assert(tgtReg != REG_NA);
1160 if (!offsetNode->IsIntegralConst(0))
1162 emitter* emit = getEmitter();
1163 regNumber offsetReg = genConsumeReg(offsetNode);
1164 regNumber indexReg = genConsumeReg(indexNode);
1165 regNumber arrReg = genConsumeReg(arrOffset->gtArrObj);
1166 noway_assert(offsetReg != REG_NA);
1167 noway_assert(indexReg != REG_NA);
1168 noway_assert(arrReg != REG_NA);
1170 regNumber tmpReg = arrOffset->GetSingleTempReg();
1172 unsigned dim = arrOffset->gtCurrDim;
1173 unsigned rank = arrOffset->gtArrRank;
1174 var_types elemType = arrOffset->gtArrElemType;
1175 unsigned offset = genOffsetOfMDArrayDimensionSize(elemType, rank, dim);
1177 // Load tmpReg with the dimension size and evaluate
1178 // tgtReg = offsetReg*tmpReg + indexReg.
1179 emit->emitIns_R_R_I(ins_Load(TYP_INT), EA_PTRSIZE, tmpReg, arrReg, offset);
1180 emit->emitIns_R_R_R_R(INS_MULADD, EA_PTRSIZE, tgtReg, tmpReg, offsetReg, indexReg);
1184 regNumber indexReg = genConsumeReg(indexNode);
1185 if (indexReg != tgtReg)
1187 inst_RV_RV(INS_mov, tgtReg, indexReg, TYP_INT);
1190 genProduceReg(arrOffset);
1193 //------------------------------------------------------------------------
1194 // indirForm: Make a temporary indir we can feed to pattern matching routines
1195 // in cases where we don't want to instantiate all the indirs that happen.
1197 GenTreeIndir CodeGen::indirForm(var_types type, GenTree* base)
1199 GenTreeIndir i(GT_IND, type, base, nullptr);
1200 i.gtRegNum = REG_NA;
1201 // has to be nonnull (because contained nodes can't be the last in block)
1202 // but don't want it to be a valid pointer
1203 i.gtNext = (GenTree*)(-1);
1207 //------------------------------------------------------------------------
1208 // intForm: Make a temporary int we can feed to pattern matching routines
1209 // in cases where we don't want to instantiate.
1211 GenTreeIntCon CodeGen::intForm(var_types type, ssize_t value)
1213 GenTreeIntCon i(type, value);
1214 i.gtRegNum = REG_NA;
1215 // has to be nonnull (because contained nodes can't be the last in block)
1216 // but don't want it to be a valid pointer
1217 i.gtNext = (GenTree*)(-1);
1221 //------------------------------------------------------------------------
1222 // genCodeForShift: Generates the code sequence for a GenTree node that
1223 // represents a bit shift or rotate operation (<<, >>, >>>, rol, ror).
1226 // tree - the bit shift node (that specifies the type of bit shift to perform).
1229 // a) All GenTrees are register allocated.
1231 void CodeGen::genCodeForShift(GenTreePtr tree)
1233 var_types targetType = tree->TypeGet();
1234 genTreeOps oper = tree->OperGet();
1235 instruction ins = genGetInsForOper(oper, targetType);
1236 emitAttr size = emitTypeSize(tree);
1238 assert(tree->gtRegNum != REG_NA);
1240 genConsumeOperands(tree->AsOp());
1242 GenTreePtr operand = tree->gtGetOp1();
1243 GenTreePtr shiftBy = tree->gtGetOp2();
1244 if (!shiftBy->IsCnsIntOrI())
1246 getEmitter()->emitIns_R_R_R(ins, size, tree->gtRegNum, operand->gtRegNum, shiftBy->gtRegNum);
1250 unsigned immWidth = emitter::getBitWidth(size); // For ARM64, immWidth will be set to 32 or 64
1251 ssize_t shiftByImm = shiftBy->gtIntCon.gtIconVal & (immWidth - 1);
1253 getEmitter()->emitIns_R_R_I(ins, size, tree->gtRegNum, operand->gtRegNum, shiftByImm);
1256 genProduceReg(tree);
1259 //------------------------------------------------------------------------
1260 // genCodeForLclAddr: Generates the code for GT_LCL_FLD_ADDR/GT_LCL_VAR_ADDR.
1265 void CodeGen::genCodeForLclAddr(GenTree* tree)
1267 assert(tree->OperIs(GT_LCL_FLD_ADDR, GT_LCL_VAR_ADDR));
1269 var_types targetType = tree->TypeGet();
1270 regNumber targetReg = tree->gtRegNum;
1272 // Address of a local var. This by itself should never be allocated a register.
1273 // If it is worth storing the address in a register then it should be cse'ed into
1274 // a temp and that would be allocated a register.
1275 noway_assert(targetType == TYP_BYREF);
1276 noway_assert(!tree->InReg());
1278 inst_RV_TT(INS_lea, targetReg, tree, 0, EA_BYREF);
1279 genProduceReg(tree);
1282 //------------------------------------------------------------------------
1283 // genCodeForLclFld: Produce code for a GT_LCL_FLD node.
1286 // tree - the GT_LCL_FLD node
1288 void CodeGen::genCodeForLclFld(GenTreeLclFld* tree)
1290 assert(tree->OperIs(GT_LCL_FLD));
1292 var_types targetType = tree->TypeGet();
1293 regNumber targetReg = tree->gtRegNum;
1294 emitter* emit = getEmitter();
1296 NYI_IF(targetType == TYP_STRUCT, "GT_LCL_FLD: struct load local field not supported");
1297 NYI_IF(targetReg == REG_NA, "GT_LCL_FLD: load local field not into a register is not supported");
1299 emitAttr size = emitTypeSize(targetType);
1300 unsigned offs = tree->gtLclOffs;
1301 unsigned varNum = tree->gtLclNum;
1302 assert(varNum < compiler->lvaCount);
1304 if (varTypeIsFloating(targetType))
1308 NYI("GT_LCL_FLD with register to register Floating point move");
1312 emit->emitIns_R_S(ins_Load(targetType), size, targetReg, varNum, offs);
1317 #ifdef _TARGET_ARM64_
1318 size = EA_SET_SIZE(size, EA_8BYTE);
1319 #endif // _TARGET_ARM64_
1320 emit->emitIns_R_S(ins_Move_Extend(targetType, tree->InReg()), size, targetReg, varNum, offs);
1323 genProduceReg(tree);
1326 //------------------------------------------------------------------------
1327 // genCodeForIndir: Produce code for a GT_IND node.
1330 // tree - the GT_IND node
1332 void CodeGen::genCodeForIndir(GenTreeIndir* tree)
1334 assert(tree->OperIs(GT_IND));
1336 var_types targetType = tree->TypeGet();
1337 regNumber targetReg = tree->gtRegNum;
1338 emitter* emit = getEmitter();
1340 genConsumeAddress(tree->Addr());
1341 emit->emitInsLoadStoreOp(ins_Load(targetType), emitTypeSize(tree), targetReg, tree);
1342 genProduceReg(tree);
1344 if (tree->gtFlags & GTF_IND_VOLATILE)
1346 #ifdef _TARGET_ARM64_
1347 // issue a INS_BARRIER_LD after a volatile LdInd operation
1348 instGen_MemoryBarrier(INS_BARRIER_LD);
1350 // issue a full memory barrier after a volatile LdInd operation
1351 instGen_MemoryBarrier();
1352 #endif // _TARGET_ARM64_
1356 // Generate code for a CpBlk node by the means of the VM memcpy helper call
1358 // a) The size argument of the CpBlk is not an integer constant
1359 // b) The size argument is a constant but is larger than CPBLK_MOVS_LIMIT bytes.
1360 void CodeGen::genCodeForCpBlk(GenTreeBlk* cpBlkNode)
1362 // Make sure we got the arguments of the cpblk operation in the right registers
1363 unsigned blockSize = cpBlkNode->Size();
1364 GenTreePtr dstAddr = cpBlkNode->Addr();
1365 assert(!dstAddr->isContained());
1367 genConsumeBlockOp(cpBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2);
1369 #ifdef _TARGET_ARM64_
1372 assert(blockSize > CPBLK_UNROLL_LIMIT);
1374 #endif // _TARGET_ARM64_
1376 if (cpBlkNode->gtFlags & GTF_BLK_VOLATILE)
1378 // issue a full memory barrier before a volatile CpBlk operation
1379 instGen_MemoryBarrier();
1382 genEmitHelperCall(CORINFO_HELP_MEMCPY, 0, EA_UNKNOWN);
1384 if (cpBlkNode->gtFlags & GTF_BLK_VOLATILE)
1386 #ifdef _TARGET_ARM64_
1387 // issue a INS_BARRIER_ISHLD after a volatile CpBlk operation
1388 instGen_MemoryBarrier(INS_BARRIER_ISHLD);
1390 // issue a full memory barrier after a volatile CpBlk operation
1391 instGen_MemoryBarrier();
1392 #endif // _TARGET_ARM64_
1396 // Generates code for InitBlk by calling the VM memset helper function.
1398 // a) The size argument of the InitBlk is not an integer constant.
1399 // b) The size argument of the InitBlk is >= INITBLK_STOS_LIMIT bytes.
1400 void CodeGen::genCodeForInitBlk(GenTreeBlk* initBlkNode)
1402 // Make sure we got the arguments of the initblk operation in the right registers
1403 unsigned size = initBlkNode->Size();
1404 GenTreePtr dstAddr = initBlkNode->Addr();
1405 GenTreePtr initVal = initBlkNode->Data();
1406 if (initVal->OperIsInitVal())
1408 initVal = initVal->gtGetOp1();
1411 assert(!dstAddr->isContained());
1412 assert(!initVal->isContained());
1413 if (initBlkNode->gtOper == GT_STORE_DYN_BLK)
1415 assert(initBlkNode->AsDynBlk()->gtDynamicSize->gtRegNum == REG_ARG_2);
1419 assert(initBlkNode->gtRsvdRegs == RBM_ARG_2);
1422 #ifdef _TARGET_ARM64_
1425 assert(size > INITBLK_UNROLL_LIMIT);
1427 #endif // _TARGET_ARM64_
1429 genConsumeBlockOp(initBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2);
1431 if (initBlkNode->gtFlags & GTF_BLK_VOLATILE)
1433 // issue a full memory barrier before a volatile initBlock Operation
1434 instGen_MemoryBarrier();
1437 genEmitHelperCall(CORINFO_HELP_MEMSET, 0, EA_UNKNOWN);
1440 //------------------------------------------------------------------------
1441 // genRegCopy: Generate a register copy.
1443 void CodeGen::genRegCopy(GenTree* treeNode)
1445 assert(treeNode->OperGet() == GT_COPY);
1447 var_types targetType = treeNode->TypeGet();
1448 regNumber targetReg = treeNode->gtRegNum;
1449 assert(targetReg != REG_NA);
1451 GenTree* op1 = treeNode->gtOp.gtOp1;
1453 // Check whether this node and the node from which we're copying the value have the same
1455 // This can happen if (currently iff) we have a SIMD vector type that fits in an integer
1456 // register, in which case it is passed as an argument, or returned from a call,
1457 // in an integer register and must be copied if it's in an xmm register.
1459 if (varTypeIsFloating(treeNode) != varTypeIsFloating(op1))
1461 NYI_ARM("genRegCopy floating point");
1462 #ifdef _TARGET_ARM64_
1463 inst_RV_RV(INS_fmov, targetReg, genConsumeReg(op1), targetType);
1464 #endif // _TARGET_ARM64_
1468 inst_RV_RV(ins_Copy(targetType), targetReg, genConsumeReg(op1), targetType);
1473 // The lclVar will never be a def.
1474 // If it is a last use, the lclVar will be killed by genConsumeReg(), as usual, and genProduceReg will
1475 // appropriately set the gcInfo for the copied value.
1476 // If not, there are two cases we need to handle:
1477 // - If this is a TEMPORARY copy (indicated by the GTF_VAR_DEATH flag) the variable
1478 // will remain live in its original register.
1479 // genProduceReg() will appropriately set the gcInfo for the copied value,
1480 // and genConsumeReg will reset it.
1481 // - Otherwise, we need to update register info for the lclVar.
1483 GenTreeLclVarCommon* lcl = op1->AsLclVarCommon();
1484 assert((lcl->gtFlags & GTF_VAR_DEF) == 0);
1486 if ((lcl->gtFlags & GTF_VAR_DEATH) == 0 && (treeNode->gtFlags & GTF_VAR_DEATH) == 0)
1488 LclVarDsc* varDsc = &compiler->lvaTable[lcl->gtLclNum];
1490 // If we didn't just spill it (in genConsumeReg, above), then update the register info
1491 if (varDsc->lvRegNum != REG_STK)
1493 // The old location is dying
1494 genUpdateRegLife(varDsc, /*isBorn*/ false, /*isDying*/ true DEBUGARG(op1));
1496 gcInfo.gcMarkRegSetNpt(genRegMask(op1->gtRegNum));
1498 genUpdateVarReg(varDsc, treeNode);
1500 // The new location is going live
1501 genUpdateRegLife(varDsc, /*isBorn*/ true, /*isDying*/ false DEBUGARG(treeNode));
1506 genProduceReg(treeNode);
1509 //------------------------------------------------------------------------
1510 // genCallInstruction: Produce code for a GT_CALL node
1512 void CodeGen::genCallInstruction(GenTreeCall* call)
1514 gtCallTypes callType = (gtCallTypes)call->gtCallType;
1516 IL_OFFSETX ilOffset = BAD_IL_OFFSET;
1518 // all virtuals should have been expanded into a control expression
1519 assert(!call->IsVirtual() || call->gtControlExpr || call->gtCallAddr);
1521 // Consume all the arg regs
1522 for (GenTreePtr list = call->gtCallLateArgs; list; list = list->MoveNext())
1524 assert(list->OperIsList());
1526 GenTreePtr argNode = list->Current();
1528 fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, argNode->gtSkipReloadOrCopy());
1529 assert(curArgTabEntry);
1531 if (curArgTabEntry->regNum == REG_STK)
1534 // Deal with multi register passed struct args.
1535 if (argNode->OperGet() == GT_FIELD_LIST)
1537 GenTreeArgList* argListPtr = argNode->AsArgList();
1538 unsigned iterationNum = 0;
1539 regNumber argReg = curArgTabEntry->regNum;
1540 for (; argListPtr != nullptr; argListPtr = argListPtr->Rest(), iterationNum++)
1542 GenTreePtr putArgRegNode = argListPtr->gtOp.gtOp1;
1543 assert(putArgRegNode->gtOper == GT_PUTARG_REG);
1545 genConsumeReg(putArgRegNode);
1547 if (putArgRegNode->gtRegNum != argReg)
1549 inst_RV_RV(ins_Move_Extend(putArgRegNode->TypeGet(), putArgRegNode->InReg()), argReg,
1550 putArgRegNode->gtRegNum);
1553 argReg = genRegArgNext(argReg);
1555 #if defined(_TARGET_ARM_)
1556 // A double register is modelled as an even-numbered single one
1557 if (putArgRegNode->TypeGet() == TYP_DOUBLE)
1559 argReg = genRegArgNext(argReg);
1561 #endif // _TARGET_ARM_
1566 regNumber argReg = curArgTabEntry->regNum;
1567 genConsumeReg(argNode);
1568 if (argNode->gtRegNum != argReg)
1570 inst_RV_RV(ins_Move_Extend(argNode->TypeGet(), argNode->InReg()), argReg, argNode->gtRegNum);
1574 // In the case of a varargs call,
1575 // the ABI dictates that if we have floating point args,
1576 // we must pass the enregistered arguments in both the
1577 // integer and floating point registers so, let's do that.
1578 if (call->IsVarargs() && varTypeIsFloating(argNode))
1580 NYI_ARM("CodeGen - IsVarargs");
1581 NYI_ARM64("CodeGen - IsVarargs");
1585 // Insert a null check on "this" pointer if asked.
1586 if (call->NeedsNullCheck())
1588 const regNumber regThis = genGetThisArgReg(call);
1590 #if defined(_TARGET_ARM_)
1591 const regNumber tmpReg = call->ExtractTempReg();
1592 getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, tmpReg, regThis, 0);
1593 #elif defined(_TARGET_ARM64_)
1594 getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, REG_ZR, regThis, 0);
1598 // Either gtControlExpr != null or gtCallAddr != null or it is a direct non-virtual call to a user or helper method.
1599 CORINFO_METHOD_HANDLE methHnd;
1600 GenTree* target = call->gtControlExpr;
1601 if (callType == CT_INDIRECT)
1603 assert(target == nullptr);
1604 target = call->gtCallAddr;
1609 methHnd = call->gtCallMethHnd;
1612 CORINFO_SIG_INFO* sigInfo = nullptr;
1614 // Pass the call signature information down into the emitter so the emitter can associate
1615 // native call sites with the signatures they were generated from.
1616 if (callType != CT_HELPER)
1618 sigInfo = call->callSig;
1622 // If fast tail call, then we are done. In this case we setup the args (both reg args
1623 // and stack args in incoming arg area) and call target. Epilog sequence would
1624 // generate "br <reg>".
1625 if (call->IsFastTailCall())
1627 // Don't support fast tail calling JIT helpers
1628 assert(callType != CT_HELPER);
1630 // Fast tail calls materialize call target either in gtControlExpr or in gtCallAddr.
1631 assert(target != nullptr);
1633 genConsumeReg(target);
1635 NYI_ARM("fast tail call");
1637 #ifdef _TARGET_ARM64_
1638 // Use IP0 as the call target register.
1639 if (target->gtRegNum != REG_IP0)
1641 inst_RV_RV(INS_mov, REG_IP0, target->gtRegNum);
1643 #endif // _TARGET_ARM64_
1648 // For a pinvoke to unmanaged code we emit a label to clear
1649 // the GC pointer state before the callsite.
1650 // We can't utilize the typical lazy killing of GC pointers
1651 // at (or inside) the callsite.
1652 if (call->IsUnmanaged())
1654 genDefineTempLabel(genCreateTempLabel());
1657 // Determine return value size(s).
1658 ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc();
1659 emitAttr retSize = EA_PTRSIZE;
1660 emitAttr secondRetSize = EA_UNKNOWN;
1662 if (call->HasMultiRegRetVal())
1664 retSize = emitTypeSize(pRetTypeDesc->GetReturnRegType(0));
1665 secondRetSize = emitTypeSize(pRetTypeDesc->GetReturnRegType(1));
1669 assert(!varTypeIsStruct(call));
1671 if (call->gtType == TYP_REF || call->gtType == TYP_ARRAY)
1675 else if (call->gtType == TYP_BYREF)
1681 // We need to propagate the IL offset information to the call instruction, so we can emit
1682 // an IL to native mapping record for the call, to support managed return value debugging.
1683 // We don't want tail call helper calls that were converted from normal calls to get a record,
1684 // so we skip this hash table lookup logic in that case.
1685 if (compiler->opts.compDbgInfo && compiler->genCallSite2ILOffsetMap != nullptr && !call->IsTailCall())
1687 (void)compiler->genCallSite2ILOffsetMap->Lookup(call, &ilOffset);
1690 if (target != nullptr)
1692 // A call target can not be a contained indirection
1693 assert(!target->isContainedIndir());
1695 genConsumeReg(target);
1697 // We have already generated code for gtControlExpr evaluating it into a register.
1698 // We just need to emit "call reg" in this case.
1700 assert(genIsValidIntReg(target->gtRegNum));
1702 genEmitCall(emitter::EC_INDIR_R, methHnd,
1703 INDEBUG_LDISASM_COMMA(sigInfo) nullptr, // addr
1704 retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), ilOffset, target->gtRegNum);
1708 // Generate a direct call to a non-virtual user defined or helper method
1709 assert(callType == CT_HELPER || callType == CT_USER_FUNC);
1711 void* addr = nullptr;
1712 if (callType == CT_HELPER)
1714 // Direct call to a helper method.
1715 CorInfoHelpFunc helperNum = compiler->eeGetHelperNum(methHnd);
1716 noway_assert(helperNum != CORINFO_HELP_UNDEF);
1718 void* pAddr = nullptr;
1719 addr = compiler->compGetHelperFtn(helperNum, (void**)&pAddr);
1721 if (addr == nullptr)
1728 // Direct call to a non-virtual user function.
1729 CORINFO_ACCESS_FLAGS aflags = CORINFO_ACCESS_ANY;
1730 if (call->IsSameThis())
1732 aflags = (CORINFO_ACCESS_FLAGS)(aflags | CORINFO_ACCESS_THIS);
1735 if ((call->NeedsNullCheck()) == 0)
1737 aflags = (CORINFO_ACCESS_FLAGS)(aflags | CORINFO_ACCESS_NONNULL);
1740 CORINFO_CONST_LOOKUP addrInfo;
1741 compiler->info.compCompHnd->getFunctionEntryPoint(methHnd, &addrInfo, aflags);
1743 addr = addrInfo.addr;
1746 assert(addr != nullptr);
1748 // Non-virtual direct call to known addresses
1750 if (!arm_Valid_Imm_For_BL((ssize_t)addr))
1752 regNumber tmpReg = call->GetSingleTempReg();
1753 instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, tmpReg, (ssize_t)addr);
1754 genEmitCall(emitter::EC_INDIR_R, methHnd, INDEBUG_LDISASM_COMMA(sigInfo) NULL, retSize, ilOffset, tmpReg);
1757 #endif // _TARGET_ARM_
1759 genEmitCall(emitter::EC_FUNC_TOKEN, methHnd, INDEBUG_LDISASM_COMMA(sigInfo) addr,
1760 retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), ilOffset);
1763 #if 0 && defined(_TARGET_ARM64_)
1764 // Use this path if you want to load an absolute call target using
1765 // a sequence of movs followed by an indirect call (blr instruction)
1767 // Load the call target address in x16
1768 instGen_Set_Reg_To_Imm(EA_8BYTE, REG_IP0, (ssize_t) addr);
1770 // indirect call to constant address in IP0
1771 genEmitCall(emitter::EC_INDIR_R,
1773 INDEBUG_LDISASM_COMMA(sigInfo)
1782 // if it was a pinvoke we may have needed to get the address of a label
1783 if (genPendingCallLabel)
1785 assert(call->IsUnmanaged());
1786 genDefineTempLabel(genPendingCallLabel);
1787 genPendingCallLabel = nullptr;
1791 // All Callee arg registers are trashed and no longer contain any GC pointers.
1792 // TODO-Bug?: As a matter of fact shouldn't we be killing all of callee trashed regs here?
1793 // For now we will assert that other than arg regs gc ref/byref set doesn't contain any other
1794 // registers from RBM_CALLEE_TRASH
1795 assert((gcInfo.gcRegGCrefSetCur & (RBM_CALLEE_TRASH & ~RBM_ARG_REGS)) == 0);
1796 assert((gcInfo.gcRegByrefSetCur & (RBM_CALLEE_TRASH & ~RBM_ARG_REGS)) == 0);
1797 gcInfo.gcRegGCrefSetCur &= ~RBM_ARG_REGS;
1798 gcInfo.gcRegByrefSetCur &= ~RBM_ARG_REGS;
1800 var_types returnType = call->TypeGet();
1801 if (returnType != TYP_VOID)
1803 regNumber returnReg;
1805 if (call->HasMultiRegRetVal())
1807 assert(pRetTypeDesc != nullptr);
1808 unsigned regCount = pRetTypeDesc->GetReturnRegCount();
1810 // If regs allocated to call node are different from ABI return
1811 // regs in which the call has returned its result, move the result
1812 // to regs allocated to call node.
1813 for (unsigned i = 0; i < regCount; ++i)
1815 var_types regType = pRetTypeDesc->GetReturnRegType(i);
1816 returnReg = pRetTypeDesc->GetABIReturnReg(i);
1817 regNumber allocatedReg = call->GetRegNumByIdx(i);
1818 if (returnReg != allocatedReg)
1820 inst_RV_RV(ins_Copy(regType), allocatedReg, returnReg, regType);
1827 if (call->IsHelperCall(compiler, CORINFO_HELP_INIT_PINVOKE_FRAME))
1829 // The CORINFO_HELP_INIT_PINVOKE_FRAME helper uses a custom calling convention that returns with
1830 // TCB in REG_PINVOKE_TCB. fgMorphCall() sets the correct argument registers.
1831 returnReg = REG_PINVOKE_TCB;
1834 #endif // _TARGET_ARM_
1835 if (varTypeIsFloating(returnType))
1837 returnReg = REG_FLOATRET;
1841 returnReg = REG_INTRET;
1844 if (call->gtRegNum != returnReg)
1846 inst_RV_RV(ins_Copy(returnType), call->gtRegNum, returnReg, returnType);
1850 genProduceReg(call);
1853 // If there is nothing next, that means the result is thrown away, so this value is not live.
1854 // However, for minopts or debuggable code, we keep it live to support managed return value debugging.
1855 if ((call->gtNext == nullptr) && !compiler->opts.MinOpts() && !compiler->opts.compDbgCode)
1857 gcInfo.gcMarkRegSetNpt(RBM_INTRET);
1861 // Produce code for a GT_JMP node.
1862 // The arguments of the caller needs to be transferred to the callee before exiting caller.
1863 // The actual jump to callee is generated as part of caller epilog sequence.
1864 // Therefore the codegen of GT_JMP is to ensure that the callee arguments are correctly setup.
1865 void CodeGen::genJmpMethod(GenTreePtr jmp)
1867 assert(jmp->OperGet() == GT_JMP);
1868 assert(compiler->compJmpOpUsed);
1870 // If no arguments, nothing to do
1871 if (compiler->info.compArgsCount == 0)
1876 // Make sure register arguments are in their initial registers
1877 // and stack arguments are put back as well.
1881 // First move any en-registered stack arguments back to the stack.
1882 // At the same time any reg arg not in correct reg is moved back to its stack location.
1884 // We are not strictly required to spill reg args that are not in the desired reg for a jmp call
1885 // But that would require us to deal with circularity while moving values around. Spilling
1886 // to stack makes the implementation simple, which is not a bad trade off given Jmp calls
1887 // are not frequent.
1888 for (varNum = 0; (varNum < compiler->info.compArgsCount); varNum++)
1890 varDsc = compiler->lvaTable + varNum;
1892 if (varDsc->lvPromoted)
1894 noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
1896 unsigned fieldVarNum = varDsc->lvFieldLclStart;
1897 varDsc = compiler->lvaTable + fieldVarNum;
1899 noway_assert(varDsc->lvIsParam);
1901 if (varDsc->lvIsRegArg && (varDsc->lvRegNum != REG_STK))
1903 // Skip reg args which are already in its right register for jmp call.
1904 // If not, we will spill such args to their stack locations.
1906 // If we need to generate a tail call profiler hook, then spill all
1907 // arg regs to free them up for the callback.
1908 if (!compiler->compIsProfilerHookNeeded() && (varDsc->lvRegNum == varDsc->lvArgReg))
1911 else if (varDsc->lvRegNum == REG_STK)
1913 // Skip args which are currently living in stack.
1917 // If we came here it means either a reg argument not in the right register or
1918 // a stack argument currently living in a register. In either case the following
1919 // assert should hold.
1920 assert(varDsc->lvRegNum != REG_STK);
1921 assert(varDsc->TypeGet() != TYP_STRUCT);
1922 var_types storeType = genActualType(varDsc->TypeGet());
1923 emitAttr storeSize = emitActualTypeSize(storeType);
1925 getEmitter()->emitIns_S_R(ins_Store(storeType), storeSize, varDsc->lvRegNum, varNum, 0);
1926 // Update lvRegNum life and GC info to indicate lvRegNum is dead and varDsc stack slot is going live.
1927 // Note that we cannot modify varDsc->lvRegNum here because another basic block may not be expecting it.
1928 // Therefore manually update life of varDsc->lvRegNum.
1929 regMaskTP tempMask = genRegMask(varDsc->lvRegNum);
1930 regSet.RemoveMaskVars(tempMask);
1931 gcInfo.gcMarkRegSetNpt(tempMask);
1932 if (compiler->lvaIsGCTracked(varDsc))
1934 VarSetOps::AddElemD(compiler, gcInfo.gcVarPtrSetCur, varNum);
1938 #ifdef PROFILING_SUPPORTED
1939 // At this point all arg regs are free.
1940 // Emit tail call profiler callback.
1941 genProfilingLeaveCallback(CORINFO_HELP_PROF_FCN_TAILCALL);
1944 // Next move any un-enregistered register arguments back to their register.
1945 regMaskTP fixedIntArgMask = RBM_NONE; // tracks the int arg regs occupying fixed args in case of a vararg method.
1946 unsigned firstArgVarNum = BAD_VAR_NUM; // varNum of the first argument in case of a vararg method.
1947 for (varNum = 0; (varNum < compiler->info.compArgsCount); varNum++)
1949 varDsc = compiler->lvaTable + varNum;
1950 if (varDsc->lvPromoted)
1952 noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
1954 unsigned fieldVarNum = varDsc->lvFieldLclStart;
1955 varDsc = compiler->lvaTable + fieldVarNum;
1957 noway_assert(varDsc->lvIsParam);
1959 // Skip if arg not passed in a register.
1960 if (!varDsc->lvIsRegArg)
1963 // Register argument
1964 noway_assert(isRegParamType(genActualType(varDsc->TypeGet())));
1966 // Is register argument already in the right register?
1967 // If not load it from its stack location.
1968 regNumber argReg = varDsc->lvArgReg; // incoming arg register
1969 regNumber argRegNext = REG_NA;
1971 if (varDsc->lvRegNum != argReg)
1973 var_types loadType = TYP_UNDEF;
1974 if (varTypeIsStruct(varDsc))
1976 // Must be <= 16 bytes or else it wouldn't be passed in registers
1977 noway_assert(EA_SIZE_IN_BYTES(varDsc->lvSize()) <= MAX_PASS_MULTIREG_BYTES);
1978 loadType = compiler->getJitGCType(varDsc->lvGcLayout[0]);
1982 loadType = compiler->mangleVarArgsType(genActualType(varDsc->TypeGet()));
1984 emitAttr loadSize = emitActualTypeSize(loadType);
1985 getEmitter()->emitIns_R_S(ins_Load(loadType), loadSize, argReg, varNum, 0);
1987 // Update argReg life and GC Info to indicate varDsc stack slot is dead and argReg is going live.
1988 // Note that we cannot modify varDsc->lvRegNum here because another basic block may not be expecting it.
1989 // Therefore manually update life of argReg. Note that GT_JMP marks the end of the basic block
1990 // and after which reg life and gc info will be recomputed for the new block in genCodeForBBList().
1991 regSet.AddMaskVars(genRegMask(argReg));
1992 gcInfo.gcMarkRegPtrVal(argReg, loadType);
1994 if (compiler->lvaIsMultiregStruct(varDsc))
1996 if (varDsc->lvIsHfa())
1998 NYI_ARM("CodeGen::genJmpMethod with multireg HFA arg");
1999 NYI_ARM64("CodeGen::genJmpMethod with multireg HFA arg");
2002 // Restore the second register.
2003 argRegNext = genRegArgNext(argReg);
2005 loadType = compiler->getJitGCType(varDsc->lvGcLayout[1]);
2006 loadSize = emitActualTypeSize(loadType);
2007 getEmitter()->emitIns_R_S(ins_Load(loadType), loadSize, argRegNext, varNum, TARGET_POINTER_SIZE);
2009 regSet.AddMaskVars(genRegMask(argRegNext));
2010 gcInfo.gcMarkRegPtrVal(argRegNext, loadType);
2013 if (compiler->lvaIsGCTracked(varDsc))
2015 VarSetOps::RemoveElemD(compiler, gcInfo.gcVarPtrSetCur, varNum);
2019 // In case of a jmp call to a vararg method ensure only integer registers are passed.
2020 if (compiler->info.compIsVarArgs)
2022 assert((genRegMask(argReg) & RBM_ARG_REGS) != RBM_NONE);
2024 fixedIntArgMask |= genRegMask(argReg);
2026 if (compiler->lvaIsMultiregStruct(varDsc))
2028 assert(argRegNext != REG_NA);
2029 fixedIntArgMask |= genRegMask(argRegNext);
2032 if (argReg == REG_ARG_0)
2034 assert(firstArgVarNum == BAD_VAR_NUM);
2035 firstArgVarNum = varNum;
2040 // Jmp call to a vararg method - if the method has fewer than fixed arguments that can be max size of reg,
2041 // load the remaining integer arg registers from the corresponding
2042 // shadow stack slots. This is for the reason that we don't know the number and type
2043 // of non-fixed params passed by the caller, therefore we have to assume the worst case
2044 // of caller passing all integer arg regs that can be max size of reg.
2046 // The caller could have passed gc-ref/byref type var args. Since these are var args
2047 // the callee no way of knowing their gc-ness. Therefore, mark the region that loads
2048 // remaining arg registers from shadow stack slots as non-gc interruptible.
2049 if (fixedIntArgMask != RBM_NONE)
2051 assert(compiler->info.compIsVarArgs);
2052 assert(firstArgVarNum != BAD_VAR_NUM);
2054 regMaskTP remainingIntArgMask = RBM_ARG_REGS & ~fixedIntArgMask;
2055 if (remainingIntArgMask != RBM_NONE)
2057 getEmitter()->emitDisableGC();
2058 for (int argNum = 0, argOffset = 0; argNum < MAX_REG_ARG; ++argNum)
2060 regNumber argReg = intArgRegs[argNum];
2061 regMaskTP argRegMask = genRegMask(argReg);
2063 if ((remainingIntArgMask & argRegMask) != 0)
2065 remainingIntArgMask &= ~argRegMask;
2066 getEmitter()->emitIns_R_S(INS_ldr, EA_PTRSIZE, argReg, firstArgVarNum, argOffset);
2069 argOffset += REGSIZE_BYTES;
2071 getEmitter()->emitEnableGC();
2076 //------------------------------------------------------------------------
2077 // genIntToIntCast: Generate code for an integer cast
2080 // treeNode - The GT_CAST node
2086 // The treeNode must have an assigned register.
2087 // For a signed convert from byte, the source must be in a byte-addressable register.
2088 // Neither the source nor target type can be a floating point type.
2090 // TODO-ARM64-CQ: Allow castOp to be a contained node without an assigned register.
2092 void CodeGen::genIntToIntCast(GenTreePtr treeNode)
2094 assert(treeNode->OperGet() == GT_CAST);
2096 GenTreePtr castOp = treeNode->gtCast.CastOp();
2097 emitter* emit = getEmitter();
2099 var_types dstType = treeNode->CastToType();
2100 var_types srcType = genActualType(castOp->TypeGet());
2101 emitAttr movSize = emitActualTypeSize(dstType);
2102 bool movRequired = false;
2105 if (varTypeIsLong(srcType))
2107 genLongToIntCast(treeNode);
2110 #endif // _TARGET_ARM_
2112 regNumber targetReg = treeNode->gtRegNum;
2113 regNumber sourceReg = castOp->gtRegNum;
2115 // For Long to Int conversion we will have a reserved integer register to hold the immediate mask
2116 regNumber tmpReg = (treeNode->AvailableTempRegCount() == 0) ? REG_NA : treeNode->GetSingleTempReg();
2118 assert(genIsValidIntReg(targetReg));
2119 assert(genIsValidIntReg(sourceReg));
2121 instruction ins = INS_invalid;
2123 genConsumeReg(castOp);
2124 Lowering::CastInfo castInfo;
2126 // Get information about the cast.
2127 Lowering::getCastDescription(treeNode, &castInfo);
2129 if (castInfo.requiresOverflowCheck)
2131 emitAttr cmpSize = EA_ATTR(genTypeSize(srcType));
2133 if (castInfo.signCheckOnly)
2135 // We only need to check for a negative value in sourceReg
2136 emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, 0);
2137 emitJumpKind jmpLT = genJumpKindForOper(GT_LT, CK_SIGNED);
2138 genJumpToThrowHlpBlk(jmpLT, SCK_OVERFLOW);
2139 noway_assert(genTypeSize(srcType) == 4 || genTypeSize(srcType) == 8);
2140 // This is only interesting case to ensure zero-upper bits.
2141 if ((srcType == TYP_INT) && (dstType == TYP_ULONG))
2143 // cast to TYP_ULONG:
2144 // We use a mov with size=EA_4BYTE
2145 // which will zero out the upper bits
2150 else if (castInfo.unsignedSource || castInfo.unsignedDest)
2152 // When we are converting from/to unsigned,
2153 // we only have to check for any bits set in 'typeMask'
2155 noway_assert(castInfo.typeMask != 0);
2156 #if defined(_TARGET_ARM_)
2157 if (arm_Valid_Imm_For_Instr(INS_tst, castInfo.typeMask, INS_FLAGS_DONT_CARE))
2159 emit->emitIns_R_I(INS_tst, cmpSize, sourceReg, castInfo.typeMask);
2163 noway_assert(tmpReg != REG_NA);
2164 instGen_Set_Reg_To_Imm(cmpSize, tmpReg, castInfo.typeMask);
2165 emit->emitIns_R_R(INS_tst, cmpSize, sourceReg, tmpReg);
2167 #elif defined(_TARGET_ARM64_)
2168 emit->emitIns_R_I(INS_tst, cmpSize, sourceReg, castInfo.typeMask);
2169 #endif // _TARGET_ARM*
2170 emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED);
2171 genJumpToThrowHlpBlk(jmpNotEqual, SCK_OVERFLOW);
2175 // For a narrowing signed cast
2177 // We must check the value is in a signed range.
2179 // Compare with the MAX
2181 noway_assert((castInfo.typeMin != 0) && (castInfo.typeMax != 0));
2183 #if defined(_TARGET_ARM_)
2184 if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, INS_FLAGS_DONT_CARE))
2185 #elif defined(_TARGET_ARM64_)
2186 if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, cmpSize))
2189 emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, castInfo.typeMax);
2193 noway_assert(tmpReg != REG_NA);
2194 instGen_Set_Reg_To_Imm(cmpSize, tmpReg, castInfo.typeMax);
2195 emit->emitIns_R_R(INS_cmp, cmpSize, sourceReg, tmpReg);
2198 emitJumpKind jmpGT = genJumpKindForOper(GT_GT, CK_SIGNED);
2199 genJumpToThrowHlpBlk(jmpGT, SCK_OVERFLOW);
2201 // Compare with the MIN
2203 #if defined(_TARGET_ARM_)
2204 if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, INS_FLAGS_DONT_CARE))
2205 #elif defined(_TARGET_ARM64_)
2206 if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, cmpSize))
2209 emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, castInfo.typeMin);
2213 noway_assert(tmpReg != REG_NA);
2214 instGen_Set_Reg_To_Imm(cmpSize, tmpReg, castInfo.typeMin);
2215 emit->emitIns_R_R(INS_cmp, cmpSize, sourceReg, tmpReg);
2218 emitJumpKind jmpLT = genJumpKindForOper(GT_LT, CK_SIGNED);
2219 genJumpToThrowHlpBlk(jmpLT, SCK_OVERFLOW);
2223 else // Non-overflow checking cast.
2225 if (genTypeSize(srcType) == genTypeSize(dstType))
2231 var_types extendType = TYP_UNKNOWN;
2233 if (genTypeSize(srcType) < genTypeSize(dstType))
2235 // If we need to treat a signed type as unsigned
2236 if ((treeNode->gtFlags & GTF_UNSIGNED) != 0)
2238 extendType = genUnsignedType(srcType);
2241 extendType = srcType;
2243 movSize = emitTypeSize(extendType);
2244 #endif // _TARGET_ARM_
2245 if (extendType == TYP_UINT)
2247 #ifdef _TARGET_ARM64_
2248 // If we are casting from a smaller type to
2249 // a larger type, then we need to make sure the
2250 // higher 4 bytes are zero to gaurentee the correct value.
2251 // Therefore using a mov with EA_4BYTE in place of EA_8BYTE
2252 // will zero the upper bits
2254 #endif // _TARGET_ARM64_
2258 else // (genTypeSize(srcType) > genTypeSize(dstType))
2260 // If we need to treat a signed type as unsigned
2261 if ((treeNode->gtFlags & GTF_UNSIGNED) != 0)
2263 extendType = genUnsignedType(dstType);
2266 extendType = dstType;
2267 #if defined(_TARGET_ARM_)
2268 movSize = emitTypeSize(extendType);
2269 #elif defined(_TARGET_ARM64_)
2270 if (extendType == TYP_INT)
2272 movSize = EA_8BYTE; // a sxtw instruction requires EA_8BYTE
2277 ins = ins_Move_Extend(extendType, castOp->InReg());
2281 // We should never be generating a load from memory instruction here!
2282 assert(!emit->emitInsIsLoad(ins));
2284 if ((ins != INS_mov) || movRequired || (targetReg != sourceReg))
2286 emit->emitIns_R_R(ins, movSize, targetReg, sourceReg);
2289 genProduceReg(treeNode);
2292 //------------------------------------------------------------------------
2293 // genFloatToFloatCast: Generate code for a cast between float and double
2296 // treeNode - The GT_CAST node
2302 // Cast is a non-overflow conversion.
2303 // The treeNode must have an assigned register.
2304 // The cast is between float and double.
2306 void CodeGen::genFloatToFloatCast(GenTreePtr treeNode)
2308 // float <--> double conversions are always non-overflow ones
2309 assert(treeNode->OperGet() == GT_CAST);
2310 assert(!treeNode->gtOverflow());
2312 regNumber targetReg = treeNode->gtRegNum;
2313 assert(genIsValidFloatReg(targetReg));
2315 GenTreePtr op1 = treeNode->gtOp.gtOp1;
2316 assert(!op1->isContained()); // Cannot be contained
2317 assert(genIsValidFloatReg(op1->gtRegNum)); // Must be a valid float reg.
2319 var_types dstType = treeNode->CastToType();
2320 var_types srcType = op1->TypeGet();
2321 assert(varTypeIsFloating(srcType) && varTypeIsFloating(dstType));
2323 genConsumeOperands(treeNode->AsOp());
2325 // treeNode must be a reg
2326 assert(!treeNode->isContained());
2328 #if defined(_TARGET_ARM_)
2330 if (srcType != dstType)
2332 instruction insVcvt = (srcType == TYP_FLOAT) ? INS_vcvt_f2d // convert Float to Double
2333 : INS_vcvt_d2f; // convert Double to Float
2335 getEmitter()->emitIns_R_R(insVcvt, emitTypeSize(treeNode), treeNode->gtRegNum, op1->gtRegNum);
2337 else if (treeNode->gtRegNum != op1->gtRegNum)
2339 getEmitter()->emitIns_R_R(INS_vmov, emitTypeSize(treeNode), treeNode->gtRegNum, op1->gtRegNum);
2342 #elif defined(_TARGET_ARM64_)
2344 if (srcType != dstType)
2346 insOpts cvtOption = (srcType == TYP_FLOAT) ? INS_OPTS_S_TO_D // convert Single to Double
2347 : INS_OPTS_D_TO_S; // convert Double to Single
2349 getEmitter()->emitIns_R_R(INS_fcvt, emitTypeSize(treeNode), treeNode->gtRegNum, op1->gtRegNum, cvtOption);
2351 else if (treeNode->gtRegNum != op1->gtRegNum)
2353 // If double to double cast or float to float cast. Emit a move instruction.
2354 getEmitter()->emitIns_R_R(INS_mov, emitTypeSize(treeNode), treeNode->gtRegNum, op1->gtRegNum);
2359 genProduceReg(treeNode);
2362 //------------------------------------------------------------------------
2363 // genCreateAndStoreGCInfo: Create and record GC Info for the function.
2365 void CodeGen::genCreateAndStoreGCInfo(unsigned codeSize,
2366 unsigned prologSize,
2367 unsigned epilogSize DEBUGARG(void* codePtr))
2369 IAllocator* allowZeroAlloc = new (compiler, CMK_GC) AllowZeroAllocator(compiler->getAllocatorGC());
2370 GcInfoEncoder* gcInfoEncoder = new (compiler, CMK_GC)
2371 GcInfoEncoder(compiler->info.compCompHnd, compiler->info.compMethodInfo, allowZeroAlloc, NOMEM);
2372 assert(gcInfoEncoder != nullptr);
2374 // Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32).
2375 gcInfo.gcInfoBlockHdrSave(gcInfoEncoder, codeSize, prologSize);
2377 // We keep the call count for the second call to gcMakeRegPtrTable() below.
2378 unsigned callCnt = 0;
2380 // First we figure out the encoder ID's for the stack slots and registers.
2381 gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS, &callCnt);
2383 // Now we've requested all the slots we'll need; "finalize" these (make more compact data structures for them).
2384 gcInfoEncoder->FinalizeSlotIds();
2386 // Now we can actually use those slot ID's to declare live ranges.
2387 gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt);
2389 #ifdef _TARGET_ARM64_
2391 if (compiler->opts.compDbgEnC)
2393 // what we have to preserve is called the "frame header" (see comments in VM\eetwain.cpp)
2397 // -saved 'this' pointer and bool for synchronized methods
2399 // 4 slots for RBP + return address + RSI + RDI
2400 int preservedAreaSize = 4 * REGSIZE_BYTES;
2402 if (compiler->info.compFlags & CORINFO_FLG_SYNCH)
2404 if (!(compiler->info.compFlags & CORINFO_FLG_STATIC))
2405 preservedAreaSize += REGSIZE_BYTES;
2407 preservedAreaSize += 1; // bool for synchronized methods
2410 // Used to signal both that the method is compiled for EnC, and also the size of the block at the top of the
2412 gcInfoEncoder->SetSizeOfEditAndContinuePreservedArea(preservedAreaSize);
2415 #endif // _TARGET_ARM64_
2417 gcInfoEncoder->Build();
2419 // GC Encoder automatically puts the GC info in the right spot using ICorJitInfo::allocGCInfo(size_t)
2420 // let's save the values anyway for debugging purposes
2421 compiler->compInfoBlkAddr = gcInfoEncoder->Emit();
2422 compiler->compInfoBlkSize = 0; // not exposed by the GCEncoder interface
2425 //-------------------------------------------------------------------------------------------
2426 // genJumpKindsForTree: Determine the number and kinds of conditional branches
2427 // necessary to implement the given GT_CMP node
2430 // cmpTree - (input) The GenTree node that is used to set the Condition codes
2431 // - The GenTree Relop node that was used to set the Condition codes
2432 // jmpKind[2] - (output) One or two conditional branch instructions
2433 // jmpToTrueLabel[2] - (output) On Arm64 both branches will always branch to the true label
2436 // Sets the proper values into the array elements of jmpKind[] and jmpToTrueLabel[]
2439 // At least one conditional branch instruction will be returned.
2440 // Typically only one conditional branch is needed
2441 // and the second jmpKind[] value is set to EJ_NONE
2443 void CodeGen::genJumpKindsForTree(GenTreePtr cmpTree, emitJumpKind jmpKind[2], bool jmpToTrueLabel[2])
2445 // On ARM both branches will always branch to the true label
2446 jmpToTrueLabel[0] = true;
2447 jmpToTrueLabel[1] = true;
2449 // For integer comparisons just use genJumpKindForOper
2450 if (!varTypeIsFloating(cmpTree->gtOp.gtOp1->gtEffectiveVal()))
2452 CompareKind compareKind = ((cmpTree->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
2453 jmpKind[0] = genJumpKindForOper(cmpTree->gtOper, compareKind);
2454 jmpKind[1] = EJ_NONE;
2456 else // We have a Floating Point Compare operation
2458 assert(cmpTree->OperIsCompare());
2460 // For details on this mapping, see the ARM Condition Code table
2461 // at section A8.3 in the ARMv7 architecture manual or
2462 // at section C1.2.3 in the ARMV8 architecture manual.
2464 // We must check the GTF_RELOP_NAN_UN to find out
2465 // if we need to branch when we have a NaN operand.
2467 if ((cmpTree->gtFlags & GTF_RELOP_NAN_UN) != 0)
2469 // Must branch if we have an NaN, unordered
2470 switch (cmpTree->gtOper)
2473 jmpKind[0] = EJ_eq; // branch or set when equal (and no NaN's)
2474 jmpKind[1] = EJ_vs; // branch or set when we have a NaN
2478 jmpKind[0] = EJ_ne; // branch or set when not equal (or have NaN's)
2479 jmpKind[1] = EJ_NONE;
2483 jmpKind[0] = EJ_lt; // branch or set when less than (or have NaN's)
2484 jmpKind[1] = EJ_NONE;
2488 jmpKind[0] = EJ_le; // branch or set when less than or equal (or have NaN's)
2489 jmpKind[1] = EJ_NONE;
2493 jmpKind[0] = EJ_hi; // branch or set when greater than (or have NaN's)
2494 jmpKind[1] = EJ_NONE;
2498 jmpKind[0] = EJ_hs; // branch or set when greater than or equal (or have NaN's)
2499 jmpKind[1] = EJ_NONE;
2506 else // ((cmpTree->gtFlags & GTF_RELOP_NAN_UN) == 0)
2508 // Do not branch if we have an NaN, unordered
2509 switch (cmpTree->gtOper)
2512 jmpKind[0] = EJ_eq; // branch or set when equal (and no NaN's)
2513 jmpKind[1] = EJ_NONE;
2517 jmpKind[0] = EJ_gt; // branch or set when greater than (and no NaN's)
2518 jmpKind[1] = EJ_lo; // branch or set when less than (and no NaN's)
2522 jmpKind[0] = EJ_lo; // branch or set when less than (and no NaN's)
2523 jmpKind[1] = EJ_NONE;
2527 jmpKind[0] = EJ_ls; // branch or set when less than or equal (and no NaN's)
2528 jmpKind[1] = EJ_NONE;
2532 jmpKind[0] = EJ_gt; // branch or set when greater than (and no NaN's)
2533 jmpKind[1] = EJ_NONE;
2537 jmpKind[0] = EJ_ge; // branch or set when greater than or equal (and no NaN's)
2538 jmpKind[1] = EJ_NONE;
2548 //------------------------------------------------------------------------
2549 // genCodeForJumpTrue: Generates code for jmpTrue statement.
2552 // tree - The GT_JTRUE tree node.
2557 void CodeGen::genCodeForJumpTrue(GenTreePtr tree)
2559 GenTree* cmp = tree->gtOp.gtOp1->gtEffectiveVal();
2560 assert(cmp->OperIsCompare());
2561 assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
2563 // Get the "kind" and type of the comparison. Note that whether it is an unsigned cmp
2564 // is governed by a flag NOT by the inherent type of the node
2565 emitJumpKind jumpKind[2];
2566 bool branchToTrueLabel[2];
2567 genJumpKindsForTree(cmp, jumpKind, branchToTrueLabel);
2568 assert(jumpKind[0] != EJ_NONE);
2570 // On ARM the branches will always branch to the true label
2571 assert(branchToTrueLabel[0]);
2572 inst_JMP(jumpKind[0], compiler->compCurBB->bbJumpDest);
2574 if (jumpKind[1] != EJ_NONE)
2576 // the second conditional branch always has to be to the true label
2577 assert(branchToTrueLabel[1]);
2578 inst_JMP(jumpKind[1], compiler->compCurBB->bbJumpDest);
2582 #if defined(_TARGET_ARM_)
2584 //------------------------------------------------------------------------
2585 // genCodeForJcc: Produce code for a GT_JCC node.
2590 void CodeGen::genCodeForJcc(GenTreeCC* tree)
2592 assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
2594 CompareKind compareKind = ((tree->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
2595 emitJumpKind jumpKind = genJumpKindForOper(tree->gtCondition, compareKind);
2597 inst_JMP(jumpKind, compiler->compCurBB->bbJumpDest);
2600 #endif // defined(_TARGET_ARM_)
2602 //------------------------------------------------------------------------
2603 // genCodeForStoreBlk: Produce code for a GT_STORE_OBJ/GT_STORE_DYN_BLK/GT_STORE_BLK node.
2608 void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp)
2610 assert(blkOp->OperIs(GT_STORE_OBJ, GT_STORE_DYN_BLK, GT_STORE_BLK));
2612 if (blkOp->OperIs(GT_STORE_OBJ) && blkOp->OperIsCopyBlkOp())
2614 assert(blkOp->AsObj()->gtGcPtrCount != 0);
2615 genCodeForCpObj(blkOp->AsObj());
2619 if (blkOp->gtBlkOpGcUnsafe)
2621 getEmitter()->emitDisableGC();
2623 bool isCopyBlk = blkOp->OperIsCopyBlkOp();
2625 switch (blkOp->gtBlkOpKind)
2627 case GenTreeBlk::BlkOpKindHelper:
2630 genCodeForCpBlk(blkOp);
2634 genCodeForInitBlk(blkOp);
2638 case GenTreeBlk::BlkOpKindUnroll:
2641 genCodeForCpBlkUnroll(blkOp);
2645 genCodeForInitBlkUnroll(blkOp);
2653 if (blkOp->gtBlkOpGcUnsafe)
2655 getEmitter()->emitEnableGC();
2659 #endif // _TARGET_ARMARCH_
2661 #endif // !LEGACY_BACKEND