From 21fe5e3369c87a17e7ac1a278a230428cb490eeb Mon Sep 17 00:00:00 2001 From: BoyBaykiller Date: Wed, 6 May 2026 22:32:20 +0200 Subject: [PATCH 1/4] * call cmp transforms from new fgOptimizeCmp --- src/coreclr/jit/compiler.h | 9 ++- src/coreclr/jit/morph.cpp | 155 ++++++++++++++++++++----------------- 2 files changed, 89 insertions(+), 75 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 7f6426b6b7bf31..68414f65b7c54f 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6909,9 +6909,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* fgOptimizeCmpLtLeGeGtFullRangeConst(GenTreeOp* cmp); #if defined(FEATURE_HW_INTRINSICS) GenTree* fgMorphHWIntrinsic(GenTreeHWIntrinsic* tree); GenTree* fgMorphHWIntrinsicRequired(GenTreeHWIntrinsic* tree); @@ -6920,7 +6922,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 8ffff718ad2af3..45b09bc76dbf26 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7796,72 +7796,25 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA 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; } @@ -8767,16 +8720,75 @@ 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 (!varTypeIsIntegralOrI(cmp)) + { + return cmp; + } + + // TODO-CQ: This should be called for all comparisons + if (cmp->OperIs(GT_LT, GT_LE, GT_GE, GT_GT) && + (cmp->gtGetOp1()->OperIs(GT_CAST) || cmp->gtGetOp2()->OperIs(GT_CAST))) + { + cmp = fgOptimizeCmpWithCasts(cmp)->AsOp(); + } + + if (cmp->gtGetOp2()->IsIntegralConst()) + { + if (cmp->OperIs(GT_LT, GT_LE, GT_GE, GT_GT)) + { + cmp = fgOptimizeCmpLtLeGeGtWithConst(cmp)->AsOp(); + } + else if (cmp->OperIs(GT_EQ, GT_NE)) + { + cmp = fgOptimizeCmpEqNeWithConst(cmp)->AsOp(); + } + } + + if (cmp->OperIs(GT_LT, GT_LE, GT_GE, GT_GT) && 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 = fgOptimizeCmpLtLeGeGtFullRangeConst(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()); @@ -9053,13 +9065,13 @@ GenTree* Compiler::fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp) } //------------------------------------------------------------------------ -// fgOptimizeRelationalComparisonWithFullRangeConst: optimizes a comparison operation. +// fgOptimizeCmpLtLeGeGtFullRangeConst: 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. @@ -9068,7 +9080,7 @@ 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::fgOptimizeCmpLtLeGeGtFullRangeConst(GenTreeOp* cmp) { if (gtTreeHasSideEffects(cmp, GTF_SIDE_EFFECT)) { @@ -9178,13 +9190,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. @@ -9194,9 +9206,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(); @@ -10847,7 +10859,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 @@ -10872,7 +10884,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 @@ -10880,9 +10892,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(); @@ -10890,8 +10903,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; @@ -10927,6 +10938,8 @@ GenTree* Compiler::fgOptimizeRelationalComparisonWithCasts(GenTreeOp* cmp) return cmp; } + assert(genActualType(op1) == genActualType(op2)); + auto isUpperZero = [this](GenTree* op) { if (op->IsIntegralConst()) { From f078de49f579bd72cf10faccb42f63ba1bfa6dce Mon Sep 17 00:00:00 2001 From: BoyBaykiller Date: Fri, 8 May 2026 01:39:42 +0200 Subject: [PATCH 2/4] * try 'fix' TP --- src/coreclr/jit/morph.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 45b09bc76dbf26..0d115e41727223 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -8732,16 +8732,13 @@ GenTree* Compiler::fgOptimizeCmp(GenTreeOp* cmp) { assert(cmp->OperIsCmpCompare()); - if (!varTypeIsIntegralOrI(cmp)) + if (cmp->gtGetOp1()->OperIs(GT_CAST) || cmp->gtGetOp2()->OperIs(GT_CAST)) { - return cmp; - } - - // TODO-CQ: This should be called for all comparisons - if (cmp->OperIs(GT_LT, GT_LE, GT_GE, GT_GT) && - (cmp->gtGetOp1()->OperIs(GT_CAST) || cmp->gtGetOp2()->OperIs(GT_CAST))) - { - cmp = fgOptimizeCmpWithCasts(cmp)->AsOp(); + // TODO-CQ: Remove this check, should be called for all + if (!cmp->OperIs(GT_EQ, GT_NE)) + { + cmp = fgOptimizeCmpWithCasts(cmp)->AsOp(); + } } if (cmp->gtGetOp2()->IsIntegralConst()) @@ -8756,7 +8753,7 @@ GenTree* Compiler::fgOptimizeCmp(GenTreeOp* cmp) } } - if (cmp->OperIs(GT_LT, GT_LE, GT_GE, GT_GT) && opts.OptimizationEnabled() && fgGlobalMorph) + if (opts.OptimizationEnabled() && fgGlobalMorph && cmp->OperIs(GT_LT, GT_LE, GT_GE, GT_GT)) { // Normalize unsigned comparisons to signed if both operands a known to be never negative. if (cmp->IsUnsigned() && varTypeIsIntegral(cmp->gtGetOp1()) && cmp->gtGetOp1()->IsNeverNegative(this) && From 09787d27181cd594af3e624822a253be37c0ed01 Mon Sep 17 00:00:00 2001 From: BoyBaykiller Date: Fri, 8 May 2026 15:20:44 +0200 Subject: [PATCH 3/4] * see if this 'fixes' tp --- src/coreclr/jit/morph.cpp | 46 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 0d115e41727223..18f84391260a32 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -8732,42 +8732,42 @@ GenTree* Compiler::fgOptimizeCmp(GenTreeOp* cmp) { assert(cmp->OperIsCmpCompare()); - if (cmp->gtGetOp1()->OperIs(GT_CAST) || cmp->gtGetOp2()->OperIs(GT_CAST)) + if (cmp->OperIs(GT_EQ, GT_NE)) { - // TODO-CQ: Remove this check, should be called for all - if (!cmp->OperIs(GT_EQ, GT_NE)) + if (cmp->gtGetOp2()->IsIntegralConst()) { - cmp = fgOptimizeCmpWithCasts(cmp)->AsOp(); + cmp = fgOptimizeCmpEqNeWithConst(cmp)->AsOp(); } } - - if (cmp->gtGetOp2()->IsIntegralConst()) + else { - if (cmp->OperIs(GT_LT, GT_LE, GT_GE, GT_GT)) + if (cmp->gtGetOp1()->OperIs(GT_CAST) || cmp->gtGetOp2()->OperIs(GT_CAST)) { - cmp = fgOptimizeCmpLtLeGeGtWithConst(cmp)->AsOp(); - } - else if (cmp->OperIs(GT_EQ, GT_NE)) - { - cmp = fgOptimizeCmpEqNeWithConst(cmp)->AsOp(); + // TODO-CQ: Should be called for all comparisons + cmp = fgOptimizeCmpWithCasts(cmp)->AsOp(); } - } - if (opts.OptimizationEnabled() && fgGlobalMorph && cmp->OperIs(GT_LT, GT_LE, GT_GE, GT_GT)) - { - // 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)) + if (cmp->gtGetOp2()->IsIntegralConst()) { - cmp->ClearUnsigned(); + cmp = fgOptimizeCmpLtLeGeGtWithConst(cmp)->AsOp(); } - if (cmp->gtGetOp1()->IsIntegralConst() || cmp->gtGetOp2()->IsIntegralConst()) + if (opts.OptimizationEnabled() && fgGlobalMorph) { - GenTree* optTree = fgOptimizeCmpLtLeGeGtFullRangeConst(cmp); - if (optTree->OperIs(GT_CNS_INT)) + // 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)) { - return optTree; + cmp->ClearUnsigned(); + } + + if (cmp->gtGetOp1()->IsIntegralConst() || cmp->gtGetOp2()->IsIntegralConst()) + { + GenTree* optTree = fgOptimizeCmpLtLeGeGtFullRangeConst(cmp); + if (optTree->OperIs(GT_CNS_INT)) + { + return optTree; + } } } } From fa9c62d98292554b0f02d0e3adbdb09a9b2abdd2 Mon Sep 17 00:00:00 2001 From: BoyBaykiller Date: Fri, 8 May 2026 21:10:34 +0200 Subject: [PATCH 4/4] * rename to fgOptimizeCmpFullRangeConst --- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/morph.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 68414f65b7c54f..cc0d00ef08bceb 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6913,7 +6913,7 @@ class Compiler GenTree* fgOptimizeCmpWithCasts(GenTreeOp* cmp); GenTree* fgOptimizeCmpEqNeWithConst(GenTreeOp* cmp); GenTree* fgOptimizeCmpLtLeGeGtWithConst(GenTreeOp* cmp); - GenTree* fgOptimizeCmpLtLeGeGtFullRangeConst(GenTreeOp* cmp); + GenTree* fgOptimizeCmpFullRangeConst(GenTreeOp* cmp); #if defined(FEATURE_HW_INTRINSICS) GenTree* fgMorphHWIntrinsic(GenTreeHWIntrinsic* tree); GenTree* fgMorphHWIntrinsicRequired(GenTreeHWIntrinsic* tree); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 18f84391260a32..7c159e7a14a6f9 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -8763,7 +8763,7 @@ GenTree* Compiler::fgOptimizeCmp(GenTreeOp* cmp) if (cmp->gtGetOp1()->IsIntegralConst() || cmp->gtGetOp2()->IsIntegralConst()) { - GenTree* optTree = fgOptimizeCmpLtLeGeGtFullRangeConst(cmp); + GenTree* optTree = fgOptimizeCmpFullRangeConst(cmp); if (optTree->OperIs(GT_CNS_INT)) { return optTree; @@ -9062,7 +9062,7 @@ GenTree* Compiler::fgOptimizeCmpEqNeWithConst(GenTreeOp* cmp) } //------------------------------------------------------------------------ -// fgOptimizeCmpLtLeGeGtFullRangeConst: 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. @@ -9077,8 +9077,10 @@ GenTree* Compiler::fgOptimizeCmpEqNeWithConst(GenTreeOp* cmp) // Assumptions: // The second operand is an integral constant or the first operand is an integral constant. // -GenTree* Compiler::fgOptimizeCmpLtLeGeGtFullRangeConst(GenTreeOp* cmp) +GenTree* Compiler::fgOptimizeCmpFullRangeConst(GenTreeOp* cmp) { + assert(cmp->OperIsCmpCompare()); + if (gtTreeHasSideEffects(cmp, GTF_SIDE_EFFECT)) { return cmp;