summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMikhail Skvortcov <m.skvortcov@partner.samsung.com>2017-03-03 15:20:04 +0300
committerMikhail Skvortcov <m.skvortcov@partner.samsung.com>2017-03-03 15:23:09 +0300
commit0d02a5a1e7c44db3d06c159a641a597b13b23178 (patch)
tree0e6abd46127420a90816dfd365f629660699bd7e /src
parent92c7f802c9d2ec5ad2d15a6bdef448883e872816 (diff)
downloadcoreclr-0d02a5a1e7c44db3d06c159a641a597b13b23178.tar.gz
coreclr-0d02a5a1e7c44db3d06c159a641a597b13b23178.tar.bz2
coreclr-0d02a5a1e7c44db3d06c159a641a597b13b23178.zip
RyuJIT/ARM32: enable codegen for long nodes.
Diffstat (limited to 'src')
-rw-r--r--src/jit/codegenarm.cpp581
-rw-r--r--src/jit/codegenlinear.h4
-rw-r--r--src/jit/gentree.cpp12
-rw-r--r--src/jit/gentree.h2
-rw-r--r--src/jit/lclvars.cpp4
-rw-r--r--src/jit/lower.cpp52
-rw-r--r--src/jit/lsra.cpp2
-rw-r--r--src/jit/lsraarm.cpp148
-rw-r--r--src/jit/morph.cpp4
9 files changed, 692 insertions, 117 deletions
diff --git a/src/jit/codegenarm.cpp b/src/jit/codegenarm.cpp
index c0243d46f3..7b12bdd9e7 100644
--- a/src/jit/codegenarm.cpp
+++ b/src/jit/codegenarm.cpp
@@ -133,6 +133,98 @@ void CodeGen::genIntrinsic(GenTreePtr treeNode)
genProduceReg(treeNode);
}
+//---------------------------------------------------------------------
+// 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->gtOp.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->AsPutArgStk()->gtSlotNum * TARGET_POINTER_SIZE;
+
+#ifdef DEBUG
+ fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(treeNode->AsPutArgStk()->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");
+ }
+ }
+}
+
//------------------------------------------------------------------------
// instGen_Set_Reg_To_Imm: Move an immediate value into an integer register.
//
@@ -271,6 +363,77 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre
}
//------------------------------------------------------------------------
+// genReturn: Generates code for return statement.
+// In case of struct return, delegates to the genStructReturn method.
+//
+// Arguments:
+// treeNode - The GT_RETURN or GT_RETFILT tree node.
+//
+// Return Value:
+// None
+//
+void CodeGen::genReturn(GenTreePtr treeNode)
+{
+ assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT);
+ GenTreePtr op1 = treeNode->gtGetOp1();
+ var_types targetType = treeNode->TypeGet();
+
+#ifdef DEBUG
+ if (targetType == TYP_VOID)
+ {
+ assert(op1 == nullptr);
+ }
+#endif
+
+ if (treeNode->TypeGet() == TYP_LONG)
+ {
+ assert(op1 != nullptr);
+ noway_assert(op1->OperGet() == GT_LONG);
+ GenTree* loRetVal = op1->gtGetOp1();
+ GenTree* hiRetVal = op1->gtGetOp2();
+ noway_assert((loRetVal->gtRegNum != REG_NA) && (hiRetVal->gtRegNum != REG_NA));
+
+ genConsumeReg(loRetVal);
+ genConsumeReg(hiRetVal);
+ if (loRetVal->gtRegNum != REG_LNGRET_LO)
+ {
+ inst_RV_RV(ins_Copy(targetType), REG_LNGRET_LO, loRetVal->gtRegNum, TYP_INT);
+ }
+ if (hiRetVal->gtRegNum != REG_LNGRET_HI)
+ {
+ inst_RV_RV(ins_Copy(targetType), REG_LNGRET_HI, hiRetVal->gtRegNum, TYP_INT);
+ }
+ }
+ else
+ {
+ if (varTypeIsStruct(treeNode))
+ {
+ NYI_ARM("struct return");
+ }
+ else if (targetType != TYP_VOID)
+ {
+ assert(op1 != nullptr);
+ noway_assert(op1->gtRegNum != REG_NA);
+
+ // !! NOTE !! genConsumeReg will clear op1 as GC ref after it has
+ // consumed a reg for the operand. This is because the variable
+ // is dead after return. But we are issuing more instructions
+ // like "profiler leave callback" after this consumption. So
+ // if you are issuing more instructions after this point,
+ // remember to keep the variable live up until the new method
+ // exit point where it is actually dead.
+ genConsumeReg(op1);
+
+ regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET;
+ if (op1->gtRegNum != retReg)
+ {
+ inst_RV_RV(ins_Move_Extend(targetType, true), retReg, op1->gtRegNum, targetType);
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------------------
// genCodeForTreeNode Generate code for a single node in the tree.
//
// Preconditions:
@@ -518,10 +681,17 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
// case is handled separately.
if (data->gtSkipReloadOrCopy()->IsMultiRegCall())
{
- NYI_ARM("st.lclVar multi-reg value");
+ genMultiRegCallStoreToLocal(treeNode);
+ break;
}
else
{
+ if (treeNode->TypeGet() == TYP_LONG)
+ {
+ genStoreLongLclVar(treeNode);
+ break;
+ }
+
genConsumeRegs(data);
regNumber dataReg = REG_NA;
@@ -576,26 +746,8 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
__fallthrough;
case GT_RETURN:
- {
- GenTreePtr op1 = treeNode->gtOp.gtOp1;
- if (targetType == TYP_VOID)
- {
- assert(op1 == nullptr);
- break;
- }
- assert(op1 != nullptr);
- op1 = op1->gtEffectiveVal();
-
- NYI_IF(op1->gtRegNum == REG_NA, "GT_RETURN: return of a value not in register");
- genConsumeReg(op1);
-
- regNumber retReg = varTypeIsFloating(op1) ? REG_FLOATRET : REG_INTRET;
- if (op1->gtRegNum != retReg)
- {
- inst_RV_RV(ins_Move_Extend(targetType, true), retReg, op1->gtRegNum, targetType);
- }
- }
- break;
+ genReturn(treeNode);
+ break;
case GT_LEA:
{
@@ -693,6 +845,22 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
// vmrs with register 0xf has special meaning of transferring flags
emit->emitIns_R(INS_vmrs, EA_4BYTE, REG_R15);
}
+ else if (varTypeIsLong(op1))
+ {
+#ifdef DEBUG
+ // The result of an unlowered long compare on a 32-bit target must either be
+ // a) materialized into a register, or
+ // b) unused.
+ //
+ // A long compare that has a result that is used but not materialized into a register should
+ // have been handled by Lowering::LowerCompare.
+
+ LIR::Use use;
+ assert((treeNode->gtRegNum != REG_NA) || !LIR::AsRange(compiler->compCurBB).TryGetUse(treeNode, &use));
+#endif
+ genCompareLong(treeNode);
+ break;
+ }
else
{
var_types op1Type = op1->TypeGet();
@@ -743,6 +911,19 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
}
break;
+ case GT_JCC:
+ {
+ GenTreeJumpCC* jcc = treeNode->AsJumpCC();
+
+ assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
+
+ CompareKind compareKind = ((jcc->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
+ emitJumpKind jumpKind = genJumpKindForOper(jcc->gtCondition, compareKind);
+
+ inst_JMP(jumpKind, compiler->compCurBB->bbJumpDest);
+ }
+ break;
+
case GT_RETURNTRAP:
{
// this is nothing but a conditional call to CORINFO_HELP_STOP_FOR_GC
@@ -860,33 +1041,8 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
break;
case GT_PUTARG_STK:
- {
- NYI_IF(targetType == TYP_STRUCT, "GT_PUTARG_STK: struct support not implemented");
-
- // Get argument offset on stack.
- // 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.
- int argOffset = treeNode->AsPutArgStk()->gtSlotNum * TARGET_POINTER_SIZE;
-#ifdef DEBUG
- fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(treeNode->AsPutArgStk()->gtCall, treeNode);
- assert(curArgTabEntry);
- assert(argOffset == (int)curArgTabEntry->slotNum * TARGET_POINTER_SIZE);
-#endif
-
- GenTreePtr data = treeNode->gtOp.gtOp1->gtEffectiveVal();
- if (data->isContained())
- {
- emit->emitIns_S_I(ins_Store(targetType), emitTypeSize(targetType), compiler->lvaOutgoingArgSpaceVar,
- argOffset, (int)data->AsIntConCommon()->IconValue());
- }
- else
- {
- genConsumeReg(data);
- emit->emitIns_S_R(ins_Store(targetType), emitTypeSize(targetType), data->gtRegNum,
- compiler->lvaOutgoingArgSpaceVar, argOffset);
- }
- }
- break;
+ genPutArgStk(treeNode->AsPutArgStk());
+ break;
case GT_PUTARG_REG:
{
@@ -1032,6 +1188,69 @@ void CodeGen::genLockedInstructions(GenTreeOp* treeNode)
NYI("genLockedInstructions");
}
+//----------------------------------------------------------------------------------
+// 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;
+}
+
//------------------------------------------------------------------------
// genTableBasedSwitch: generate code for a switch statement based on a table of ip-relative offsets
//
@@ -1041,8 +1260,6 @@ void CodeGen::genTableBasedSwitch(GenTree* treeNode)
regNumber idxReg = treeNode->gtOp.gtOp1->gtRegNum;
regNumber baseReg = treeNode->gtOp.gtOp2->gtRegNum;
- regNumber tmpReg = genRegNumFromMask(treeNode->gtRsvdRegs);
-
getEmitter()->emitIns_R_ARX(INS_ldr, EA_4BYTE, REG_PC, baseReg, idxReg, TARGET_POINTER_SIZE, 0);
}
@@ -1369,12 +1586,14 @@ void CodeGen::genCallInstruction(GenTreePtr node)
}
// Determine return value size(s).
- ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc();
- emitAttr retSize = EA_PTRSIZE;
+ ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc();
+ emitAttr retSize = EA_PTRSIZE;
+ emitAttr secondRetSize = EA_UNKNOWN;
if (call->HasMultiRegRetVal())
{
- NYI_ARM("has multi reg ret val");
+ retSize = emitTypeSize(pRetTypeDesc->GetReturnRegType(0));
+ secondRetSize = emitTypeSize(pRetTypeDesc->GetReturnRegType(1));
}
else
{
@@ -1565,6 +1784,158 @@ void CodeGen::genLeaInstruction(GenTreeAddrMode* lea)
}
//------------------------------------------------------------------------
+// genCompareLong: Generate code for comparing two longs when the result of the compare
+// is manifested in a register.
+//
+// Arguments:
+// treeNode - the compare tree
+//
+// Return Value:
+// None.
+//
+// Comments:
+// For long compares, we need to compare the high parts of operands first, then the low parts.
+// If the high compare is false, we do not need to compare the low parts. For less than and
+// greater than, if the high compare is true, we can assume the entire compare is true.
+//
+void CodeGen::genCompareLong(GenTreePtr treeNode)
+{
+ assert(treeNode->OperIsCompare());
+
+ GenTreeOp* tree = treeNode->AsOp();
+ GenTreePtr op1 = tree->gtOp1;
+ GenTreePtr op2 = tree->gtOp2;
+
+ assert(varTypeIsLong(op1->TypeGet()));
+ assert(varTypeIsLong(op2->TypeGet()));
+
+ regNumber targetReg = treeNode->gtRegNum;
+
+ genConsumeOperands(tree);
+
+ GenTreePtr loOp1 = op1->gtGetOp1();
+ GenTreePtr hiOp1 = op1->gtGetOp2();
+ GenTreePtr loOp2 = op2->gtGetOp1();
+ GenTreePtr hiOp2 = op2->gtGetOp2();
+
+ // Create compare for the high parts
+ instruction ins = INS_cmp;
+ var_types cmpType = TYP_INT;
+ emitAttr cmpAttr = emitTypeSize(cmpType);
+
+ // Emit the compare instruction
+ getEmitter()->emitInsBinary(ins, cmpAttr, hiOp1, hiOp2);
+
+ // If the result is not being materialized in a register, we're done.
+ if (targetReg == REG_NA)
+ {
+ return;
+ }
+
+ BasicBlock* labelTrue = genCreateTempLabel();
+ BasicBlock* labelFalse = genCreateTempLabel();
+ BasicBlock* labelNext = genCreateTempLabel();
+
+ genJccLongHi(tree->gtOper, labelTrue, labelFalse, tree->IsUnsigned());
+ getEmitter()->emitInsBinary(ins, cmpAttr, loOp1, loOp2);
+ genJccLongLo(tree->gtOper, labelTrue, labelFalse);
+
+ genDefineTempLabel(labelFalse);
+ getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), tree->gtRegNum, 0);
+ getEmitter()->emitIns_J(INS_b, labelNext);
+
+ genDefineTempLabel(labelTrue);
+ getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), tree->gtRegNum, 1);
+
+ genDefineTempLabel(labelNext);
+
+ genProduceReg(tree);
+}
+
+void CodeGen::genJccLongHi(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse, bool isUnsigned)
+{
+ if (cmp != GT_NE)
+ {
+ jumpFalse->bbFlags |= BBF_JMP_TARGET | BBF_HAS_LABEL;
+ }
+
+ switch (cmp)
+ {
+ case GT_EQ:
+ inst_JMP(EJ_ne, jumpFalse);
+ break;
+
+ case GT_NE:
+ inst_JMP(EJ_ne, jumpTrue);
+ break;
+
+ case GT_LT:
+ case GT_LE:
+ if (isUnsigned)
+ {
+ inst_JMP(EJ_hi, jumpFalse);
+ inst_JMP(EJ_lo, jumpTrue);
+ }
+ else
+ {
+ inst_JMP(EJ_gt, jumpFalse);
+ inst_JMP(EJ_lt, jumpTrue);
+ }
+ break;
+
+ case GT_GE:
+ case GT_GT:
+ if (isUnsigned)
+ {
+ inst_JMP(EJ_lo, jumpFalse);
+ inst_JMP(EJ_hi, jumpTrue);
+ }
+ else
+ {
+ inst_JMP(EJ_lt, jumpFalse);
+ inst_JMP(EJ_gt, jumpTrue);
+ }
+ break;
+
+ default:
+ noway_assert(!"expected a comparison operator");
+ }
+}
+
+void CodeGen::genJccLongLo(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse)
+{
+ switch (cmp)
+ {
+ case GT_EQ:
+ inst_JMP(EJ_eq, jumpTrue);
+ break;
+
+ case GT_NE:
+ inst_JMP(EJ_ne, jumpTrue);
+ break;
+
+ case GT_LT:
+ inst_JMP(EJ_lo, jumpTrue);
+ break;
+
+ case GT_LE:
+ inst_JMP(EJ_ls, jumpTrue);
+ break;
+
+ case GT_GE:
+ inst_JMP(EJ_hs, jumpTrue);
+ break;
+
+ case GT_GT:
+ inst_JMP(EJ_hi, jumpTrue);
+ break;
+
+ default:
+ noway_assert(!"expected comparison");
+ }
+}
+
+//------------------------------------------------------------------------
// genSetRegToCond: Generate code to materialize a condition into a register.
//
// Arguments:
@@ -1605,6 +1976,52 @@ void CodeGen::genSetRegToCond(regNumber dstReg, GenTreePtr tree)
}
//------------------------------------------------------------------------
+// genLongToIntCast: Generate code for long to int casts.
+//
+// Arguments:
+// cast - The GT_CAST node
+//
+// Return Value:
+// None.
+//
+// Assumptions:
+// The cast node and its sources (via GT_LONG) must have been assigned registers.
+// The destination cannot be a floating point type or a small integer type.
+//
+void CodeGen::genLongToIntCast(GenTree* cast)
+{
+ assert(cast->OperGet() == GT_CAST);
+
+ GenTree* src = cast->gtGetOp1();
+ noway_assert(src->OperGet() == GT_LONG);
+
+ genConsumeRegs(src);
+
+ var_types srcType = ((cast->gtFlags & GTF_UNSIGNED) != 0) ? TYP_ULONG : TYP_LONG;
+ var_types dstType = cast->CastToType();
+ regNumber loSrcReg = src->gtGetOp1()->gtRegNum;
+ regNumber hiSrcReg = src->gtGetOp2()->gtRegNum;
+ regNumber dstReg = cast->gtRegNum;
+
+ assert((dstType == TYP_INT) || (dstType == TYP_UINT));
+ assert(genIsValidIntReg(loSrcReg));
+ assert(genIsValidIntReg(hiSrcReg));
+ assert(genIsValidIntReg(dstReg));
+
+ if (cast->gtOverflow())
+ {
+ NYI("genLongToIntCast: overflow check");
+ }
+
+ if (dstReg != loSrcReg)
+ {
+ inst_RV_RV(INS_mov, dstReg, loSrcReg, TYP_INT, EA_4BYTE);
+ }
+
+ genProduceReg(cast);
+}
+
+//------------------------------------------------------------------------
// genIntToIntCast: Generate code for an integer cast
//
// Arguments:
@@ -1630,6 +2047,12 @@ void CodeGen::genIntToIntCast(GenTreePtr treeNode)
emitAttr movSize = emitActualTypeSize(dstType);
bool movRequired = false;
+ if (varTypeIsLong(srcType))
+ {
+ genLongToIntCast(treeNode);
+ return;
+ }
+
regNumber targetReg = treeNode->gtRegNum;
regNumber sourceReg = castOp->gtRegNum;
@@ -2010,6 +2433,58 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
regTracker.rsTrashRegsForGCInterruptability();
}
+//------------------------------------------------------------------------
+// genStoreLongLclVar: Generate code to store a non-enregistered long lclVar
+//
+// Arguments:
+// treeNode - A TYP_LONG lclVar node.
+//
+// Return Value:
+// None.
+//
+// Assumptions:
+// 'treeNode' must be a TYP_LONG lclVar node for a lclVar that has NOT been promoted.
+// Its operand must be a GT_LONG node.
+//
+void CodeGen::genStoreLongLclVar(GenTree* treeNode)
+{
+ emitter* emit = getEmitter();
+
+ GenTreeLclVarCommon* lclNode = treeNode->AsLclVarCommon();
+ unsigned lclNum = lclNode->gtLclNum;
+ LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]);
+ assert(varDsc->TypeGet() == TYP_LONG);
+ assert(!varDsc->lvPromoted);
+ GenTreePtr op1 = treeNode->gtOp.gtOp1;
+ noway_assert(op1->OperGet() == GT_LONG || op1->OperGet() == GT_MUL_LONG);
+ genConsumeRegs(op1);
+
+ if (op1->OperGet() == GT_LONG)
+ {
+ // Definitions of register candidates will have been lowered to 2 int lclVars.
+ assert(!treeNode->InReg());
+
+ GenTreePtr loVal = op1->gtGetOp1();
+ GenTreePtr hiVal = op1->gtGetOp2();
+
+ // NYI: Contained immediates.
+ NYI_IF((loVal->gtRegNum == REG_NA) || (hiVal->gtRegNum == REG_NA),
+ "Store of long lclVar with contained immediate");
+
+ emit->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, loVal->gtRegNum, lclNum, 0);
+ emit->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, hiVal->gtRegNum, lclNum, genTypeSize(TYP_INT));
+ }
+ else if (op1->OperGet() == GT_MUL_LONG)
+ {
+ assert((op1->gtFlags & GTF_MUL_64RSLT) != 0);
+
+ // Stack store
+ getEmitter()->emitIns_S_R(ins_Store(TYP_INT), emitTypeSize(TYP_INT), REG_LNGRET_LO, lclNum, 0);
+ getEmitter()->emitIns_S_R(ins_Store(TYP_INT), emitTypeSize(TYP_INT), REG_LNGRET_HI, lclNum,
+ genTypeSize(TYP_INT));
+ }
+}
+
#endif // _TARGET_ARM_
#endif // !LEGACY_BACKEND
diff --git a/src/jit/codegenlinear.h b/src/jit/codegenlinear.h
index c8a5af657a..af545fe782 100644
--- a/src/jit/codegenlinear.h
+++ b/src/jit/codegenlinear.h
@@ -57,6 +57,10 @@ void genCompareInt(GenTreePtr treeNode);
#if !defined(_TARGET_64BIT_)
void genCompareLong(GenTreePtr treeNode);
+#if defined(_TARGET_ARM_)
+void genJccLongHi(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse, bool isUnsigned = false);
+void genJccLongLo(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse);
+#endif // defined(_TARGET_ARM_)
#endif
#ifdef FEATURE_SIMD
diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp
index 1dc83cede2..947314f4c7 100644
--- a/src/jit/gentree.cpp
+++ b/src/jit/gentree.cpp
@@ -6927,7 +6927,7 @@ GenTreeCall* Compiler::gtNewCallNode(
// Initialize spill flags of gtOtherRegs
node->ClearOtherRegFlags();
-#if defined(_TARGET_X86_) && !defined(LEGACY_BACKEND)
+#if (defined(_TARGET_X86_) || defined(_TARGET_ARM_)) && !defined(LEGACY_BACKEND)
// Initialize the multi-reg long return info if necessary
if (varTypeIsLong(node))
{
@@ -6941,7 +6941,7 @@ GenTreeCall* Compiler::gtNewCallNode(
// must be a long returned in two registers
assert(retTypeDesc->GetReturnRegCount() == 2);
}
-#endif // defined(_TARGET_X86_) && !defined(_LEGACY_BACKEND_)
+#endif // (defined(_TARGET_X86_) || defined(_TARGET_ARM_)) && !defined(_LEGACY_BACKEND_)
return node;
}
@@ -17023,7 +17023,7 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HA
//
void ReturnTypeDesc::InitializeLongReturnType(Compiler* comp)
{
-#if defined(_TARGET_X86_)
+#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
// Setups up a ReturnTypeDesc for returning a long using two registers
//
@@ -17031,11 +17031,11 @@ void ReturnTypeDesc::InitializeLongReturnType(Compiler* comp)
m_regType[0] = TYP_INT;
m_regType[1] = TYP_INT;
-#else // not _TARGET_X86_
+#else // not (_TARGET_X86_ or _TARGET_ARM_)
m_regType[0] = TYP_LONG;
-#endif // _TARGET_X86_
+#endif // _TARGET_X86_ or _TARGET_ARM_
#ifdef DEBUG
m_inited = true;
@@ -17111,7 +17111,7 @@ regNumber ReturnTypeDesc::GetABIReturnReg(unsigned idx)
}
}
-#elif defined(_TARGET_X86_)
+#elif defined(_TARGET_X86_) || defined(_TARGET_ARM_)
if (idx == 0)
{
diff --git a/src/jit/gentree.h b/src/jit/gentree.h
index cd1034de21..a85ca20eba 100644
--- a/src/jit/gentree.h
+++ b/src/jit/gentree.h
@@ -3398,7 +3398,7 @@ struct GenTreeCall final : public GenTree
//
bool HasMultiRegRetVal() const
{
-#if defined(_TARGET_X86_) && !defined(LEGACY_BACKEND)
+#if (defined(_TARGET_X86_) || defined(_TARGET_ARM_)) && !defined(LEGACY_BACKEND)
// LEGACY_BACKEND does not use multi reg returns for calls with long return types
return varTypeIsLong(gtType);
#elif FEATURE_MULTIREG_RET
diff --git a/src/jit/lclvars.cpp b/src/jit/lclvars.cpp
index 6e4465b2f9..3a5cfdf88b 100644
--- a/src/jit/lclvars.cpp
+++ b/src/jit/lclvars.cpp
@@ -5998,11 +5998,15 @@ void Compiler::lvaAssignFrameOffsetsToPromotedStructs()
//
if (varDsc->lvIsStructField
#ifndef UNIX_AMD64_ABI
+#if !defined(_TARGET_ARM_) || defined(LEGACY_BACKEND)
+ // Non-legacy ARM: lo/hi parts of a promoted long arg need to be updated.
+
// For System V platforms there is no outgoing args space.
// A register passed struct arg is homed on the stack in a separate local var.
// The offset of these structs is already calculated in lvaAssignVirtualFrameOffsetToArg methos.
// Make sure the code below is not executed for these structs and the offset is not changed.
&& !varDsc->lvIsParam
+#endif // !defined(_TARGET_ARM_) || defined(LEGACY_BACKEND)
#endif // UNIX_AMD64_ABI
)
{
diff --git a/src/jit/lower.cpp b/src/jit/lower.cpp
index 8869895561..2a65a949fd 100644
--- a/src/jit/lower.cpp
+++ b/src/jit/lower.cpp
@@ -1124,25 +1124,41 @@ void Lowering::LowerArg(GenTreeCall* call, GenTreePtr* ppArg)
{
if (isReg)
{
- NYI("Lowering of long register argument");
- }
+ noway_assert(arg->OperGet() == GT_LONG);
+ assert(info->numRegs == 2);
+
+ GenTreePtr argLo = arg->gtGetOp1();
+ GenTreePtr argHi = arg->gtGetOp2();
+
+ GenTreeFieldList* fieldList = new (comp, GT_FIELD_LIST) GenTreeFieldList(argLo, 0, TYP_INT, nullptr);
+ (void)new (comp, GT_FIELD_LIST) GenTreeFieldList(argHi, 4, TYP_INT, fieldList);
+
+ putArg = NewPutArg(call, fieldList, info, TYP_VOID);
- // For longs, we will replace the GT_LONG with a GT_FIELD_LIST, and put that under a PUTARG_STK.
- // Although the hi argument needs to be pushed first, that will be handled by the general case,
- // in which the fields will be reversed.
- noway_assert(arg->OperGet() == GT_LONG);
- assert(info->numSlots == 2);
- GenTreePtr argLo = arg->gtGetOp1();
- GenTreePtr argHi = arg->gtGetOp2();
- GenTreeFieldList* fieldList = new (comp, GT_FIELD_LIST) GenTreeFieldList(argLo, 0, TYP_INT, nullptr);
- // Only the first fieldList node (GTF_FIELD_LIST_HEAD) is in the instruction sequence.
- (void)new (comp, GT_FIELD_LIST) GenTreeFieldList(argHi, 4, TYP_INT, fieldList);
- putArg = NewPutArg(call, fieldList, info, TYP_VOID);
-
- // We can't call ReplaceArgWithPutArgOrCopy here because it presumes that we are keeping the original arg.
- BlockRange().InsertBefore(arg, fieldList, putArg);
- BlockRange().Remove(arg);
- *ppArg = putArg;
+ BlockRange().InsertBefore(arg, putArg);
+ BlockRange().Remove(arg);
+ *ppArg = fieldList;
+ info->node = fieldList;
+ }
+ else
+ {
+ // For longs, we will replace the GT_LONG with a GT_FIELD_LIST, and put that under a PUTARG_STK.
+ // Although the hi argument needs to be pushed first, that will be handled by the general case,
+ // in which the fields will be reversed.
+ noway_assert(arg->OperGet() == GT_LONG);
+ assert(info->numSlots == 2);
+ GenTreePtr argLo = arg->gtGetOp1();
+ GenTreePtr argHi = arg->gtGetOp2();
+ GenTreeFieldList* fieldList = new (comp, GT_FIELD_LIST) GenTreeFieldList(argLo, 0, TYP_INT, nullptr);
+ // Only the first fieldList node (GTF_FIELD_LIST_HEAD) is in the instruction sequence.
+ (void)new (comp, GT_FIELD_LIST) GenTreeFieldList(argHi, 4, TYP_INT, fieldList);
+ putArg = NewPutArg(call, fieldList, info, TYP_VOID);
+
+ // We can't call ReplaceArgWithPutArgOrCopy here because it presumes that we are keeping the original arg.
+ BlockRange().InsertBefore(arg, fieldList, putArg);
+ BlockRange().Remove(arg);
+ *ppArg = putArg;
+ }
}
else
#endif // !defined(_TARGET_64BIT_)
diff --git a/src/jit/lsra.cpp b/src/jit/lsra.cpp
index ab622b6e0e..45af8326d4 100644
--- a/src/jit/lsra.cpp
+++ b/src/jit/lsra.cpp
@@ -4005,8 +4005,6 @@ void LinearScan::buildRefPositionsForNode(GenTree* tree,
#if defined(_TARGET_AMD64_)
// Multi-reg call node is the only node that could produce multi-reg value
assert(produce <= 1 || (tree->IsMultiRegCall() && produce == MAX_RET_REG_COUNT));
-#elif defined(_TARGET_ARM_)
- assert(!varTypeIsMultiReg(tree->TypeGet()));
#endif // _TARGET_xxx_
// Add kill positions before adding def positions
diff --git a/src/jit/lsraarm.cpp b/src/jit/lsraarm.cpp
index 7817f4f5de..22164762d9 100644
--- a/src/jit/lsraarm.cpp
+++ b/src/jit/lsraarm.cpp
@@ -81,6 +81,24 @@ void Lowering::TreeNodeInfoInitCmp(GenTreePtr tree)
info->srcCount = 2;
info->dstCount = 1;
+
+ GenTreePtr op1 = tree->gtOp.gtOp1;
+ GenTreePtr op2 = tree->gtOp.gtOp2;
+ var_types op1Type = op1->TypeGet();
+ var_types op2Type = op2->TypeGet();
+
+ // Long compares will consume GT_LONG nodes, each of which produces two results.
+ // Thus for each long operand there will be an additional source.
+ // TODO-ARM-CQ: Mark hiOp2 and loOp2 as contained if it is a constant.
+ if (varTypeIsLong(op1Type))
+ {
+ info->srcCount++;
+ }
+ if (varTypeIsLong(op2Type))
+ {
+ info->srcCount++;
+ }
+
CheckImmedAndMakeContained(tree, tree->gtOp.gtOp2);
}
@@ -290,42 +308,76 @@ void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
LinearScan* l = m_lsra;
Compiler* compiler = comp;
- GenTree* op1 = tree->gtGetOp1();
- regMaskTP useCandidates = RBM_NONE;
-
- info->srcCount = (tree->TypeGet() == TYP_VOID) ? 0 : 1;
- info->dstCount = 0;
-
- if (varTypeIsStruct(tree))
+ if (tree->TypeGet() == TYP_LONG)
{
- NYI_ARM("struct return");
+ GenTree* op1 = tree->gtGetOp1();
+ noway_assert(op1->OperGet() == GT_LONG);
+ GenTree* loVal = op1->gtGetOp1();
+ GenTree* hiVal = op1->gtGetOp2();
+ info->srcCount = 2;
+ loVal->gtLsraInfo.setSrcCandidates(l, RBM_LNGRET_LO);
+ hiVal->gtLsraInfo.setSrcCandidates(l, RBM_LNGRET_HI);
+ info->dstCount = 0;
}
else
{
- // Non-struct type return - determine useCandidates
- switch (tree->TypeGet())
+ GenTree* op1 = tree->gtGetOp1();
+ regMaskTP useCandidates = RBM_NONE;
+
+ info->srcCount = (tree->TypeGet() == TYP_VOID) ? 0 : 1;
+ info->dstCount = 0;
+
+ if (varTypeIsStruct(tree))
{
- case TYP_VOID:
- useCandidates = RBM_NONE;
- break;
- case TYP_FLOAT:
- useCandidates = RBM_FLOATRET;
- break;
- case TYP_DOUBLE:
- useCandidates = RBM_DOUBLERET;
- break;
- case TYP_LONG:
- useCandidates = RBM_LNGRET;
- break;
- default:
- useCandidates = RBM_INTRET;
- break;
+ // op1 has to be either an lclvar or a multi-reg returning call
+ if (op1->OperGet() == GT_LCL_VAR)
+ {
+ GenTreeLclVarCommon* lclVarCommon = op1->AsLclVarCommon();
+ LclVarDsc* varDsc = &(compiler->lvaTable[lclVarCommon->gtLclNum]);
+ assert(varDsc->lvIsMultiRegRet);
+
+ // Mark var as contained if not enregistrable.
+ if (!varTypeIsEnregisterableStruct(op1))
+ {
+ MakeSrcContained(tree, op1);
+ }
+ }
+ else
+ {
+ noway_assert(op1->IsMultiRegCall());
+
+ ReturnTypeDesc* retTypeDesc = op1->AsCall()->GetReturnTypeDesc();
+ info->srcCount = retTypeDesc->GetReturnRegCount();
+ useCandidates = retTypeDesc->GetABIReturnRegs();
+ }
+ }
+ else
+ {
+ // Non-struct type return - determine useCandidates
+ switch (tree->TypeGet())
+ {
+ case TYP_VOID:
+ useCandidates = RBM_NONE;
+ break;
+ case TYP_FLOAT:
+ useCandidates = RBM_FLOATRET;
+ break;
+ case TYP_DOUBLE:
+ useCandidates = RBM_DOUBLERET;
+ break;
+ case TYP_LONG:
+ useCandidates = RBM_LNGRET;
+ break;
+ default:
+ useCandidates = RBM_INTRET;
+ break;
+ }
}
- }
- if (useCandidates != RBM_NONE)
- {
- tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, useCandidates);
+ if (useCandidates != RBM_NONE)
+ {
+ tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, useCandidates);
+ }
}
}
@@ -656,7 +708,6 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
DISPNODE(tree);
NYI_IF(tree->TypeGet() == TYP_STRUCT, "lowering struct");
- NYI_IF(tree->TypeGet() == TYP_LONG, "lowering long");
NYI_IF(tree->TypeGet() == TYP_DOUBLE, "lowering double");
switch (tree->OperGet())
@@ -666,7 +717,14 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_STORE_LCL_FLD:
case GT_STORE_LCL_VAR:
- info->srcCount = 1;
+ if (tree->gtGetOp1()->OperGet() == GT_LONG)
+ {
+ info->srcCount = 2;
+ }
+ else
+ {
+ info->srcCount = 1;
+ }
info->dstCount = 0;
LowerStoreLoc(tree->AsLclVarCommon());
TreeNodeInfoInitStoreLoc(tree->AsLclVarCommon());
@@ -737,6 +795,12 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
}
#endif // DEBUG
+ if (varTypeIsLong(castOpType))
+ {
+ noway_assert(castOp->OperGet() == GT_LONG);
+ info->srcCount = 2;
+ }
+
if (tree->gtOverflow())
{
NYI_ARM("overflow checks");
@@ -769,9 +833,8 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
break;
case GT_SWITCH_TABLE:
- info->srcCount = 2;
- info->internalIntCount = 1;
- info->dstCount = 0;
+ info->srcCount = 2;
+ info->dstCount = 0;
break;
case GT_ASG:
@@ -844,6 +907,21 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
info->dstCount = 0;
break;
+ case GT_LONG:
+ if ((tree->gtLIRFlags & LIR::Flags::IsUnusedValue) != 0)
+ {
+ // An unused GT_LONG node needs to consume its sources.
+ info->srcCount = 2;
+ }
+ else
+ {
+ // Passthrough
+ info->srcCount = 0;
+ }
+
+ info->dstCount = 0;
+ break;
+
case GT_CNS_DBL:
info->srcCount = 0;
info->dstCount = 1;
@@ -1032,7 +1110,6 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
unsigned varNum = tree->gtLclVarCommon.gtLclNum;
LclVarDsc* varDsc = comp->lvaTable + varNum;
NYI_IF(varTypeIsStruct(varDsc), "lowering struct var");
- NYI_IF(varTypeIsLong(varDsc), "lowering long var");
}
case GT_PHYSREG:
case GT_CLS_VAR_ADDR:
@@ -1042,6 +1119,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_PUTARG_STK:
case GT_LABEL:
case GT_PINVOKE_PROLOG:
+ case GT_JCC:
info->dstCount = tree->IsValue() ? 1 : 0;
if (kind & (GTK_CONST | GTK_LEAF))
{
diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp
index 29d93253b3..3eb565a192 100644
--- a/src/jit/morph.cpp
+++ b/src/jit/morph.cpp
@@ -92,7 +92,7 @@ GenTreePtr Compiler::fgMorphIntoHelperCall(GenTreePtr tree, int helper, GenTreeA
tree->gtCall.gtEntryPoint.addr = nullptr;
#endif
-#if defined(_TARGET_X86_) && !defined(LEGACY_BACKEND)
+#if (defined(_TARGET_X86_) || defined(_TARGET_ARM_)) && !defined(LEGACY_BACKEND)
if (varTypeIsLong(tree))
{
GenTreeCall* callNode = tree->AsCall();
@@ -101,7 +101,7 @@ GenTreePtr Compiler::fgMorphIntoHelperCall(GenTreePtr tree, int helper, GenTreeA
retTypeDesc->InitializeLongReturnType(this);
callNode->ClearOtherRegs();
}
-#endif
+#endif // _TARGET_XXX_
/* Perform the morphing */