https://gcc.gnu.org/g:e8621d3bdaf022078b98614a003ea2f5bf95e7b4

commit r17-5260-ge8621d3bdaf022078b98614a003ea2f5bf95e7b4
Author: Kyrylo Tkachov <[email protected]>
Date:   Thu Oct 15 20:08:43 2026 +0200

    match-sat-alu.pd: Check operand types of signed SAT_ADD [PR127916]
    
    .SAT_ADD saturates in the type of its first operand.  Two
    signed_integer_sat_add forms do not check that this is the result type.
    
    The .ADD_OVERFLOW (X, Y) form lost its types_match check in r15-6406:
    
      signed char
      another_sat (int a, int b)
      {
        signed char r;
        if (__builtin_add_overflow (a, b, &r))
          return a < 0 ? SCHAR_MIN : SCHAR_MAX;
        return r;
      }
    
      __complex__ signed char _6;
      ...
      _6 = .ADD_OVERFLOW (a_4(D), b_5(D));
      _2 = IMAGPART_EXPR <_6>;
      if (_2 != 0)
      ...
      _1 = REALPART_EXPR <_6>;
      ...
      _11 = _10 ^ 127;
      # _3 = PHI <_11(4), _1(3)>
    
    widening_mul replaces the PHI with _3 = .SAT_ADD (a_4(D), b_5(D)), which
    saturates in int, so another_sat (-1, 200) returns -57 instead of -128.
    The vectorizer goes wrong in the same way.  Operands of the other sign
    and constants that do not fit the result type also go wrong.
    
    The IMM = -1 form matches X + -1 under a conversion to the result type,
    which can change the precision:
    
      short
      dec (int a)
      {
        unsigned ua = a, r = ua - 1;
        if ((a & (int) -ua) < 0)
          r = 0x8000u;
        return r;
      }
    
    At -Os this ICEs in wi::eq_p when checking is enabled.  Without checking,
    dec (INT_MIN) returns 0 instead of -32768.
    
    Require X to have the result type in both forms.  In the .ADD_OVERFLOW
    form, also require Y to have it or to be a constant that fits it.  Both
    callers convert such a constant.  Assert in the SAT_ADD callers that the
    first operand has the result type.
    
    Narrower Y operands no longer match.  Trunk got those right only because
    expand converts each .SAT_ADD operand.
    
    Bootstrapped and tested as part of the series on
    aarch64-unknown-linux-gnu and x86_64-pc-linux-gnu.
    
            PR tree-optimization/127916
    
    gcc/ChangeLog:
    
            * match-sat-alu.pd (signed_integer_sat_add): Check the operand types
            of the .ADD_OVERFLOW form and of the IMM = -1 form.
            * tree-ssa-math-opts.cc (match_saturation_add_with_assign): Assert
            that the first operand has the type of the result.
            (match_saturation_add): Likewise.
            * tree-vect-patterns.cc (vect_recog_sat_add_pattern): Likewise.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/torture/pr127916.c: New test.
    
    Signed-off-by: Kyrylo Tkachov <[email protected]>

Diff:
---
 gcc/match-sat-alu.pd                    |  11 +++-
 gcc/testsuite/gcc.dg/torture/pr127916.c | 113 ++++++++++++++++++++++++++++++++
 gcc/tree-ssa-math-opts.cc               |  16 ++++-
 gcc/tree-vect-patterns.cc               |   4 ++
 4 files changed, 140 insertions(+), 4 deletions(-)

diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd
index 4b04e6097505..5f3389a4ab61 100644
--- a/gcc/match-sat-alu.pd
+++ b/gcc/match-sat-alu.pd
@@ -456,7 +456,12 @@ along with GCC; see the file COPYING3.  If not see
       SAT_S_ADD = IMAGPART_EXPR (SUM) != 0 ? (-(T)(X < 0) ^ MAX) : SUM  */
   (cond^ (ne (imagpart (IFN_ADD_OVERFLOW:c@2 @0 @1)) integer_zerop)
         (signed_integer_sat_val @0)
-        (realpart @2)))
+        (realpart @2))
+  /* The overflow flag describes X + Y in T only when X and Y are values
+     of T.  The callers convert a constant Y to T.  */
+  (if (types_match (type, @0)
+       && (types_match (type, @1)
+          || (TREE_CODE (@1) == INTEGER_CST && int_fits_type_p (@1, type))))))
  (match (signed_integer_sat_add @0 @1)
   /* T SUM = (T)((UT)X + (UT)Y)
      SAT_S_ADD = (X ^ SUM) < 0 & ~((X ^ Y) < 0) ? (-(T)(X < 0) ^ MAX) : SUM  */
@@ -492,7 +497,9 @@ along with GCC; see the file COPYING3.  If not see
      wide_int c2 = wi::to_wide (@2);
      wide_int sum = wi::add (c1, c2);
     }
-    (if (wi::eq_p (sum, wi::max_value (precision, SIGNED))))))
+    /* The outer conversion must not change the type of X.  */
+    (if (types_match (type, @0)
+        && wi::eq_p (sum, wi::max_value (precision, SIGNED))))))
 
 (match (signed_integer_sat_add @0 @1)
   /* T SUM = (T)((UT)X + (UT)IMM)
diff --git a/gcc/testsuite/gcc.dg/torture/pr127916.c 
b/gcc/testsuite/gcc.dg/torture/pr127916.c
new file mode 100644
index 000000000000..ffa159615aeb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr127916.c
@@ -0,0 +1,113 @@
+/* PR tree-optimization/127916 */
+/* { dg-do run } */
+
+/* The operands of these additions do not have the type of the result, so
+   they are not saturating additions in that type.  */
+
+__attribute__((noipa)) signed char
+narrow (int a, int b)
+{
+  signed char r;
+  if (__builtin_add_overflow (a, b, &r))
+    return a < 0 ? -128 : 127;
+  return r;
+}
+
+__attribute__((noipa)) int
+mixed (int a, unsigned b)
+{
+  int r;
+  if (__builtin_add_overflow (a, b, &r))
+    return a < 0 ? -__INT_MAX__ - 1 : __INT_MAX__;
+  return r;
+}
+
+__attribute__((noipa)) signed char
+imm (signed char a)
+{
+  signed char r;
+  if (__builtin_add_overflow (a, 200, &r))
+    return a < 0 ? -128 : 127;
+  return r;
+}
+
+/* A - 1 saturated in int, but converted to short.  */
+
+__attribute__((noipa)) short
+dec (int a)
+{
+  unsigned ua = a, r = ua - 1;
+  if ((a & (int) -ua) < 0)
+    r = 0x8000u;
+  return r;
+}
+
+#define N 64
+int x[N], y[N];
+signed char z[N];
+
+__attribute__((noipa)) void
+narrow_loop (void)
+{
+  for (int i = 0; i < N; i++)
+    {
+      signed char r;
+      if (__builtin_add_overflow (x[i], y[i], &r))
+       r = x[i] < 0 ? -128 : 127;
+      z[i] = r;
+    }
+}
+
+/* These are saturating additions.  */
+
+__attribute__((noipa)) signed char
+imm_fits (signed char a)
+{
+  signed char r;
+  if (__builtin_add_overflow (a, 9, &r))
+    return a < 0 ? -128 : 127;
+  return r;
+}
+
+#ifdef __BITINT_MAXWIDTH__
+__attribute__((noipa)) _BitInt(32)
+dec_bitint (int a)
+{
+  unsigned ua = a, r = ua - 1;
+  if ((a & (int) -ua) < 0)
+    r = 0x80000000u;
+  return r;
+}
+#endif
+
+int
+main (void)
+{
+  if (narrow (-1, 200) != -128 || narrow (100, 100) != 127
+      || narrow (1, 2) != 3)
+    __builtin_abort ();
+  if (mixed (-1, 0x80000000u) != __INT_MAX__
+      || mixed (0, 0x80000000u) != __INT_MAX__ || mixed (1, 2) != 3)
+    __builtin_abort ();
+  if (imm (-1) != -128 || imm (-100) != 100)
+    __builtin_abort ();
+  if (dec (-__INT_MAX__ - 1) != -32768 || dec (5) != 4)
+    __builtin_abort ();
+  for (int i = 0; i < N; i++)
+    {
+      x[i] = i;
+      y[i] = 100;
+    }
+  narrow_loop ();
+  for (int i = 0; i < N; i++)
+    if (z[i] != (i < 28 ? i + 100 : 127))
+      __builtin_abort ();
+  if (imm_fits (120) != 127 || imm_fits (-128) != -119)
+    __builtin_abort ();
+#ifdef __BITINT_MAXWIDTH__
+  if (dec_bitint (-__INT_MAX__ - 1) != -__INT_MAX__ - 1
+      || dec_bitint (5) != 4)
+    __builtin_abort ();
+#endif
+  return 0;
+}
diff --git a/gcc/tree-ssa-math-opts.cc b/gcc/tree-ssa-math-opts.cc
index d74711cf4b87..0fcfca4872cd 100644
--- a/gcc/tree-ssa-math-opts.cc
+++ b/gcc/tree-ssa-math-opts.cc
@@ -4192,8 +4192,15 @@ match_saturation_add_with_assign (gimple_stmt_iterator 
*gsi, gassign *stmt)
 
   if (gimple_unsigned_integer_sat_add (lhs, ops, NULL)
       || gimple_signed_integer_sat_add (lhs, ops, NULL))
-    return build_saturation_binary_arith_call_and_replace (gsi, IFN_SAT_ADD,
-                                                          lhs, ops[0], ops[1]);
+    {
+      /* .SAT_ADD saturates in the type of its first operand, which must be
+        the type of the result.  */
+      gcc_checking_assert (types_compatible_p (TREE_TYPE (lhs),
+                                              TREE_TYPE (ops[0])));
+      return build_saturation_binary_arith_call_and_replace (gsi, IFN_SAT_ADD,
+                                                            lhs, ops[0],
+                                                            ops[1]);
+    }
 
   return false;
 }
@@ -4250,6 +4257,11 @@ match_saturation_add (gimple_stmt_iterator *gsi, gphi 
*phi)
       && !gimple_signed_integer_sat_add (phi_result, ops, NULL))
     return false;
 
+  /* .SAT_ADD saturates in the type of its first operand, which must be the
+     type of the result.  */
+  gcc_checking_assert (types_compatible_p (TREE_TYPE (phi_result),
+                                          TREE_TYPE (ops[0])));
+
   if (!TYPE_UNSIGNED (TREE_TYPE (ops[0])) && TREE_CODE (ops[1]) == INTEGER_CST)
     ops[1] = fold_convert (TREE_TYPE (ops[0]), ops[1]);
 
diff --git a/gcc/tree-vect-patterns.cc b/gcc/tree-vect-patterns.cc
index 89bafc47c6a0..6b6ff94cf0aa 100644
--- a/gcc/tree-vect-patterns.cc
+++ b/gcc/tree-vect-patterns.cc
@@ -4742,6 +4742,10 @@ vect_recog_sat_add_pattern (vec_info *vinfo, 
stmt_vec_info stmt_vinfo,
   if (gimple_unsigned_integer_sat_add (lhs, ops, NULL)
       || gimple_signed_integer_sat_add (lhs, ops, NULL))
     {
+      /* .SAT_ADD saturates in the type of its first operand, which must be
+        the type of the result.  */
+      gcc_checking_assert (types_compatible_p (TREE_TYPE (lhs),
+                                              TREE_TYPE (ops[0])));
       if (TREE_CODE (ops[1]) == INTEGER_CST)
        ops[1] = fold_convert (TREE_TYPE (ops[0]), ops[1]);

Reply via email to