Hi!

r17-126 has changed the simplification in a way where it can now
introduce signed integer overflow into a program which didn't have it before,
e.g. x != (INT_MAX - 3) ? x + 5 : (INT_MIN + 1) when x is int
doesn't invoke signed integer overflow for x equal to INT_MAX - 3 (it does
for various other values and for many doesn't), while the simplified
x + 5 invokes it even for that value.

The following patch fixes it by checking for overflow on the CST1 + CST2
addition, if TYPE_OVERFLOW_WRAPS or if CST1 + CST2 doesn't overflow,
it does what it previously did, otherwise rewrites x + CST2 into
unsigned.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-10-07  Jakub Jelinek  <[email protected]>

        PR tree-optimization/126459
        * match.pd (x != CST1 ? x + CST2 : CST3 -> x + CST2): If
        CST1 + CST2 overflows and type overflow doesn't wrap,
        simplify into corresponding unsigned addition.

        * gcc.dg/torture/pr126459.c: New test.

--- a/gcc/match.pd      2026-10-03 21:28:13.092320517 +0200
+++ b/gcc/match.pd      2026-10-06 21:51:21.966900769 +0200
@@ -5369,9 +5369,16 @@ (define_operator_list SYNC_FETCH_AND_AND
     tree cst1 = uniform_integer_cst_p (@1);
     tree cst2 = uniform_integer_cst_p (@3);
     tree cst3 = uniform_integer_cst_p (@4);
-  }
-  (if (wi::to_wide (cst1) + wi::to_wide (cst2) == wi::to_wide (cst3))
-   @2))))
+    wi::overflow_type ovf;
+   }
+   (if (wi::add (wi::to_wide (cst1), wi::to_wide (cst2),
+                TYPE_SIGN (TREE_TYPE (cst1)), &ovf) == wi::to_wide (cst3))
+    (if (!ovf || TYPE_OVERFLOW_WRAPS (TREE_TYPE (cst1)))
+     @2
+     (with {
+       tree utype = unsigned_type_for (type);
+      }
+      (convert (plus (convert:utype @0) (convert:utype @3)))))))))
 
 /* Simplifications of shift and rotates.  */
 
--- a/gcc/testsuite/gcc.dg/torture/pr126459.c   2026-10-06 22:05:01.595899608 
+0200
+++ b/gcc/testsuite/gcc.dg/torture/pr126459.c   2026-10-06 22:04:43.130147484 
+0200
@@ -0,0 +1,34 @@
+/* PR tree-optimization/126459 */
+/* { dg-do run } */
+
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-(INT_MAX) - 1)
+
+__attribute__((noipa)) int
+foo (int x)
+{
+  return (x != INT_MAX ? x + 1 : INT_MIN) > x;
+}
+
+__attribute__((noipa)) int
+bar (int x)
+{
+  return (x != INT_MIN ? x - 1 : INT_MAX) < x;
+}
+
+__attribute__((noipa)) int
+baz (int x)
+{
+  return (x != INT_MIN ? x + INT_MIN : 0) < 0;
+}
+
+int
+main ()
+{
+  if (foo (INT_MAX) != 0 || foo (1) != 1)
+    __builtin_abort ();
+  if (bar (INT_MIN) != 0 || bar (2) != 1)
+    __builtin_abort ();
+  if (baz (INT_MIN) != 0 || baz (3) != 1)
+    __builtin_abort ();
+}

        Jakub

Reply via email to