On Fri, Sep 11, 2026 at 6:09 AM <[email protected]> wrote:
>
> From: Kyrylo Tkachov <[email protected]>
>
> 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.
>
> Ok for trunk?

Ok.

>
> Changes since v2:
> - Rebase on current trunk.
>
> 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]>
> ---
>  gcc/match-sat-alu.pd                          | 25 +++++++++++------
>  .../gcc.target/aarch64/sat_u_sub_swapped-1.c  | 27 +++++++++++++++++++
>  2 files changed, 44 insertions(+), 8 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/sat_u_sub_swapped-1.c
>
> diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd
> index 03bba82e4a6..2f6b842d86e 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 00000000000..fb42b6b07e3
> --- /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" } } */
> --
> 2.50.1 (Apple Git-155)
>

Reply via email to