diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index 08b7bf0368555a..f695ce5a73ae08 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -13324,6 +13324,56 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) /* No match - exit */ } + + // Skip optimization if non-NEG operand is constant. + // Both op1 and op2 are not constant because it was already checked above. + if (opts.OptimizationEnabled() && fgGlobalMorph && + (((op1->gtFlags & GTF_EXCEPT) == 0) || ((op2->gtFlags & GTF_EXCEPT) == 0))) + { + // a - -b = > a + b + // SUB(a, (NEG(b)) => ADD(a, b) + + if (!op1->OperIs(GT_NEG) && op2->OperIs(GT_NEG)) + { + // tree: SUB + // op1: a + // op2: NEG + // op2Child: b + + GenTree* op2Child = op2->AsOp()->gtOp1; // b + oper = GT_ADD; + tree->SetOper(oper, GenTree::PRESERVE_VN); + tree->AsOp()->gtOp2 = op2Child; + + DEBUG_DESTROY_NODE(op2); + + op2 = op2Child; + } + + // -a - -b = > b - a + // SUB(NEG(a), (NEG(b)) => SUB(b, a) + + if (op1->OperIs(GT_NEG) && op2->OperIs(GT_NEG)) + { + // tree: SUB + // op1: NEG + // op1Child: a + // op2: NEG + // op2Child: b + + GenTree* op1Child = op1->AsOp()->gtOp1; // a + GenTree* op2Child = op2->AsOp()->gtOp1; // b + tree->AsOp()->gtOp1 = op2Child; + tree->AsOp()->gtOp2 = op1Child; + + DEBUG_DESTROY_NODE(op1); + DEBUG_DESTROY_NODE(op2); + + op1 = op2Child; + op2 = op1Child; + } + } + break; #ifdef TARGET_ARM64 @@ -13533,6 +13583,57 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) } } } + + if (opts.OptimizationEnabled() && fgGlobalMorph && + (((op1->gtFlags & GTF_EXCEPT) == 0) || ((op2->gtFlags & GTF_EXCEPT) == 0))) + { + // - a + b = > b - a + // ADD((NEG(a), b) => SUB(b, a) + + // Skip optimization if non-NEG operand is constant. + if (op1->OperIs(GT_NEG) && !op2->OperIs(GT_NEG) && + !(op2->IsCnsIntOrI() && varTypeIsIntegralOrI(typ))) + { + // tree: ADD + // op1: NEG + // op2: b + // op1Child: a + + GenTree* op1Child = op1->AsOp()->gtOp1; // a + oper = GT_SUB; + tree->SetOper(oper, GenTree::PRESERVE_VN); + tree->AsOp()->gtOp1 = op2; + tree->AsOp()->gtOp2 = op1Child; + + DEBUG_DESTROY_NODE(op1); + + op1 = op2; + op2 = op1Child; + } + + // a + -b = > a - b + // ADD(a, (NEG(b)) => SUB(a, b) + + if (!op1->OperIs(GT_NEG) && op2->OperIs(GT_NEG)) + { + // a is non cosntant because it was already canonicalized to have + // variable on the left and constant on the right. + + // tree: ADD + // op1: a + // op2: NEG + // op2Child: b + + GenTree* op2Child = op2->AsOp()->gtOp1; // a + oper = GT_SUB; + tree->SetOper(oper, GenTree::PRESERVE_VN); + tree->AsOp()->gtOp2 = op2Child; + + DEBUG_DESTROY_NODE(op2); + + op2 = op2Child; + } + } } /* See if we can fold GT_MUL by const nodes */ else if (oper == GT_MUL && op2->IsCnsIntOrI() && !optValnumCSE_phase)