diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 9568aceac47f88..587c7b829acb88 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7125,9 +7125,11 @@ class Compiler GenTree* fgOptimizeCast(GenTreeCast* cast); GenTree* fgOptimizeCastOnStore(GenTree* store); GenTree* fgOptimizeBitCast(GenTreeUnOp* bitCast); - GenTree* fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp); - GenTree* fgOptimizeRelationalComparisonWithConst(GenTreeOp* cmp); - GenTree* fgOptimizeRelationalComparisonWithFullRangeConst(GenTreeOp* cmp); + GenTree* fgOptimizeCmp(GenTreeOp* cmp); + GenTree* fgOptimizeCmpWithCasts(GenTreeOp* cmp); + GenTree* fgOptimizeCmpEqNeWithConst(GenTreeOp* cmp); + GenTree* fgOptimizeCmpLtLeGeGtWithConst(GenTreeOp* cmp); + GenTree* fgOptimizeCmpFullRangeConst(GenTreeOp* cmp); #if defined(FEATURE_HW_INTRINSICS) GenTree* fgMorphHWIntrinsic(GenTreeHWIntrinsic* tree); GenTree* fgMorphHWIntrinsicRequired(GenTreeHWIntrinsic* tree); @@ -7136,7 +7138,6 @@ class Compiler GenTree* fgOptimizeHWIntrinsicAssociative(GenTreeHWIntrinsic* node); #endif // FEATURE_HW_INTRINSICS GenTree* fgOptimizeCommutativeArithmetic(GenTreeOp* tree); - GenTree* fgOptimizeRelationalComparisonWithCasts(GenTreeOp* cmp); GenTree* fgOptimizeAddition(GenTreeOp* add); GenTree* fgOptimizeMultiply(GenTreeOp* mul); GenTree* fgOptimizeBitwiseAnd(GenTreeOp* andOp); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 0a8073a84b3d23..3735ce0724e3d8 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7852,72 +7852,25 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, bool* optAssertionPropDone) case GT_EQ: case GT_NE: - { - fgPushConstantsRight(tree->AsOp()); - assert(tree->OperIsCompare()); - - oper = tree->OperGet(); - op1 = tree->gtGetOp1(); - op2 = tree->gtGetOp2(); - - if (op2->IsIntegralConst()) - { - tree = fgOptimizeEqualityComparisonWithConst(tree->AsOp()); - assert(tree->OperIsCompare()); - - oper = tree->OperGet(); - op1 = tree->gtGetOp1(); - op2 = tree->gtGetOp2(); - } - break; - } - case GT_LT: case GT_LE: case GT_GE: case GT_GT: { fgPushConstantsRight(tree->AsOp()); - assert(tree->OperIsCompare()); - - oper = tree->OperGet(); - op1 = tree->gtGetOp1(); - op2 = tree->gtGetOp2(); - - if (op1->OperIs(GT_CAST) || op2->OperIs(GT_CAST)) - { - tree = fgOptimizeRelationalComparisonWithCasts(tree->AsOp()); - oper = tree->OperGet(); - op1 = tree->gtGetOp1(); - op2 = tree->gtGetOp2(); - } + assert(tree->OperIsCmpCompare()); - if (op2->IsIntegralConst()) + tree = fgOptimizeCmp(tree->AsOp()); + if (!tree->OperIsSimple()) { - tree = fgOptimizeRelationalComparisonWithConst(tree->AsOp()); - oper = tree->OperGet(); - op1 = tree->gtGetOp1(); - op2 = tree->gtGetOp2(); + return tree; } - if (opts.OptimizationEnabled() && fgGlobalMorph) - { - // Normalize unsigned comparisons to signed if both operands a known to be never negative. - if (tree->IsUnsigned() && varTypeIsIntegral(op1) && op1->IsNeverNegative(this) && - op2->IsNeverNegative(this)) - { - tree->ClearUnsigned(); - } + typ = tree->TypeGet(); + oper = tree->OperGet(); + op1 = tree->gtGetOp1(); + op2 = tree->gtGetOp2(); - if (op2->IsIntegralConst() || op1->IsIntegralConst()) - { - tree = fgOptimizeRelationalComparisonWithFullRangeConst(tree->AsOp()); - if (tree->OperIs(GT_CNS_INT)) - { - return tree; - } - } - } break; } @@ -8823,16 +8776,72 @@ GenTree* Compiler::fgOptimizeBitCast(GenTreeUnOp* bitCast) } //------------------------------------------------------------------------ -// fgOptimizeEqualityComparisonWithConst: optimizes various EQ/NE(OP, CONST) patterns. +// fgOptimizeCmp: Optimizes the GT_EQ/GT_NE/GT_LT/GT_LE/GT_GE/GT_GT tree // // Arguments: -// cmp - The GT_NE/GT_EQ tree the second operand of which is an integral constant +// cmp - The compare tree +// +// Return Value: +// The optimized tree that can have any shape. +// +GenTree* Compiler::fgOptimizeCmp(GenTreeOp* cmp) +{ + assert(cmp->OperIsCmpCompare()); + + if (cmp->OperIs(GT_EQ, GT_NE)) + { + if (cmp->gtGetOp2()->IsIntegralConst()) + { + cmp = fgOptimizeCmpEqNeWithConst(cmp)->AsOp(); + } + } + else + { + if (cmp->gtGetOp1()->OperIs(GT_CAST) || cmp->gtGetOp2()->OperIs(GT_CAST)) + { + // TODO-CQ: Should be called for all comparisons + cmp = fgOptimizeCmpWithCasts(cmp)->AsOp(); + } + + if (cmp->gtGetOp2()->IsIntegralConst()) + { + cmp = fgOptimizeCmpLtLeGeGtWithConst(cmp)->AsOp(); + } + + if (opts.OptimizationEnabled() && fgGlobalMorph) + { + // Normalize unsigned comparisons to signed if both operands a known to be never negative. + if (cmp->IsUnsigned() && varTypeIsIntegral(cmp->gtGetOp1()) && cmp->gtGetOp1()->IsNeverNegative(this) && + cmp->gtGetOp2()->IsNeverNegative(this)) + { + cmp->ClearUnsigned(); + } + + if (cmp->gtGetOp1()->IsIntegralConst() || cmp->gtGetOp2()->IsIntegralConst()) + { + GenTree* optTree = fgOptimizeCmpFullRangeConst(cmp); + if (optTree->OperIs(GT_CNS_INT)) + { + return optTree; + } + } + } + } + + return cmp; +} + +//------------------------------------------------------------------------ +// fgOptimizeCmpEqNeWithConst: optimizes various EQ/NE(OP, CONST) patterns. +// +// Arguments: +// cmp - The GT_EQ/GT_NE tree the second operand of which is an integral constant // // Return Value: // The optimized tree, "cmp" in case no optimizations were done. // Currently only returns relop trees. // -GenTree* Compiler::fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp) +GenTree* Compiler::fgOptimizeCmpEqNeWithConst(GenTreeOp* cmp) { assert(cmp->OperIs(GT_EQ, GT_NE)); assert(cmp->gtGetOp2()->IsIntegralConst()); @@ -9109,13 +9118,13 @@ GenTree* Compiler::fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp) } //------------------------------------------------------------------------ -// fgOptimizeRelationalComparisonWithFullRangeConst: optimizes a comparison operation. +// fgOptimizeCmpFullRangeConst: optimizes a comparison operation. // // Recognizes "Always false"/"Always true" comparisons against various full range constant operands and morphs // them into zero/one. // // Arguments: -// cmp - the GT_LT/GT_GT tree to morph. +// cmp - the GT_LT/GT_LE/GT_GE/GT_GT tree to morph. // // Return Value: // 1. The unmodified "cmp" tree. @@ -9124,8 +9133,10 @@ GenTree* Compiler::fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp) // Assumptions: // The second operand is an integral constant or the first operand is an integral constant. // -GenTree* Compiler::fgOptimizeRelationalComparisonWithFullRangeConst(GenTreeOp* cmp) +GenTree* Compiler::fgOptimizeCmpFullRangeConst(GenTreeOp* cmp) { + assert(cmp->OperIsCmpCompare()); + if (gtTreeHasSideEffects(cmp, GTF_SIDE_EFFECT)) { return cmp; @@ -9234,13 +9245,13 @@ GenTree* Compiler::fgOptimizeRelationalComparisonWithFullRangeConst(GenTreeOp* c } //------------------------------------------------------------------------ -// fgOptimizeRelationalComparisonWithConst: optimizes a comparison operation. +// fgOptimizeCmpLtLeGeGtWithConst: optimizes a comparison operation. // // Recognizes comparisons against various constant operands and morphs // them, if possible, into comparisons against zero. // // Arguments: -// cmp - the GT_LE/GT_LT/GT_GE/GT_GT tree to morph. +// cmp - the GT_LT/GT_LE/GT_GE/GT_GT tree to morph. // // Return Value: // The "cmp" tree, possibly with a modified oper. @@ -9250,9 +9261,9 @@ GenTree* Compiler::fgOptimizeRelationalComparisonWithFullRangeConst(GenTreeOp* c // The operands have been swapped so that any constants are on the right. // The second operand is an integral constant. // -GenTree* Compiler::fgOptimizeRelationalComparisonWithConst(GenTreeOp* cmp) +GenTree* Compiler::fgOptimizeCmpLtLeGeGtWithConst(GenTreeOp* cmp) { - assert(cmp->OperIs(GT_LE, GT_LT, GT_GE, GT_GT)); + assert(cmp->OperIs(GT_LT, GT_LE, GT_GE, GT_GT)); assert(cmp->gtGetOp2()->IsIntegralConst()); GenTree* op1 = cmp->gtGetOp1(); @@ -10903,7 +10914,7 @@ GenTree* Compiler::fgOptimizeBitwiseAnd(GenTreeOp* andOp) } //------------------------------------------------------------------------ -// fgOptimizeRelationalComparisonWithCasts: Recognizes comparisons against +// fgOptimizeCmpWithCasts: Recognizes comparisons against // various cast operands and tries to remove them. E.g.: // // * GE int @@ -10928,7 +10939,7 @@ GenTree* Compiler::fgOptimizeBitwiseAnd(GenTreeOp* andOp) // These patterns quite often show up along with index checks // // Arguments: -// cmp - the GT_LE/GT_LT/GT_GE/GT_GT tree to morph. +// cmp - the GT_EQ/GT_NE/GT_LT/GT_LE/GT_GE/GT_GT tree to morph. // // Return Value: // Returns the same tree where operands might have narrower types @@ -10936,9 +10947,10 @@ GenTree* Compiler::fgOptimizeBitwiseAnd(GenTreeOp* andOp) // Notes: // TODO-Casts: consider unifying this function with "optNarrowTree" // -GenTree* Compiler::fgOptimizeRelationalComparisonWithCasts(GenTreeOp* cmp) +GenTree* Compiler::fgOptimizeCmpWithCasts(GenTreeOp* cmp) { - assert(cmp->OperIs(GT_LE, GT_LT, GT_GE, GT_GT)); + assert(cmp->OperIsCmpCompare()); + assert(cmp->gtGetOp1()->OperIs(GT_CAST) || cmp->gtGetOp2()->OperIs(GT_CAST)); GenTree* op1 = cmp->gtGetOp1(); GenTree* op2 = cmp->gtGetOp2(); @@ -10946,8 +10958,6 @@ GenTree* Compiler::fgOptimizeRelationalComparisonWithCasts(GenTreeOp* cmp) // Caller is expected to call this function only if we have at least one CAST node assert(op1->OperIs(GT_CAST) || op2->OperIs(GT_CAST)); - assert(genActualType(op1) == genActualType(op2)); - if (!op1->TypeIs(TYP_LONG)) { return cmp; @@ -10983,6 +10993,8 @@ GenTree* Compiler::fgOptimizeRelationalComparisonWithCasts(GenTreeOp* cmp) return cmp; } + assert(genActualType(op1) == genActualType(op2)); + auto isUpperZero = [this](GenTree* op) { if (op->IsIntegralConst()) {