diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index ea235f6ea230a9..c7d6c50bf3b0b5 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -2604,7 +2604,9 @@ GenTree* Compiler::optVNBasedFoldExpr(BasicBlock* block, GenTree* parent, GenTre { case GT_CALL: return optVNBasedFoldExpr_Call(block, parent, tree->AsCall()); - + case GT_ADD: + case GT_SUB: + return optVNBasedFoldAddOrSubExpr(block, parent, tree); // We can add more VN-based foldings here. default: @@ -2613,6 +2615,78 @@ GenTree* Compiler::optVNBasedFoldExpr(BasicBlock* block, GenTree* parent, GenTre return nullptr; } +//------------------------------------------------------------------------------ +// optVNBasedFoldAddOrSubExpr: reduce successive variable adds/subs into a single multiply. +// e.g., i + i + i + i => i * 4. +// e.g., i - i - i - i => - i * 2. +// +// Arguments: +// block - The block containing the tree. +// parent - The parent node of the tree. +// tree - The ADD/SUB tree. +// +// Return Value: +// Returns a potentially new or a transformed tree node. +// Returns nullptr when no transformation is possible. +// +// +GenTree* Compiler::optVNBasedFoldAddOrSubExpr(BasicBlock* block, GenTree* parent, GenTree* tree) +{ + // ADD(_, V0) OR SUB(_, V0) starts the pattern match. + if (!tree->OperIs(GT_ADD, GT_SUB) || tree->gtOverflow()) + { + return nullptr; + } + + genTreeOps targetOp = tree->OperGet(); +#if !defined(TARGET_64BIT) && !defined(TARGET_WASM) + // Transforming 64-bit ADD/SUB to 64-bit MUL on 32-bit system results in replacing + // ADD/SUB ops with a helper function call. Don't apply optimization in that case. + if (tree->TypeIs(TYP_LONG)) + { + return nullptr; + } +#endif // !defined(TARGET_64BIT) && !defined(TARGET_WASM) + + GenTree* lclVarTree = tree->AsOp()->gtOp2; + GenTree* consTree = tree->AsOp()->gtOp1; + + GenTree* op1 = consTree; + GenTree* op2 = lclVarTree; + + if (!op2->OperIs(GT_LCL_VAR) || !varTypeIsIntegral(op2)) + { + return nullptr; + } + + unsigned lclNum = op2->AsLclVarCommon()->GetLclNum(); + + if ((op1->OperIs(GT_LCL_VAR) && op1->AsLclVarCommon()->GetLclNum() == lclNum) || + (op1->OperIs(GT_COMMA) && op1->AsOp()->gtOp1->OperIs(GT_STORE_LCL_VAR) && + op1->AsOp()->gtOp1->AsLclVar()->GetLclNum() == lclNum)) + { + return gtNewOperNode(GT_MUL, tree->TypeGet(), consTree, + gtNewIconNode(targetOp == GT_ADD ? 2 : 0, tree->TypeGet())); + } + else if (targetOp == GT_SUB && op1->OperIs(GT_CNS_INT) && op1->AsIntCon()->gtIconVal == 0) + { + return gtNewOperNode(GT_MUL, tree->TypeGet(), op2, gtNewIconNode(-1, tree->TypeGet())); + } + else if (op1->OperIs(GT_MUL) && + (((op1->AsOp()->gtOp1->OperIs(GT_LCL_VAR) && + op1->AsOp()->gtOp1->AsLclVarCommon()->GetLclNum() == lclNum) || + (op1->AsOp()->gtOp1->OperIs(GT_COMMA) && op1->AsOp()->gtOp1->AsOp()->gtOp1->OperIs(GT_STORE_LCL_VAR) && + op1->AsOp()->gtOp1->AsOp()->gtOp1->AsLclVar()->GetLclNum() == lclNum)) && + op1->AsOp()->gtOp2->OperIs(GT_CNS_INT))) + { + return gtNewOperNode(GT_MUL, tree->TypeGet(), op1->AsOp()->gtOp1, + gtNewIconNode(op1->AsOp()->gtOp2->AsIntCon()->gtIconVal + (targetOp == GT_ADD ? 1 : -1), + tree->TypeGet())); + } + + return nullptr; +} + //------------------------------------------------------------------------------ // optVNBasedFoldConstExpr: Substitutes tree with an evaluated constant while // managing side-effects. diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index a0c79914d7b328..0da91a03ba15fd 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6787,8 +6787,6 @@ class Compiler GenTreeOp* fgMorphCommutative(GenTreeOp* tree); - GenTree* fgMorphReduceAddOps(GenTree* tree); - public: GenTree* fgMorphTree(GenTree* tree, MorphAddrContext* mac = nullptr); @@ -8568,6 +8566,7 @@ class Compiler GenTree* optVNBasedFoldExpr_Call(BasicBlock* block, GenTree* parent, GenTreeCall* call); GenTree* optVNBasedFoldExpr_Call_Memmove(GenTreeCall* call); GenTree* optVNBasedFoldExpr_Call_Memset(GenTreeCall* call); + GenTree* optVNBasedFoldAddOrSubExpr(BasicBlock* block, GenTree* parent, GenTree* tree); AssertionIndex GetAssertionCount() { diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 7360b98dfc6483..4f2a89c2e8022d 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7521,13 +7521,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA break; } - if (opts.OptimizationEnabled() && fgGlobalMorph) - { - GenTree* morphed = fgMorphReduceAddOps(tree); - if (morphed != tree) - return fgMorphTree(morphed); - } - /*------------------------------------------------------------------------- * Process the first operand, if any */ @@ -15446,82 +15439,6 @@ bool Compiler::fgCanTailCallViaJitHelper(GenTreeCall* call) #endif } -//------------------------------------------------------------------------ -// fgMorphReduceAddOps: reduce successive variable adds into a single multiply, -// e.g., i + i + i + i => i * 4. -// -// Arguments: -// tree - tree for reduction -// -// Return Value: -// reduced tree if pattern matches, original tree otherwise -// -GenTree* Compiler::fgMorphReduceAddOps(GenTree* tree) -{ - // ADD(_, V0) starts the pattern match. - if (!tree->OperIs(GT_ADD) || tree->gtOverflow()) - { - return tree; - } - -#if !defined(TARGET_64BIT) && !defined(TARGET_WASM) - // Transforming 64-bit ADD to 64-bit MUL on 32-bit system results in replacing - // ADD ops with a helper function call. Don't apply optimization in that case. - if (tree->TypeIs(TYP_LONG)) - { - return tree; - } -#endif // !defined(TARGET_64BIT) && !defined(TARGET_WASM) - - GenTree* lclVarTree = tree->AsOp()->gtOp2; - GenTree* consTree = tree->AsOp()->gtOp1; - - GenTree* op1 = consTree; - GenTree* op2 = lclVarTree; - - if (!op2->OperIs(GT_LCL_VAR) || !varTypeIsIntegral(op2)) - { - return tree; - } - - int foldCount = 0; - unsigned lclNum = op2->AsLclVarCommon()->GetLclNum(); - - // Search for pattern of shape ADD(ADD(ADD(lclNum, lclNum), lclNum), lclNum). - while (true) - { - // ADD(lclNum, lclNum), end of tree - if (op1->OperIs(GT_LCL_VAR) && op1->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_LCL_VAR) && - op2->AsLclVarCommon()->GetLclNum() == lclNum) - { - foldCount += 2; - break; - } - // ADD(ADD(X, Y), lclNum), keep descending - else if (op1->OperIs(GT_ADD) && !op1->gtOverflow() && op2->OperIs(GT_LCL_VAR) && - op2->AsLclVarCommon()->GetLclNum() == lclNum) - { - foldCount++; - op2 = op1->AsOp()->gtOp2; - op1 = op1->AsOp()->gtOp1; - } - // Any other case is a pattern we won't attempt to fold for now. - else - { - return tree; - } - } - - // V0 + V0 ... + V0 becomes V0 * foldCount, where postorder transform will optimize - // accordingly - consTree->BashToConst(foldCount, tree->TypeGet()); - - GenTree* morphed = gtNewOperNode(GT_MUL, tree->TypeGet(), lclVarTree, consTree); - DEBUG_DESTROY_NODE(tree); - - return morphed; -} - //------------------------------------------------------------------------ // Compiler::MorphMDArrayTempCache::TempList::GetTemp: return a local variable number to use as a temporary variable // in multi-dimensional array operation expansion.