diff options
author | Carol Eidt <carol.eidt@microsoft.com> | 2018-08-29 10:54:39 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-29 10:54:39 -0700 |
commit | 305b5529b0612c283ccb2b1c21f43e4e801f9871 (patch) | |
tree | fb1acffda7b86d508a750b35c9960c4b781acf8a /src | |
parent | 1eedc35a644fece43d7674c4d2d56b5f2c605936 (diff) | |
parent | 9e55f6cf2c20945077974311be03951ce9eb346b (diff) | |
download | coreclr-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.cpp | 52 | ||||
-rw-r--r-- | src/jit/codegenlinear.cpp | 54 | ||||
-rw-r--r-- | src/jit/codegenxarch.cpp | 87 | ||||
-rw-r--r-- | src/jit/gentree.cpp | 18 | ||||
-rw-r--r-- | src/jit/gentree.h | 28 | ||||
-rw-r--r-- | src/jit/gtlist.h | 4 | ||||
-rw-r--r-- | src/jit/gtstructs.h | 4 | ||||
-rw-r--r-- | src/jit/lsra.cpp | 55 | ||||
-rw-r--r-- | src/jit/lsrabuild.cpp | 9 | ||||
-rw-r--r-- | src/jit/lsraxarch.cpp | 17 |
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; } |