diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index d9e256c164e345..d71fd5ce8dd4e3 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6661,6 +6661,7 @@ class Compiler GenTree* fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node); GenTree* fgOptimizeHWIntrinsicAssociative(GenTreeHWIntrinsic* node); #endif // FEATURE_HW_INTRINSICS + GenTree* fgOptimizeDistributiveArithemtic(GenTreeOp* tree); GenTree* fgOptimizeCommutativeArithmetic(GenTreeOp* tree); GenTree* fgOptimizeRelationalComparisonWithCasts(GenTreeOp* cmp); GenTree* fgOptimizeAddition(GenTreeOp* add); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 9456da4e99c5fb..3a607ae6aac0eb 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7862,6 +7862,8 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA goto CM_OVF_OP; } + // TODO: Call fgOptimizeDistributiveArithemtic + fgOptimizeCommutativeArithmetic + if (!fgGlobalMorph) { break; @@ -8068,6 +8070,10 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA case GT_OR: case GT_XOR: case GT_AND: + if (oper == GT_ADD || oper == GT_OR || oper == GT_AND) + { + tree = fgOptimizeDistributiveArithemtic(tree->AsOp()); + } tree = fgOptimizeCommutativeArithmetic(tree->AsOp()); if (!tree->OperIsSimple()) { @@ -10269,7 +10275,73 @@ GenTree* Compiler::fgOptimizeHWIntrinsicAssociative(GenTreeHWIntrinsic* tree) #endif // FEATURE_HW_INTRINSICS //------------------------------------------------------------------------ -// fgOptimizeCommutativeArithmetic: Optimizes commutative operations. +// fgOptimizeDistributiveArithemtic: Optimizes distributive arithemtic. +// +// Arguments: +// tree - the unchecked GT_ADD/GT_SUB/GT_OR/GT_AND tree to optimize. +// +// Return Value: +// The unchanged tree or optimized tree with oper GT_MUL/GT_OR/GT_AND. +// +GenTree* Compiler::fgOptimizeDistributiveArithemtic(GenTreeOp* tree) +{ + assert(tree->OperIs(GT_ADD, GT_SUB, GT_OR, GT_AND)); + assert(!tree->gtOverflowEx()); + + if (opts.OptimizationDisabled()) + { + return tree; + } + + if (!varTypeIsIntegralOrI(tree)) + { + return tree; + } + + if (((tree->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) != 0) || ((tree->gtFlags & GTF_ORDER_SIDEEFF) != 0)) + { + return tree; + } + + GenTree* op1 = tree->gtGetOp1(); + GenTree* op2 = tree->gtGetOp2(); + + auto isDistributiveOver = [](genTreeOps op1, genTreeOps op2) { + // op1 is distributive over op2 iff: + // ((A op1 B) op2 (A op1 C)) <==> (A op1 (B op2 C)) + + switch (op1) + { + case GT_MUL: + return op2 == GT_ADD || op2 == GT_SUB; + + case GT_AND: + return op2 == GT_OR || op2 == GT_AND; + + case GT_OR: + return op2 == GT_AND || op2 == GT_OR; + + default: + return false; + } + }; + + if ((op1->OperGet() == op2->OperGet()) && isDistributiveOver(op1->OperGet(), tree->OperGet())) + { + if (GenTree::Compare(op1->gtGetOp1(), op2->gtGetOp1())) + { + tree->AsOp()->gtOp1 = op1->gtGetOp1(); + tree->AsOp()->gtOp2 = gtNewOperNode(tree->OperGet(), tree->TypeGet(), op1->gtGetOp2(), op2->gtGetOp2()); + tree->SetOper(op1->OperGet(), GenTree::PRESERVE_VN); + fgMorphTreeDone(tree->gtGetOp2()); + } + } + + return tree; +} + +//------------------------------------------------------------------------ +// fgOptimizeCommutativeArithmetic: Optimizes commutative arithemtic. // // Arguments: // tree - the unchecked GT_ADD/GT_MUL/GT_OR/GT_XOR/GT_AND tree to optimize.