https://gcc.gnu.org/g:2ed77dddba2e7f2d4e45ee8606e9b22acb6fb1f3

commit r17-4219-g2ed77dddba2e7f2d4e45ee8606e9b22acb6fb1f3
Author: Kyrylo Tkachov <[email protected]>
Date:   Thu Jul 30 18:13:06 2026 +0200

    match-sat-alu.pd: Recognize more unsigned saturating subtraction forms
    
    Equivalent unsigned saturating subtraction forms can reverse the comparison
    operands or the conditional arms:
    
      B < A ? A - B : 0
      A < B ? 0 : A - B
    
    The middle end can keep either comparison operand order.  Control flow with
    two returns can also reverse the conditional arms.
    
    Recognize both forms and reversed comparisons in the multiply-by-predicate
    form.
    
    AArch64 -O2:
    
    before:
    
            sub     w2, w0, w1
            cmp     w1, w0
            csel    w0, w2, wzr, cc
    
    after:
    
            subs    w0, w0, w1
            csel    w0, w0, wzr, cs
    
    The vector form replaces a comparison, a subtraction, and an AND with one
    UQSUB.
    
    Bootstrapped and tested on aarch64-none-linux-gnu.
    
    gcc/ChangeLog:
    
            * match-sat-alu.pd (unsigned_integer_sat_sub): Accept commuted
            comparison operand orders and the arm order from a two-return 
source.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/aarch64/sat_u_sub_swapped-1.c: New test.
    
    Signed-off-by: Kyrylo Tkachov <[email protected]>

Diff:
---
 gcc/match-sat-alu.pd                               | 25 +++++++++++++-------
 .../gcc.target/aarch64/sat_u_sub_swapped-1.c       | 27 ++++++++++++++++++++++
 2 files changed, 44 insertions(+), 8 deletions(-)

diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd
index 45f74ca5ace8..9cbea1e668bb 100644
--- a/gcc/match-sat-alu.pd
+++ b/gcc/match-sat-alu.pd
@@ -144,22 +144,31 @@ along with GCC; see the file COPYING3.  If not see
 /* Saturation sub for unsigned integer.  */
 (if (INTEGRAL_TYPE_P (type) && TYPE_UNSIGNED (type))
  (match (unsigned_integer_sat_sub @0 @1)
-  /* SAT_U_SUB = X > Y ? X - Y : 0  */
-  (cond^ (gt @0 @1) (minus @0 @1) integer_zerop)
+  /* SAT_U_SUB = X > Y ? X - Y : 0, and the commuted comparison.  */
+  (cond^ (gt:c @0 @1) (minus @0 @1) integer_zerop)
   (if (types_match (type, @0, @1))))
  (match (unsigned_integer_sat_sub @0 @1)
-  /* SAT_U_SUB = X >= Y ? X - Y : 0  */
-  (cond^ (ge @0 @1) (convert? (minus (convert1? @0) (convert1? @1)))
+  /* SAT_U_SUB = X >= Y ? X - Y : 0, and the commuted comparison.  */
+  (cond^ (ge:c @0 @1) (convert? (minus (convert1? @0) (convert1? @1)))
                    integer_zerop)
   (if (TYPE_UNSIGNED (TREE_TYPE (@0)) && types_match (@0, @1))))
  (match (unsigned_integer_sat_sub @0 @1)
-  /* SAT_U_SUB = (X - Y) * (X > Y)  */
-  (mult:c (minus @0 @1) (convert (gt @0 @1)))
+  /* SAT_U_SUB = (X - Y) * (X > Y), and the commuted comparison.  */
+  (mult:c (minus @0 @1) (convert (gt:c @0 @1)))
   (if (types_match (type, @0, @1))))
  (match (unsigned_integer_sat_sub @0 @1)
-  /* SAT_U_SUB = (X - Y) * (X >= Y)  */
-  (mult:c (minus @0 @1) (convert (ge @0 @1)))
+  /* SAT_U_SUB = (X - Y) * (X >= Y), and the commuted comparison.  */
+  (mult:c (minus @0 @1) (convert (ge:c @0 @1)))
   (if (types_match (type, @0, @1))))
+ /* A source with two returns can keep zero on the taken arm.  Accept a
+    commuted comparison in this form too.  */
+ (match (unsigned_integer_sat_sub @0 @1)
+  (cond^ (lt:c @0 @1) integer_zerop (minus @0 @1))
+  (if (types_match (type, @0, @1))))
+ (match (unsigned_integer_sat_sub @0 @1)
+  (cond^ (le:c @0 @1) integer_zerop
+        (convert? (minus (convert1? @0) (convert1? @1))))
+  (if (TYPE_UNSIGNED (TREE_TYPE (@0)) && types_match (@0, @1))))
  /* The wrapped difference exceeds the minuend exactly when the subtraction
     borrows, so a source that names the difference and then tests it against
     the minuend is a saturating subtract.  */
diff --git a/gcc/testsuite/gcc.target/aarch64/sat_u_sub_swapped-1.c 
b/gcc/testsuite/gcc.target/aarch64/sat_u_sub_swapped-1.c
new file mode 100644
index 000000000000..fb42b6b07e37
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sat_u_sub_swapped-1.c
@@ -0,0 +1,27 @@
+/* { 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;
+
+/* The same saturating subtract with the subtrahend written on the left of
+   the comparison.  GIMPLE orders comparison operands by SSA version, so
+   which spelling survives does not follow the source.  */
+
+#define DEF(N, T)                                              \
+  T f1_##N (T a, T b) { return b < a ? a - b : 0; }            \
+  T f2_##N (T a, T b) { return b <= a ? a - b : 0; }           \
+  T f3_##N (T a, T b) { return (T) ((a - b) * (T) (b < a)); }  \
+  T f4_##N (T a, T b) { return (T) ((a - b) * (T) (b <= a)); } \
+  T f5_##N (T a, T b) { if (b >= a) return 0; return a - b; }  \
+  T f6_##N (T a, T b) { return b >= a ? 0 : a - b; }           \
+  T f7_##N (T a, T b) { if (a < b) return 0; return a - b; }
+
+DEF (8, u8)
+DEF (16, u16)
+DEF (32, u32)
+DEF (64, u64)
+
+/* { dg-final { scan-tree-dump-times "\\.SAT_SUB " 28 "optimized" } } */

Reply via email to