Merge similar arm32/arm64 codegen functions
authorBruce Forstall <brucefo@microsoft.com>
Tue, 11 Apr 2017 23:36:18 +0000 (16:36 -0700)
committerBruce Forstall <brucefo@microsoft.com>
Tue, 11 Apr 2017 23:36:18 +0000 (16:36 -0700)
Commit migrated from https://github.com/dotnet/coreclr/commit/8ce3737178f65ef5096ad9728f23cd451e89ac72

src/coreclr/src/jit/codegenarmarch.cpp
src/coreclr/src/jit/compiler.h
src/coreclr/src/jit/emitarm.h

index 1b3a520..8c06e5e 100644 (file)
@@ -24,8 +24,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 #include "gcinfo.h"
 #include "emit.h"
 
-#ifdef _TARGET_ARM_
-
 //------------------------------------------------------------------------
 // genSetRegToIcon: Generate code that will set the given register to the integer constant.
 //
@@ -42,32 +40,6 @@ void CodeGen::genSetRegToIcon(regNumber reg, ssize_t val, var_types type, insFla
     instGen_Set_Reg_To_Imm(emitActualTypeSize(type), reg, val, flags);
 }
 
-#endif // _TARGET_ARM_
-
-#ifdef _TARGET_ARM64_
-
-/*****************************************************************************
- *
- *  Generate code that will set the given register to the integer constant.
- */
-
-void CodeGen::genSetRegToIcon(regNumber reg, ssize_t val, var_types type, insFlags flags)
-{
-    // Reg cannot be a FP reg
-    assert(!genIsValidFloatReg(reg));
-
-    // The only TYP_REF constant that can come this path is a managed 'null' since it is not
-    // relocatable.  Other ref type constants (e.g. string objects) go through a different
-    // code path.
-    noway_assert(type != TYP_REF || val == 0);
-
-    instGen_Set_Reg_To_Imm(emitActualTypeSize(type), reg, val, flags);
-}
-
-#endif // _TARGET_ARM64_
-
-#ifdef _TARGET_ARM_
-
 //---------------------------------------------------------------------
 // genIntrinsic - generate code for a given intrinsic
 //
@@ -90,63 +62,18 @@ void CodeGen::genIntrinsic(GenTreePtr treeNode)
     {
         case CORINFO_INTRINSIC_Abs:
             genConsumeOperands(treeNode->AsOp());
-            getEmitter()->emitInsBinary(INS_vabs, emitTypeSize(treeNode), treeNode, srcNode);
+            getEmitter()->emitInsBinary(INS_ABS, emitTypeSize(treeNode), treeNode, srcNode);
             break;
 
         case CORINFO_INTRINSIC_Round:
             NYI_ARM("genIntrinsic for round - not implemented yet");
-            break;
-
-        case CORINFO_INTRINSIC_Sqrt:
-            genConsumeOperands(treeNode->AsOp());
-            getEmitter()->emitInsBinary(INS_vsqrt, emitTypeSize(treeNode), treeNode, srcNode);
-            break;
-
-        default:
-            assert(!"genIntrinsic: Unsupported intrinsic");
-            unreached();
-    }
-
-    genProduceReg(treeNode);
-}
-
-#endif // _TARGET_ARM_
-
-#ifdef _TARGET_ARM64_
-
-//---------------------------------------------------------------------
-// genIntrinsic - generate code for a given intrinsic
-//
-// Arguments
-//    treeNode - the GT_INTRINSIC node
-//
-// Return value:
-//    None
-//
-void CodeGen::genIntrinsic(GenTreePtr treeNode)
-{
-    // Both operand and its result must be of the same floating point type.
-    GenTreePtr srcNode = treeNode->gtOp.gtOp1;
-    assert(varTypeIsFloating(srcNode));
-    assert(srcNode->TypeGet() == treeNode->TypeGet());
-
-    // Right now only Abs/Round/Sqrt are treated as math intrinsics.
-    //
-    switch (treeNode->gtIntrinsic.gtIntrinsicId)
-    {
-        case CORINFO_INTRINSIC_Abs:
-            genConsumeOperands(treeNode->AsOp());
-            getEmitter()->emitInsBinary(INS_fabs, emitTypeSize(treeNode), treeNode, srcNode);
-            break;
-
-        case CORINFO_INTRINSIC_Round:
             genConsumeOperands(treeNode->AsOp());
-            getEmitter()->emitInsBinary(INS_frintn, emitTypeSize(treeNode), treeNode, srcNode);
+            getEmitter()->emitInsBinary(INS_ROUND, emitTypeSize(treeNode), treeNode, srcNode);
             break;
 
         case CORINFO_INTRINSIC_Sqrt:
             genConsumeOperands(treeNode->AsOp());
-            getEmitter()->emitInsBinary(INS_fsqrt, emitTypeSize(treeNode), treeNode, srcNode);
+            getEmitter()->emitInsBinary(INS_SQRT, emitTypeSize(treeNode), treeNode, srcNode);
             break;
 
         default:
@@ -157,106 +84,6 @@ void CodeGen::genIntrinsic(GenTreePtr treeNode)
     genProduceReg(treeNode);
 }
 
-#endif // _TARGET_ARM64_
-
-#ifdef _TARGET_ARM_
-
-//---------------------------------------------------------------------
-// genPutArgStk - generate code for a GT_PUTARG_STK node
-//
-// Arguments
-//    treeNode - the GT_PUTARG_STK node
-//
-// Return value:
-//    None
-//
-void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
-{
-    assert(treeNode->OperGet() == GT_PUTARG_STK);
-    var_types  targetType = treeNode->TypeGet();
-    GenTreePtr source     = treeNode->gtOp1;
-    emitter*   emit       = getEmitter();
-
-    // This is the varNum for our store operations,
-    // typically this is the varNum for the Outgoing arg space
-    // When we are generating a tail call it will be the varNum for arg0
-    unsigned varNumOut;
-    unsigned argOffsetMax; // Records the maximum size of this area for assert checks
-
-    // Get argument offset to use with 'varNumOut'
-    // Here we cross check that argument offset hasn't changed from lowering to codegen since
-    // we are storing arg slot number in GT_PUTARG_STK node in lowering phase.
-    unsigned argOffsetOut = treeNode->gtSlotNum * TARGET_POINTER_SIZE;
-
-#ifdef DEBUG
-    fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(treeNode->gtCall, treeNode);
-    assert(curArgTabEntry);
-    assert(argOffsetOut == (curArgTabEntry->slotNum * TARGET_POINTER_SIZE));
-#endif // DEBUG
-
-    varNumOut    = compiler->lvaOutgoingArgSpaceVar;
-    argOffsetMax = compiler->lvaOutgoingArgSpaceSize;
-
-    bool isStruct = (targetType == TYP_STRUCT) || (source->OperGet() == GT_FIELD_LIST);
-
-    if (!isStruct) // a normal non-Struct argument
-    {
-        instruction storeIns  = ins_Store(targetType);
-        emitAttr    storeAttr = emitTypeSize(targetType);
-
-        // If it is contained then source must be the integer constant zero
-        if (source->isContained())
-        {
-            assert(source->OperGet() == GT_CNS_INT);
-            assert(source->AsIntConCommon()->IconValue() == 0);
-            NYI("genPutArgStk: contained zero source");
-        }
-        else
-        {
-            genConsumeReg(source);
-            emit->emitIns_S_R(storeIns, storeAttr, source->gtRegNum, varNumOut, argOffsetOut);
-        }
-        argOffsetOut += EA_SIZE_IN_BYTES(storeAttr);
-        assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
-    }
-    else // We have some kind of a struct argument
-    {
-        assert(source->isContained()); // We expect that this node was marked as contained in LowerArm
-
-        if (source->OperGet() == GT_FIELD_LIST)
-        {
-            // Deal with the multi register passed struct args.
-            GenTreeFieldList* fieldListPtr = source->AsFieldList();
-
-            // Evaluate each of the GT_FIELD_LIST items into their register
-            // and store their register into the outgoing argument area
-            for (; fieldListPtr != nullptr; fieldListPtr = fieldListPtr->Rest())
-            {
-                GenTreePtr nextArgNode = fieldListPtr->gtOp.gtOp1;
-                genConsumeReg(nextArgNode);
-
-                regNumber reg  = nextArgNode->gtRegNum;
-                var_types type = nextArgNode->TypeGet();
-                emitAttr  attr = emitTypeSize(type);
-
-                // Emit store instructions to store the registers produced by the GT_FIELD_LIST into the outgoing
-                // argument area
-                emit->emitIns_S_R(ins_Store(type), attr, reg, varNumOut, argOffsetOut);
-                argOffsetOut += EA_SIZE_IN_BYTES(attr);
-                assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
-            }
-        }
-        else // We must have a GT_OBJ or a GT_LCL_VAR
-        {
-            NYI("genPutArgStk: GT_OBJ or GT_LCL_VAR source of struct type");
-        }
-    }
-}
-
-#endif // _TARGET_ARM_
-
-#ifdef _TARGET_ARM64_
-
 //---------------------------------------------------------------------
 // genPutArgStk - generate code for a GT_PUTARG_STK node
 //
@@ -279,10 +106,6 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
     unsigned varNumOut;
     unsigned argOffsetMax; // Records the maximum size of this area for assert checks
 
-    // This is the varNum for our load operations,
-    // only used when we have a multireg struct with a LclVar source
-    unsigned varNumInp = BAD_VAR_NUM;
-
     // Get argument offset to use with 'varNumOut'
     // Here we cross check that argument offset hasn't changed from lowering to codegen since
     // we are storing arg slot number in GT_PUTARG_STK node in lowering phase.
@@ -299,6 +122,9 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
     // All other calls - stk arg is setup in out-going arg area.
     if (treeNode->putInIncomingArgArea())
     {
+        NYI_ARM("genPutArgStk: fast tail call");
+
+#ifdef _TARGET_ARM64_
         varNumOut    = getFirstArgWithStackSlot();
         argOffsetMax = compiler->compArgSize;
 #if FEATURE_FASTTAILCALL
@@ -311,12 +137,14 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
         LclVarDsc* varDsc = &(compiler->lvaTable[varNumOut]);
         assert(varDsc != nullptr);
 #endif // FEATURE_FASTTAILCALL
+#endif // _TARGET_ARM64_
     }
     else
     {
         varNumOut    = compiler->lvaOutgoingArgSpaceVar;
         argOffsetMax = compiler->lvaOutgoingArgSpaceSize;
     }
+
     bool isStruct = (targetType == TYP_STRUCT) || (source->OperGet() == GT_FIELD_LIST);
 
     if (!isStruct) // a normal non-Struct argument
@@ -329,7 +157,11 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
         {
             assert(source->OperGet() == GT_CNS_INT);
             assert(source->AsIntConCommon()->IconValue() == 0);
+            NYI_ARM("genPutArgStk: contained zero source");
+
+#ifdef _TARGET_ARM64_
             emit->emitIns_S_R(storeIns, storeAttr, REG_ZR, varNumOut, argOffsetOut);
+#endif // _TARGET_ARM64_
         }
         else
         {
@@ -341,7 +173,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
     }
     else // We have some kind of a struct argument
     {
-        assert(source->isContained()); // We expect that this node was marked as contained in LowerArm64
+        assert(source->isContained()); // We expect that this node was marked as contained in Lower
 
         if (source->OperGet() == GT_FIELD_LIST)
         {
@@ -370,6 +202,10 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
         {
             noway_assert((source->OperGet() == GT_LCL_VAR) || (source->OperGet() == GT_OBJ));
 
+            NYI_ARM("genPutArgStk: GT_OBJ or GT_LCL_VAR source of struct type");
+
+#ifdef _TARGET_ARM64_
+
             var_types targetType = source->TypeGet();
             noway_assert(varTypeIsStruct(targetType));
 
@@ -421,6 +257,10 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
             int      structSize;
             bool     isHfa;
 
+            // This is the varNum for our load operations,
+            // only used when we have a multireg struct with a LclVar source
+            unsigned varNumInp = BAD_VAR_NUM;
+
             // Setup the structSize, isHFa, and gcPtrCount
             if (varNode != nullptr)
             {
@@ -683,81 +523,12 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
                 argOffsetOut += EA_SIZE_IN_BYTES(nextAttr);
                 assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
             }
-        }
-    }
-}
 
 #endif // _TARGET_ARM64_
-
-#ifdef _TARGET_ARM_
-
-//----------------------------------------------------------------------------------
-// genMultiRegCallStoreToLocal: store multi-reg return value of a call node to a local
-//
-// Arguments:
-//    treeNode  -  Gentree of GT_STORE_LCL_VAR
-//
-// Return Value:
-//    None
-//
-// Assumption:
-//    The child of store is a multi-reg call node.
-//    genProduceReg() on treeNode is made by caller of this routine.
-//
-void CodeGen::genMultiRegCallStoreToLocal(GenTreePtr treeNode)
-{
-    assert(treeNode->OperGet() == GT_STORE_LCL_VAR);
-
-    // Longs are returned in two return registers on Arm32.
-    assert(varTypeIsLong(treeNode));
-
-    // Assumption: current Arm32 implementation requires that a multi-reg long
-    // var in 'var = call' is flagged as lvIsMultiRegRet to prevent it from
-    // being promoted.
-    unsigned   lclNum = treeNode->AsLclVarCommon()->gtLclNum;
-    LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]);
-    noway_assert(varDsc->lvIsMultiRegRet);
-
-    GenTree*     op1       = treeNode->gtGetOp1();
-    GenTree*     actualOp1 = op1->gtSkipReloadOrCopy();
-    GenTreeCall* call      = actualOp1->AsCall();
-    assert(call->HasMultiRegRetVal());
-
-    genConsumeRegs(op1);
-
-    ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc();
-    unsigned        regCount    = retTypeDesc->GetReturnRegCount();
-    assert(regCount <= MAX_RET_REG_COUNT);
-
-    // Stack store
-    int offset = 0;
-    for (unsigned i = 0; i < regCount; ++i)
-    {
-        var_types type = retTypeDesc->GetReturnRegType(i);
-        regNumber reg  = call->GetRegNumByIdx(i);
-        if (op1->IsCopyOrReload())
-        {
-            // GT_COPY/GT_RELOAD will have valid reg for those positions
-            // that need to be copied or reloaded.
-            regNumber reloadReg = op1->AsCopyOrReload()->GetRegNumByIdx(i);
-            if (reloadReg != REG_NA)
-            {
-                reg = reloadReg;
-            }
         }
-
-        assert(reg != REG_NA);
-        getEmitter()->emitIns_S_R(ins_Store(type), emitTypeSize(type), reg, lclNum, offset);
-        offset += genTypeSize(type);
     }
-
-    varDsc->lvRegNum = REG_STK;
 }
 
-#endif // _TARGET_ARM_
-
-#ifdef _TARGET_ARM64_
-
 //----------------------------------------------------------------------------------
 // genMultiRegCallStoreToLocal: store multi-reg return value of a call node to a local
 //
@@ -775,12 +546,17 @@ void CodeGen::genMultiRegCallStoreToLocal(GenTreePtr treeNode)
 {
     assert(treeNode->OperGet() == GT_STORE_LCL_VAR);
 
+#if defined(_TARGET_ARM_)
+    // Longs are returned in two return registers on Arm32.
+    assert(varTypeIsLong(treeNode));
+#elif defined(_TARGET_ARM64_)
     // Structs of size >=9 and <=16 are returned in two return registers on ARM64 and HFAs.
     assert(varTypeIsStruct(treeNode));
+#endif // _TARGET_*
 
-    // Assumption: current ARM64 implementation requires that a multi-reg struct
+    // Assumption: current implementation requires that a multi-reg
     // var in 'var = call' is flagged as lvIsMultiRegRet to prevent it from
-    // being struct promoted.
+    // being promoted.
     unsigned   lclNum = treeNode->AsLclVarCommon()->gtLclNum;
     LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]);
     noway_assert(varDsc->lvIsMultiRegRet);
@@ -797,7 +573,7 @@ void CodeGen::genMultiRegCallStoreToLocal(GenTreePtr treeNode)
 
     if (treeNode->gtRegNum != REG_NA)
     {
-        // Right now the only enregistrable structs supported are SIMD types.
+        // Right now the only enregistrable multi-reg return types supported are SIMD types.
         assert(varTypeIsSIMD(treeNode));
         NYI("GT_STORE_LCL_VAR of a SIMD enregisterable struct");
     }
@@ -829,109 +605,50 @@ void CodeGen::genMultiRegCallStoreToLocal(GenTreePtr treeNode)
     }
 }
 
-#endif // _TARGET_ARM64_
-
-#ifdef _TARGET_ARM_
-
 //------------------------------------------------------------------------
 // genRangeCheck: generate code for GT_ARR_BOUNDS_CHECK node.
 //
 void CodeGen::genRangeCheck(GenTreePtr oper)
 {
+#ifdef FEATURE_SIMD
+    noway_assert(oper->OperGet() == GT_ARR_BOUNDS_CHECK || oper->OperGet() == GT_SIMD_CHK);
+#else  // !FEATURE_SIMD
     noway_assert(oper->OperGet() == GT_ARR_BOUNDS_CHECK);
+#endif // !FEATURE_SIMD
+
     GenTreeBoundsChk* bndsChk = oper->AsBoundsChk();
 
-    GenTreePtr arrIdx    = bndsChk->gtIndex->gtEffectiveVal();
-    GenTreePtr arrLen    = bndsChk->gtArrLen->gtEffectiveVal();
+    GenTreePtr arrLen    = bndsChk->gtArrLen;
+    GenTreePtr arrIndex  = bndsChk->gtIndex;
     GenTreePtr arrRef    = NULL;
     int        lenOffset = 0;
 
-    genConsumeIfReg(arrIdx);
-    genConsumeIfReg(arrLen);
-
-    GenTree *    src1, *src2;
+    GenTree*     src1;
+    GenTree*     src2;
     emitJumpKind jmpKind;
 
-    if (arrIdx->isContainedIntOrIImmed())
+    genConsumeRegs(arrIndex);
+    genConsumeRegs(arrLen);
+
+    if (arrIndex->isContainedIntOrIImmed())
     {
         // To encode using a cmp immediate, we place the
         //  constant operand in the second position
         src1    = arrLen;
-        src2    = arrIdx;
+        src2    = arrIndex;
         jmpKind = genJumpKindForOper(GT_LE, CK_UNSIGNED);
     }
     else
     {
-        src1    = arrIdx;
+        src1    = arrIndex;
         src2    = arrLen;
         jmpKind = genJumpKindForOper(GT_GE, CK_UNSIGNED);
     }
 
-    getEmitter()->emitInsBinary(INS_cmp, emitAttr(TYP_INT), src1, src2);
+    getEmitter()->emitInsBinary(INS_cmp, EA_4BYTE, src1, src2);
     genJumpToThrowHlpBlk(jmpKind, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
 }
 
-#endif // _TARGET_ARM_
-
-#ifdef _TARGET_ARM64_
-
-// generate code for BoundsCheck nodes
-void CodeGen::genRangeCheck(GenTreePtr oper)
-{
-#ifdef FEATURE_SIMD
-    noway_assert(oper->OperGet() == GT_ARR_BOUNDS_CHECK || oper->OperGet() == GT_SIMD_CHK);
-#else  // !FEATURE_SIMD
-    noway_assert(oper->OperGet() == GT_ARR_BOUNDS_CHECK);
-#endif // !FEATURE_SIMD
-
-    GenTreeBoundsChk* bndsChk = oper->AsBoundsChk();
-
-    GenTreePtr arrLen    = bndsChk->gtArrLen;
-    GenTreePtr arrIndex  = bndsChk->gtIndex;
-    GenTreePtr arrRef    = NULL;
-    int        lenOffset = 0;
-
-    GenTree *    src1, *src2;
-    emitJumpKind jmpKind;
-
-    genConsumeRegs(arrIndex);
-    genConsumeRegs(arrLen);
-
-    if (arrIndex->isContainedIntOrIImmed())
-    {
-        // To encode using a cmp immediate, we place the
-        //  constant operand in the second position
-        src1    = arrLen;
-        src2    = arrIndex;
-        jmpKind = genJumpKindForOper(GT_LE, CK_UNSIGNED);
-    }
-    else
-    {
-        src1    = arrIndex;
-        src2    = arrLen;
-        jmpKind = genJumpKindForOper(GT_GE, CK_UNSIGNED);
-    }
-
-    GenTreeIntConCommon* intConst = nullptr;
-    if (src2->isContainedIntOrIImmed())
-    {
-        intConst = src2->AsIntConCommon();
-    }
-
-    if (intConst != nullptr)
-    {
-        getEmitter()->emitIns_R_I(INS_cmp, EA_4BYTE, src1->gtRegNum, intConst->IconValue());
-    }
-    else
-    {
-        getEmitter()->emitIns_R_R(INS_cmp, EA_4BYTE, src1->gtRegNum, src2->gtRegNum);
-    }
-
-    genJumpToThrowHlpBlk(jmpKind, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
-}
-
-#endif // _TARGET_ARM64_
-
 //------------------------------------------------------------------------
 // genOffsetOfMDArrayLowerBound: Returns the offset from the Array object to the
 //   lower bound for the given dimension.
@@ -1022,8 +739,6 @@ void CodeGen::genCodeForArrIndex(GenTreeArrIndex* arrIndex)
     genProduceReg(arrIndex);
 }
 
-#ifdef _TARGET_ARM_
-
 //------------------------------------------------------------------------
 // genCodeForArrOffset: Generates code to compute the flattened array offset for
 //    one dimension of an array reference:
@@ -1052,89 +767,31 @@ void CodeGen::genCodeForArrOffset(GenTreeArrOffs* arrOffset)
     {
         emitter*  emit      = getEmitter();
         regNumber offsetReg = genConsumeReg(offsetNode);
+        regNumber indexReg  = genConsumeReg(indexNode);
+        regNumber arrReg    = genConsumeReg(arrOffset->gtArrObj);
         noway_assert(offsetReg != REG_NA);
-        regNumber indexReg = genConsumeReg(indexNode);
         noway_assert(indexReg != REG_NA);
-        GenTreePtr arrObj = arrOffset->gtArrObj;
-        regNumber  arrReg = genConsumeReg(arrObj);
         noway_assert(arrReg != REG_NA);
+
         regMaskTP tmpRegMask = arrOffset->gtRsvdRegs;
         regNumber tmpReg     = genRegNumFromMask(tmpRegMask);
         noway_assert(tmpReg != REG_NA);
+
         unsigned  dim      = arrOffset->gtCurrDim;
         unsigned  rank     = arrOffset->gtArrRank;
         var_types elemType = arrOffset->gtArrElemType;
         unsigned  offset   = genOffsetOfMDArrayDimensionSize(elemType, rank, dim);
 
-        // Load tmpReg with the dimension size
+// Load tmpReg with the dimension size and evaluate
+// tgtReg = offsetReg*dim_size + indexReg.
+#if defined(_TARGET_ARM_)
         emit->emitIns_R_R_I(ins_Load(TYP_INT), EA_4BYTE, tmpReg, arrReg, offset); // a 4 BYTE sign extending load
-
-        // Evaluate tgtReg = offsetReg*dim_size + indexReg.
         emit->emitIns_R_R_R(INS_MUL, EA_4BYTE, tgtReg, tmpReg, offsetReg);
         emit->emitIns_R_R_R(INS_add, EA_4BYTE, tgtReg, tgtReg, indexReg);
-    }
-    else
-    {
-        regNumber indexReg = genConsumeReg(indexNode);
-        if (indexReg != tgtReg)
-        {
-            inst_RV_RV(INS_mov, tgtReg, indexReg, TYP_INT);
-        }
-    }
-    genProduceReg(arrOffset);
-}
-
-#endif // _TARGET_ARM_
-
-#ifdef _TARGET_ARM64_
-
-//------------------------------------------------------------------------
-// genCodeForArrOffset: Generates code to compute the flattened array offset for
-//    one dimension of an array reference:
-//        result = (prevDimOffset * dimSize) + effectiveIndex
-//    where dimSize is obtained from the arrObj operand
-//
-// Arguments:
-//    arrOffset - the node for which we're generating code
-//
-// Return Value:
-//    None.
-//
-// Notes:
-//    dimSize and effectiveIndex are always non-negative, the former by design,
-//    and the latter because it has been normalized to be zero-based.
-
-void CodeGen::genCodeForArrOffset(GenTreeArrOffs* arrOffset)
-{
-    GenTreePtr offsetNode = arrOffset->gtOffset;
-    GenTreePtr indexNode  = arrOffset->gtIndex;
-    regNumber  tgtReg     = arrOffset->gtRegNum;
-
-    noway_assert(tgtReg != REG_NA);
-
-    if (!offsetNode->IsIntegralConst(0))
-    {
-        emitter*  emit      = getEmitter();
-        regNumber offsetReg = genConsumeReg(offsetNode);
-        noway_assert(offsetReg != REG_NA);
-        regNumber indexReg = genConsumeReg(indexNode);
-        noway_assert(indexReg != REG_NA);
-        GenTreePtr arrObj = arrOffset->gtArrObj;
-        regNumber  arrReg = genConsumeReg(arrObj);
-        noway_assert(arrReg != REG_NA);
-        regMaskTP tmpRegMask = arrOffset->gtRsvdRegs;
-        regNumber tmpReg     = genRegNumFromMask(tmpRegMask);
-        noway_assert(tmpReg != REG_NA);
-        unsigned  dim      = arrOffset->gtCurrDim;
-        unsigned  rank     = arrOffset->gtArrRank;
-        var_types elemType = arrOffset->gtArrElemType;
-        unsigned  offset   = genOffsetOfMDArrayDimensionSize(elemType, rank, dim);
-
-        // Load tmpReg with the dimension size
+#elif defined(_TARGET_ARM64_)
         emit->emitIns_R_R_I(ins_Load(TYP_INT), EA_8BYTE, tmpReg, arrReg, offset); // a 4 BYTE sign extending load
-
-        // Evaluate tgtReg = offsetReg*dim_size + indexReg.
         emit->emitIns_R_R_R_R(INS_madd, EA_4BYTE, tgtReg, tmpReg, offsetReg, indexReg);
+#endif // _TARGET_*
     }
     else
     {
@@ -1147,8 +804,6 @@ void CodeGen::genCodeForArrOffset(GenTreeArrOffs* arrOffset)
     genProduceReg(arrOffset);
 }
 
-#endif // _TARGET_ARM64_
-
 //------------------------------------------------------------------------
 // indirForm: Make a temporary indir we can feed to pattern matching routines
 //    in cases where we don't want to instantiate all the indirs that happen.
@@ -1177,8 +832,6 @@ GenTreeIntCon CodeGen::intForm(var_types type, ssize_t value)
     return i;
 }
 
-#ifdef _TARGET_ARM_
-
 //------------------------------------------------------------------------
 // genCodeForShift: Generates the code sequence for a GenTree node that
 // represents a bit shift or rotate operation (<<, >>, >>>, rol, ror).
@@ -1208,49 +861,7 @@ void CodeGen::genCodeForShift(GenTreePtr tree)
     }
     else
     {
-        unsigned immWidth   = size * BITS_PER_BYTE;
-        ssize_t  shiftByImm = shiftBy->gtIntCon.gtIconVal & (immWidth - 1);
-
-        getEmitter()->emitIns_R_R_I(ins, size, tree->gtRegNum, operand->gtRegNum, shiftByImm);
-    }
-
-    genProduceReg(tree);
-}
-
-#endif // _TARGET_ARM_
-
-#ifdef _TARGET_ARM64_
-
-//------------------------------------------------------------------------
-// genCodeForShift: Generates the code sequence for a GenTree node that
-// represents a bit shift or rotate operation (<<, >>, >>>, rol, ror).
-//
-// Arguments:
-//    tree - the bit shift node (that specifies the type of bit shift to perform).
-//
-// Assumptions:
-//    a) All GenTrees are register allocated.
-//
-void CodeGen::genCodeForShift(GenTreePtr tree)
-{
-    var_types   targetType = tree->TypeGet();
-    genTreeOps  oper       = tree->OperGet();
-    instruction ins        = genGetInsForOper(oper, targetType);
-    emitAttr    size       = emitTypeSize(tree);
-
-    assert(tree->gtRegNum != REG_NA);
-
-    GenTreePtr operand = tree->gtGetOp1();
-    genConsumeOperands(tree->AsOp());
-
-    GenTreePtr shiftBy = tree->gtGetOp2();
-    if (!shiftBy->IsCnsIntOrI())
-    {
-        getEmitter()->emitIns_R_R_R(ins, size, tree->gtRegNum, operand->gtRegNum, shiftBy->gtRegNum);
-    }
-    else
-    {
-        unsigned immWidth   = emitter::getBitWidth(size); // immWidth will be set to 32 or 64
+        unsigned immWidth   = emitter::getBitWidth(size); // For ARM64, immWidth will be set to 32 or 64
         ssize_t  shiftByImm = shiftBy->gtIntCon.gtIconVal & (immWidth - 1);
 
         getEmitter()->emitIns_R_R_I(ins, size, tree->gtRegNum, operand->gtRegNum, shiftByImm);
@@ -1259,10 +870,6 @@ void CodeGen::genCodeForShift(GenTreePtr tree)
     genProduceReg(tree);
 }
 
-#endif // _TARGET_ARM64_
-
-#ifdef _TARGET_ARM_
-
 // Generate code for a CpBlk node by the means of the VM memcpy helper call
 // Preconditions:
 // a) The size argument of the CpBlk is not an integer constant
@@ -1275,38 +882,17 @@ void CodeGen::genCodeForCpBlk(GenTreeBlk* cpBlkNode)
     assert(!dstAddr->isContained());
 
     genConsumeBlockOp(cpBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2);
-    genEmitHelperCall(CORINFO_HELP_MEMCPY, 0, EA_UNKNOWN);
-}
-
-#endif // _TARGET_ARM_
 
 #ifdef _TARGET_ARM64_
-
-// Generate code for a CpBlk node by the means of the VM memcpy helper call
-// Preconditions:
-// a) The size argument of the CpBlk is not an integer constant
-// b) The size argument is a constant but is larger than CPBLK_MOVS_LIMIT bytes.
-void CodeGen::genCodeForCpBlk(GenTreeBlk* cpBlkNode)
-{
-    // Make sure we got the arguments of the cpblk operation in the right registers
-    unsigned   blockSize = cpBlkNode->Size();
-    GenTreePtr dstAddr   = cpBlkNode->Addr();
-    assert(!dstAddr->isContained());
-
-    genConsumeBlockOp(cpBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2);
-
     if (blockSize != 0)
     {
         assert(blockSize > CPBLK_UNROLL_LIMIT);
     }
+#endif // _TARGET_ARM64_
 
     genEmitHelperCall(CORINFO_HELP_MEMCPY, 0, EA_UNKNOWN);
 }
 
-#endif // _TARGET_ARM64_
-
-#ifdef _TARGET_ARM_
-
 // Generates code for InitBlk by calling the VM memset helper function.
 // Preconditions:
 // a) The size argument of the InitBlk is not an integer constant.
@@ -1333,54 +919,17 @@ void CodeGen::genCodeForInitBlk(GenTreeBlk* initBlkNode)
         assert(initBlkNode->gtRsvdRegs == RBM_ARG_2);
     }
 
-    genConsumeBlockOp(initBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2);
-    genEmitHelperCall(CORINFO_HELP_MEMSET, 0, EA_UNKNOWN);
-}
-
-#endif // _TARGET_ARM_
-
 #ifdef _TARGET_ARM64_
-
-// Generates code for InitBlk by calling the VM memset helper function.
-// Preconditions:
-// a) The size argument of the InitBlk is not an integer constant.
-// b) The size argument of the InitBlk is >= INITBLK_STOS_LIMIT bytes.
-void CodeGen::genCodeForInitBlk(GenTreeBlk* initBlkNode)
-{
-    // Make sure we got the arguments of the initblk operation in the right registers
-    unsigned   size    = initBlkNode->Size();
-    GenTreePtr dstAddr = initBlkNode->Addr();
-    GenTreePtr initVal = initBlkNode->Data();
-    if (initVal->OperIsInitVal())
-    {
-        initVal = initVal->gtGetOp1();
-    }
-
-    assert(!dstAddr->isContained());
-    assert(!initVal->isContained());
-    if (initBlkNode->gtOper == GT_STORE_DYN_BLK)
-    {
-        assert(initBlkNode->AsDynBlk()->gtDynamicSize->gtRegNum == REG_ARG_2);
-    }
-    else
-    {
-        assert(initBlkNode->gtRsvdRegs == RBM_ARG_2);
-    }
-
     if (size != 0)
     {
         assert(size > INITBLK_UNROLL_LIMIT);
     }
+#endif // _TARGET_ARM64_
 
     genConsumeBlockOp(initBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2);
-
     genEmitHelperCall(CORINFO_HELP_MEMSET, 0, EA_UNKNOWN);
 }
 
-#endif // _TARGET_ARM64_
-
-#ifdef _TARGET_ARM_
-
 //------------------------------------------------------------------------
 // genRegCopy: Generate a register copy.
 //
@@ -1402,7 +951,10 @@ void CodeGen::genRegCopy(GenTree* treeNode)
 
     if (varTypeIsFloating(treeNode) != varTypeIsFloating(op1))
     {
-        NYI("genRegCopy floating point");
+        NYI_ARM("genRegCopy floating point");
+#ifdef _TARGET_ARM64_
+        inst_RV_RV(INS_fmov, targetReg, genConsumeReg(op1), targetType);
+#endif // _TARGET_ARM64_
     }
     else
     {
@@ -1447,92 +999,22 @@ void CodeGen::genRegCopy(GenTree* treeNode)
     genProduceReg(treeNode);
 }
 
-#endif // _TARGET_ARM_
-
-#ifdef _TARGET_ARM64_
-
-void CodeGen::genRegCopy(GenTree* treeNode)
+//------------------------------------------------------------------------
+// genCallInstruction: Produce code for a GT_CALL node
+//
+void CodeGen::genCallInstruction(GenTreeCall* call)
 {
-    assert(treeNode->OperGet() == GT_COPY);
-
-    var_types targetType = treeNode->TypeGet();
-    regNumber targetReg  = treeNode->gtRegNum;
-    assert(targetReg != REG_NA);
+    gtCallTypes callType = (gtCallTypes)call->gtCallType;
 
-    GenTree* op1 = treeNode->gtOp.gtOp1;
+    IL_OFFSETX ilOffset = BAD_IL_OFFSET;
 
-    // Check whether this node and the node from which we're copying the value have the same
-    // register type.
-    // This can happen if (currently iff) we have a SIMD vector type that fits in an integer
-    // register, in which case it is passed as an argument, or returned from a call,
-    // in an integer register and must be copied if it's in an xmm register.
+    // all virtuals should have been expanded into a control expression
+    assert(!call->IsVirtual() || call->gtControlExpr || call->gtCallAddr);
 
-    if (varTypeIsFloating(treeNode) != varTypeIsFloating(op1))
-    {
-        inst_RV_RV(INS_fmov, targetReg, genConsumeReg(op1), targetType);
-    }
-    else
+    // Consume all the arg regs
+    for (GenTreePtr list = call->gtCallLateArgs; list; list = list->MoveNext())
     {
-        inst_RV_RV(ins_Copy(targetType), targetReg, genConsumeReg(op1), targetType);
-    }
-
-    if (op1->IsLocal())
-    {
-        // The lclVar will never be a def.
-        // If it is a last use, the lclVar will be killed by genConsumeReg(), as usual, and genProduceReg will
-        // appropriately set the gcInfo for the copied value.
-        // If not, there are two cases we need to handle:
-        // - If this is a TEMPORARY copy (indicated by the GTF_VAR_DEATH flag) the variable
-        //   will remain live in its original register.
-        //   genProduceReg() will appropriately set the gcInfo for the copied value,
-        //   and genConsumeReg will reset it.
-        // - Otherwise, we need to update register info for the lclVar.
-
-        GenTreeLclVarCommon* lcl = op1->AsLclVarCommon();
-        assert((lcl->gtFlags & GTF_VAR_DEF) == 0);
-
-        if ((lcl->gtFlags & GTF_VAR_DEATH) == 0 && (treeNode->gtFlags & GTF_VAR_DEATH) == 0)
-        {
-            LclVarDsc* varDsc = &compiler->lvaTable[lcl->gtLclNum];
-
-            // If we didn't just spill it (in genConsumeReg, above), then update the register info
-            if (varDsc->lvRegNum != REG_STK)
-            {
-                // The old location is dying
-                genUpdateRegLife(varDsc, /*isBorn*/ false, /*isDying*/ true DEBUGARG(op1));
-
-                gcInfo.gcMarkRegSetNpt(genRegMask(op1->gtRegNum));
-
-                genUpdateVarReg(varDsc, treeNode);
-
-                // The new location is going live
-                genUpdateRegLife(varDsc, /*isBorn*/ true, /*isDying*/ false DEBUGARG(treeNode));
-            }
-        }
-    }
-    genProduceReg(treeNode);
-}
-
-#endif // _TARGET_ARM64_
-
-#ifdef _TARGET_ARM_
-
-//------------------------------------------------------------------------
-// genCallInstruction: Produce code for a GT_CALL node
-//
-void CodeGen::genCallInstruction(GenTreeCall* call)
-{
-    gtCallTypes callType = (gtCallTypes)call->gtCallType;
-
-    IL_OFFSETX ilOffset = BAD_IL_OFFSET;
-
-    // all virtuals should have been expanded into a control expression
-    assert(!call->IsVirtual() || call->gtControlExpr || call->gtCallAddr);
-
-    // Consume all the arg regs
-    for (GenTreePtr list = call->gtCallLateArgs; list; list = list->MoveNext())
-    {
-        assert(list->OperIsList());
+        assert(list->OperIsList());
 
         GenTreePtr argNode = list->Current();
 
@@ -1581,13 +1063,16 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
         if (call->IsVarargs() && varTypeIsFloating(argNode))
         {
             NYI_ARM("CodeGen - IsVarargs");
+            NYI_ARM64("CodeGen - IsVarargs");
         }
     }
 
     // Insert a null check on "this" pointer if asked.
     if (call->NeedsNullCheck())
     {
-        const regNumber regThis  = genGetThisArgReg(call);
+        const regNumber regThis = genGetThisArgReg(call);
+
+#if defined(_TARGET_ARM_)
         regMaskTP       tempMask = genFindLowestBit(call->gtRsvdRegs);
         const regNumber tmpReg   = genRegNumFromMask(tempMask);
         if (genCountBits(call->gtRsvdRegs) > 1)
@@ -1595,6 +1080,9 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
             call->gtRsvdRegs &= ~tempMask;
         }
         getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, tmpReg, regThis, 0);
+#elif defined(_TARGET_ARM64_)
+        getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, REG_ZR, regThis, 0);
+#endif // _TARGET_*
     }
 
     // Either gtControlExpr != null or gtCallAddr != null or it is a direct non-virtual call to a user or helper method.
@@ -1621,10 +1109,30 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
     }
 #endif // DEBUG
 
-    // If fast tail call, then we are done.
+    // If fast tail call, then we are done.  In this case we setup the args (both reg args
+    // and stack args in incoming arg area) and call target.  Epilog sequence would
+    // generate "br <reg>".
     if (call->IsFastTailCall())
     {
+        // Don't support fast tail calling JIT helpers
+        assert(callType != CT_HELPER);
+
+        // Fast tail calls materialize call target either in gtControlExpr or in gtCallAddr.
+        assert(target != nullptr);
+
+        genConsumeReg(target);
+
         NYI_ARM("fast tail call");
+
+#ifdef _TARGET_ARM64_
+        // Use IP0 as the call target register.
+        if (target->gtRegNum != REG_IP0)
+        {
+            inst_RV_RV(INS_mov, REG_IP0, target->gtRegNum);
+        }
+#endif // _TARGET_ARM64_
+
+        return;
     }
 
     // For a pinvoke to unmanaged code we emit a label to clear
@@ -1671,7 +1179,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
 
     if (target != nullptr)
     {
-        // For ARM a call target can not be a contained indirection
+        // A call target can not be a contained indirection
         assert(!target->isContainedIndir());
 
         genConsumeReg(target);
@@ -1683,7 +1191,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
 
         genEmitCall(emitter::EC_INDIR_R, methHnd,
                     INDEBUG_LDISASM_COMMA(sigInfo) nullptr, // addr
-                    retSize, ilOffset, target->gtRegNum);
+                    retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), ilOffset, target->gtRegNum);
     }
     else
     {
@@ -1725,8 +1233,10 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
             addr = addrInfo.addr;
         }
 
-        assert(addr);
-        // Non-virtual direct call to known addresses
+        assert(addr != nullptr);
+
+// Non-virtual direct call to known addresses
+#ifdef _TARGET_ARM_
         if (!arm_Valid_Imm_For_BL((ssize_t)addr))
         {
             regNumber tmpReg = genRegNumFromMask(call->gtRsvdRegs);
@@ -1734,303 +1244,13 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
             genEmitCall(emitter::EC_INDIR_R, methHnd, INDEBUG_LDISASM_COMMA(sigInfo) NULL, retSize, ilOffset, tmpReg);
         }
         else
-        {
-            genEmitCall(emitter::EC_FUNC_TOKEN, methHnd, INDEBUG_LDISASM_COMMA(sigInfo) addr, retSize, ilOffset);
-        }
-    }
-
-    // if it was a pinvoke we may have needed to get the address of a label
-    if (genPendingCallLabel)
-    {
-        assert(call->IsUnmanaged());
-        genDefineTempLabel(genPendingCallLabel);
-        genPendingCallLabel = nullptr;
-    }
-
-    // Update GC info:
-    // All Callee arg registers are trashed and no longer contain any GC pointers.
-    // TODO-ARM-Bug?: As a matter of fact shouldn't we be killing all of callee trashed regs here?
-    // For now we will assert that other than arg regs gc ref/byref set doesn't contain any other
-    // registers from RBM_CALLEE_TRASH
-    assert((gcInfo.gcRegGCrefSetCur & (RBM_CALLEE_TRASH & ~RBM_ARG_REGS)) == 0);
-    assert((gcInfo.gcRegByrefSetCur & (RBM_CALLEE_TRASH & ~RBM_ARG_REGS)) == 0);
-    gcInfo.gcRegGCrefSetCur &= ~RBM_ARG_REGS;
-    gcInfo.gcRegByrefSetCur &= ~RBM_ARG_REGS;
-
-    var_types returnType = call->TypeGet();
-    if (returnType != TYP_VOID)
-    {
-        regNumber returnReg;
-
-        if (call->HasMultiRegRetVal())
-        {
-            assert(pRetTypeDesc != nullptr);
-            unsigned regCount = pRetTypeDesc->GetReturnRegCount();
-
-            // If regs allocated to call node are different from ABI return
-            // regs in which the call has returned its result, move the result
-            // to regs allocated to call node.
-            for (unsigned i = 0; i < regCount; ++i)
-            {
-                var_types regType      = pRetTypeDesc->GetReturnRegType(i);
-                returnReg              = pRetTypeDesc->GetABIReturnReg(i);
-                regNumber allocatedReg = call->GetRegNumByIdx(i);
-                if (returnReg != allocatedReg)
-                {
-                    inst_RV_RV(ins_Copy(regType), allocatedReg, returnReg, regType);
-                }
-            }
-        }
-        else
-        {
-            if (call->IsHelperCall(compiler, CORINFO_HELP_INIT_PINVOKE_FRAME))
-            {
-                // The CORINFO_HELP_INIT_PINVOKE_FRAME helper uses a custom calling convention that returns with
-                // TCB in REG_PINVOKE_TCB. fgMorphCall() sets the correct argument registers.
-                returnReg = REG_PINVOKE_TCB;
-            }
-            else if (varTypeIsFloating(returnType))
-            {
-                returnReg = REG_FLOATRET;
-            }
-            else
-            {
-                returnReg = REG_INTRET;
-            }
-
-            if (call->gtRegNum != returnReg)
-            {
-                inst_RV_RV(ins_Copy(returnType), call->gtRegNum, returnReg, returnType);
-            }
-        }
-
-        genProduceReg(call);
-    }
-
-    // If there is nothing next, that means the result is thrown away, so this value is not live.
-    // However, for minopts or debuggable code, we keep it live to support managed return value debugging.
-    if ((call->gtNext == nullptr) && !compiler->opts.MinOpts() && !compiler->opts.compDbgCode)
-    {
-        gcInfo.gcMarkRegSetNpt(RBM_INTRET);
-    }
-}
-
 #endif // _TARGET_ARM_
-
-#ifdef _TARGET_ARM64_
-
-// Produce code for a GT_CALL node
-void CodeGen::genCallInstruction(GenTreeCall* call)
-{
-    gtCallTypes callType = (gtCallTypes)call->gtCallType;
-
-    IL_OFFSETX ilOffset = BAD_IL_OFFSET;
-
-    // all virtuals should have been expanded into a control expression
-    assert(!call->IsVirtual() || call->gtControlExpr || call->gtCallAddr);
-
-    // Consume all the arg regs
-    for (GenTreePtr list = call->gtCallLateArgs; list; list = list->MoveNext())
-    {
-        assert(list->OperIsList());
-
-        GenTreePtr argNode = list->Current();
-
-        fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, argNode->gtSkipReloadOrCopy());
-        assert(curArgTabEntry);
-
-        if (curArgTabEntry->regNum == REG_STK)
-            continue;
-
-        // Deal with multi register passed struct args.
-        if (argNode->OperGet() == GT_FIELD_LIST)
-        {
-            GenTreeArgList* argListPtr   = argNode->AsArgList();
-            unsigned        iterationNum = 0;
-            regNumber       argReg       = curArgTabEntry->regNum;
-            for (; argListPtr != nullptr; argListPtr = argListPtr->Rest(), iterationNum++)
-            {
-                GenTreePtr putArgRegNode = argListPtr->gtOp.gtOp1;
-                assert(putArgRegNode->gtOper == GT_PUTARG_REG);
-
-                genConsumeReg(putArgRegNode);
-
-                if (putArgRegNode->gtRegNum != argReg)
-                {
-                    inst_RV_RV(ins_Move_Extend(putArgRegNode->TypeGet(), putArgRegNode->InReg()), argReg,
-                               putArgRegNode->gtRegNum);
-                }
-
-                argReg = genRegArgNext(argReg);
-            }
-        }
-        else
-        {
-            regNumber argReg = curArgTabEntry->regNum;
-            genConsumeReg(argNode);
-            if (argNode->gtRegNum != argReg)
-            {
-                inst_RV_RV(ins_Move_Extend(argNode->TypeGet(), argNode->InReg()), argReg, argNode->gtRegNum);
-            }
-        }
-
-        // In the case of a varargs call,
-        // the ABI dictates that if we have floating point args,
-        // we must pass the enregistered arguments in both the
-        // integer and floating point registers so, let's do that.
-        if (call->IsVarargs() && varTypeIsFloating(argNode))
-        {
-            NYI_ARM64("CodeGen - IsVarargs");
-        }
-    }
-
-    // Insert a null check on "this" pointer if asked.
-    if (call->NeedsNullCheck())
-    {
-        const regNumber regThis = genGetThisArgReg(call);
-        getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, REG_ZR, regThis, 0);
-    }
-
-    // Either gtControlExpr != null or gtCallAddr != null or it is a direct non-virtual call to a user or helper method.
-    CORINFO_METHOD_HANDLE methHnd;
-    GenTree*              target = call->gtControlExpr;
-    if (callType == CT_INDIRECT)
-    {
-        assert(target == nullptr);
-        target  = call->gtCallAddr;
-        methHnd = nullptr;
-    }
-    else
-    {
-        methHnd = call->gtCallMethHnd;
-    }
-
-    CORINFO_SIG_INFO* sigInfo = nullptr;
-#ifdef DEBUG
-    // Pass the call signature information down into the emitter so the emitter can associate
-    // native call sites with the signatures they were generated from.
-    if (callType != CT_HELPER)
-    {
-        sigInfo = call->callSig;
-    }
-#endif // DEBUG
-
-    // If fast tail call, then we are done.  In this case we setup the args (both reg args
-    // and stack args in incoming arg area) and call target in IP0.  Epilog sequence would
-    // generate "br IP0".
-    if (call->IsFastTailCall())
-    {
-        // Don't support fast tail calling JIT helpers
-        assert(callType != CT_HELPER);
-
-        // Fast tail calls materialize call target either in gtControlExpr or in gtCallAddr.
-        assert(target != nullptr);
-
-        genConsumeReg(target);
-
-        if (target->gtRegNum != REG_IP0)
-        {
-            inst_RV_RV(INS_mov, REG_IP0, target->gtRegNum);
-        }
-        return;
-    }
-
-    // For a pinvoke to unmanged code we emit a label to clear
-    // the GC pointer state before the callsite.
-    // We can't utilize the typical lazy killing of GC pointers
-    // at (or inside) the callsite.
-    if (call->IsUnmanaged())
-    {
-        genDefineTempLabel(genCreateTempLabel());
-    }
-
-    // Determine return value size(s).
-    ReturnTypeDesc* pRetTypeDesc  = call->GetReturnTypeDesc();
-    emitAttr        retSize       = EA_PTRSIZE;
-    emitAttr        secondRetSize = EA_UNKNOWN;
-
-    if (call->HasMultiRegRetVal())
-    {
-        retSize       = emitTypeSize(pRetTypeDesc->GetReturnRegType(0));
-        secondRetSize = emitTypeSize(pRetTypeDesc->GetReturnRegType(1));
-    }
-    else
-    {
-        assert(!varTypeIsStruct(call));
-
-        if (call->gtType == TYP_REF || call->gtType == TYP_ARRAY)
-        {
-            retSize = EA_GCREF;
-        }
-        else if (call->gtType == TYP_BYREF)
-        {
-            retSize = EA_BYREF;
-        }
-    }
-
-    // We need to propagate the IL offset information to the call instruction, so we can emit
-    // an IL to native mapping record for the call, to support managed return value debugging.
-    // We don't want tail call helper calls that were converted from normal calls to get a record,
-    // so we skip this hash table lookup logic in that case.
-    if (compiler->opts.compDbgInfo && compiler->genCallSite2ILOffsetMap != nullptr && !call->IsTailCall())
-    {
-        (void)compiler->genCallSite2ILOffsetMap->Lookup(call, &ilOffset);
-    }
-
-    if (target != nullptr)
-    {
-        // For Arm64 a call target can not be a contained indirection
-        assert(!target->isContainedIndir());
-
-        // We have already generated code for gtControlExpr evaluating it into a register.
-        // We just need to emit "call reg" in this case.
-        //
-        assert(genIsValidIntReg(target->gtRegNum));
-
-        genEmitCall(emitter::EC_INDIR_R, methHnd,
-                    INDEBUG_LDISASM_COMMA(sigInfo) nullptr, // addr
-                    retSize, secondRetSize, ilOffset, genConsumeReg(target));
-    }
-    else
-    {
-        // Generate a direct call to a non-virtual user defined or helper method
-        assert(callType == CT_HELPER || callType == CT_USER_FUNC);
-
-        void* addr = nullptr;
-        if (callType == CT_HELPER)
         {
-            // Direct call to a helper method.
-            CorInfoHelpFunc helperNum = compiler->eeGetHelperNum(methHnd);
-            noway_assert(helperNum != CORINFO_HELP_UNDEF);
-
-            void* pAddr = nullptr;
-            addr        = compiler->compGetHelperFtn(helperNum, (void**)&pAddr);
-
-            if (addr == nullptr)
-            {
-                addr = pAddr;
-            }
+            genEmitCall(emitter::EC_FUNC_TOKEN, methHnd, INDEBUG_LDISASM_COMMA(sigInfo) addr,
+                        retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), ilOffset);
         }
-        else
-        {
-            // Direct call to a non-virtual user function.
-            CORINFO_ACCESS_FLAGS aflags = CORINFO_ACCESS_ANY;
-            if (call->IsSameThis())
-            {
-                aflags = (CORINFO_ACCESS_FLAGS)(aflags | CORINFO_ACCESS_THIS);
-            }
 
-            if ((call->NeedsNullCheck()) == 0)
-            {
-                aflags = (CORINFO_ACCESS_FLAGS)(aflags | CORINFO_ACCESS_NONNULL);
-            }
-
-            CORINFO_CONST_LOOKUP addrInfo;
-            compiler->info.compCompHnd->getFunctionEntryPoint(methHnd, &addrInfo, aflags);
-
-            addr = addrInfo.addr;
-        }
-#if 0
+#if 0 && defined(_TARGET_ARM64_)
         // Use this path if you want to load an absolute call target using 
         //  a sequence of movs followed by an indirect call (blr instruction)
 
@@ -2046,267 +1266,90 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
                     secondRetSize,
                     ilOffset,
                     REG_IP0);
-#else
-        // Non-virtual direct call to known addresses
-        genEmitCall(emitter::EC_FUNC_TOKEN, methHnd, INDEBUG_LDISASM_COMMA(sigInfo) addr, retSize, secondRetSize,
-                    ilOffset);
 #endif
     }
 
     // if it was a pinvoke we may have needed to get the address of a label
-    if (genPendingCallLabel)
-    {
-        assert(call->IsUnmanaged());
-        genDefineTempLabel(genPendingCallLabel);
-        genPendingCallLabel = nullptr;
-    }
-
-    // Update GC info:
-    // All Callee arg registers are trashed and no longer contain any GC pointers.
-    // TODO-ARM64-Bug?: As a matter of fact shouldn't we be killing all of callee trashed regs here?
-    // For now we will assert that other than arg regs gc ref/byref set doesn't contain any other
-    // registers from RBM_CALLEE_TRASH
-    assert((gcInfo.gcRegGCrefSetCur & (RBM_CALLEE_TRASH & ~RBM_ARG_REGS)) == 0);
-    assert((gcInfo.gcRegByrefSetCur & (RBM_CALLEE_TRASH & ~RBM_ARG_REGS)) == 0);
-    gcInfo.gcRegGCrefSetCur &= ~RBM_ARG_REGS;
-    gcInfo.gcRegByrefSetCur &= ~RBM_ARG_REGS;
-
-    var_types returnType = call->TypeGet();
-    if (returnType != TYP_VOID)
-    {
-        regNumber returnReg;
-
-        if (call->HasMultiRegRetVal())
-        {
-            assert(pRetTypeDesc != nullptr);
-            unsigned regCount = pRetTypeDesc->GetReturnRegCount();
-
-            // If regs allocated to call node are different from ABI return
-            // regs in which the call has returned its result, move the result
-            // to regs allocated to call node.
-            for (unsigned i = 0; i < regCount; ++i)
-            {
-                var_types regType      = pRetTypeDesc->GetReturnRegType(i);
-                returnReg              = pRetTypeDesc->GetABIReturnReg(i);
-                regNumber allocatedReg = call->GetRegNumByIdx(i);
-                if (returnReg != allocatedReg)
-                {
-                    inst_RV_RV(ins_Copy(regType), allocatedReg, returnReg, regType);
-                }
-            }
-        }
-        else
-        {
-            if (varTypeIsFloating(returnType))
-            {
-                returnReg = REG_FLOATRET;
-            }
-            else
-            {
-                returnReg = REG_INTRET;
-            }
-
-            if (call->gtRegNum != returnReg)
-            {
-                inst_RV_RV(ins_Copy(returnType), call->gtRegNum, returnReg, returnType);
-            }
-        }
-
-        genProduceReg(call);
-    }
-
-    // If there is nothing next, that means the result is thrown away, so this value is not live.
-    // However, for minopts or debuggable code, we keep it live to support managed return value debugging.
-    if ((call->gtNext == nullptr) && !compiler->opts.MinOpts() && !compiler->opts.compDbgCode)
-    {
-        gcInfo.gcMarkRegSetNpt(RBM_INTRET);
-    }
-}
-
-#endif // _TARGET_ARM64_
-
-#ifdef _TARGET_ARM_
-
-//------------------------------------------------------------------------
-// genIntToIntCast: Generate code for an integer cast
-//
-// Arguments:
-//    treeNode - The GT_CAST node
-//
-// Return Value:
-//    None.
-//
-// Assumptions:
-//    The treeNode must have an assigned register.
-//    For a signed convert from byte, the source must be in a byte-addressable register.
-//    Neither the source nor target type can be a floating point type.
-//
-void CodeGen::genIntToIntCast(GenTreePtr treeNode)
-{
-    assert(treeNode->OperGet() == GT_CAST);
-
-    GenTreePtr castOp = treeNode->gtCast.CastOp();
-    emitter*   emit   = getEmitter();
-
-    var_types dstType     = treeNode->CastToType();
-    var_types srcType     = genActualType(castOp->TypeGet());
-    emitAttr  movSize     = emitActualTypeSize(dstType);
-    bool      movRequired = false;
-
-    if (varTypeIsLong(srcType))
-    {
-        genLongToIntCast(treeNode);
-        return;
-    }
-
-    regNumber targetReg = treeNode->gtRegNum;
-    regNumber sourceReg = castOp->gtRegNum;
-
-    // For Long to Int conversion we will have a reserved integer register to hold the immediate mask
-    regNumber tmpReg = (treeNode->gtRsvdRegs == RBM_NONE) ? REG_NA : genRegNumFromMask(treeNode->gtRsvdRegs);
-
-    assert(genIsValidIntReg(targetReg));
-    assert(genIsValidIntReg(sourceReg));
-
-    instruction ins = INS_invalid;
-
-    genConsumeReg(castOp);
-    Lowering::CastInfo castInfo;
+    if (genPendingCallLabel)
+    {
+        assert(call->IsUnmanaged());
+        genDefineTempLabel(genPendingCallLabel);
+        genPendingCallLabel = nullptr;
+    }
 
-    // Get information about the cast.
-    Lowering::getCastDescription(treeNode, &castInfo);
+    // Update GC info:
+    // All Callee arg registers are trashed and no longer contain any GC pointers.
+    // TODO-Bug?: As a matter of fact shouldn't we be killing all of callee trashed regs here?
+    // For now we will assert that other than arg regs gc ref/byref set doesn't contain any other
+    // registers from RBM_CALLEE_TRASH
+    assert((gcInfo.gcRegGCrefSetCur & (RBM_CALLEE_TRASH & ~RBM_ARG_REGS)) == 0);
+    assert((gcInfo.gcRegByrefSetCur & (RBM_CALLEE_TRASH & ~RBM_ARG_REGS)) == 0);
+    gcInfo.gcRegGCrefSetCur &= ~RBM_ARG_REGS;
+    gcInfo.gcRegByrefSetCur &= ~RBM_ARG_REGS;
 
-    if (castInfo.requiresOverflowCheck)
+    var_types returnType = call->TypeGet();
+    if (returnType != TYP_VOID)
     {
-        emitAttr cmpSize = EA_ATTR(genTypeSize(srcType));
+        regNumber returnReg;
 
-        if (castInfo.signCheckOnly)
+        if (call->HasMultiRegRetVal())
         {
-            // We only need to check for a negative value in sourceReg
-            emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, 0);
-            emitJumpKind jmpLT = genJumpKindForOper(GT_LT, CK_SIGNED);
-            genJumpToThrowHlpBlk(jmpLT, SCK_OVERFLOW);
-            noway_assert(genTypeSize(srcType) == 4 || genTypeSize(srcType) == 8);
-            // This is only interesting case to ensure zero-upper bits.
-            if ((srcType == TYP_INT) && (dstType == TYP_ULONG))
+            assert(pRetTypeDesc != nullptr);
+            unsigned regCount = pRetTypeDesc->GetReturnRegCount();
+
+            // If regs allocated to call node are different from ABI return
+            // regs in which the call has returned its result, move the result
+            // to regs allocated to call node.
+            for (unsigned i = 0; i < regCount; ++i)
             {
-                // cast to TYP_ULONG:
-                // We use a mov with size=EA_4BYTE
-                // which will zero out the upper bits
-                movSize     = EA_4BYTE;
-                movRequired = true;
+                var_types regType      = pRetTypeDesc->GetReturnRegType(i);
+                returnReg              = pRetTypeDesc->GetABIReturnReg(i);
+                regNumber allocatedReg = call->GetRegNumByIdx(i);
+                if (returnReg != allocatedReg)
+                {
+                    inst_RV_RV(ins_Copy(regType), allocatedReg, returnReg, regType);
+                }
             }
         }
-        else if (castInfo.unsignedSource || castInfo.unsignedDest)
-        {
-            // When we are converting from/to unsigned,
-            // we only have to check for any bits set in 'typeMask'
-
-            noway_assert(castInfo.typeMask != 0);
-            emit->emitIns_R_I(INS_tst, cmpSize, sourceReg, castInfo.typeMask);
-            emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED);
-            genJumpToThrowHlpBlk(jmpNotEqual, SCK_OVERFLOW);
-        }
         else
         {
-            // For a narrowing signed cast
-            //
-            // We must check the value is in a signed range.
-
-            // Compare with the MAX
-
-            noway_assert((castInfo.typeMin != 0) && (castInfo.typeMax != 0));
-
-            if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, INS_FLAGS_DONT_CARE))
+#ifdef _TARGET_ARM_
+            if (call->IsHelperCall(compiler, CORINFO_HELP_INIT_PINVOKE_FRAME))
             {
-                emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, castInfo.typeMax);
+                // The CORINFO_HELP_INIT_PINVOKE_FRAME helper uses a custom calling convention that returns with
+                // TCB in REG_PINVOKE_TCB. fgMorphCall() sets the correct argument registers.
+                returnReg = REG_PINVOKE_TCB;
             }
             else
+#endif // _TARGET_ARM_
+                if (varTypeIsFloating(returnType))
             {
-                noway_assert(tmpReg != REG_NA);
-                instGen_Set_Reg_To_Imm(cmpSize, tmpReg, castInfo.typeMax);
-                emit->emitIns_R_R(INS_cmp, cmpSize, sourceReg, tmpReg);
-            }
-
-            emitJumpKind jmpGT = genJumpKindForOper(GT_GT, CK_SIGNED);
-            genJumpToThrowHlpBlk(jmpGT, SCK_OVERFLOW);
-
-            // Compare with the MIN
-
-            if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, INS_FLAGS_DONT_CARE))
-            {
-                emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, castInfo.typeMin);
+                returnReg = REG_FLOATRET;
             }
             else
             {
-                noway_assert(tmpReg != REG_NA);
-                instGen_Set_Reg_To_Imm(cmpSize, tmpReg, castInfo.typeMin);
-                emit->emitIns_R_R(INS_cmp, cmpSize, sourceReg, tmpReg);
+                returnReg = REG_INTRET;
             }
 
-            emitJumpKind jmpLT = genJumpKindForOper(GT_LT, CK_SIGNED);
-            genJumpToThrowHlpBlk(jmpLT, SCK_OVERFLOW);
-        }
-        ins = INS_mov;
-    }
-    else // Non-overflow checking cast.
-    {
-        if (genTypeSize(srcType) == genTypeSize(dstType))
-        {
-            ins = INS_mov;
-        }
-        else
-        {
-            var_types extendType = TYP_UNKNOWN;
-
-            // If we need to treat a signed type as unsigned
-            if ((treeNode->gtFlags & GTF_UNSIGNED) != 0)
-            {
-                extendType  = genUnsignedType(srcType);
-                movSize     = emitTypeSize(extendType);
-                movRequired = true;
-            }
-            else
+            if (call->gtRegNum != returnReg)
             {
-                if (genTypeSize(srcType) < genTypeSize(dstType))
-                {
-                    extendType = srcType;
-                    movSize    = emitTypeSize(srcType);
-                    if (srcType == TYP_UINT)
-                    {
-                        movRequired = true;
-                    }
-                }
-                else // (genTypeSize(srcType) > genTypeSize(dstType))
-                {
-                    extendType = dstType;
-                    movSize    = emitTypeSize(dstType);
-                }
+                inst_RV_RV(ins_Copy(returnType), call->gtRegNum, returnReg, returnType);
             }
-
-            ins = ins_Move_Extend(extendType, castOp->InReg());
         }
-    }
 
-    // We should never be generating a load from memory instruction here!
-    assert(!emit->emitInsIsLoad(ins));
+        genProduceReg(call);
+    }
 
-    if ((ins != INS_mov) || movRequired || (targetReg != sourceReg))
+    // If there is nothing next, that means the result is thrown away, so this value is not live.
+    // However, for minopts or debuggable code, we keep it live to support managed return value debugging.
+    if ((call->gtNext == nullptr) && !compiler->opts.MinOpts() && !compiler->opts.compDbgCode)
     {
-        emit->emitIns_R_R(ins, movSize, targetReg, sourceReg);
+        gcInfo.gcMarkRegSetNpt(RBM_INTRET);
     }
-
-    genProduceReg(treeNode);
 }
 
-#endif // _TARGET_ARM_
-
-#ifdef _TARGET_ARM64_
-
 //------------------------------------------------------------------------
 // genIntToIntCast: Generate code for an integer cast
-//    This method handles integer overflow checking casts
-//    as well as ordinary integer casts.
 //
 // Arguments:
 //    treeNode - The GT_CAST node
@@ -2315,7 +1358,7 @@ void CodeGen::genIntToIntCast(GenTreePtr treeNode)
 //    None.
 //
 // Assumptions:
-//    The treeNode is not a contained node and must have an assigned register.
+//    The treeNode must have an assigned register.
 //    For a signed convert from byte, the source must be in a byte-addressable register.
 //    Neither the source nor target type can be a floating point type.
 //
@@ -2333,6 +1376,14 @@ void CodeGen::genIntToIntCast(GenTreePtr treeNode)
     emitAttr  movSize     = emitActualTypeSize(dstType);
     bool      movRequired = false;
 
+#ifdef _TARGET_ARM_
+    if (varTypeIsLong(srcType))
+    {
+        genLongToIntCast(treeNode);
+        return;
+    }
+#endif // _TARGET_ARM_
+
     regNumber targetReg = treeNode->gtRegNum;
     regNumber sourceReg = castOp->gtRegNum;
 
@@ -2352,7 +1403,6 @@ void CodeGen::genIntToIntCast(GenTreePtr treeNode)
 
     if (castInfo.requiresOverflowCheck)
     {
-
         emitAttr cmpSize = EA_ATTR(genTypeSize(srcType));
 
         if (castInfo.signCheckOnly)
@@ -2392,7 +1442,11 @@ void CodeGen::genIntToIntCast(GenTreePtr treeNode)
 
             noway_assert((castInfo.typeMin != 0) && (castInfo.typeMax != 0));
 
+#if defined(_TARGET_ARM_)
+            if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, INS_FLAGS_DONT_CARE))
+#elif defined(_TARGET_ARM64_)
             if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, cmpSize))
+#endif // _TARGET_*
             {
                 emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, castInfo.typeMax);
             }
@@ -2406,9 +1460,13 @@ void CodeGen::genIntToIntCast(GenTreePtr treeNode)
             emitJumpKind jmpGT = genJumpKindForOper(GT_GT, CK_SIGNED);
             genJumpToThrowHlpBlk(jmpGT, SCK_OVERFLOW);
 
-            // Compare with the MIN
+// Compare with the MIN
 
+#if defined(_TARGET_ARM_)
+            if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, INS_FLAGS_DONT_CARE))
+#elif defined(_TARGET_ARM64_)
             if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, cmpSize))
+#endif // _TARGET_*
             {
                 emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, castInfo.typeMin);
             }
@@ -2446,24 +1504,33 @@ void CodeGen::genIntToIntCast(GenTreePtr treeNode)
                 if (genTypeSize(srcType) < genTypeSize(dstType))
                 {
                     extendType = srcType;
+#ifdef _TARGET_ARM_
+                    movSize = emitTypeSize(srcType);
+#endif // _TARGET_ARM_
                     if (srcType == TYP_UINT)
                     {
+#ifdef _TARGET_ARM64_
                         // If we are casting from a smaller type to
                         // a larger type, then we need to make sure the
                         // higher 4 bytes are zero to gaurentee the correct value.
                         // Therefore using a mov with EA_4BYTE in place of EA_8BYTE
                         // will zero the upper bits
-                        movSize     = EA_4BYTE;
+                        movSize = EA_4BYTE;
+#endif // _TARGET_ARM64_
                         movRequired = true;
                     }
                 }
                 else // (genTypeSize(srcType) > genTypeSize(dstType))
                 {
                     extendType = dstType;
+#if defined(_TARGET_ARM_)
+                    movSize = emitTypeSize(dstType);
+#elif defined(_TARGET_ARM64_)
                     if (dstType == TYP_INT)
                     {
                         movSize = EA_8BYTE; // a sxtw instruction requires EA_8BYTE
                     }
+#endif // _TARGET_*
                 }
             }
 
@@ -2482,10 +1549,6 @@ void CodeGen::genIntToIntCast(GenTreePtr treeNode)
     genProduceReg(treeNode);
 }
 
-#endif // _TARGET_ARM64_
-
-#ifdef _TARGET_ARM_
-
 //------------------------------------------------------------------------
 // genFloatToFloatCast: Generate code for a cast between float and double
 //
@@ -2522,6 +1585,8 @@ void CodeGen::genFloatToFloatCast(GenTreePtr treeNode)
     // treeNode must be a reg
     assert(!treeNode->isContained());
 
+#if defined(_TARGET_ARM_)
+
     if (srcType != dstType)
     {
         instruction insVcvt = (srcType == TYP_FLOAT) ? INS_vcvt_f2d  // convert Float to Double
@@ -2534,48 +1599,7 @@ void CodeGen::genFloatToFloatCast(GenTreePtr treeNode)
         getEmitter()->emitIns_R_R(INS_vmov, emitTypeSize(treeNode), treeNode->gtRegNum, op1->gtRegNum);
     }
 
-    genProduceReg(treeNode);
-}
-
-#endif // _TARGET_ARM_
-
-#ifdef _TARGET_ARM64_
-
-//------------------------------------------------------------------------
-// genFloatToFloatCast: Generate code for a cast between float and double
-//
-// Arguments:
-//    treeNode - The GT_CAST node
-//
-// Return Value:
-//    None.
-//
-// Assumptions:
-//    Cast is a non-overflow conversion.
-//    The treeNode must have an assigned register.
-//    The cast is between float and double or vice versa.
-//
-void CodeGen::genFloatToFloatCast(GenTreePtr treeNode)
-{
-    // float <--> double conversions are always non-overflow ones
-    assert(treeNode->OperGet() == GT_CAST);
-    assert(!treeNode->gtOverflow());
-
-    regNumber targetReg = treeNode->gtRegNum;
-    assert(genIsValidFloatReg(targetReg));
-
-    GenTreePtr op1 = treeNode->gtOp.gtOp1;
-    assert(!op1->isContained());               // Cannot be contained
-    assert(genIsValidFloatReg(op1->gtRegNum)); // Must be a valid float reg.
-
-    var_types dstType = treeNode->CastToType();
-    var_types srcType = op1->TypeGet();
-    assert(varTypeIsFloating(srcType) && varTypeIsFloating(dstType));
-
-    genConsumeOperands(treeNode->AsOp());
-
-    // treeNode must be a reg
-    assert(!treeNode->isContained());
+#elif defined(_TARGET_ARM64_)
 
     if (srcType != dstType)
     {
@@ -2590,13 +1614,11 @@ void CodeGen::genFloatToFloatCast(GenTreePtr treeNode)
         getEmitter()->emitIns_R_R(INS_mov, emitTypeSize(treeNode), treeNode->gtRegNum, op1->gtRegNum);
     }
 
+#endif // _TARGET_*
+
     genProduceReg(treeNode);
 }
 
-#endif // _TARGET_ARM64_
-
-#ifdef _TARGET_ARM_
-
 //------------------------------------------------------------------------
 // genCreateAndStoreGCInfo: Create and record GC Info for the function.
 //
@@ -2607,48 +1629,6 @@ void CodeGen::genCreateAndStoreGCInfo(unsigned codeSize,
     IAllocator*    allowZeroAlloc = new (compiler, CMK_GC) AllowZeroAllocator(compiler->getAllocatorGC());
     GcInfoEncoder* gcInfoEncoder  = new (compiler, CMK_GC)
         GcInfoEncoder(compiler->info.compCompHnd, compiler->info.compMethodInfo, allowZeroAlloc, NOMEM);
-    assert(gcInfoEncoder);
-
-    // Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32).
-    gcInfo.gcInfoBlockHdrSave(gcInfoEncoder, codeSize, prologSize);
-
-    // We keep the call count for the second call to gcMakeRegPtrTable() below.
-    unsigned callCnt = 0;
-    // First we figure out the encoder ID's for the stack slots and registers.
-    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS, &callCnt);
-    // Now we've requested all the slots we'll need; "finalize" these (make more compact data structures for them).
-    gcInfoEncoder->FinalizeSlotIds();
-    // Now we can actually use those slot ID's to declare live ranges.
-    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt);
-
-    gcInfoEncoder->Build();
-
-    // GC Encoder automatically puts the GC info in the right spot using ICorJitInfo::allocGCInfo(size_t)
-    // let's save the values anyway for debugging purposes
-    compiler->compInfoBlkAddr = gcInfoEncoder->Emit();
-    compiler->compInfoBlkSize = 0; // not exposed by the GCEncoder interface
-}
-
-#endif // _TARGET_ARM_
-
-#ifdef _TARGET_ARM64_
-
-/*****************************************************************************
- *
- *  Create and record GC Info for the function.
- */
-void CodeGen::genCreateAndStoreGCInfo(unsigned codeSize,
-                                      unsigned prologSize,
-                                      unsigned epilogSize DEBUGARG(void* codePtr))
-{
-    genCreateAndStoreGCInfoX64(codeSize, prologSize DEBUGARG(codePtr));
-}
-
-void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize DEBUGARG(void* codePtr))
-{
-    IAllocator*    allowZeroAlloc = new (compiler, CMK_GC) AllowZeroAllocator(compiler->getAllocatorGC());
-    GcInfoEncoder* gcInfoEncoder  = new (compiler, CMK_GC)
-        GcInfoEncoder(compiler->info.compCompHnd, compiler->info.compMethodInfo, allowZeroAlloc, NOMEM);
     assert(gcInfoEncoder != nullptr);
 
     // Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32).
@@ -2666,6 +1646,8 @@ void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize
     // Now we can actually use those slot ID's to declare live ranges.
     gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt);
 
+#ifdef _TARGET_ARM64_
+
     if (compiler->opts.compDbgEnC)
     {
         // what we have to preserve is called the "frame header" (see comments in VM\eetwain.cpp)
@@ -2690,6 +1672,8 @@ void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize
         gcInfoEncoder->SetSizeOfEditAndContinuePreservedArea(preservedAreaSize);
     }
 
+#endif // _TARGET_ARM64_
+
     gcInfoEncoder->Build();
 
     // GC Encoder automatically puts the GC info in the right spot using ICorJitInfo::allocGCInfo(size_t)
@@ -2698,8 +1682,6 @@ void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize
     compiler->compInfoBlkSize = 0; // not exposed by the GCEncoder interface
 }
 
-#endif // _TARGET_ARM64_
-
 #endif // _TARGET_ARMARCH_
 
 #endif // !LEGACY_BACKEND
index 832ed03..1071f4d 100644 (file)
@@ -9482,6 +9482,10 @@ const instruction INS_ADDC            = INS_adc;
 const instruction INS_SUBC            = INS_sbc;
 const instruction INS_NOT             = INS_mvn;
 
+const instruction INS_ABS   = INS_vabs;
+const instruction INS_ROUND = INS_invalid;
+const instruction INS_SQRT  = INS_vsqrt;
+
 #endif
 
 #ifdef _TARGET_ARM64_
@@ -9503,6 +9507,10 @@ const instruction INS_ADDC            = INS_adc;
 const instruction INS_SUBC            = INS_sbc;
 const instruction INS_NOT             = INS_mvn;
 
+const instruction INS_ABS   = INS_fabs;
+const instruction INS_ROUND = INS_frintn;
+const instruction INS_SQRT  = INS_fsqrt;
+
 #endif
 
 /*****************************************************************************/
index 4ec1893..ec42667 100644 (file)
@@ -232,6 +232,13 @@ inline static bool insOptsROR(insOpts opt)
     return (opt == INS_OPTS_ROR);
 }
 
+// Returns the number of bits used by the given 'size'.
+inline static unsigned getBitWidth(emitAttr size)
+{
+    assert(size <= EA_8BYTE);
+    return (unsigned)size * BITS_PER_BYTE;
+}
+
 /************************************************************************/
 /*           The public entry points to output instructions             */
 /************************************************************************/