https://gcc.gnu.org/g:33cd7bbb32c5eee4aff7aa06f351aa983be419bc
commit r17-669-g33cd7bbb32c5eee4aff7aa06f351aa983be419bc Author: Avinal Kumar <[email protected]> Date: Thu May 21 15:54:12 2026 +0530 match: Handle X != INT_MIN ? -X : INT_MIN [PR125050] The pattern X != C1 ? -X : C2 currently bails out when C1 is INT_MIN and the type doesn't wrap, because a signed negation of INT_MIN is undefined behavior. But the whole expression is well-defined: it is equivalent to (signed)(-(unsigned)X). Handle the wi::only_sign_bit_p case by emitting an unsigned negate instead of giving up, mirroring what the abs pattern already does for the same edge case. PR tree-optimization/125050 gcc/ChangeLog: * match.pd: (X != C1 ? -X : C2): Handle C1 being INT_MIN by emitting (signed)(-(unsigned)X) instead of bailing out. gcc/testsuite/ChangeLog: * gcc.dg/fold-condneg-2.c: Update expected optimization. * gcc.dg/pr125050.c: New test. * gcc.dg/tree-ssa/phi-opt-50.c: New test. * gcc.dg/tree-ssa/phi-opt-51.c: New test. Signed-off-by: Avinal Kumar <[email protected]> Diff: --- gcc/match.pd | 12 ++++++++---- gcc/testsuite/gcc.dg/fold-condneg-2.c | 4 +++- gcc/testsuite/gcc.dg/pr125050.c | 13 +++++++++++++ gcc/testsuite/gcc.dg/tree-ssa/phi-opt-50.c | 21 +++++++++++++++++++++ gcc/testsuite/gcc.dg/tree-ssa/phi-opt-51.c | 21 +++++++++++++++++++++ 5 files changed, 66 insertions(+), 5 deletions(-) diff --git a/gcc/match.pd b/gcc/match.pd index 195d10c80c73..c6272c1e24ea 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -6755,14 +6755,18 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) ) #endif -/* X != C1 ? -X : C2 simplifies to -X when -C1 == C2. */ +/* X != C1 ? -X : C2 simplifies to -X when -C1 == C2. Additionally, + when C1 is the minimum signed value (e.g. INT_MIN), -X would be + undefined for signed types, so emit (signed)(-(unsigned)X) instead. */ (simplify (cond (ne @0 INTEGER_CST@1) (negate@3 @0) INTEGER_CST@2) (if (!TYPE_SATURATING (type) - && (TYPE_OVERFLOW_WRAPS (type) - || !wi::only_sign_bit_p (wi::to_wide (@1))) && wi::eq_p (wi::neg (wi::to_wide (@1)), wi::to_wide (@2))) - @3)) + (if (TYPE_OVERFLOW_WRAPS (type) + || !wi::only_sign_bit_p (wi::to_wide (@1))) + @3 + (with { tree utype = unsigned_type_for (TREE_TYPE (@0)); } + (convert (negate (convert:utype @0))))))) /* X != C1 ? ~X : C2 simplifies to ~X when ~C1 == C2. */ (simplify diff --git a/gcc/testsuite/gcc.dg/fold-condneg-2.c b/gcc/testsuite/gcc.dg/fold-condneg-2.c index 1af24636ec76..932f883a6e27 100644 --- a/gcc/testsuite/gcc.dg/fold-condneg-2.c +++ b/gcc/testsuite/gcc.dg/fold-condneg-2.c @@ -8,4 +8,6 @@ int test(int x) return x != INT_MIN ? -x : INT_MIN; } -/* { dg-final { scan-tree-dump "goto" "optimized" } } */ +/* The branch should now be eliminated since we emit + (signed)(-(unsigned)x) which is well-defined. */ +/* { dg-final { scan-tree-dump-not "goto" "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/pr125050.c b/gcc/testsuite/gcc.dg/pr125050.c new file mode 100644 index 000000000000..5f2009a7b4de --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr125050.c @@ -0,0 +1,13 @@ +/* PR tree-optimization/125050 */ +/* { dg-do compile } */ +/* { dg-options "-O1 -fdump-tree-phiopt2" } */ + +int f(int a) +{ + if (a == -__INT_MAX__ - 1) + return -__INT_MAX__ - 1; + return -a; +} + +/* This should be converted to (int)(-(unsigned)a). */ +/* { dg-final { scan-tree-dump-not "if " "phiopt2" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-50.c b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-50.c new file mode 100644 index 000000000000..3cfb768c1d95 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-50.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-O1 -fdump-tree-phiopt2-raw" } */ + +/* a == INT_MIN ? INT_MIN : -a -> (int)(-(unsigned)a) */ +int f1(int a) +{ + if (a == -__INT_MAX__ - 1) + return -__INT_MAX__ - 1; + return -a; +} + +/* a != INT_MIN ? -a : INT_MIN -> (int)(-(unsigned)a) */ +int f2(int a) +{ + if (a != -__INT_MAX__ - 1) + return -a; + return -__INT_MAX__ - 1; +} + +/* { dg-final { scan-tree-dump-times "negate_expr" 2 "phiopt2" } } */ +/* { dg-final { scan-tree-dump-not "gimple_cond <" "phiopt2" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-51.c b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-51.c new file mode 100644 index 000000000000..6c5809f6ddf1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-51.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-O1 -fdump-tree-phiopt2-raw" } */ + +/* Try the same pattern with different integer types. */ + +long f_long(long a) +{ + if (a == -__LONG_MAX__ - 1) + return -__LONG_MAX__ - 1; + return -a; +} + +long long f_ll(long long a) +{ + if (a == -__LONG_LONG_MAX__ - 1) + return -__LONG_LONG_MAX__ - 1; + return -a; +} + +/* { dg-final { scan-tree-dump-times "negate_expr" 2 "phiopt2" } } */ +/* { dg-final { scan-tree-dump-not "gimple_cond <" "phiopt2" } } */
