Skip to content

Commit

Permalink
Optimize CAST(int <- long) on 32 bit targets (#53040)
Browse files Browse the repository at this point in the history
* Optimize CAST(int <- long) on 32 bit targets

* Revert "Optimize CAST(int <- long) on 32 bit targets"

Revert the implementation in lowering

* Optimize CAST(int <- long) on 32 bit targets

Move the code from lowering to long decomposition.

* Fixed the "Arguments" note for DecomposeNode

* Added the function header

For OptimizeCastFromDecomposedLong.

* Remove the TODO comment

While correct, it has questionable value.

* Add a more detailed dump output

* Do not try to optimize checked casts

It is easy to get it wrong. Let the frontend handle this.

* Do not depend on tree order

Previous version of the code assumed that there could be
no nodes between the cast and its operand. That is not
a correct assumption to make in LIR.
  • Loading branch information
SingleAccretion authored Jun 25, 2021
1 parent cef40a1 commit b5b8863
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 1 deletion.
103 changes: 102 additions & 1 deletion src/coreclr/jit/decomposelongs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/decomposelongs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit b5b8863

Please sign in to comment.