https://gcc.gnu.org/g:6833708b4eaf3ce1c22313bc173ff4253becc86b
commit r17-4480-g6833708b4eaf3ce1c22313bc173ff4253becc86b Author: Kyrylo Tkachov <[email protected]> Date: Thu Jul 30 18:13:06 2026 +0200 match-sat-alu.pd: Recognize strict widened unsigned saturation clamps A widened saturating addition can clamp the sum with either of these forms: SUM <= MAX ? SUM : MAX SUM < MAX ? SUM : MAX The strict comparison is canonicalized to SUM <= MAX - 1. It is equivalent because equality makes both arms produce MAX. Accept MAX and MAX - 1 as the comparison limit. AArch64 -O2: before: uxtw x2, w1 add x2, x2, w0, uxtw add w0, w0, w1 mov w1, 4294967294 cmp x2, x1 csinv w0, w0, wzr, ls after: adds w0, w0, w1 csinv w0, w0, wzr, cc Bootstrapped and tested on aarch64-none-linux-gnu. gcc/ChangeLog: * match-sat-alu.pd (unsigned_integer_sat_add): Accept a compare against MAX - 1 in the widened form. gcc/testsuite/ChangeLog: * gcc.target/aarch64/sat_u_add_widen_lt-1.c: New test. Signed-off-by: Kyrylo Tkachov <[email protected]> Diff: --- gcc/match-sat-alu.pd | 6 +++- .../gcc.target/aarch64/sat_u_add_widen_lt-1.c | 32 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd index 2f6b842d86e6..42a4e49e00e6 100644 --- a/gcc/match-sat-alu.pd +++ b/gcc/match-sat-alu.pd @@ -138,8 +138,12 @@ along with GCC; see the file COPYING3. If not see unsigned widen_precision = TYPE_PRECISION (TREE_TYPE (@2)); wide_int max = wi::mask (precision, false, widen_precision); wide_int c4 = wi::to_wide (@4); + /* A source written with < rather than <= puts MAX - 1 in the compare. + The sum equal to MAX then takes the other arm, which is also MAX. */ + wide_int c4_add_1 = wi::add (c4, wi::uhwi (1, widen_precision)); } - (if (wi::eq_p (c4, max) && widen_precision > precision)))))) + (if ((wi::eq_p (c4, max) || wi::eq_p (c4_add_1, max)) + && widen_precision > precision)))))) /* Saturation sub for unsigned integer. */ (if (INTEGRAL_TYPE_P (type) && TYPE_UNSIGNED (type)) diff --git a/gcc/testsuite/gcc.target/aarch64/sat_u_add_widen_lt-1.c b/gcc/testsuite/gcc.target/aarch64/sat_u_add_widen_lt-1.c new file mode 100644 index 000000000000..114602e2f8fb --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sat_u_add_widen_lt-1.c @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned long long u64; + +/* Written with < rather than <=, so the compare holds MAX - 1. The sum + equal to MAX takes the other arm, which is MAX as well. */ + +u32 +lt32 (u32 a, u32 b) +{ + u64 t = (u64) a + b; + return (u32) (t < 0xffffffffull ? t : 0xffffffffull); +} + +u32 +le32 (u32 a, u32 b) +{ + u64 t = (u64) a + b; + return (u32) (t <= 0xffffffffull ? t : 0xffffffffull); +} + +u16 +lt16 (u16 a, u16 b) +{ + u32 t = (u32) a + b; + return (u16) (t < 0xffffu ? t : 0xffffu); +} + +/* { dg-final { scan-tree-dump-times "\\.SAT_ADD " 3 "optimized" } } */
