{
assert((op1->gtFlags & GTF_MUL_64RSLT) != 0);
- GenTreeMulLong* mul = op1->AsMulLong();
+ GenTreeMultiRegOp* mul = op1->AsMultiRegOp();
// Stack store
getEmitter()->emitIns_S_R(ins_Store(TYP_INT), emitTypeSize(TYP_INT), mul->gtRegNum, lclNum, 0);
// Return Value:
// None.
//
-void CodeGen::genCodeForMulLong(GenTreeMulLong* node)
+void CodeGen::genCodeForMulLong(GenTreeMultiRegOp* node)
{
+ assert(node->OperGet() == GT_MUL_LONG);
genConsumeOperands(node);
GenTree* src1 = node->gtOp1;
GenTree* src2 = node->gtOp2;
#ifdef _TARGET_ARM_
case GT_MUL_LONG:
- genCodeForMulLong(treeNode->AsMulLong());
+ genCodeForMulLong(treeNode->AsMultiRegOp());
break;
#endif // _TARGET_ARM_
}
else
{
- // TODO-Arm-Bug: We cannot assume the second destination be the next of targetReg
- // since LSRA doesn't know that register is used. So we cannot write code like:
- //
- // inst_RV_RV_RV(INS_vmov_d2i, targetReg, REG_NEXT(targetReg), genConsumeReg(op1), EA_8BYTE);
- NYI_ARM("genRegCopy from 'double' to 'int'+'int'");
+ regNumber otherReg = (regNumber)treeNode->AsCopyOrReload()->gtOtherRegs[0];
+ assert(otherReg != REG_NA);
+ inst_RV_RV_RV(INS_vmov_d2i, targetReg, otherReg, genConsumeReg(op1), EA_8BYTE);
}
}
#endif // !_TARGET_ARM64_
#endif // _TARGET_ARMARCH_
#if defined(_TARGET_ARM_)
-void genCodeForMulLong(GenTreeMulLong* treeNode);
+void genCodeForMulLong(GenTreeMultiRegOp* treeNode);
#endif // _TARGET_ARM_
#if !defined(_TARGET_64BIT_)
GenTree* gtNewBlkOpNode(GenTreePtr dst, GenTreePtr srcOrFillVal, unsigned size, bool isVolatile, bool isCopyBlock);
+ GenTree* gtNewPutArgReg(var_types type, GenTreePtr arg);
+
protected:
void gtBlockOpInit(GenTreePtr result, GenTreePtr dst, GenTreePtr srcOrFillVal, bool isVolatile);
return result;
}
+//------------------------------------------------------------------------
+// gtNewPutArgReg: Creates a new PutArgReg node.
+//
+// Arguments:
+// type - The actual type of the argument
+// arg - The argument node
+//
+// Return Value:
+// Returns the newly created PutArgReg node.
+//
+// Notes:
+// The node is generated as GenTreeMultiRegOp on armel, as GenTreeOp on all the other archs
+//
+GenTreePtr Compiler::gtNewPutArgReg(var_types type, GenTreePtr arg)
+{
+ assert(arg != nullptr);
+
+ GenTreePtr node = nullptr;
+#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
+ // A PUTARG_REG could be a MultiRegOp on armel since we could move a double register to two int registers.
+ if (opts.compUseSoftFP)
+ {
+ node = new (this, GT_PUTARG_REG) GenTreeMultiRegOp(GT_PUTARG_REG, type, arg, nullptr);
+ }
+ else
+#endif
+ {
+ node = gtNewOperNode(GT_PUTARG_REG, type, arg);
+ }
+
+ return node;
+}
+
/*****************************************************************************
*
* Clones the given tree value and returns a copy of the given tree.
#ifdef FEATURE_SIMD
case GT_SIMD:
#endif // !FEATURE_SIMD
+#if !defined(LEGACY_BACKEND) && _TARGET_ARM_
+ case GT_PUTARG_REG:
+#endif // !defined(LEGACY_BACKEND) && _TARGET_ARM_
return true;
default:
return false;
};
#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
-struct GenTreeMulLong : public GenTreeOp
+struct GenTreeMultiRegOp : public GenTreeOp
{
regNumber gtOtherReg;
- GenTreeMulLong(var_types type, GenTreePtr op1, GenTreePtr op2) : GenTreeOp(GT_MUL_LONG, type, op1, op2)
+ GenTreeMultiRegOp(genTreeOps oper, var_types type, GenTreePtr op1, GenTreePtr op2)
+ : GenTreeOp(oper, type, op1, op2), gtOtherReg(REG_NA)
{
}
#if DEBUGGABLE_GENTREE
- GenTreeMulLong() : GenTreeOp()
+ GenTreeMultiRegOp() : GenTreeOp()
{
}
#endif
}
#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
- if (gtOper == GT_MUL_LONG || OperIsPutArgSplit())
+ if (gtOper == GT_MUL_LONG || gtOper == GT_PUTARG_REG || gtOper == GT_COPY || OperIsPutArgSplit())
{
return true;
}
#if defined(_TARGET_X86_)
GTNODE(MUL_LONG , GenTreeOp ,1,GTK_BINOP)
#elif defined (_TARGET_ARM_)
-GTNODE(MUL_LONG , GenTreeMulLong ,1,GTK_BINOP)
+GTNODE(MUL_LONG , GenTreeMultiRegOp ,1,GTK_BINOP)
#endif
// The following are nodes that specify shifts that take a GT_LONG op1. The GT_LONG
GTNODE(EMITNOP , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // emitter-placed nop
GTNODE(PINVOKE_PROLOG , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // pinvoke prolog seq
GTNODE(PINVOKE_EPILOG , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // pinvoke epilog seq
+#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
+GTNODE(PUTARG_REG , GenTreeMultiRegOp ,0,GTK_UNOP) // operator that places outgoing arg in register
+#else
GTNODE(PUTARG_REG , GenTreeOp ,0,GTK_UNOP) // operator that places outgoing arg in register
+#endif
GTNODE(PUTARG_STK , GenTreePutArgStk ,0,GTK_UNOP|GTK_NOVALUE) // operator that places outgoing arg in stack
#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
GTNODE(PUTARG_SPLIT , GenTreePutArgSplit ,0,GTK_UNOP) // operator that places outgoing arg in registers with stack (split struct in ARM32)
GTSTRUCT_1(AllocObj , GT_ALLOCOBJ)
GTSTRUCT_2(CC , GT_JCC, GT_SETCC)
#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
-GTSTRUCT_1(MulLong , GT_MUL_LONG)
+GTSTRUCT_2(MultiRegOp , GT_MUL_LONG, GT_PUTARG_REG)
#endif
/*****************************************************************************/
#undef GTSTRUCT_0
#endif // FEATURE_MULTIREG_ARGS
#endif // not defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
{
- putArg = comp->gtNewOperNode(GT_PUTARG_REG, type, arg);
+ putArg = comp->gtNewPutArgReg(type, arg);
}
}
else
{
#ifdef _TARGET_ARMARCH_
- // For vararg call, reg args should be all integer.
+ // For vararg call or on armel, reg args should be all integer.
// Insert a copy to move float value to integer register.
if ((call->IsVarargs() || comp->opts.compUseSoftFP) && varTypeIsFloating(type))
{
- var_types intType = (type == TYP_DOUBLE) ? TYP_LONG : TYP_INT;
- GenTreePtr intArg = comp->gtNewOperNode(GT_COPY, intType, arg);
+ var_types intType = (type == TYP_DOUBLE) ? TYP_LONG : TYP_INT;
+
+ GenTreePtr intArg = new (comp, GT_COPY) GenTreeCopyOrReload(GT_COPY, intType, arg);
info->node = intArg;
ReplaceArgWithPutArgOrCopy(ppArg, intArg);
tree->gtRegNum = reg;
}
#if defined(_TARGET_ARM_)
- else if (tree->OperGet() == GT_MUL_LONG)
+ else if (tree->OperGet() == GT_MUL_LONG || tree->OperGet() == GT_PUTARG_REG)
{
assert(regIdx == 1);
- GenTreeMulLong* mul = tree->AsMulLong();
- mul->gtOtherReg = reg;
+ GenTreeMultiRegOp* mul = tree->AsMultiRegOp();
+ mul->gtOtherReg = reg;
+ }
+ else if (tree->OperGet() == GT_COPY)
+ {
+ assert(regIdx == 1);
+ GenTreeCopyOrReload* copy = tree->AsCopyOrReload();
+ copy->gtOtherRegs[0] = reg;
}
else if (tree->OperGet() == GT_PUTARG_SPLIT)
{
// push defs
LocationInfoList locationInfoList;
LsraLocation defLocation = currentLoc + 1;
+#ifdef ARM_SOFTFP
+ regMaskTP remainingUseCandidates = useCandidates;
+#endif
for (int i = 0; i < produce; i++)
{
regMaskTP currCandidates = candidates;
currCandidates = genFindLowestReg(candidates);
candidates &= ~currCandidates;
}
-#endif
+#ifdef ARM_SOFTFP
+ // If oper is GT_PUTARG_REG, set bits in useCandidates must be in sequential order.
+ else if (tree->OperGet() == GT_PUTARG_REG || tree->OperGet() == GT_COPY)
+ {
+ useCandidates = genFindLowestReg(remainingUseCandidates);
+ remainingUseCandidates &= ~useCandidates;
+ }
+#endif // ARM_SOFTFP
+#endif // _TARGET_ARM_
if (interval == nullptr)
{
info->internalIntCount = 1;
break;
+ case GT_COPY:
+ info->srcCount = 1;
+ info->dstCount = 1;
+#ifdef ARM_SOFTFP
+ // This case currently only occurs for double types that are passed as TYP_LONG;
+ // actual long types would have been decomposed by now.
+ if (tree->TypeGet() == TYP_LONG)
+ {
+ info->dstCount = 2;
+ }
+#endif
+ break;
+
+ case GT_PUTARG_REG:
+#ifdef ARM_SOFTFP
+ // This case currently only occurs for double types that are passed as TYP_LONG;
+ // actual long types would have been decomposed by now.
+ if (tree->TypeGet() == TYP_LONG)
+ {
+ info->srcCount = 2;
+ }
+ else
+#endif
+ {
+ info->srcCount = 1;
+ }
+ info->dstCount = info->srcCount;
+ break;
+
default:
#ifdef DEBUG
char message[256];
case GT_CLS_VAR_ADDR:
case GT_IL_OFFSET:
case GT_CNS_INT:
- case GT_PUTARG_REG:
case GT_PUTARG_STK:
case GT_LABEL:
case GT_PINVOKE_PROLOG:
case GT_SETCC:
case GT_MEMORYBARRIER:
case GT_OBJ:
- case GT_COPY:
case GT_PUTARG_SPLIT:
info->dstCount = tree->IsValue() ? 1 : 0;
if (kind & (GTK_CONST | GTK_LEAF))
info.srcCount++;
// Set the register requirements for the node.
- const regMaskTP argMask = genRegMask(argReg);
+ regMaskTP argMask = genRegMask(argReg);
+#ifdef ARM_SOFTFP
+ // If type of node is `long` then it is actually `double`.
+ // The actual `long` types must have been transformed as a field list with two fields.
+ if (node->TypeGet() == TYP_LONG)
+ {
+ info.srcCount++;
+ assert(genRegArgNext(argReg) == REG_NEXT(argReg));
+ argMask |= genRegMask(REG_NEXT(argReg));
+ }
+#endif // ARM_SOFTFP
node->gtLsraInfo.setDstCandidates(m_lsra, argMask);
node->gtLsraInfo.setSrcCandidates(m_lsra, argMask);