Skip to content

Commit 7b6a578

Browse files
authored
[RyuJIT] Fold "(X op C1) op C2" to "X op (C1 op C2)" for commutative operators (#43567)
Fold "(X op C1) op C2" to "X op (C1 op C2)"
1 parent 298d019 commit 7b6a578

File tree

2 files changed

+78
-41
lines changed

2 files changed

+78
-41
lines changed

src/coreclr/src/jit/compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5760,6 +5760,7 @@ class Compiler
57605760
GenTree* fgMorphConst(GenTree* tree);
57615761

57625762
GenTreeLclVar* fgMorphTryFoldObjAsLclVar(GenTreeObj* obj);
5763+
GenTree* fgMorphCommutative(GenTreeOp* tree);
57635764

57645765
public:
57655766
GenTree* fgMorphTree(GenTree* tree, MorphAddrContext* mac = nullptr);

src/coreclr/src/jit/morph.cpp

Lines changed: 77 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11611,6 +11611,68 @@ GenTree* Compiler::fgMorphFieldAssignToSIMDIntrinsicSet(GenTree* tree)
1161111611

1161211612
#endif // FEATURE_SIMD
1161311613

11614+
//------------------------------------------------------------------------------
11615+
// fgMorphCommutative : Try to simplify "(X op C1) op C2" to "X op C3"
11616+
// for commutative operators.
11617+
//
11618+
// Arguments:
11619+
// tree - node to fold
11620+
//
11621+
// return value:
11622+
// A folded GenTree* instance or nullptr if something prevents folding.
11623+
//
11624+
11625+
GenTree* Compiler::fgMorphCommutative(GenTreeOp* tree)
11626+
{
11627+
assert(varTypeIsIntegralOrI(tree->TypeGet()));
11628+
assert(tree->OperIs(GT_ADD, GT_MUL, GT_OR, GT_AND, GT_XOR));
11629+
11630+
// op1 can be GT_COMMA, in this case we're going to fold
11631+
// "(op (COMMA(... (op X C1))) C2)" to "(COMMA(... (op X C3)))"
11632+
GenTree* op1 = tree->gtGetOp1()->gtEffectiveVal(true);
11633+
genTreeOps oper = tree->OperGet();
11634+
11635+
if (!op1->OperIs(oper) || !tree->gtGetOp2()->IsCnsIntOrI() || !op1->gtGetOp2()->IsCnsIntOrI() ||
11636+
op1->gtGetOp1()->IsCnsIntOrI() || gtIsActiveCSE_Candidate(op1))
11637+
{
11638+
return nullptr;
11639+
}
11640+
11641+
if (tree->OperMayOverflow() && (tree->gtOverflow() || op1->gtOverflow()))
11642+
{
11643+
return nullptr;
11644+
}
11645+
11646+
GenTreeIntCon* cns1 = op1->gtGetOp2()->AsIntCon();
11647+
GenTreeIntCon* cns2 = tree->gtGetOp2()->AsIntCon();
11648+
11649+
if (!varTypeIsIntegralOrI(tree->TypeGet()) || cns1->TypeIs(TYP_REF) || !cns1->TypeIs(cns2->TypeGet()))
11650+
{
11651+
return nullptr;
11652+
}
11653+
11654+
GenTree* foldedCns = gtFoldExprConst(gtNewOperNode(oper, cns1->TypeGet(), cns1, cns2));
11655+
if (!foldedCns->IsCnsIntOrI())
11656+
{
11657+
// Give up if we can't fold "C1 op C2"
11658+
return nullptr;
11659+
}
11660+
11661+
cns1->gtIconVal = foldedCns->AsIntCon()->IconValue();
11662+
if ((oper == GT_ADD) && foldedCns->IsCnsIntOrI())
11663+
{
11664+
cns1->AsIntCon()->gtFieldSeq =
11665+
GetFieldSeqStore()->Append(cns1->AsIntCon()->gtFieldSeq, cns2->AsIntCon()->gtFieldSeq);
11666+
}
11667+
11668+
GenTreeOp* newTree = tree->gtGetOp1()->AsOp();
11669+
DEBUG_DESTROY_NODE(tree);
11670+
DEBUG_DESTROY_NODE(cns2);
11671+
DEBUG_DESTROY_NODE(foldedCns);
11672+
INDEBUG(newTree->gtOp2->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
11673+
return newTree;
11674+
}
11675+
1161411676
/*****************************************************************************
1161511677
*
1161611678
* Transform the given GTK_SMPOP tree for code generation.
@@ -13451,49 +13513,8 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
1345113513

1345213514
if (op2->IsCnsIntOrI() && varTypeIsIntegralOrI(typ))
1345313515
{
13454-
// Fold "((x+icon1)+icon2) to (x+(icon1+icon2))"
13455-
// Fold "((comma(y, x+icon1)+icon2) to comma(y, x+(icon1+icon2))"
1345613516
CLANG_FORMAT_COMMENT_ANCHOR;
1345713517

13458-
const bool commasOnly = true;
13459-
GenTree* op1EffectiveValue = op1->gtEffectiveVal(commasOnly);
13460-
13461-
if (op1EffectiveValue->gtOper == GT_ADD && !gtIsActiveCSE_Candidate(op1EffectiveValue) &&
13462-
!op1EffectiveValue->gtOverflow() && op1EffectiveValue->AsOp()->gtOp2->IsCnsIntOrI() &&
13463-
(op1EffectiveValue->AsOp()->gtOp2->OperGet() == op2->OperGet()) &&
13464-
(op1EffectiveValue->AsOp()->gtOp2->TypeGet() != TYP_REF) && (op2->TypeGet() != TYP_REF))
13465-
{
13466-
cns1 = op1EffectiveValue->AsOp()->gtOp2;
13467-
13468-
cns1->AsIntConCommon()->SetIconValue(cns1->AsIntConCommon()->IconValue() +
13469-
op2->AsIntConCommon()->IconValue());
13470-
#ifdef TARGET_64BIT
13471-
if (cns1->TypeGet() == TYP_INT)
13472-
{
13473-
// we need to properly re-sign-extend or truncate after adding two int constants above
13474-
cns1->AsIntCon()->TruncateOrSignExtend32();
13475-
}
13476-
#endif // TARGET_64BIT
13477-
13478-
if (cns1->OperGet() == GT_CNS_INT)
13479-
{
13480-
cns1->AsIntCon()->gtFieldSeq =
13481-
GetFieldSeqStore()->Append(cns1->AsIntCon()->gtFieldSeq, op2->AsIntCon()->gtFieldSeq);
13482-
}
13483-
DEBUG_DESTROY_NODE(op2);
13484-
13485-
GenTree* oldTree = tree;
13486-
tree = tree->AsOp()->gtOp1;
13487-
op1 = tree->AsOp()->gtOp1;
13488-
op2 = tree->AsOp()->gtOp2;
13489-
DEBUG_DESTROY_NODE(oldTree);
13490-
13491-
if (tree->OperGet() != GT_ADD)
13492-
{
13493-
return tree;
13494-
}
13495-
}
13496-
1349713518
// Fold (x + 0).
1349813519

1349913520
if ((op2->AsIntConCommon()->IconValue() == 0) && !gtIsActiveCSE_Candidate(tree))
@@ -13669,6 +13690,21 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
1366913690
op2 = tree->AsOp()->gtOp2;
1367013691
}
1367113692

13693+
if (varTypeIsIntegralOrI(tree->TypeGet()) && tree->OperIs(GT_ADD, GT_MUL, GT_AND, GT_OR, GT_XOR))
13694+
{
13695+
GenTree* foldedTree = fgMorphCommutative(tree->AsOp());
13696+
if (foldedTree != nullptr)
13697+
{
13698+
tree = foldedTree;
13699+
op1 = tree->gtGetOp1();
13700+
op2 = tree->gtGetOp2();
13701+
if (!tree->OperIs(oper))
13702+
{
13703+
return tree;
13704+
}
13705+
}
13706+
}
13707+
1367213708
break;
1367313709

1367413710
case GT_NOT:

0 commit comments

Comments
 (0)