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" } } */

Reply via email to