diff options
Diffstat (limited to 'src/jit/decomposelongs.cpp')
-rw-r--r-- | src/jit/decomposelongs.cpp | 1056 |
1 files changed, 868 insertions, 188 deletions
diff --git a/src/jit/decomposelongs.cpp b/src/jit/decomposelongs.cpp index cf66487367..98b8b081fc 100644 --- a/src/jit/decomposelongs.cpp +++ b/src/jit/decomposelongs.cpp @@ -65,7 +65,7 @@ void DecomposeLongs::DecomposeBlock(BasicBlock* block) assert(block->isEmpty() || block->IsLIR()); m_blockWeight = block->getBBWeight(m_compiler); - m_range = &LIR::AsRange(block); + m_range = &LIR::AsRange(block); DecomposeRangeHelper(); } @@ -90,7 +90,7 @@ void DecomposeLongs::DecomposeRange(Compiler* compiler, unsigned blockWeight, LI DecomposeLongs decomposer(compiler); decomposer.m_blockWeight = blockWeight; - decomposer.m_range = ⦥ + decomposer.m_range = ⦥ decomposer.DecomposeRangeHelper(); } @@ -111,13 +111,7 @@ void DecomposeLongs::DecomposeRangeHelper() GenTree* node = Range().FirstNonPhiNode(); while (node != nullptr) { - LIR::Use use; - if (!Range().TryGetUse(node, &use)) - { - use = LIR::Use::GetDummyUse(Range(), node); - } - - node = DecomposeNode(use); + node = DecomposeNode(node); } assert(Range().CheckLIR(m_compiler)); @@ -132,10 +126,8 @@ void DecomposeLongs::DecomposeRangeHelper() // Return Value: // The next node to process. // -GenTree* DecomposeLongs::DecomposeNode(LIR::Use& use) +GenTree* DecomposeLongs::DecomposeNode(GenTree* tree) { - GenTree* tree = use.Def(); - // Handle the case where we are implicitly using the lower half of a long lclVar. if ((tree->TypeGet() == TYP_INT) && tree->OperIsLocal()) { @@ -171,14 +163,15 @@ GenTree* DecomposeLongs::DecomposeNode(LIR::Use& use) } #endif // DEBUG + LIR::Use use; + if (!Range().TryGetUse(tree, &use)) + { + use = LIR::Use::GetDummyUse(Range(), tree); + } + GenTree* nextNode = nullptr; switch (tree->OperGet()) { - case GT_PHI: - case GT_PHI_ARG: - nextNode = tree->gtNext; - break; - case GT_LCL_VAR: nextNode = DecomposeLclVar(use); break; @@ -212,8 +205,7 @@ GenTree* DecomposeLongs::DecomposeNode(LIR::Use& use) break; case GT_STORE_LCL_FLD: - assert(tree->gtOp.gtOp1->OperGet() == GT_LONG); - NYI("st.lclFld of of TYP_LONG"); + nextNode = DecomposeStoreLclFld(use); break; case GT_IND: @@ -239,23 +231,11 @@ GenTree* DecomposeLongs::DecomposeNode(LIR::Use& use) break; case GT_MUL: - NYI("Arithmetic binary operators on TYP_LONG - GT_MUL"); - break; - - case GT_DIV: - NYI("Arithmetic binary operators on TYP_LONG - GT_DIV"); - break; - - case GT_MOD: - NYI("Arithmetic binary operators on TYP_LONG - GT_MOD"); - break; - - case GT_UDIV: - NYI("Arithmetic binary operators on TYP_LONG - GT_UDIV"); + nextNode = DecomposeMul(use); break; case GT_UMOD: - NYI("Arithmetic binary operators on TYP_LONG - GT_UMOD"); + nextNode = DecomposeUMod(use); break; case GT_LSH: @@ -266,11 +246,7 @@ GenTree* DecomposeLongs::DecomposeNode(LIR::Use& use) case GT_ROL: case GT_ROR: - NYI("Arithmetic binary operators on TYP_LONG - ROTATE"); - break; - - case GT_MULHI: - NYI("Arithmetic binary operators on TYP_LONG - MULHI"); + nextNode = DecomposeRotate(use); break; case GT_LOCKADD: @@ -288,6 +264,37 @@ GenTree* DecomposeLongs::DecomposeNode(LIR::Use& use) } } + // If we replaced the argument to a GT_FIELD_LIST element with a GT_LONG node, split that field list + // element into two elements: one for each half of the GT_LONG. + if ((use.Def()->OperGet() == GT_LONG) && !use.IsDummyUse() && (use.User()->OperGet() == GT_FIELD_LIST)) + { + GenTreeOp* value = use.Def()->AsOp(); + Range().Remove(value); + + // The node returned by `use.User()` is the head of the field list. We need to find the actual node that uses + // the `GT_LONG` so that we can split it. + GenTreeFieldList* listNode = use.User()->AsFieldList(); + for (; listNode != nullptr; listNode = listNode->Rest()) + { + if (listNode->Current() == value) + { + break; + } + } + + assert(listNode != nullptr); + GenTree* rest = listNode->gtOp2; + + GenTreeFieldList* loNode = listNode; + loNode->gtOp1 = value->gtOp1; + loNode->gtFieldType = TYP_INT; + + GenTreeFieldList* hiNode = + new (m_compiler, GT_FIELD_LIST) GenTreeFieldList(value->gtOp2, loNode->gtFieldOffset + 4, TYP_INT, loNode); + + hiNode->gtOp2 = rest; + } + #ifdef DEBUG if (m_compiler->verbose) { @@ -308,23 +315,25 @@ GenTree* DecomposeLongs::DecomposeNode(LIR::Use& use) // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. // loResult - the decomposed low part -// hiResult - the decomposed high part. This must follow loResult in the linear order, -// as the new GT_LONG node will be inserted immediately after it. +// hiResult - the decomposed high part +// insertResultAfter - the node that the GT_LONG should be inserted after // // Return Value: // The next node to process. // -GenTree* DecomposeLongs::FinalizeDecomposition(LIR::Use& use, GenTree* loResult, GenTree* hiResult) +GenTree* DecomposeLongs::FinalizeDecomposition(LIR::Use& use, + GenTree* loResult, + GenTree* hiResult, + GenTree* insertResultAfter) { assert(use.IsInitialized()); assert(loResult != nullptr); assert(hiResult != nullptr); assert(Range().Contains(loResult)); assert(Range().Contains(hiResult)); - assert(loResult->Precedes(hiResult)); GenTree* gtLong = new (m_compiler, GT_LONG) GenTreeOp(GT_LONG, TYP_LONG, loResult, hiResult); - Range().InsertAfter(hiResult, gtLong); + Range().InsertAfter(insertResultAfter, gtLong); use.ReplaceWith(m_compiler, gtLong); @@ -366,8 +375,6 @@ GenTree* DecomposeLongs::DecomposeLclVar(LIR::Use& use) } else { - noway_assert(varDsc->lvLRACandidate == false); - loResult->SetOper(GT_LCL_FLD); loResult->AsLclFld()->gtLclOffs = 0; loResult->AsLclFld()->gtFieldSeq = FieldSeqStore::NotAField(); @@ -380,7 +387,7 @@ GenTree* DecomposeLongs::DecomposeLclVar(LIR::Use& use) m_compiler->lvaIncRefCnts(loResult); m_compiler->lvaIncRefCnts(hiResult); - return FinalizeDecomposition(use, loResult, hiResult); + return FinalizeDecomposition(use, loResult, hiResult, hiResult); } //------------------------------------------------------------------------ @@ -404,7 +411,7 @@ GenTree* DecomposeLongs::DecomposeLclFld(LIR::Use& use) GenTree* hiResult = m_compiler->gtNewLclFldNode(loResult->gtLclNum, TYP_INT, loResult->gtLclOffs + 4); Range().InsertAfter(loResult, hiResult); - return FinalizeDecomposition(use, loResult, hiResult); + return FinalizeDecomposition(use, loResult, hiResult, hiResult); } //------------------------------------------------------------------------ @@ -423,59 +430,118 @@ GenTree* DecomposeLongs::DecomposeStoreLclVar(LIR::Use& use) GenTree* tree = use.Def(); GenTree* rhs = tree->gtGetOp1(); - if ((rhs->OperGet() == GT_PHI) || (rhs->OperGet() == GT_CALL)) + if ((rhs->OperGet() == GT_PHI) || (rhs->OperGet() == GT_CALL) || + ((rhs->OperGet() == GT_MUL_LONG) && (rhs->gtFlags & GTF_MUL_64RSLT) != 0)) { // GT_CALLs are not decomposed, so will not be converted to GT_LONG // GT_STORE_LCL_VAR = GT_CALL are handled in genMultiRegCallStoreToLocal + // GT_MULs are not decomposed, so will not be converted to GT_LONG return tree->gtNext; } noway_assert(rhs->OperGet() == GT_LONG); + unsigned varNum = tree->AsLclVarCommon()->gtLclNum; LclVarDsc* varDsc = m_compiler->lvaTable + varNum; + if (!varDsc->lvPromoted) + { + // We cannot decompose a st.lclVar that is not promoted because doing so + // changes its liveness semantics. For example, consider the following + // decomposition of a st.lclVar into two st.lclFlds: + // + // Before: + // + // /--* t0 int + // +--* t1 int + // t2 = * gt_long long + // + // /--* t2 long + // * st.lclVar long V0 + // + // After: + // /--* t0 int + // * st.lclFld int V0 [+0] + // + // /--* t1 int + // * st.lclFld int V0 [+4] + // + // Before decomposition, the `st.lclVar` is a simple def of `V0`. After + // decomposition, each `st.lclFld` is a partial def of `V0`. This partial + // def is treated as both a use and a def of the appropriate lclVar. This + // difference will affect any situation in which the liveness of a variable + // at a def matters (e.g. dead store elimination, live-in sets, etc.). As + // a result, we leave these stores as-is and generate the decomposed store + // in the code generator. + // + // NOTE: this does extend the lifetime of the low half of the `GT_LONG` + // node as compared to the decomposed form. If we start doing more code + // motion in the backend, this may cause some CQ issues and some sort of + // decomposition could be beneficial. + return tree->gtNext; + } + + assert(varDsc->lvFieldCnt == 2); m_compiler->lvaDecRefCnts(tree); - GenTree* loRhs = rhs->gtGetOp1(); - GenTree* hiRhs = rhs->gtGetOp2(); - GenTree* hiStore = m_compiler->gtNewLclLNode(varNum, TYP_INT); + GenTreeOp* value = rhs->AsOp(); + Range().Remove(value); - if (varDsc->lvPromoted) - { - assert(varDsc->lvFieldCnt == 2); + const unsigned loVarNum = varDsc->lvFieldLclStart; + GenTree* loStore = tree; + loStore->AsLclVarCommon()->SetLclNum(loVarNum); + loStore->gtOp.gtOp1 = value->gtOp1; + loStore->gtType = TYP_INT; - unsigned loVarNum = varDsc->lvFieldLclStart; - unsigned hiVarNum = loVarNum + 1; - tree->AsLclVarCommon()->SetLclNum(loVarNum); - hiStore->SetOper(GT_STORE_LCL_VAR); - hiStore->AsLclVarCommon()->SetLclNum(hiVarNum); - } - else - { - noway_assert(varDsc->lvLRACandidate == false); + const unsigned hiVarNum = loVarNum + 1; + GenTree* hiStore = m_compiler->gtNewLclLNode(hiVarNum, TYP_INT); + hiStore->SetOper(GT_STORE_LCL_VAR); + hiStore->gtOp.gtOp1 = value->gtOp2; + hiStore->gtFlags |= GTF_VAR_DEF; - tree->SetOper(GT_STORE_LCL_FLD); - tree->AsLclFld()->gtLclOffs = 0; - tree->AsLclFld()->gtFieldSeq = FieldSeqStore::NotAField(); + m_compiler->lvaIncRefCnts(loStore); + m_compiler->lvaIncRefCnts(hiStore); - hiStore->SetOper(GT_STORE_LCL_FLD); - hiStore->AsLclFld()->gtLclOffs = 4; - hiStore->AsLclFld()->gtFieldSeq = FieldSeqStore::NotAField(); - } + Range().InsertAfter(tree, hiStore); - // 'tree' is going to steal the loRhs node for itself, so we need to remove the - // GT_LONG node from the threading. - Range().Remove(rhs); + return hiStore->gtNext; +} - tree->gtOp.gtOp1 = loRhs; - tree->gtType = TYP_INT; +//------------------------------------------------------------------------ +// DecomposeStoreLclFld: Decompose GT_STORE_LCL_FLD. +// +// Arguments: +// use - the LIR::Use object for the def that needs to be decomposed. +// +// Return Value: +// The next node to process. +// +GenTree* DecomposeLongs::DecomposeStoreLclFld(LIR::Use& use) +{ + assert(use.IsInitialized()); + assert(use.Def()->OperGet() == GT_STORE_LCL_FLD); - hiStore->gtOp.gtOp1 = hiRhs; - hiStore->gtFlags |= GTF_VAR_DEF; + GenTreeLclFld* store = use.Def()->AsLclFld(); + + GenTreeOp* value = store->gtOp1->AsOp(); + assert(value->OperGet() == GT_LONG); + Range().Remove(value); + + // The original store node will be repurposed to store the low half of the GT_LONG. + GenTreeLclFld* loStore = store; + loStore->gtOp1 = value->gtOp1; + loStore->gtType = TYP_INT; + loStore->gtFlags |= GTF_VAR_USEASG; - m_compiler->lvaIncRefCnts(tree); + // Create the store for the upper half of the GT_LONG and insert it after the low store. + GenTreeLclFld* hiStore = m_compiler->gtNewLclFldNode(loStore->gtLclNum, TYP_INT, loStore->gtLclOffs + 4); + hiStore->SetOper(GT_STORE_LCL_FLD); + hiStore->gtOp1 = value->gtOp2; + hiStore->gtFlags |= (GTF_VAR_DEF | GTF_VAR_USEASG); + + // Bump the ref count for the destination. m_compiler->lvaIncRefCnts(hiStore); - Range().InsertAfter(tree, hiStore); + Range().InsertAfter(loStore, hiStore); return hiStore->gtNext; } @@ -494,35 +560,103 @@ GenTree* DecomposeLongs::DecomposeCast(LIR::Use& use) assert(use.IsInitialized()); assert(use.Def()->OperGet() == GT_CAST); - GenTree* tree = use.Def(); + GenTree* cast = use.Def()->AsCast(); GenTree* loResult = nullptr; GenTree* hiResult = nullptr; - assert(tree->gtPrev == tree->gtGetOp1()); - NYI_IF(tree->gtOverflow(), "TYP_LONG cast with overflow"); - switch (tree->AsCast()->CastFromType()) + var_types srcType = cast->CastFromType(); + var_types dstType = cast->CastToType(); + + if ((cast->gtFlags & GTF_UNSIGNED) != 0) + { + srcType = genUnsignedType(srcType); + } + + if (varTypeIsLong(srcType)) + { + if (cast->gtOverflow() && (varTypeIsUnsigned(srcType) != varTypeIsUnsigned(dstType))) + { + GenTree* srcOp = cast->gtGetOp1(); + noway_assert(srcOp->OperGet() == GT_LONG); + GenTree* loSrcOp = srcOp->gtGetOp1(); + GenTree* hiSrcOp = srcOp->gtGetOp2(); + + // + // When casting between long types an overflow check is needed only if the types + // have different signedness. In both cases (long->ulong and ulong->long) we only + // need to check if the high part is negative or not. Use the existing cast node + // to perform a int->uint cast of the high part to take advantage of the overflow + // check provided by codegen. + // + + loResult = loSrcOp; + + hiResult = cast; + hiResult->gtType = TYP_INT; + hiResult->AsCast()->gtCastType = TYP_UINT; + hiResult->gtFlags &= ~GTF_UNSIGNED; + hiResult->gtOp.gtOp1 = hiSrcOp; + + Range().Remove(cast); + Range().Remove(srcOp); + Range().InsertAfter(hiSrcOp, hiResult); + } + else + { + NYI("Unimplemented long->long no-op cast decomposition"); + } + } + else if (varTypeIsIntegralOrI(srcType)) { - case TYP_INT: - if (tree->gtFlags & GTF_UNSIGNED) + if (cast->gtOverflow() && !varTypeIsUnsigned(srcType) && varTypeIsUnsigned(dstType)) + { + // + // An overflow check is needed only when casting from a signed type to ulong. + // Change the cast type to uint to take advantage of the overflow check provided + // by codegen and then zero extend the resulting uint to ulong. + // + + loResult = cast; + loResult->AsCast()->gtCastType = TYP_UINT; + loResult->gtType = TYP_INT; + + hiResult = m_compiler->gtNewZeroConNode(TYP_INT); + + Range().InsertAfter(loResult, hiResult); + } + else + { + if (varTypeIsUnsigned(srcType)) { - loResult = tree->gtGetOp1(); - Range().Remove(tree); + loResult = cast->gtGetOp1(); + hiResult = m_compiler->gtNewZeroConNode(TYP_INT); - hiResult = new (m_compiler, GT_CNS_INT) GenTreeIntCon(TYP_INT, 0); + Range().Remove(cast); Range().InsertAfter(loResult, hiResult); } else { - NYI("Lowering of signed cast TYP_INT->TYP_LONG"); - } - break; + LIR::Use src(Range(), &(cast->gtOp.gtOp1), cast); + unsigned lclNum = src.ReplaceWithLclVar(m_compiler, m_blockWeight); - default: - NYI("Unimplemented type for Lowering of cast to TYP_LONG"); - break; + loResult = src.Def(); + + GenTree* loCopy = m_compiler->gtNewLclvNode(lclNum, TYP_INT); + GenTree* shiftBy = m_compiler->gtNewIconNode(31, TYP_INT); + hiResult = m_compiler->gtNewOperNode(GT_RSH, TYP_INT, loCopy, shiftBy); + + Range().Remove(cast); + Range().InsertAfter(loResult, loCopy, shiftBy, hiResult); + m_compiler->lvaIncRefCnts(loCopy); + } + } + } + else + { + NYI("Unimplemented cast decomposition"); } - return FinalizeDecomposition(use, loResult, hiResult); + return FinalizeDecomposition(use, loResult, hiResult, hiResult); } //------------------------------------------------------------------------ @@ -549,7 +683,7 @@ GenTree* DecomposeLongs::DecomposeCnsLng(LIR::Use& use) GenTree* hiResult = new (m_compiler, GT_CNS_INT) GenTreeIntCon(TYP_INT, hiVal); Range().InsertAfter(loResult, hiResult); - return FinalizeDecomposition(use, loResult, hiResult); + return FinalizeDecomposition(use, loResult, hiResult, hiResult); } //------------------------------------------------------------------------ @@ -567,35 +701,7 @@ GenTree* DecomposeLongs::DecomposeCall(LIR::Use& use) assert(use.Def()->OperGet() == GT_CALL); // We only need to force var = call() if the call's result is used. - if (use.IsDummyUse()) - return use.Def()->gtNext; - - GenTree* user = use.User(); - if (user->OperGet() == GT_STORE_LCL_VAR) - { - // If parent is already a STORE_LCL_VAR, we can skip it if - // it is already marked as lvIsMultiRegRet. - unsigned varNum = user->AsLclVarCommon()->gtLclNum; - if (m_compiler->lvaTable[varNum].lvIsMultiRegRet) - { - return use.Def()->gtNext; - } - else if (!m_compiler->lvaTable[varNum].lvPromoted) - { - // If var wasn't promoted, we can just set lvIsMultiRegRet. - m_compiler->lvaTable[varNum].lvIsMultiRegRet = true; - return use.Def()->gtNext; - } - } - - GenTree* originalNode = use.Def(); - - // Otherwise, we need to force var = call() - unsigned varNum = use.ReplaceWithLclVar(m_compiler, m_blockWeight); - m_compiler->lvaTable[varNum].lvIsMultiRegRet = true; - - // Decompose the new LclVar use - return DecomposeLclVar(use); + return StoreNodeToVar(use); } //------------------------------------------------------------------------ @@ -627,7 +733,7 @@ GenTree* DecomposeLongs::DecomposeStoreInd(LIR::Use& use) // + --* t155 long // * storeIndir long - GenTree* gtLong = tree->gtOp.gtOp2; + GenTree* gtLong = tree->gtOp.gtOp2; // Save address to a temp. It is used in storeIndLow and storeIndHigh trees. LIR::Use address(Range(), &tree->gtOp.gtOp1, tree); @@ -721,12 +827,13 @@ GenTree* DecomposeLongs::DecomposeInd(LIR::Use& use) GenTreePtr addrHigh = new (m_compiler, GT_LEA) GenTreeAddrMode(TYP_REF, addrBaseHigh, nullptr, 0, genTypeSize(TYP_INT)); GenTreePtr indHigh = new (m_compiler, GT_IND) GenTreeIndir(GT_IND, TYP_INT, addrHigh, nullptr); + indHigh->gtFlags |= (indLow->gtFlags & (GTF_GLOB_REF | GTF_EXCEPT | GTF_IND_FLAGS)); m_compiler->lvaIncRefCnts(addrBaseHigh); Range().InsertAfter(indLow, addrBaseHigh, addrHigh, indHigh); - return FinalizeDecomposition(use, indLow, indHigh); + return FinalizeDecomposition(use, indLow, indHigh, indHigh); } //------------------------------------------------------------------------ @@ -758,7 +865,7 @@ GenTree* DecomposeLongs::DecomposeNot(LIR::Use& use) GenTree* hiResult = new (m_compiler, GT_NOT) GenTreeOp(GT_NOT, TYP_INT, hiOp1, nullptr); Range().InsertAfter(loResult, hiResult); - return FinalizeDecomposition(use, loResult, hiResult); + return FinalizeDecomposition(use, loResult, hiResult, hiResult); } //------------------------------------------------------------------------ @@ -779,14 +886,6 @@ GenTree* DecomposeLongs::DecomposeNeg(LIR::Use& use) GenTree* gtLong = tree->gtGetOp1(); noway_assert(gtLong->OperGet() == GT_LONG); - LIR::Use op1(Range(), >Long->gtOp.gtOp1, gtLong); - op1.ReplaceWithLclVar(m_compiler, m_blockWeight); - - LIR::Use op2(Range(), >Long->gtOp.gtOp2, gtLong); - op2.ReplaceWithLclVar(m_compiler, m_blockWeight); - - // Neither GT_NEG nor the introduced temporaries have side effects. - tree->gtFlags &= ~GTF_ALL_EFFECT; GenTree* loOp1 = gtLong->gtGetOp1(); GenTree* hiOp1 = gtLong->gtGetOp2(); @@ -799,11 +898,10 @@ GenTree* DecomposeLongs::DecomposeNeg(LIR::Use& use) GenTree* zero = m_compiler->gtNewZeroConNode(TYP_INT); GenTree* hiAdjust = m_compiler->gtNewOperNode(GT_ADD_HI, TYP_INT, hiOp1, zero); GenTree* hiResult = m_compiler->gtNewOperNode(GT_NEG, TYP_INT, hiAdjust); - hiResult->gtFlags = tree->gtFlags; Range().InsertAfter(loResult, zero, hiAdjust, hiResult); - return FinalizeDecomposition(use, loResult, hiResult); + return FinalizeDecomposition(use, loResult, hiResult, hiResult); } //------------------------------------------------------------------------ @@ -864,14 +962,19 @@ GenTree* DecomposeLongs::DecomposeArith(LIR::Use& use) } } - return FinalizeDecomposition(use, loResult, hiResult); + return FinalizeDecomposition(use, loResult, hiResult, hiResult); } //------------------------------------------------------------------------ -// DecomposeShift: Decompose GT_LSH, GT_RSH, GT_RSZ. For shift nodes, we need to use -// the shift helper functions, so we here convert the shift into a helper call by -// pulling its arguments out of linear order and making them the args to a call, then -// replacing the original node with the new call. +// DecomposeShift: Decompose GT_LSH, GT_RSH, GT_RSZ. For shift nodes being shifted +// by a constant int, we can inspect the shift amount and decompose to the appropriate +// node types, generating a shl/shld pattern for GT_LSH, a shrd/shr pattern for GT_RSZ, +// and a shrd/sar pattern for GT_SHR for most shift amounts. Shifting by 0, >= 32 and +// >= 64 are special cased to produce better code patterns. +// +// For all other shift nodes, we need to use the shift helper functions, so we here convert +// the shift into a helper call by pulling its arguments out of linear order and making +// them the args to a call, then replacing the original node with the new call. // // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. @@ -883,66 +986,646 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) { assert(use.IsInitialized()); - GenTree* tree = use.Def(); - GenTree* gtLong = tree->gtGetOp1(); - genTreeOps oper = tree->OperGet(); + GenTree* tree = use.Def(); + GenTree* gtLong = tree->gtGetOp1(); + GenTree* loOp1 = gtLong->gtGetOp1(); + GenTree* hiOp1 = gtLong->gtGetOp2(); + GenTree* shiftByOp = tree->gtGetOp2(); + + genTreeOps oper = tree->OperGet(); + genTreeOps shiftByOper = shiftByOp->OperGet(); assert((oper == GT_LSH) || (oper == GT_RSH) || (oper == GT_RSZ)); - LIR::Use loOp1Use(Range(), >Long->gtOp.gtOp1, gtLong); - loOp1Use.ReplaceWithLclVar(m_compiler, m_blockWeight); + // If we are shifting by a constant int, we do not want to use a helper, instead, we decompose. + if (shiftByOper == GT_CNS_INT) + { + unsigned int count = shiftByOp->gtIntCon.gtIconVal; + Range().Remove(shiftByOp); - LIR::Use hiOp1Use(Range(), >Long->gtOp.gtOp2, gtLong); - hiOp1Use.ReplaceWithLclVar(m_compiler, m_blockWeight); + if (count == 0) + { + GenTree* next = tree->gtNext; + // Remove tree and don't do anything else. + Range().Remove(tree); + use.ReplaceWith(m_compiler, gtLong); + return next; + } - LIR::Use shiftWidthUse(Range(), &tree->gtOp.gtOp2, tree); - shiftWidthUse.ReplaceWithLclVar(m_compiler, m_blockWeight); + GenTree* loResult; + GenTree* hiResult; - GenTree* loOp1 = gtLong->gtGetOp1(); - GenTree* hiOp1 = gtLong->gtGetOp2(); + GenTree* insertAfter; - GenTree* shiftWidthOp = tree->gtGetOp2(); + switch (oper) + { + case GT_LSH: + { + Range().Remove(hiOp1); + if (count < 32) + { + // Hi is a GT_LSH_HI, lo is a GT_LSH. Will produce: + // reg1 = lo + // shl lo, shift + // shld hi, reg1, shift + + Range().Remove(gtLong); + loOp1 = RepresentOpAsLocalVar(loOp1, gtLong, >Long->gtOp.gtOp1); + unsigned loOp1LclNum = loOp1->AsLclVarCommon()->gtLclNum; + Range().Remove(loOp1); + + GenTree* shiftByHi = m_compiler->gtNewIconNode(count, TYP_INT); + GenTree* shiftByLo = m_compiler->gtNewIconNode(count, TYP_INT); + + loResult = m_compiler->gtNewOperNode(GT_LSH, TYP_INT, loOp1, shiftByLo); + + // Create a GT_LONG that contains loCopy and hiOp1. This will be used in codegen to + // generate the shld instruction + GenTree* loCopy = m_compiler->gtNewLclvNode(loOp1LclNum, TYP_INT); + GenTree* hiOp = new (m_compiler, GT_LONG) GenTreeOp(GT_LONG, TYP_LONG, loCopy, hiOp1); + hiResult = m_compiler->gtNewOperNode(GT_LSH_HI, TYP_INT, hiOp, shiftByHi); + + m_compiler->lvaIncRefCnts(loCopy); + + Range().InsertBefore(tree, loCopy, hiOp1, hiOp); + Range().InsertBefore(tree, shiftByHi, hiResult); + Range().InsertBefore(tree, loOp1, shiftByLo, loResult); + + insertAfter = loResult; + } + else + { + assert(count >= 32); + + if (count < 64) + { + if (count == 32) + { + // Move loOp1 into hiResult (shift of 32 bits is just a mov of lo to hi) + // We need to make sure that we save lo to a temp variable so that we don't overwrite lo + // before saving it to hi in the case that we are doing an inplace shift. I.e.: + // x = x << 32 + + LIR::Use loOp1Use(Range(), >Long->gtOp.gtOp1, gtLong); + loOp1Use.ReplaceWithLclVar(m_compiler, m_blockWeight); + + hiResult = loOp1Use.Def(); + Range().Remove(gtLong); + } + else + { + Range().Remove(gtLong); + Range().Remove(loOp1); + assert(count > 32 && count < 64); + + // Move loOp1 into hiResult, do a GT_LSH with count - 32. + // We will compute hiResult before loResult in this case, so we don't need to store lo to a + // temp + GenTree* shiftBy = m_compiler->gtNewIconNode(count - 32, TYP_INT); + hiResult = m_compiler->gtNewOperNode(oper, TYP_INT, loOp1, shiftBy); + Range().InsertBefore(tree, loOp1, shiftBy, hiResult); + } + } + else + { + Range().Remove(gtLong); + Range().Remove(loOp1); + assert(count >= 64); + + // Zero out hi (shift of >= 64 bits moves all the bits out of the two registers) + hiResult = m_compiler->gtNewZeroConNode(TYP_INT); + Range().InsertBefore(tree, hiResult); + } + + // Zero out loResult (shift of >= 32 bits shifts all lo bits to hiResult) + loResult = m_compiler->gtNewZeroConNode(TYP_INT); + Range().InsertBefore(tree, loResult); + + insertAfter = loResult; + } + } + break; + case GT_RSZ: + { + Range().Remove(gtLong); + + if (count < 32) + { + // Hi is a GT_RSZ, lo is a GT_RSH_LO. Will produce: + // reg1 = hi + // shrd lo, reg1, shift + // shr hi, shift + + hiOp1 = RepresentOpAsLocalVar(hiOp1, gtLong, >Long->gtOp.gtOp2); + unsigned hiOp1LclNum = hiOp1->AsLclVarCommon()->gtLclNum; + GenTree* hiCopy = m_compiler->gtNewLclvNode(hiOp1LclNum, TYP_INT); + + GenTree* shiftByHi = m_compiler->gtNewIconNode(count, TYP_INT); + GenTree* shiftByLo = m_compiler->gtNewIconNode(count, TYP_INT); + + m_compiler->lvaIncRefCnts(hiCopy); + + hiResult = m_compiler->gtNewOperNode(GT_RSZ, TYP_INT, hiOp1, shiftByHi); + + // Create a GT_LONG that contains loOp1 and hiCopy. This will be used in codegen to + // generate the shrd instruction + GenTree* loOp = new (m_compiler, GT_LONG) GenTreeOp(GT_LONG, TYP_LONG, loOp1, hiCopy); + loResult = m_compiler->gtNewOperNode(GT_RSH_LO, TYP_INT, loOp, shiftByLo); + + Range().InsertBefore(tree, hiCopy, loOp); + Range().InsertBefore(tree, shiftByLo, loResult); + Range().InsertBefore(tree, shiftByHi, hiResult); + } + else + { + Range().Remove(loOp1); + Range().Remove(hiOp1); + assert(count >= 32); + if (count < 64) + { + if (count == 32) + { + // Move hiOp1 into loResult. + loResult = hiOp1; + Range().InsertBefore(tree, loResult); + } + else + { + assert(count > 32 && count < 64); + + // Move hiOp1 into loResult, do a GT_RSZ with count - 32. + GenTree* shiftBy = m_compiler->gtNewIconNode(count - 32, TYP_INT); + loResult = m_compiler->gtNewOperNode(oper, TYP_INT, hiOp1, shiftBy); + Range().InsertBefore(tree, hiOp1, shiftBy, loResult); + } + } + else + { + assert(count >= 64); + + // Zero out lo + loResult = m_compiler->gtNewZeroConNode(TYP_INT); + Range().InsertBefore(tree, loResult); + } + + // Zero out hi + hiResult = m_compiler->gtNewZeroConNode(TYP_INT); + Range().InsertBefore(tree, hiResult); + } + + insertAfter = hiResult; + } + break; + case GT_RSH: + { + Range().Remove(gtLong); + Range().Remove(loOp1); + + hiOp1 = RepresentOpAsLocalVar(hiOp1, gtLong, >Long->gtOp.gtOp2); + unsigned hiOp1LclNum = hiOp1->AsLclVarCommon()->gtLclNum; + GenTree* hiCopy = m_compiler->gtNewLclvNode(hiOp1LclNum, TYP_INT); + Range().Remove(hiOp1); + + if (count < 32) + { + // Hi is a GT_RSH, lo is a GT_RSH_LO. Will produce: + // reg1 = hi + // shrd lo, reg1, shift + // sar hi, shift + + GenTree* shiftByHi = m_compiler->gtNewIconNode(count, TYP_INT); + GenTree* shiftByLo = m_compiler->gtNewIconNode(count, TYP_INT); + m_compiler->lvaIncRefCnts(hiCopy); + + hiResult = m_compiler->gtNewOperNode(GT_RSH, TYP_INT, hiOp1, shiftByHi); + + // Create a GT_LONG that contains loOp1 and hiCopy. This will be used in codegen to + // generate the shrd instruction + GenTree* loOp = new (m_compiler, GT_LONG) GenTreeOp(GT_LONG, TYP_LONG, loOp1, hiCopy); + loResult = m_compiler->gtNewOperNode(GT_RSH_LO, TYP_INT, loOp, shiftByLo); + + Range().InsertBefore(tree, loOp1, hiCopy, loOp); + Range().InsertBefore(tree, shiftByLo, loResult); + Range().InsertBefore(tree, shiftByHi, hiOp1, hiResult); + } + else + { + assert(count >= 32); + if (count < 64) + { + if (count == 32) + { + // Move hiOp1 into loResult. + loResult = hiOp1; + Range().InsertBefore(tree, loResult); + } + else + { + assert(count > 32 && count < 64); + + // Move hiOp1 into loResult, do a GT_RSH with count - 32. + GenTree* shiftBy = m_compiler->gtNewIconNode(count - 32, TYP_INT); + loResult = m_compiler->gtNewOperNode(oper, TYP_INT, hiOp1, shiftBy); + Range().InsertBefore(tree, hiOp1, shiftBy, loResult); + } + + // Propagate sign bit in hiResult + GenTree* shiftBy = m_compiler->gtNewIconNode(31, TYP_INT); + hiResult = m_compiler->gtNewOperNode(GT_RSH, TYP_INT, hiCopy, shiftBy); + Range().InsertBefore(tree, shiftBy, hiCopy, hiResult); + + m_compiler->lvaIncRefCnts(hiCopy); + } + else + { + assert(count >= 64); + + // Propagate sign bit in loResult + GenTree* loShiftBy = m_compiler->gtNewIconNode(31, TYP_INT); + loResult = m_compiler->gtNewOperNode(GT_RSH, TYP_INT, hiCopy, loShiftBy); + Range().InsertBefore(tree, hiCopy, loShiftBy, loResult); + + // Propagate sign bit in hiResult + GenTree* shiftBy = m_compiler->gtNewIconNode(31, TYP_INT); + hiResult = m_compiler->gtNewOperNode(GT_RSH, TYP_INT, hiOp1, shiftBy); + Range().InsertBefore(tree, shiftBy, hiOp1, hiResult); + + m_compiler->lvaIncRefCnts(hiCopy); + } + } + + insertAfter = hiResult; + } + break; + default: + unreached(); + } - Range().Remove(gtLong); - Range().Remove(loOp1); - Range().Remove(hiOp1); + // Remove tree from Range + Range().Remove(tree); - Range().Remove(shiftWidthOp); + return FinalizeDecomposition(use, loResult, hiResult, insertAfter); + } + else + { + // arguments are single used, but LIR call can work only with local vars. + shiftByOp = RepresentOpAsLocalVar(shiftByOp, tree, &tree->gtOp.gtOp2); + loOp1 = RepresentOpAsLocalVar(loOp1, gtLong, >Long->gtOp.gtOp1); + hiOp1 = RepresentOpAsLocalVar(hiOp1, gtLong, >Long->gtOp.gtOp2); - // TODO-X86-CQ: If the shift operand is a GT_CNS_INT, we should pipe the instructions through to codegen - // and generate the shift instructions ourselves there, rather than replacing it with a helper call. + Range().Remove(shiftByOp); + Range().Remove(gtLong); + Range().Remove(loOp1); + Range().Remove(hiOp1); - unsigned helper; + unsigned helper; - switch (oper) + switch (oper) + { + case GT_LSH: + helper = CORINFO_HELP_LLSH; + break; + case GT_RSH: + helper = CORINFO_HELP_LRSH; + break; + case GT_RSZ: + helper = CORINFO_HELP_LRSZ; + break; + default: + unreached(); + } + + GenTreeArgList* argList = m_compiler->gtNewArgList(loOp1, hiOp1, shiftByOp); + + GenTree* call = m_compiler->gtNewHelperCallNode(helper, TYP_LONG, 0, argList); + call->gtFlags |= tree->gtFlags & GTF_ALL_EFFECT; + + GenTreeCall* callNode = call->AsCall(); + ReturnTypeDesc* retTypeDesc = callNode->GetReturnTypeDesc(); + retTypeDesc->InitializeLongReturnType(m_compiler); + + call = m_compiler->fgMorphArgs(callNode); + Range().InsertAfter(tree, LIR::SeqTree(m_compiler, call)); + + Range().Remove(tree); + use.ReplaceWith(m_compiler, call); + return call; + } +} + +//------------------------------------------------------------------------ +// DecomposeRotate: Decompose GT_ROL and GT_ROR with constant shift amounts. We can +// inspect the rotate amount and decompose to the appropriate node types, generating +// a shld/shld pattern for GT_ROL, a shrd/shrd pattern for GT_ROR, for most rotate +// amounts. +// +// Arguments: +// use - the LIR::Use object for the def that needs to be decomposed. +// +// Return Value: +// The next node to process. +// +GenTree* DecomposeLongs::DecomposeRotate(LIR::Use& use) +{ + GenTree* tree = use.Def(); + GenTree* gtLong = tree->gtGetOp1(); + GenTree* rotateByOp = tree->gtGetOp2(); + + genTreeOps oper = tree->OperGet(); + + assert((oper == GT_ROL) || (oper == GT_ROR)); + assert(rotateByOp->IsCnsIntOrI()); + + // For longs, we need to change rols into two GT_LSH_HIs and rors into two GT_RSH_LOs + // so we will get: + // + // shld lo, hi, rotateAmount + // shld hi, loCopy, rotateAmount + // + // or: + // + // shrd lo, hi, rotateAmount + // shrd hi, loCopy, rotateAmount + + if (oper == GT_ROL) { - case GT_LSH: - helper = CORINFO_HELP_LLSH; - break; - case GT_RSH: - helper = CORINFO_HELP_LRSH; - break; - case GT_RSZ: - helper = CORINFO_HELP_LRSZ; - break; - default: - unreached(); + oper = GT_LSH_HI; + } + else + { + oper = GT_RSH_LO; } - GenTreeArgList* argList = m_compiler->gtNewArgList(loOp1, hiOp1, shiftWidthOp); + unsigned int count = rotateByOp->gtIntCon.gtIconVal; + Range().Remove(rotateByOp); + + // Make sure the rotate amount is between 0 and 63. + assert((count < 64) && (count != 0)); + + GenTree* loResult; + GenTree* hiResult; + + if (count == 32) + { + // If the rotate amount is 32, then swap hi and lo + LIR::Use loOp1Use(Range(), >Long->gtOp.gtOp1, gtLong); + loOp1Use.ReplaceWithLclVar(m_compiler, m_blockWeight); + + LIR::Use hiOp1Use(Range(), >Long->gtOp.gtOp2, gtLong); + hiOp1Use.ReplaceWithLclVar(m_compiler, m_blockWeight); + + hiResult = loOp1Use.Def(); + loResult = hiOp1Use.Def(); + gtLong->gtOp.gtOp1 = loResult; + gtLong->gtOp.gtOp2 = hiResult; + + GenTree* next = tree->gtNext; + // Remove tree and don't do anything else. + Range().Remove(tree); + use.ReplaceWith(m_compiler, gtLong); + return next; + } + else + { + GenTree* loOp1; + GenTree* hiOp1; + + if (count > 32) + { + // If count > 32, we swap hi and lo, and subtract 32 from count + hiOp1 = gtLong->gtGetOp1(); + loOp1 = gtLong->gtGetOp2(); + + Range().Remove(gtLong); + loOp1 = RepresentOpAsLocalVar(loOp1, gtLong, >Long->gtOp.gtOp2); + hiOp1 = RepresentOpAsLocalVar(hiOp1, gtLong, >Long->gtOp.gtOp1); + + count -= 32; + } + else + { + loOp1 = gtLong->gtGetOp1(); + hiOp1 = gtLong->gtGetOp2(); + + Range().Remove(gtLong); + loOp1 = RepresentOpAsLocalVar(loOp1, gtLong, >Long->gtOp.gtOp1); + hiOp1 = RepresentOpAsLocalVar(hiOp1, gtLong, >Long->gtOp.gtOp2); + } + + unsigned loOp1LclNum = loOp1->AsLclVarCommon()->gtLclNum; + unsigned hiOp1LclNum = hiOp1->AsLclVarCommon()->gtLclNum; + + Range().Remove(loOp1); + Range().Remove(hiOp1); + + GenTree* rotateByHi = m_compiler->gtNewIconNode(count, TYP_INT); + GenTree* rotateByLo = m_compiler->gtNewIconNode(count, TYP_INT); + + // Create a GT_LONG that contains loOp1 and hiCopy. This will be used in codegen to + // generate the shld instruction + GenTree* hiCopy = m_compiler->gtNewLclvNode(hiOp1LclNum, TYP_INT); + GenTree* loOp = new (m_compiler, GT_LONG) GenTreeOp(GT_LONG, TYP_LONG, hiCopy, loOp1); + loResult = m_compiler->gtNewOperNode(oper, TYP_INT, loOp, rotateByLo); + + // Create a GT_LONG that contains loCopy and hiOp1. This will be used in codegen to + // generate the shld instruction + GenTree* loCopy = m_compiler->gtNewLclvNode(loOp1LclNum, TYP_INT); + GenTree* hiOp = new (m_compiler, GT_LONG) GenTreeOp(GT_LONG, TYP_LONG, loCopy, hiOp1); + hiResult = m_compiler->gtNewOperNode(oper, TYP_INT, hiOp, rotateByHi); + + m_compiler->lvaIncRefCnts(loCopy); + m_compiler->lvaIncRefCnts(hiCopy); + + Range().InsertBefore(tree, hiCopy, loOp1, loOp); + Range().InsertBefore(tree, rotateByLo, loResult); + Range().InsertBefore(tree, loCopy, hiOp1, hiOp); + Range().InsertBefore(tree, rotateByHi, hiResult); + + Range().Remove(tree); + + return FinalizeDecomposition(use, loResult, hiResult, hiResult); + } +} + +//------------------------------------------------------------------------ +// DecomposeMul: Decompose GT_MUL. The only GT_MULs that make it to decompose are +// those with the GTF_MUL_64RSLT flag set. These muls result in a mul instruction that +// returns its result in two registers like GT_CALLs do. Additionally, these muls are +// guaranteed to be in the form long = (long)int * (long)int. Therefore, to decompose +// these nodes, we convert them into GT_MUL_LONGs, undo the cast from int to long by +// stripping out the lo ops, and force them into the form var = mul, as we do for +// GT_CALLs. In codegen, we then produce a mul instruction that produces the result +// in edx:eax, and store those registers on the stack in genStoreLongLclVar. +// +// All other GT_MULs have been converted to helper calls in morph.cpp +// +// Arguments: +// use - the LIR::Use object for the def that needs to be decomposed. +// +// Return Value: +// The next node to process. +// +GenTree* DecomposeLongs::DecomposeMul(LIR::Use& use) +{ + assert(use.IsInitialized()); + + GenTree* tree = use.Def(); + genTreeOps oper = tree->OperGet(); + + assert(oper == GT_MUL); + assert((tree->gtFlags & GTF_MUL_64RSLT) != 0); + + GenTree* op1 = tree->gtGetOp1(); + GenTree* op2 = tree->gtGetOp2(); + + GenTree* loOp1 = op1->gtGetOp1(); + GenTree* hiOp1 = op1->gtGetOp2(); + GenTree* loOp2 = op2->gtGetOp1(); + GenTree* hiOp2 = op2->gtGetOp2(); + + Range().Remove(hiOp1); + Range().Remove(hiOp2); + Range().Remove(op1); + Range().Remove(op2); + + // Get rid of the hi ops. We don't need them. + tree->gtOp.gtOp1 = loOp1; + tree->gtOp.gtOp2 = loOp2; + tree->SetOperRaw(GT_MUL_LONG); + + return StoreNodeToVar(use); +} + +//------------------------------------------------------------------------ +// DecomposeUMod: Decompose GT_UMOD. The only GT_UMODs that make it to decompose +// are guaranteed to be an unsigned long mod with op2 which is a cast to long from +// a constant int whose value is between 2 and 0x3fffffff. All other GT_UMODs are +// morphed into helper calls. These GT_UMODs will actually return an int value in +// RDX. In decompose, we make the lo operation a TYP_INT GT_UMOD, with op2 as the +// original lo half and op1 as a GT_LONG. We make the hi part 0, so we end up with: +// +// GT_UMOD[TYP_INT] ( GT_LONG [TYP_LONG] (loOp1, hiOp1), loOp2 [TYP_INT] ) +// +// With the expectation that we will generate: +// +// EDX = hiOp1 +// EAX = loOp1 +// reg = loOp2 +// idiv reg +// EDX is the remainder, and result of GT_UMOD +// mov hiReg = 0 +// +// Arguments: +// use - the LIR::Use object for the def that needs to be decomposed. +// +// Return Value: +// The next node to process. +// +GenTree* DecomposeLongs::DecomposeUMod(LIR::Use& use) +{ + assert(use.IsInitialized()); + + GenTree* tree = use.Def(); + genTreeOps oper = tree->OperGet(); + + assert(oper == GT_UMOD); + + GenTree* op1 = tree->gtGetOp1(); + GenTree* op2 = tree->gtGetOp2(); + assert(op1->OperGet() == GT_LONG); + assert(op2->OperGet() == GT_LONG); + + GenTree* loOp2 = op2->gtGetOp1(); + GenTree* hiOp2 = op2->gtGetOp2(); + + assert(loOp2->OperGet() == GT_CNS_INT); + assert(hiOp2->OperGet() == GT_CNS_INT); + assert((loOp2->gtIntCon.gtIconVal >= 2) && (loOp2->gtIntCon.gtIconVal <= 0x3fffffff)); + assert(hiOp2->gtIntCon.gtIconVal == 0); + + // Get rid of op2's hi part. We don't need it. + Range().Remove(hiOp2); + Range().Remove(op2); + + // Lo part is the GT_UMOD + GenTree* loResult = tree; + loResult->gtOp.gtOp2 = loOp2; + loResult->gtType = TYP_INT; - GenTree* call = m_compiler->gtNewHelperCallNode(helper, TYP_LONG, 0, argList); + // Set the high part to 0 + GenTree* hiResult = m_compiler->gtNewZeroConNode(TYP_INT); - GenTreeCall* callNode = call->AsCall(); - ReturnTypeDesc* retTypeDesc = callNode->GetReturnTypeDesc(); - retTypeDesc->InitializeLongReturnType(m_compiler); + Range().InsertAfter(loResult, hiResult); - call = m_compiler->fgMorphArgs(callNode); - Range().InsertAfter(tree, LIR::SeqTree(m_compiler, call)); - - Range().Remove(tree); - use.ReplaceWith(m_compiler, call); - return call; + return FinalizeDecomposition(use, loResult, hiResult, hiResult); +} + +//------------------------------------------------------------------------ +// StoreNodeToVar: Check if the user is a STORE_LCL_VAR, and if it isn't, +// store the node to a var. Then decompose the new LclVar. +// +// Arguments: +// use - the LIR::Use object for the def that needs to be decomposed. +// +// Return Value: +// The next node to process. +// +GenTree* DecomposeLongs::StoreNodeToVar(LIR::Use& use) +{ + if (use.IsDummyUse()) + return use.Def()->gtNext; + + GenTree* tree = use.Def(); + GenTree* user = use.User(); + + if (user->OperGet() == GT_STORE_LCL_VAR) + { + // If parent is already a STORE_LCL_VAR, we can skip it if + // it is already marked as lvIsMultiRegRet. + unsigned varNum = user->AsLclVarCommon()->gtLclNum; + if (m_compiler->lvaTable[varNum].lvIsMultiRegRet) + { + return tree->gtNext; + } + else if (!m_compiler->lvaTable[varNum].lvPromoted) + { + // If var wasn't promoted, we can just set lvIsMultiRegRet. + m_compiler->lvaTable[varNum].lvIsMultiRegRet = true; + return tree->gtNext; + } + } + + // Otherwise, we need to force var = call() + unsigned varNum = use.ReplaceWithLclVar(m_compiler, m_blockWeight); + m_compiler->lvaTable[varNum].lvIsMultiRegRet = true; + + // Decompose the new LclVar use + return DecomposeLclVar(use); +} + +//------------------------------------------------------------------------ +// Check is op already local var, if not store it to local. +// +// Arguments: +// op - GenTree* to represent as local variable +// user - user of op +// edge - edge from user to op +// +// Return Value: +// op represented as local var +// +GenTree* DecomposeLongs::RepresentOpAsLocalVar(GenTree* op, GenTree* user, GenTree** edge) +{ + if (op->OperGet() == GT_LCL_VAR) + { + return op; + } + else + { + LIR::Use opUse(Range(), edge, user); + opUse.ReplaceWithLclVar(m_compiler, m_blockWeight); + return *edge; + } } //------------------------------------------------------------------------ @@ -965,9 +1648,6 @@ genTreeOps DecomposeLongs::GetHiOper(genTreeOps oper) case GT_SUB: return GT_SUB_HI; break; - case GT_MUL: - return GT_MUL_HI; - break; case GT_DIV: return GT_DIV_HI; break; |