From 8de9d19442da3411b3b163d634734dbc15545058 Mon Sep 17 00:00:00 2001 From: BoyBaykiller Date: Mon, 13 Apr 2026 20:43:45 +0200 Subject: [PATCH 1/2] * initial impl --- src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/morph.cpp | 61 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index d9e256c164e345..e303bb3dda67ad 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6662,6 +6662,7 @@ class Compiler GenTree* fgOptimizeHWIntrinsicAssociative(GenTreeHWIntrinsic* node); #endif // FEATURE_HW_INTRINSICS GenTree* fgOptimizeCommutativeArithmetic(GenTreeOp* tree); + GenTreeOp* fgOptimizeDistributiveArithemtic(GenTreeOp* tree); GenTree* fgOptimizeRelationalComparisonWithCasts(GenTreeOp* cmp); GenTree* fgOptimizeAddition(GenTreeOp* add); GenTree* fgOptimizeMultiply(GenTreeOp* mul); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 9456da4e99c5fb..f68620775f9e8f 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -10268,6 +10268,62 @@ GenTree* Compiler::fgOptimizeHWIntrinsicAssociative(GenTreeHWIntrinsic* tree) } #endif // FEATURE_HW_INTRINSICS +//------------------------------------------------------------------------ +// fgOptimizeDistributiveArithemtic: Optimizes distributive operations. +// +// Arguments: +// tree - the unchecked GT_ADD/GT_SUB/GT_OR/GT_AND tree to optimize. +// +// Return Value: +// The optimized tree that can have any shape. +// +GenTreeOp* Compiler::fgOptimizeDistributiveArithemtic(GenTreeOp* tree) +{ + assert(!tree->gtOverflowEx()); + + 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 op2 B) op1 (A op2 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; + + case GT_OR: + return op2 == GT_AND; + + default: + return false; + } + }; + + if ((op1->OperGet() == op2->OperGet()) && isDistributiveOver(op1->OperGet(), tree->OperGet()) && + varTypeIsIntegralOrI(tree)) + { + if (GenTree::Compare(op1->gtGetOp1(), op2->gtGetOp1())) + { + tree->AsOp()->gtOp1 = op1->gtGetOp1(); + tree->AsOp()->gtOp2 = gtNewOperNode(tree->OperGet(), op1->TypeGet(), op1->gtGetOp2(), op2->gtGetOp2()); + tree->SetOper(op1->OperGet(), GenTree::PRESERVE_VN); + fgMorphTreeDone(tree->gtGetOp2()); + } + } + + return tree; +} + //------------------------------------------------------------------------ // fgOptimizeCommutativeArithmetic: Optimizes commutative operations. // @@ -10315,6 +10371,11 @@ GenTree* Compiler::fgOptimizeCommutativeArithmetic(GenTreeOp* tree) } } + if (tree->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_OR)) + { + tree = fgOptimizeDistributiveArithemtic(tree); + } + GenTree* optimizedTree = nullptr; if (tree->OperIs(GT_ADD)) { From 1cf54f19942d9600d2dd9c780f8ddc6afa080f87 Mon Sep 17 00:00:00 2001 From: BoyBaykiller Date: Mon, 13 Apr 2026 23:57:27 +0200 Subject: [PATCH 2/2] * call from fgMorphSmpOp * add OR and AND are 'distributive' over themselves --- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/morph.cpp | 41 ++++++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index e303bb3dda67ad..d71fd5ce8dd4e3 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6661,8 +6661,8 @@ class Compiler GenTree* fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node); GenTree* fgOptimizeHWIntrinsicAssociative(GenTreeHWIntrinsic* node); #endif // FEATURE_HW_INTRINSICS + GenTree* fgOptimizeDistributiveArithemtic(GenTreeOp* tree); GenTree* fgOptimizeCommutativeArithmetic(GenTreeOp* tree); - GenTreeOp* fgOptimizeDistributiveArithemtic(GenTreeOp* tree); GenTree* fgOptimizeRelationalComparisonWithCasts(GenTreeOp* cmp); GenTree* fgOptimizeAddition(GenTreeOp* add); GenTree* fgOptimizeMultiply(GenTreeOp* mul); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index f68620775f9e8f..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,18 +10275,29 @@ GenTree* Compiler::fgOptimizeHWIntrinsicAssociative(GenTreeHWIntrinsic* tree) #endif // FEATURE_HW_INTRINSICS //------------------------------------------------------------------------ -// fgOptimizeDistributiveArithemtic: Optimizes distributive operations. +// fgOptimizeDistributiveArithemtic: Optimizes distributive arithemtic. // // Arguments: // tree - the unchecked GT_ADD/GT_SUB/GT_OR/GT_AND tree to optimize. // // Return Value: -// The optimized tree that can have any shape. +// The unchanged tree or optimized tree with oper GT_MUL/GT_OR/GT_AND. // -GenTreeOp* Compiler::fgOptimizeDistributiveArithemtic(GenTreeOp* tree) +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; @@ -10291,7 +10308,7 @@ GenTreeOp* Compiler::fgOptimizeDistributiveArithemtic(GenTreeOp* tree) auto isDistributiveOver = [](genTreeOps op1, genTreeOps op2) { // op1 is distributive over op2 iff: - // ((A op2 B) op1 (A op2 C)) <==> (A op1 (B op2 C)) + // ((A op1 B) op2 (A op1 C)) <==> (A op1 (B op2 C)) switch (op1) { @@ -10299,23 +10316,22 @@ GenTreeOp* Compiler::fgOptimizeDistributiveArithemtic(GenTreeOp* tree) return op2 == GT_ADD || op2 == GT_SUB; case GT_AND: - return op2 == GT_OR; + return op2 == GT_OR || op2 == GT_AND; case GT_OR: - return op2 == GT_AND; + return op2 == GT_AND || op2 == GT_OR; default: return false; } }; - if ((op1->OperGet() == op2->OperGet()) && isDistributiveOver(op1->OperGet(), tree->OperGet()) && - varTypeIsIntegralOrI(tree)) + 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(), op1->TypeGet(), op1->gtGetOp2(), op2->gtGetOp2()); + tree->AsOp()->gtOp2 = gtNewOperNode(tree->OperGet(), tree->TypeGet(), op1->gtGetOp2(), op2->gtGetOp2()); tree->SetOper(op1->OperGet(), GenTree::PRESERVE_VN); fgMorphTreeDone(tree->gtGetOp2()); } @@ -10325,7 +10341,7 @@ GenTreeOp* Compiler::fgOptimizeDistributiveArithemtic(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. @@ -10371,11 +10387,6 @@ GenTree* Compiler::fgOptimizeCommutativeArithmetic(GenTreeOp* tree) } } - if (tree->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_OR)) - { - tree = fgOptimizeDistributiveArithemtic(tree); - } - GenTree* optimizedTree = nullptr; if (tree->OperIs(GT_ADD)) {