diff --git a/src/coreclr/jit/decomposelongs.cpp b/src/coreclr/jit/decomposelongs.cpp index f563b448abe132..7e17db1ae91f1a 100644 --- a/src/coreclr/jit/decomposelongs.cpp +++ b/src/coreclr/jit/decomposelongs.cpp @@ -114,7 +114,7 @@ void DecomposeLongs::DecomposeRangeHelper() // DecomposeNode: Decompose long-type trees into lower and upper halves. // // Arguments: -// use - the LIR::Use object for the def that needs to be decomposed. +// tree - the tree that will, if needed, be decomposed. // // Return Value: // The next node to process. @@ -279,6 +279,13 @@ GenTree* DecomposeLongs::DecomposeNode(GenTree* tree) } #endif + // When casting from a decomposed long to a smaller integer we can discard the high part. + if (m_compiler->opts.OptimizationEnabled() && !use.IsDummyUse() && use.User()->OperIs(GT_CAST) && + use.User()->TypeIs(TYP_INT) && use.Def()->OperIs(GT_LONG)) + { + nextNode = OptimizeCastFromDecomposedLong(use.User()->AsCast(), nextNode); + } + return nextNode; } @@ -1784,6 +1791,100 @@ GenTree* DecomposeLongs::DecomposeHWIntrinsicGetElement(LIR::Use& use, GenTreeHW #endif // FEATURE_HW_INTRINSICS +//------------------------------------------------------------------------ +// OptimizeCastFromDecomposedLong: optimizes a cast from GT_LONG by discarding +// the high part of the source and, if the cast is to INT, the cast node itself. +// Accounts for side effects and marks nodes unused as neccessary. +// +// Only accepts casts to integer types that are not long. +// Does not optimize checked casts. +// +// Arguments: +// cast - the cast tree that has a GT_LONG node as its operand. +// nextNode - the next candidate for decomposition. +// +// Return Value: +// The next node to process in DecomposeRange: "nextNode->gtNext" if +// "cast == nextNode", simply "nextNode" otherwise. +// +// Notes: +// Because "nextNode" usually is "cast", and this method may remove "cast" +// from the linear order, it needs to return the updated "nextNode". Instead +// of receiving it as an argument, it could assume that "nextNode" is always +// "cast->CastOp()->gtNext", but not making that assumption seems better. +// +GenTree* DecomposeLongs::OptimizeCastFromDecomposedLong(GenTreeCast* cast, GenTree* nextNode) +{ + GenTreeOp* src = cast->CastOp()->AsOp(); + var_types dstType = cast->CastToType(); + + assert(src->OperIs(GT_LONG)); + assert(genActualType(dstType) == TYP_INT); + + if (cast->gtOverflow()) + { + return nextNode; + } + + GenTree* loSrc = src->gtGetOp1(); + GenTree* hiSrc = src->gtGetOp2(); + + JITDUMP("Optimizing a truncating cast [%06u] from decomposed LONG [%06u]\n", cast->gtTreeID, src->gtTreeID); + INDEBUG(GenTree* treeToDisplay = cast); + + // TODO-CQ: we could go perform this removal transitively. + // See also identical code in shift decomposition. + if ((hiSrc->gtFlags & (GTF_ALL_EFFECT | GTF_SET_FLAGS)) == 0) + { + JITDUMP("Removing the HI part of [%06u] and marking its operands unused:\n", src->gtTreeID); + DISPNODE(hiSrc); + Range().Remove(hiSrc, /* markOperandsUnused */ true); + } + else + { + JITDUMP("The HI part of [%06u] has side effects, marking it unused\n", src->gtTreeID); + hiSrc->SetUnusedValue(); + } + + JITDUMP("Removing the LONG source:\n"); + DISPNODE(src); + Range().Remove(src); + + if (varTypeIsSmall(dstType)) + { + JITDUMP("Cast is to a small type, keeping it, the new source is [%06u]\n", loSrc->gtTreeID); + cast->CastOp() = loSrc; + } + else + { + LIR::Use useOfCast; + if (Range().TryGetUse(cast, &useOfCast)) + { + useOfCast.ReplaceWith(m_compiler, loSrc); + } + else + { + loSrc->SetUnusedValue(); + } + + if (nextNode == cast) + { + nextNode = nextNode->gtNext; + } + + INDEBUG(treeToDisplay = loSrc); + JITDUMP("Removing the cast:\n"); + DISPNODE(cast); + + Range().Remove(cast); + } + + JITDUMP("Final result:\n") + DISPTREERANGE(Range(), treeToDisplay); + + return nextNode; +} + //------------------------------------------------------------------------ // 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. diff --git a/src/coreclr/jit/decomposelongs.h b/src/coreclr/jit/decomposelongs.h index 4a7320553bdf76..7b5ae19be4f438 100644 --- a/src/coreclr/jit/decomposelongs.h +++ b/src/coreclr/jit/decomposelongs.h @@ -63,6 +63,8 @@ class DecomposeLongs GenTree* DecomposeHWIntrinsicGetElement(LIR::Use& use, GenTreeHWIntrinsic* node); #endif // FEATURE_HW_INTRINSICS + GenTree* OptimizeCastFromDecomposedLong(GenTreeCast* cast, GenTree* nextNode); + // Helper functions GenTree* FinalizeDecomposition(LIR::Use& use, GenTree* loResult, GenTree* hiResult, GenTree* insertResultAfter); GenTree* RepresentOpAsLocalVar(GenTree* op, GenTree* user, GenTree** edge);