https://gcc.gnu.org/g:76466425366d0f30effcdc1cfe729be28db4b88e

commit r17-3946-g76466425366d0f30effcdc1cfe729be28db4b88e
Author: Kyrylo Tkachov <[email protected]>
Date:   Thu Jul 30 18:13:04 2026 +0200

    match-sat-alu.pd: Recognize X + MIN (~X, Y) as unsigned saturating addition
    
    For unsigned X, ~X is MAX - X.  MIN (~X, Y) limits the addend to the largest
    value that cannot overflow.  X + MIN (~X, Y) is therefore saturating 
addition.
    
    Recognize this form.  Require the MIN to have one use so that the 
replacement
    removes it.  Add a negative test where the MIN remains live.
    
    AArch64 -O2:
    
    before:
    
            mvn     w2, w0
            cmp     w2, w1
            csel    w2, w2, w1, ls
            add     w0, w2, w0
    
    after:
    
            adds    w0, w0, w1
            csinv   w0, w0, wzr, cc
    
    Four instructions become two.
    
    Bootstrapped and tested on aarch64-none-linux-gnu.
    
    Changes since v1:
    - Keep the explicit `single_use` test because `:s` is not enforced in
      predicate-only `match` rules.
    - Add a negative test in which the MIN result remains live.
    
    gcc/ChangeLog:
    
            * match-sat-alu.pd (unsigned_integer_sat_add): Add the
            X + MIN (~X, Y) form.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/aarch64/sat_u_add_min_not-1.c: New test.
            * gcc.target/aarch64/sat_u_add_min_not-2.c: Likewise.
    
    Signed-off-by: Kyrylo Tkachov <[email protected]>

Diff:
---
 gcc/match-sat-alu.pd                                   |  7 +++++++
 gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-1.c | 18 ++++++++++++++++++
 gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-2.c | 17 +++++++++++++++++
 3 files changed, 42 insertions(+)

diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd
index bfba3f64d8e0..83cd8963147c 100644
--- a/gcc/match-sat-alu.pd
+++ b/gcc/match-sat-alu.pd
@@ -68,6 +68,13 @@ along with GCC; see the file COPYING3.  If not see
      wide_int sum = wi::add (cst_1, cst_2);
     }
     (if (wi::eq_p (max, sum))))))
+ (match (unsigned_integer_sat_add @0 @1)
+  /* SAT_U_ADD = X + MIN (~X, Y).  ~X is MAX - X, so the MIN caps Y at the
+     largest addend that does not overflow.  The MIN has to be single use:
+     while it stays live the saturating add is computed beside it instead of
+     replacing it, which costs an instruction.  */
+  (plus:c (min:c@2 (bit_not @0) @1) @0)
+  (if (single_use (@2))))
  (match (unsigned_integer_sat_add @0 @1)
   /* SUM = ADD_OVERFLOW (X, Y)
      SAT_U_ADD = REALPART (SUM) | -IMAGPART (SUM)   */
diff --git a/gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-1.c 
b/gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-1.c
new file mode 100644
index 000000000000..58ed36897abd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-1.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+
+#define DEF(T)                                         \
+  T f_##T (T a, T b) { T t = ~a; return a + (b < t ? b : t); } \
+  T g_##T (T a, T b) { T t = ~a; return (t < b ? t : b) + a; }
+
+DEF (u8)
+DEF (u16)
+DEF (u32)
+DEF (u64)
+
+/* { dg-final { scan-tree-dump-times "\\.SAT_ADD " 8 "optimized" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-2.c 
b/gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-2.c
new file mode 100644
index 000000000000..9e1e6ffa19e5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-2.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+typedef unsigned int u32;
+
+/* Keep the MIN live so that replacing the addition would add work.  */
+
+u32
+add_min_live (u32 a, u32 b, u32 *out)
+{
+  u32 t = ~a;
+  u32 m = b < t ? b : t;
+  *out = m;
+  return a + m;
+}
+
+/* { dg-final { scan-tree-dump-not "\\.SAT_ADD " "optimized" } } */

Reply via email to