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]);
