diff options
Diffstat (limited to 'src/jit/decomposelongs.cpp')
-rw-r--r-- | src/jit/decomposelongs.cpp | 210 |
1 files changed, 147 insertions, 63 deletions
diff --git a/src/jit/decomposelongs.cpp b/src/jit/decomposelongs.cpp index 407ae1c35b..d284c1cb47 100644 --- a/src/jit/decomposelongs.cpp +++ b/src/jit/decomposelongs.cpp @@ -265,7 +265,7 @@ GenTree* DecomposeLongs::DecomposeNode(GenTree* tree) default: { JITDUMP("Illegal TYP_LONG node %s in Decomposition.", GenTree::NodeName(tree->OperGet())); - noway_assert(!"Illegal TYP_LONG node in Decomposition."); + assert(!"Illegal TYP_LONG node in Decomposition."); break; } } @@ -580,6 +580,8 @@ GenTree* DecomposeLongs::DecomposeCast(LIR::Use& use) srcType = genUnsignedType(srcType); } + bool skipDecomposition = false; + if (varTypeIsLong(srcType)) { if (cast->gtOverflow() && (varTypeIsUnsigned(srcType) != varTypeIsUnsigned(dstType))) @@ -605,9 +607,7 @@ GenTree* DecomposeLongs::DecomposeCast(LIR::Use& use) hiResult->gtFlags &= ~GTF_UNSIGNED; hiResult->gtOp.gtOp1 = hiSrcOp; - Range().Remove(cast); Range().Remove(srcOp); - Range().InsertAfter(hiSrcOp, hiResult); } else { @@ -634,13 +634,26 @@ GenTree* DecomposeLongs::DecomposeCast(LIR::Use& use) } else { - if (varTypeIsUnsigned(srcType)) + if (!use.IsDummyUse() && (use.User()->OperGet() == GT_MUL)) + { + // + // This int->long cast is used by a GT_MUL that will be transformed by DecomposeMul into a + // GT_LONG_MUL and as a result the high operand produced by the cast will become dead. + // Skip cast decomposition so DecomposeMul doesn't need to bother with dead code removal, + // especially in the case of sign extending casts that also introduce new lclvars. + // + + assert((use.User()->gtFlags & GTF_MUL_64RSLT) != 0); + + skipDecomposition = true; + } + else if (varTypeIsUnsigned(srcType)) { loResult = cast->gtGetOp1(); hiResult = m_compiler->gtNewZeroConNode(TYP_INT); + Range().InsertAfter(cast, hiResult); Range().Remove(cast); - Range().InsertAfter(loResult, hiResult); } else { @@ -653,9 +666,10 @@ GenTree* DecomposeLongs::DecomposeCast(LIR::Use& use) 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); + Range().InsertAfter(cast, loCopy, shiftBy, hiResult); m_compiler->lvaIncRefCnts(loCopy); + + Range().Remove(cast); } } } @@ -664,6 +678,11 @@ GenTree* DecomposeLongs::DecomposeCast(LIR::Use& use) NYI("Unimplemented cast decomposition"); } + if (skipDecomposition) + { + return cast->gtNext; + } + return FinalizeDecomposition(use, loResult, hiResult, hiResult); } @@ -994,15 +1013,25 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) { assert(use.IsInitialized()); - GenTree* tree = use.Def(); - GenTree* gtLong = tree->gtGetOp1(); + GenTree* shift = use.Def(); + GenTree* gtLong = shift->gtGetOp1(); GenTree* loOp1 = gtLong->gtGetOp1(); GenTree* hiOp1 = gtLong->gtGetOp2(); - GenTree* shiftByOp = tree->gtGetOp2(); + GenTree* shiftByOp = shift->gtGetOp2(); - genTreeOps oper = tree->OperGet(); + genTreeOps oper = shift->OperGet(); genTreeOps shiftByOper = shiftByOp->OperGet(); + // tLo = ... + // ... + // tHi = ... + // ... + // tLong = long tLo, tHi + // ... + // tShiftAmount = ... + // ... + // tShift = shift tLong, tShiftAmount + assert((oper == GT_LSH) || (oper == GT_RSH) || (oper == GT_RSZ)); // If we are shifting by a constant int, we do not want to use a helper, instead, we decompose. @@ -1013,9 +1042,9 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) if (count == 0) { - GenTree* next = tree->gtNext; - // Remove tree and don't do anything else. - Range().Remove(tree); + GenTree* next = shift->gtNext; + // Remove shift and don't do anything else. + Range().Remove(shift); use.ReplaceWith(m_compiler, gtLong); return next; } @@ -1029,15 +1058,27 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) { 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 + // For shifts of < 32 bits, we transform the code to: + // + // tLo = ... + // st.lclVar vLo, tLo + // ... + // tHi = ... + // ... + // tShiftLo = lsh vLo, tShiftAmountLo + // tShitHiLong = long vLo, tHi + // tShiftHi = lsh_hi tShiftHiLong, tShiftAmountHi + // + // This 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); @@ -1055,16 +1096,25 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) m_compiler->lvaIncRefCnts(loCopy); - Range().InsertBefore(tree, loCopy, hiOp1, hiOp); - Range().InsertBefore(tree, shiftByHi, hiResult); - Range().InsertBefore(tree, loOp1, shiftByLo, loResult); + Range().InsertBefore(shift, loOp1, shiftByLo, loResult); + Range().InsertBefore(shift, loCopy, hiOp, shiftByHi, hiResult); - insertAfter = loResult; + insertAfter = hiResult; } else { assert(count >= 32); + // Since we're left shifting at least 32 bits, we can remove the hi part of the shifted value iff + // it has no side effects. + // + // TODO-CQ: we could go perform this removal transitively (i.e. iteratively remove everything that + // feeds the hi operand while there are no side effects) + if ((hiOp1->gtFlags & GTF_ALL_EFFECT) == 0) + { + Range().Remove(hiOp1); + } + if (count < 64) { if (count == 32) @@ -1083,7 +1133,6 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) else { Range().Remove(gtLong); - Range().Remove(loOp1); assert(count > 32 && count < 64); // Move loOp1 into hiResult, do a GT_LSH with count - 32. @@ -1091,23 +1140,33 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) // 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); + Range().InsertBefore(shift, shiftBy, hiResult); } } else { - Range().Remove(gtLong); - Range().Remove(loOp1); assert(count >= 64); + Range().Remove(gtLong); + + // Since we're left shifting at least 64 bits, we can remove the lo part of the shifted value + // iff it has no side effects. + // + // TODO-CQ: we could go perform this removal transitively (i.e. iteratively remove everything + // that feeds the lo operand while there are no side effects) + if ((loOp1->gtFlags & GTF_ALL_EFFECT) == 0) + { + Range().Remove(loOp1); + } + // 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); + Range().InsertBefore(shift, hiResult); } // Zero out loResult (shift of >= 32 bits shifts all lo bits to hiResult) loResult = m_compiler->gtNewZeroConNode(TYP_INT); - Range().InsertBefore(tree, loResult); + Range().InsertBefore(shift, loResult); insertAfter = loResult; } @@ -1140,14 +1199,22 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) 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); + Range().InsertBefore(shift, hiCopy, loOp); + Range().InsertBefore(shift, shiftByLo, loResult); + Range().InsertBefore(shift, shiftByHi, hiResult); } else { - Range().Remove(loOp1); - Range().Remove(hiOp1); + // Since we're right shifting at least 32 bits, we can remove the lo part of the shifted value iff + // it has no side effects. + // + // TODO-CQ: we could go perform this removal transitively (i.e. iteratively remove everything that + // feeds the lo operand while there are no side effects) + if ((loOp1->gtFlags & GTF_ALL_EFFECT) == 0) + { + Range().Remove(loOp1); + } + assert(count >= 32); if (count < 64) { @@ -1155,7 +1222,6 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) { // Move hiOp1 into loResult. loResult = hiOp1; - Range().InsertBefore(tree, loResult); } else { @@ -1164,21 +1230,31 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) // 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); + Range().InsertBefore(shift, shiftBy, loResult); } } else { assert(count >= 64); + // Since we're right shifting at least 64 bits, we can remove the hi part of the shifted value + // iff it has no side effects. + // + // TODO-CQ: we could go perform this removal transitively (i.e. iteratively remove everything + // that feeds the hi operand while there are no side effects) + if ((hiOp1->gtFlags & GTF_ALL_EFFECT) == 0) + { + Range().Remove(hiOp1); + } + // Zero out lo loResult = m_compiler->gtNewZeroConNode(TYP_INT); - Range().InsertBefore(tree, loResult); + Range().InsertBefore(shift, loResult); } // Zero out hi hiResult = m_compiler->gtNewZeroConNode(TYP_INT); - Range().InsertBefore(tree, hiResult); + Range().InsertBefore(shift, hiResult); } insertAfter = hiResult; @@ -1187,7 +1263,6 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) case GT_RSH: { Range().Remove(gtLong); - Range().Remove(loOp1); hiOp1 = RepresentOpAsLocalVar(hiOp1, gtLong, >Long->gtOp.gtOp2); unsigned hiOp1LclNum = hiOp1->AsLclVarCommon()->gtLclNum; @@ -1212,20 +1287,31 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) 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); + Range().InsertBefore(shift, hiCopy, loOp); + Range().InsertBefore(shift, shiftByLo, loResult); + Range().InsertBefore(shift, shiftByHi, hiOp1, hiResult); } else { assert(count >= 32); + + // Since we're right shifting at least 32 bits, we can remove the lo part of the shifted value iff + // it has no side effects. + // + // TODO-CQ: we could go perform this removal transitively (i.e. iteratively remove everything that + // feeds the lo operand while there are no side effects) + if ((loOp1->gtFlags & GTF_ALL_EFFECT) == 0) + { + Range().Remove(loOp1); + } + if (count < 64) { if (count == 32) { // Move hiOp1 into loResult. loResult = hiOp1; - Range().InsertBefore(tree, loResult); + Range().InsertBefore(shift, loResult); } else { @@ -1234,13 +1320,13 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) // 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); + Range().InsertBefore(shift, 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); + Range().InsertBefore(shift, shiftBy, hiCopy, hiResult); m_compiler->lvaIncRefCnts(hiCopy); } @@ -1251,12 +1337,12 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) // 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); + Range().InsertBefore(shift, 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); + Range().InsertBefore(shift, shiftBy, hiOp1, hiResult); m_compiler->lvaIncRefCnts(hiCopy); } @@ -1269,15 +1355,16 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) unreached(); } - // Remove tree from Range - Range().Remove(tree); + // Remove shift from Range + Range().Remove(shift); 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); + // Because calls must be created as HIR and lowered to LIR, we need to dump + // any LIR temps into lclVars before using them as arguments. + shiftByOp = RepresentOpAsLocalVar(shiftByOp, shift, &shift->gtOp.gtOp2); loOp1 = RepresentOpAsLocalVar(loOp1, gtLong, >Long->gtOp.gtOp1); hiOp1 = RepresentOpAsLocalVar(hiOp1, gtLong, >Long->gtOp.gtOp2); @@ -1306,16 +1393,16 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) 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; + call->gtFlags |= shift->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().InsertAfter(shift, LIR::SeqTree(m_compiler, call)); - Range().Remove(tree); + Range().Remove(shift); use.ReplaceWith(m_compiler, call); return call; } @@ -1486,19 +1573,16 @@ GenTree* DecomposeLongs::DecomposeMul(LIR::Use& use) GenTree* op1 = tree->gtGetOp1(); GenTree* op2 = tree->gtGetOp2(); - GenTree* loOp1 = op1->gtGetOp1(); - GenTree* hiOp1 = op1->gtGetOp2(); - GenTree* loOp2 = op2->gtGetOp1(); - GenTree* hiOp2 = op2->gtGetOp2(); + // We expect both operands to be int->long casts. DecomposeCast specifically + // ignores such casts when they are used by GT_MULs. + assert((op1->OperGet() == GT_CAST) && (op1->TypeGet() == TYP_LONG)); + assert((op2->OperGet() == GT_CAST) && (op2->TypeGet() == TYP_LONG)); - 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->gtOp.gtOp1 = op1->gtGetOp1(); + tree->gtOp.gtOp2 = op2->gtGetOp1(); tree->SetOperRaw(GT_MUL_LONG); return StoreNodeToVar(use); |