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; +}
