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" } } */

Reply via email to