summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarol Eidt <carol.eidt@microsoft.com>2018-08-29 10:54:39 -0700
committerGitHub <noreply@github.com>2018-08-29 10:54:39 -0700
commit305b5529b0612c283ccb2b1c21f43e4e801f9871 (patch)
treefb1acffda7b86d508a750b35c9960c4b781acf8a /src
parent1eedc35a644fece43d7674c4d2d56b5f2c605936 (diff)
parent9e55f6cf2c20945077974311be03951ce9eb346b (diff)
downloadcoreclr-305b5529b0612c283ccb2b1c21f43e4e801f9871.tar.gz
coreclr-305b5529b0612c283ccb2b1c21f43e4e801f9871.tar.bz2
coreclr-305b5529b0612c283ccb2b1c21f43e4e801f9871.zip
Merge pull request #19695 from CarolEidt/Fix19397
Handle multiReg COPY
Diffstat (limited to 'src')
-rw-r--r--src/jit/codegenarm.cpp52
-rw-r--r--src/jit/codegenlinear.cpp54
-rw-r--r--src/jit/codegenxarch.cpp87
-rw-r--r--src/jit/gentree.cpp18
-rw-r--r--src/jit/gentree.h28
-rw-r--r--src/jit/gtlist.h4
-rw-r--r--src/jit/gtstructs.h4
-rw-r--r--src/jit/lsra.cpp55
-rw-r--r--src/jit/lsrabuild.cpp9
-rw-r--r--src/jit/lsraxarch.cpp17
10 files changed, 154 insertions, 174 deletions
diff --git a/src/jit/codegenarm.cpp b/src/jit/codegenarm.cpp
index 7097fd73cb..63ef942934 100644
--- a/src/jit/codegenarm.cpp
+++ b/src/jit/codegenarm.cpp
@@ -1634,58 +1634,6 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
}
//------------------------------------------------------------------------
-// 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);
- GenTree* 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->gtHasReg());
-
- GenTree* loVal = op1->gtGetOp1();
- GenTree* hiVal = op1->gtGetOp2();
-
- noway_assert((loVal->gtRegNum != REG_NA) && (hiVal->gtRegNum != REG_NA));
-
- 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);
-
- GenTreeMultiRegOp* mul = op1->AsMultiRegOp();
-
- // Stack store
- getEmitter()->emitIns_S_R(ins_Store(TYP_INT), emitTypeSize(TYP_INT), mul->gtRegNum, lclNum, 0);
- getEmitter()->emitIns_S_R(ins_Store(TYP_INT), emitTypeSize(TYP_INT), mul->gtOtherReg, lclNum,
- genTypeSize(TYP_INT));
- }
-}
-
-//------------------------------------------------------------------------
// genCodeForMulLong: Generates code for int*int->long multiplication
//
// Arguments:
diff --git a/src/jit/codegenlinear.cpp b/src/jit/codegenlinear.cpp
index 134a909cfe..9962a627f2 100644
--- a/src/jit/codegenlinear.cpp
+++ b/src/jit/codegenlinear.cpp
@@ -1947,3 +1947,57 @@ void CodeGen::genCodeForCast(GenTreeOp* tree)
}
// The per-case functions call genProduceReg()
}
+
+#if !defined(_TARGET_64BIT_)
+//------------------------------------------------------------------------
+// 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);
+ GenTree* op1 = treeNode->gtOp.gtOp1;
+
+ // A GT_LONG is always contained, so it cannot have RELOAD or COPY inserted between it and its consumer,
+ // but a MUL_LONG may.
+ noway_assert(op1->OperIs(GT_LONG) || op1->gtSkipReloadOrCopy()->OperIs(GT_MUL_LONG));
+ genConsumeRegs(op1);
+
+ if (op1->OperGet() == GT_LONG)
+ {
+ GenTree* loVal = op1->gtGetOp1();
+ GenTree* hiVal = op1->gtGetOp2();
+
+ noway_assert((loVal->gtRegNum != REG_NA) && (hiVal->gtRegNum != REG_NA));
+
+ 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
+ {
+ assert((op1->gtSkipReloadOrCopy()->gtFlags & GTF_MUL_64RSLT) != 0);
+ // This is either a multi-reg MUL_LONG, or a multi-reg reload or copy.
+ assert(op1->IsMultiRegNode() && (op1->GetMultiRegCount() == 2));
+
+ // Stack store
+ emit->emitIns_S_R(ins_Store(TYP_INT), emitTypeSize(TYP_INT), op1->GetRegByIndex(0), lclNum, 0);
+ emit->emitIns_S_R(ins_Store(TYP_INT), emitTypeSize(TYP_INT), op1->GetRegByIndex(1), lclNum,
+ genTypeSize(TYP_INT));
+ }
+}
+#endif // !defined(_TARGET_64BIT_)
diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp
index 30e4d1256a..caccf4700b 100644
--- a/src/jit/codegenxarch.cpp
+++ b/src/jit/codegenxarch.cpp
@@ -4530,29 +4530,41 @@ void CodeGen::genCodeForIndir(GenTreeIndir* tree)
genProduceReg(tree);
}
+//------------------------------------------------------------------------
+// genRegCopy: Produce code for a GT_COPY node.
+//
+// Arguments:
+// tree - the GT_COPY node
+//
+// Notes:
+// This will copy the register(s) produced by this nodes source, to
+// the register(s) allocated to this GT_COPY node.
+// It has some special handling for these casess:
+// - when the source and target registers are in different register files
+// (note that this is *not* a conversion).
+// - when the source is a lclVar whose home location is being moved to a new
+// register (rather than just being copied for temporary use).
+//
void CodeGen::genRegCopy(GenTree* treeNode)
{
assert(treeNode->OperGet() == GT_COPY);
GenTree* op1 = treeNode->gtOp.gtOp1;
- if (op1->IsMultiRegCall())
+ if (op1->IsMultiRegNode())
{
genConsumeReg(op1);
- GenTreeCopyOrReload* copyTree = treeNode->AsCopyOrReload();
- GenTreeCall* call = op1->AsCall();
- ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc();
- unsigned regCount = retTypeDesc->GetReturnRegCount();
+ GenTreeCopyOrReload* copyTree = treeNode->AsCopyOrReload();
+ unsigned regCount = treeNode->GetMultiRegCount();
for (unsigned i = 0; i < regCount; ++i)
{
- var_types type = retTypeDesc->GetReturnRegType(i);
- regNumber fromReg = call->GetRegNumByIdx(i);
+ var_types type = op1->GetRegTypeByIndex(i);
+ regNumber fromReg = op1->GetRegByIndex(i);
regNumber toReg = copyTree->GetRegNumByIdx(i);
- // A Multi-reg GT_COPY node will have valid reg only for those
- // positions that corresponding result reg of call node needs
- // to be copied.
+ // A Multi-reg GT_COPY node will have a valid reg only for those positions for which a corresponding
+ // result reg of the multi-reg node needs to be copied.
if (toReg != REG_NA)
{
assert(toReg != fromReg);
@@ -8620,61 +8632,6 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
regSet.verifyRegistersUsed(killMask);
}
-#if !defined(_TARGET_64BIT_)
-//-----------------------------------------------------------------------------
-//
-// Code Generation for Long integers
-//
-//-----------------------------------------------------------------------------
-
-//------------------------------------------------------------------------
-// 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);
- GenTree* op1 = treeNode->gtOp.gtOp1;
- noway_assert(op1->OperGet() == GT_LONG || op1->OperGet() == GT_MUL_LONG);
- genConsumeRegs(op1);
-
- if (op1->OperGet() == GT_LONG)
- {
- GenTree* loVal = op1->gtGetOp1();
- GenTree* hiVal = op1->gtGetOp2();
-
- noway_assert((loVal->gtRegNum != REG_NA) && (hiVal->gtRegNum != REG_NA));
-
- 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 // !defined(_TARGET_64BIT_)
-
/*****************************************************************************
* Unit testing of the XArch emitter: generate a bunch of instructions into the prolog
* (it's as good a place as any), then use COMPlus_JitLateDisasm=* to see if the late
diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp
index c0cdc0aa12..466d4f747d 100644
--- a/src/jit/gentree.cpp
+++ b/src/jit/gentree.cpp
@@ -747,18 +747,22 @@ int GenTree::GetRegisterDstCount() const
{
return (const_cast<GenTree*>(this))->AsPutArgSplit()->gtNumRegs;
}
- // A PUTARG_REG could be a MultiRegOp on ARM since we could move a double register to two int registers,
- // either for all double parameters w/SoftFP or for varargs).
- else
+#endif
+#if !defined(_TARGET_64BIT_)
+ else if (OperIsMultiRegOp())
{
+ // A MultiRegOp is a GT_MUL_LONG, GT_PUTARG_REG, or GT_BITCAST.
+ // For the latter two (ARM-only), they only have multiple registers if they produce a long value
+ // (GT_MUL_LONG always produces a long value).
+ CLANG_FORMAT_COMMENT_ANCHOR;
#ifdef _TARGET_ARM_
- assert(OperIsMultiRegOp());
return (TypeGet() == TYP_LONG) ? 2 : 1;
#else
- unreached();
-#endif // _TARGET_ARM_
+ assert(OperIs(GT_MUL_LONG));
+ return 2;
+#endif
}
-#endif // FEATURE_ARG_SPLIT
+#endif
assert(!"Unexpected multi-reg node");
return 0;
}
diff --git a/src/jit/gentree.h b/src/jit/gentree.h
index 724bae66cb..5841e9ed2c 100644
--- a/src/jit/gentree.h
+++ b/src/jit/gentree.h
@@ -1213,13 +1213,18 @@ public:
bool OperIsMultiRegOp() const
{
+#if !defined(_TARGET_64BIT_)
+ if (OperIs(GT_MUL_LONG))
+ {
+ return true;
+ }
#if defined(_TARGET_ARM_)
- if ((gtOper == GT_MUL_LONG) || (gtOper == GT_PUTARG_REG) || (gtOper == GT_BITCAST))
+ if (OperIs(GT_PUTARG_REG, GT_BITCAST))
{
return true;
}
-#endif
-
+#endif // _TARGET_ARM_
+#endif // _TARGET_64BIT_
return false;
}
@@ -3839,7 +3844,7 @@ struct GenTreeCmpXchg : public GenTree
#endif
};
-#if defined(_TARGET_ARM_)
+#if !defined(_TARGET_64BIT_)
struct GenTreeMultiRegOp : public GenTreeOp
{
regNumber gtOtherReg;
@@ -3994,7 +3999,7 @@ struct GenTreeMultiRegOp : public GenTreeOp
}
#endif
};
-#endif // defined(_TARGET_ARM_)
+#endif // !defined(_TARGET_64BIT_)
struct GenTreeFptrVal : public GenTree
{
@@ -5425,6 +5430,11 @@ struct GenTreePutArgSplit : public GenTreePutArgStk
#endif // FEATURE_ARG_SPLIT
// Represents GT_COPY or GT_RELOAD node
+//
+// As it turns out, these are only needed on targets that happen to have multi-reg returns.
+// However, they are actually needed on any target that has any multi-reg ops. It is just
+// coincidence that those are the same (and there isn't a FEATURE_MULTIREG_OPS).
+//
struct GenTreeCopyOrReload : public GenTreeUnOp
{
#if FEATURE_MULTIREG_RET
@@ -6031,7 +6041,7 @@ inline bool GenTree::IsMultiRegNode() const
return true;
}
-#if defined(_TARGET_ARM_)
+#if !defined(_TARGET_64BIT_)
if (OperIsMultiRegOp() || OperIsPutArgSplit() || (gtOper == GT_COPY))
{
return true;
@@ -6061,7 +6071,7 @@ inline unsigned GenTree::GetMultiRegCount()
return AsPutArgSplit()->gtNumRegs;
}
#endif
-#if defined(_TARGET_ARM_)
+#if !defined(_TARGET_64BIT_)
if (OperIsMultiRegOp())
{
return AsMultiRegOp()->GetRegCount();
@@ -6102,7 +6112,7 @@ inline regNumber GenTree::GetRegByIndex(int regIndex)
return AsPutArgSplit()->GetRegNumByIdx(regIndex);
}
#endif
-#if defined(_TARGET_ARM_)
+#if !defined(_TARGET_64BIT_)
if (OperIsMultiRegOp())
{
return AsMultiRegOp()->GetRegNumByIdx(regIndex);
@@ -6143,7 +6153,7 @@ inline var_types GenTree::GetRegTypeByIndex(int regIndex)
return AsPutArgSplit()->GetRegType(regIndex);
}
#endif
-#if defined(_TARGET_ARM_)
+#if !defined(_TARGET_64BIT_)
if (OperIsMultiRegOp())
{
return AsMultiRegOp()->GetRegType(regIndex);
diff --git a/src/jit/gtlist.h b/src/jit/gtlist.h
index dd972b6d0d..9fd12753fb 100644
--- a/src/jit/gtlist.h
+++ b/src/jit/gtlist.h
@@ -181,9 +181,7 @@ GTNODE(SUB_HI , GenTreeOp ,0,GTK_BINOP)
// with long results are morphed into helper calls. It is similar to GT_MULHI,
// the difference being that GT_MULHI drops the lo part of the result, whereas
// GT_MUL_LONG keeps both parts of the result.
-#if defined(_TARGET_X86_)
-GTNODE(MUL_LONG , GenTreeOp ,1,GTK_BINOP)
-#elif defined (_TARGET_ARM_)
+#if !defined(_TARGET_64BIT_)
GTNODE(MUL_LONG , GenTreeMultiRegOp ,1,GTK_BINOP)
#endif
diff --git a/src/jit/gtstructs.h b/src/jit/gtstructs.h
index bc64a34169..2eac435dd5 100644
--- a/src/jit/gtstructs.h
+++ b/src/jit/gtstructs.h
@@ -121,7 +121,9 @@ GTSTRUCT_1(HWIntrinsic , GT_HWIntrinsic)
GTSTRUCT_1(AllocObj , GT_ALLOCOBJ)
GTSTRUCT_1(RuntimeLookup, GT_RUNTIMELOOKUP)
GTSTRUCT_2(CC , GT_JCC, GT_SETCC)
-#if defined(_TARGET_ARM_)
+#if defined(_TARGET_X86_)
+GTSTRUCT_1(MultiRegOp , GT_MUL_LONG)
+#elif defined (_TARGET_ARM_)
GTSTRUCT_3(MultiRegOp , GT_MUL_LONG, GT_PUTARG_REG, GT_BITCAST)
#endif
/*****************************************************************************/
diff --git a/src/jit/lsra.cpp b/src/jit/lsra.cpp
index 7a86f33310..02bfc2a4e2 100644
--- a/src/jit/lsra.cpp
+++ b/src/jit/lsra.cpp
@@ -130,21 +130,23 @@ void lsraAssignRegToTree(GenTree* tree, regNumber reg, unsigned regIdx)
{
tree->gtRegNum = reg;
}
-#if FEATURE_ARG_SPLIT
-#ifdef _TARGET_ARM_
+#if !defined(_TARGET_64BIT_)
else if (tree->OperIsMultiRegOp())
{
assert(regIdx == 1);
GenTreeMultiRegOp* mul = tree->AsMultiRegOp();
mul->gtOtherReg = reg;
}
-#endif // _TARGET_ARM_
+#endif // _TARGET_64BIT_
+#if FEATURE_MULTIREG_RET
else if (tree->OperGet() == GT_COPY)
{
assert(regIdx == 1);
GenTreeCopyOrReload* copy = tree->AsCopyOrReload();
copy->gtOtherRegs[0] = (regNumberSmall)reg;
}
+#endif // FEATURE_MULTIREG_RET
+#if FEATURE_ARG_SPLIT
else if (tree->OperIsPutArgSplit())
{
GenTreePutArgSplit* putArg = tree->AsPutArgSplit();
@@ -6176,11 +6178,10 @@ void LinearScan::insertCopyOrReload(BasicBlock* block, GenTree* tree, unsigned m
#endif
}
- // If the parent is a reload/copy node, then tree must be a multi-reg call node
- // that has already had one of its registers spilled. This is because multi-reg
- // call node is the only node whose RefTypeDef positions get independently
- // spilled or reloaded. It is possible that one of its RefTypeDef position got
- // spilled and the next use of it requires it to be in a different register.
+ // If the parent is a reload/copy node, then tree must be a multi-reg node
+ // that has already had one of its registers spilled.
+ // It is possible that one of its RefTypeDef positions got spilled and the next
+ // use of it requires it to be in a different register.
//
// In this case set the i'th position reg of reload/copy node to the reg allocated
// for copy/reload refPosition. Essentially a copy/reload node will have a reg
@@ -6190,8 +6191,7 @@ void LinearScan::insertCopyOrReload(BasicBlock* block, GenTree* tree, unsigned m
if (parent->IsCopyOrReload())
{
noway_assert(parent->OperGet() == oper);
- noway_assert(tree->IsMultiRegCall());
- GenTreeCall* call = tree->AsCall();
+ noway_assert(tree->IsMultiRegNode());
GenTreeCopyOrReload* copyOrReload = parent->AsCopyOrReload();
noway_assert(copyOrReload->GetRegNumByIdx(multiRegIdx) == REG_NA);
copyOrReload->SetRegNumByIdx(refPosition->assignedReg(), multiRegIdx);
@@ -8762,8 +8762,24 @@ void LinearScan::lsraGetOperandString(GenTree* tree,
}
else
{
- _snprintf_s(operandString, operandStringLength, operandStringLength, "%s%s",
- getRegName(tree->gtRegNum, useFloatReg(tree->TypeGet())), lastUseChar);
+ regNumber reg = tree->gtRegNum;
+ int charCount = _snprintf_s(operandString, operandStringLength, operandStringLength, "%s%s",
+ getRegName(reg, genIsValidFloatReg(reg)), lastUseChar);
+ operandString += charCount;
+ operandStringLength -= charCount;
+
+ if (tree->IsMultiRegNode())
+ {
+ unsigned regCount = tree->GetMultiRegCount();
+ for (unsigned regIndex = 1; regIndex < regCount; regIndex++)
+ {
+ regNumber reg = tree->GetRegByIndex(regIndex);
+ charCount = _snprintf_s(operandString, operandStringLength, operandStringLength, ",%s%s",
+ getRegName(reg, genIsValidFloatReg(reg)), lastUseChar);
+ operandString += charCount;
+ operandStringLength -= charCount;
+ }
+ }
}
}
break;
@@ -8893,18 +8909,13 @@ void LinearScan::DumpOperandDefs(
if (dstCount != 0)
{
// This operand directly produces registers; print it.
- for (int i = 0; i < dstCount; i++)
+ if (!first)
{
- if (!first)
- {
- printf(",");
- }
-
- lsraGetOperandString(operand, mode, operandString, operandStringLength);
- printf("%s", operandString);
-
- first = false;
+ printf(",");
}
+ lsraGetOperandString(operand, mode, operandString, operandStringLength);
+ printf("%s", operandString);
+ first = false;
}
else if (operand->isContained())
{
diff --git a/src/jit/lsrabuild.cpp b/src/jit/lsrabuild.cpp
index 706bc0c422..8e97492f71 100644
--- a/src/jit/lsrabuild.cpp
+++ b/src/jit/lsrabuild.cpp
@@ -2826,18 +2826,9 @@ int LinearScan::BuildStoreLoc(GenTreeLclVarCommon* storeLoc)
{
if (op1->OperIs(GT_MUL_LONG))
{
-#ifdef _TARGET_X86_
- // This is actually a bug. A GT_MUL_LONG produces two registers, but is modeled as only producing
- // eax (and killing edx). This only works because it always occurs as var = GT_MUL_LONG (ensured by
- // DecomposeMul), and therefore edx won't be reused before the store.
- // TODO-X86-Cleanup: GT_MUL_LONG should be a multireg node on x86, just as on ARM.
- srcCount = 1;
- singleUseRef = BuildUse(op1);
-#else
srcCount = 2;
BuildUse(op1, allRegs(TYP_INT), 0);
BuildUse(op1, allRegs(TYP_INT), 1);
-#endif
}
else
{
diff --git a/src/jit/lsraxarch.cpp b/src/jit/lsraxarch.cpp
index aeb86e54fd..56e3cc741b 100644
--- a/src/jit/lsraxarch.cpp
+++ b/src/jit/lsraxarch.cpp
@@ -327,11 +327,13 @@ int LinearScan::BuildNode(GenTree* tree)
srcCount = BuildModDiv(tree->AsOp());
break;
- case GT_MUL:
- case GT_MULHI:
#if defined(_TARGET_X86_)
case GT_MUL_LONG:
+ dstCount = 2;
+ __fallthrough;
#endif
+ case GT_MUL:
+ case GT_MULHI:
srcCount = BuildMul(tree->AsOp());
break;
@@ -682,8 +684,9 @@ int LinearScan::BuildNode(GenTree* tree)
} // end switch (tree->OperGet())
- // We need to be sure that we've set srcCount and dstCount appropriately
- assert((dstCount < 2) || (tree->IsMultiRegCall() && dstCount == MAX_RET_REG_COUNT));
+ // We need to be sure that we've set srcCount and dstCount appropriately.
+ // Not that for XARCH, the maximum number of registers defined is 2.
+ assert((dstCount < 2) || ((dstCount == 2) && tree->IsMultiRegNode()));
assert(isLocalDefUse == (tree->IsValue() && tree->IsUnusedValue()));
assert(!tree->IsUnusedValue() || (dstCount != 0));
assert(dstCount == tree->GetRegisterDstCount());
@@ -2821,6 +2824,7 @@ int LinearScan::BuildMul(GenTree* tree)
}
int srcCount = BuildBinaryUses(tree->AsOp());
+ int dstCount = 1;
regMaskTP dstCandidates = RBM_NONE;
bool isUnsignedMultiply = ((tree->gtFlags & GTF_UNSIGNED) != 0);
@@ -2862,7 +2866,8 @@ int LinearScan::BuildMul(GenTree* tree)
else if (tree->OperGet() == GT_MUL_LONG)
{
// have to use the encoding:RDX:RAX = RAX * rm
- dstCandidates = RBM_RAX;
+ dstCandidates = RBM_RAX | RBM_RDX;
+ dstCount = 2;
}
#endif
GenTree* containedMemOp = nullptr;
@@ -2876,7 +2881,7 @@ int LinearScan::BuildMul(GenTree* tree)
containedMemOp = op2;
}
regMaskTP killMask = getKillSetForMul(tree->AsOp());
- BuildDefsWithKills(tree, 1, dstCandidates, killMask);
+ BuildDefsWithKills(tree, dstCount, dstCandidates, killMask);
return srcCount;
}