diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 9568aceac47f88..4e95fdcf4a707e 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3941,6 +3941,7 @@ class Compiler GenTree* gtFoldExpr(GenTree* tree); GenTree* gtFoldExprConst(GenTree* tree); + GenTree* gtFoldDistributiveArithmetic(GenTree* tree); GenTree* gtFoldIndirConst(GenTreeIndir* indir); GenTree* gtFoldExprSpecial(GenTree* tree); GenTree* gtFoldExprSpecialFloating(GenTree* tree); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index d91d1961bc2ba4..5d55dbeb2d084b 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -15050,6 +15050,10 @@ GenTree* Compiler::gtFoldExpr(GenTree* tree) return gtFoldExprCompare(tree); } + else if (tree->OperIs(GT_AND, GT_OR, GT_XOR, GT_ADD, GT_SUB)) + { + return gtFoldDistributiveArithmetic(tree); + } } /* Return the original node (folded/bashed or not) */ @@ -18019,6 +18023,78 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree) return tree; } +//------------------------------------------------------------------------ +// gtFoldDistributiveArithmetic: Optimizes distributive Arithmetic. +// +// Arguments: +// tree - the unchecked GT_AND/GT_OR/GT_XOR/GT_ADD/GT_SUB tree to optimize. +// +// Return Value: +// The unchanged tree or optimized tree with oper GT_MUL/GT_OR/GT_AND. +// +GenTree* Compiler::gtFoldDistributiveArithmetic(GenTree* tree) +{ + assert(tree->OperIs(GT_AND, GT_OR, GT_XOR, GT_ADD, GT_SUB)); + + if (opts.OptimizationDisabled()) + { + return tree; + } + + if (tree->gtOverflowEx() || !varTypeIsIntegralOrI(tree)) + { + return tree; + } + + if ((tree->gtFlags & (GTF_PERSISTENT_SIDE_EFFECTS | GTF_ORDER_SIDEEFF)) != 0) + { + return tree; + } + + GenTree* op1 = tree->gtGetOp1(); + GenTree* op2 = tree->gtGetOp2(); + + auto isLeftDistributive = [](genTreeOps op1, genTreeOps op2) { + // op1 is left distributive over op2 iff: + // "A op1 (B op2 C)" <==> "(A op1 B) op2 (A op1 C)" + switch (op1) + { + case GT_AND: + return op2 == GT_OR || op2 == GT_XOR || op2 == GT_AND; + + case GT_OR: + return op2 == GT_AND || op2 == GT_OR; + + case GT_MUL: + return op2 == GT_ADD || op2 == GT_SUB; + + default: + return false; + } + }; + + if ((op1->OperGet() == op2->OperGet()) && isLeftDistributive(op1->OperGet(), tree->OperGet())) + { + if (op1->gtGetOp1()->OperIsAnyLocal() && op2->gtGetOp1()->OperIsAnyLocal()) + { + if (GenTree::Compare(op1->gtGetOp1(), op2->gtGetOp1())) + { + tree->AsOp()->gtOp1 = op1->gtGetOp1(); + tree->AsOp()->gtOp2 = + gtFoldExpr(gtNewOperNode(tree->OperGet(), tree->TypeGet(), op1->gtGetOp2(), op2->gtGetOp2())); + tree->SetOper(op1->OperGet(), GenTree::PRESERVE_VN); + + if (fgGlobalMorph) + { + fgMorphTreeDone(tree->gtGetOp2()); + } + } + } + } + + return tree; +} + //------------------------------------------------------------------------ // gtFoldIndirConst: Attempt to fold an "IND(addr)" expression to a constant. // diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 0a8073a84b3d23..465c8c6335095b 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -10440,7 +10440,7 @@ void Compiler::fgPushConstantsRight(GenTreeOp* tree) } //------------------------------------------------------------------------ -// fgOptimizeCommutativeArithmetic: Optimizes commutative operations. +// fgOptimizeCommutativeArithmetic: Optimizes commutative arithemtic. // // Arguments: // tree - the unchecked GT_ADD/GT_MUL/GT_OR/GT_XOR/GT_AND tree to optimize. diff --git a/src/coreclr/jit/optimizebools.cpp b/src/coreclr/jit/optimizebools.cpp index 54f2b5bc4203be..830c548c3807da 100644 --- a/src/coreclr/jit/optimizebools.cpp +++ b/src/coreclr/jit/optimizebools.cpp @@ -1198,6 +1198,12 @@ void OptBoolsDsc::optOptimizeBoolsUpdateTrees() GenTree* cmpOp1 = m_foldOp == GT_NONE ? m_c1 : m_compiler->gtNewOperNode(m_foldOp, m_foldType, m_c1, m_c2); + // There may be new opportunities for distributive arithmetic optimization + if (m_foldOp != GT_NONE) + { + cmpOp1 = m_compiler->gtFoldExpr(cmpOp1); + } + GenTree* t1Comp = m_testInfo1.compTree; t1Comp->SetOper(m_cmpOp); t1Comp->AsOp()->gtOp1 = cmpOp1;