https://gcc.gnu.org/g:6ac8df7a4992dc7c5eec1217808bea31c085ec74

commit r17-5259-g6ac8df7a4992dc7c5eec1217808bea31c085ec74
Author: Kyrylo Tkachov <[email protected]>
Date:   Thu Oct 15 19:36:08 2026 +0200

    match-sat-alu.pd: Check the .ADD_OVERFLOW type of SAT_U_ADD [PR127760]
    
    r17-4076 added unsigned_integer_sat_add forms that select a separate
    X + Y, but did not check the type of the .ADD_OVERFLOW result.  Its
    overflow flag describes that type, which can differ from the type of
    X + Y:
    
      unsigned
      u_sat (unsigned a, unsigned b)
      {
        unsigned char r;
        if (__builtin_add_overflow (a, b, &r))
          return -1u;
        return a + b;
      }
    
      __complex__ unsigned char _5;
      ...
      _5 = .ADD_OVERFLOW (a_3(D), b_4(D));
      _1 = IMAGPART_EXPR <_5>;
      if (_1 != 0)
      ...
      _6 = a_3(D) + b_4(D);
      # _2 = PHI <4294967295(2), _6(3)>
    
    widening_mul replaces the PHI with a 32-bit .SAT_ADD (a_3(D), b_4(D)), so
    u_sat (200, 100) returns 300 instead of UINT_MAX.  The vectorizer uses the
    same matcher, so loops of this form also became wrong vector .SAT_ADDs.
    
    Require the parts of the .ADD_OVERFLOW result to have the type of the
    addition.  The other unsigned .ADD_OVERFLOW forms select REALPART of the
    result, which already ties the two types.
    
    Bootstrapped and tested on aarch64-unknown-linux-gnu and
    x86_64-pc-linux-gnu.
    
            PR tree-optimization/127760
    
    gcc/ChangeLog:
    
            * match-sat-alu.pd (unsigned_integer_sat_add): Check the type of the
            .ADD_OVERFLOW result in the forms that select a separate X + Y.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/torture/pr127760.c: New test.
    
    Signed-off-by: Kyrylo Tkachov <[email protected]>

Diff:
---
 gcc/match-sat-alu.pd                    | 12 ++++--
 gcc/testsuite/gcc.dg/torture/pr127760.c | 69 +++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+), 4 deletions(-)

diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd
index c79f8986ff50..4b04e6097505 100644
--- a/gcc/match-sat-alu.pd
+++ b/gcc/match-sat-alu.pd
@@ -111,16 +111,20 @@ along with GCC; see the file COPYING3.  If not see
   (cond^ (ne (imagpart (IFN_ADD_OVERFLOW@2 @0 INTEGER_CST@1)) integer_zerop)
         integer_minus_onep (realpart @2))
   (if (types_match (type, @0) && int_fits_type_p (@1, type))))
+ /* The overflow flag of SUM describes X + Y only when the parts of SUM have
+    the type of X + Y.  */
  (match (unsigned_integer_sat_add @0 @1)
   /* SUM = ADD_OVERFLOW (X, Y)
      SAT_U_ADD = IMAGPART (SUM) == 0 ? X + Y : -1.  */
-  (cond^ (eq (imagpart (IFN_ADD_OVERFLOW @0 @1)) integer_zerop)
-        (plus @0 @1) integer_minus_onep))
+  (cond^ (eq (imagpart (IFN_ADD_OVERFLOW@2 @0 @1)) integer_zerop)
+        (plus @0 @1) integer_minus_onep)
+  (if (types_match (type, TREE_TYPE (TREE_TYPE (@2))))))
  (match (unsigned_integer_sat_add @0 @1)
   /* SUM = ADD_OVERFLOW (X, Y)
      SAT_U_ADD = IMAGPART (SUM) != 0 ? -1 : X + Y.  */
-  (cond^ (ne (imagpart (IFN_ADD_OVERFLOW @0 @1)) integer_zerop)
-        integer_minus_onep (plus @0 @1)))
+  (cond^ (ne (imagpart (IFN_ADD_OVERFLOW@2 @0 @1)) integer_zerop)
+        integer_minus_onep (plus @0 @1))
+  (if (types_match (type, TREE_TYPE (TREE_TYPE (@2))))))
  /* A source that tests for overflow against the complement of an operand,
     such as a > MAX - b ? MAX : a + b, reaches the recognisers as a compare
     with ~Y, since ~Y is MAX - Y.  The strict and non-strict forms are both
diff --git a/gcc/testsuite/gcc.dg/torture/pr127760.c 
b/gcc/testsuite/gcc.dg/torture/pr127760.c
new file mode 100644
index 000000000000..fff1dc81e7f6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr127760.c
@@ -0,0 +1,69 @@
+/* PR tree-optimization/127760 */
+/* { dg-do run } */
+
+/* The overflow flag of __builtin_add_overflow is computed for the type of
+   the result, so none of these is a saturating addition in the type of
+   a + b.  */
+
+__attribute__((noipa)) unsigned
+narrow_ne (unsigned a, unsigned b)
+{
+  unsigned char r;
+  if (__builtin_add_overflow (a, b, &r))
+    return -1u;
+  return a + b;
+}
+
+__attribute__((noipa)) unsigned
+narrow_eq (unsigned a, unsigned b)
+{
+  unsigned char r;
+  int ovf = __builtin_add_overflow (a, b, &r);
+  unsigned res = -1u;
+  if (!ovf)
+    res = a + b;
+  return res;
+}
+
+__attribute__((noipa)) unsigned
+sgn (unsigned a, unsigned b)
+{
+  int r;
+  if (__builtin_add_overflow (a, b, &r))
+    return -1u;
+  return a + b;
+}
+
+#define N 64
+unsigned x[N], y[N], z[N];
+
+__attribute__((noipa)) void
+narrow_loop (void)
+{
+  for (int i = 0; i < N; i++)
+    {
+      unsigned char r;
+      z[i] = __builtin_add_overflow (x[i], y[i], &r) ? -1u : x[i] + y[i];
+    }
+}
+
+int
+main (void)
+{
+  if (narrow_ne (200, 100) != -1u || narrow_ne (100, 100) != 200)
+    __builtin_abort ();
+  if (narrow_eq (200, 100) != -1u || narrow_eq (100, 100) != 200)
+    __builtin_abort ();
+  if (sgn (0x80000000u, 0) != -1u || sgn (1, 2) != 3)
+    __builtin_abort ();
+  for (int i = 0; i < N; i++)
+    {
+      x[i] = 200;
+      y[i] = i;
+    }
+  narrow_loop ();
+  for (int i = 0; i < N; i++)
+    if (z[i] != (i < 56 ? 200 + i : -1u))
+      __builtin_abort ();
+  return 0;
+}

Reply via email to