| Message ID | 20260902145254.77832-6-ktkachov@nvidia.com |
|---|---|
| State | New |
| Headers | show |
| Series | Saturating arithmetic matching improvements | expand |
On Wed, Sep 2, 2026 at 7:55 AM <ktkachov@nvidia.com> wrote: > > From: Kyrylo Tkachov <ktkachov@nvidia.com> > > For unsigned subtraction, the wrapped difference is greater than the minuend > exactly when the subtraction borrows. These forms are therefore saturating > subtraction: > > R = X - Y > R > X ? 0 : R > R <= X ? R : 0 > > Recognize both comparison operand orders and both result arm orders. > > AArch64 -O3, vector body: > > before: > > sub v30.4s, v31.4s, v30.4s > cmhs v31.4s, v31.4s, v30.4s > and v31.16b, v31.16b, v30.16b > > after: > > uqsub v30.4s, v31.4s, v30.4s > > Three vector operations become one. > Remove four existing expected-failure markers for the supported forms. > > Bootstrapped and tested on aarch64-none-linux-gnu. > > Ok for trunk? Ok. > > gcc/ChangeLog: > > * match-sat-alu.pd (unsigned_integer_sat_sub): Add the four > forms that compare the difference with the minuend. > > gcc/testsuite/ChangeLog: > > * gcc.target/aarch64/sat_u_sub_borrow-1.c: New test. > * gcc.target/aarch64/saturating_arithmetic_1.c: Remove the xfail > on usub. > * gcc.target/aarch64/sve/saturating_arithmetic_1.c: Remove the > xfail on usubq. > * gcc.target/aarch64/sve/saturating_arithmetic_3.c: Likewise. > * gcc.target/aarch64/sve/saturating_arithmetic_4.c: Likewise. > > Signed-off-by: Kyrylo Tkachov <ktkachov@nvidia.com> > --- > gcc/match-sat-alu.pd | 9 ++++++++ > .../gcc.target/aarch64/sat_u_sub_borrow-1.c | 22 +++++++++++++++++++ > .../aarch64/saturating_arithmetic_1.c | 2 +- > .../aarch64/sve/saturating_arithmetic_1.c | 2 +- > .../aarch64/sve/saturating_arithmetic_3.c | 2 +- > .../aarch64/sve/saturating_arithmetic_4.c | 2 +- > 6 files changed, 35 insertions(+), 4 deletions(-) > create mode 100644 gcc/testsuite/gcc.target/aarch64/sat_u_sub_borrow-1.c > > diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd > index 8c7bc0782a0..dfb2eb9800f 100644 > --- a/gcc/match-sat-alu.pd > +++ b/gcc/match-sat-alu.pd > @@ -131,6 +131,15 @@ along with GCC; see the file COPYING3. If not see > /* SAT_U_SUB = (X - Y) * (X >= Y) */ > (mult:c (minus @0 @1) (convert (ge @0 @1))) > (if (types_match (type, @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. */ > + (match (unsigned_integer_sat_sub @0 @1) > + /* SAT_U_SUB = (X - Y) > X ? 0 : X - Y, and the commuted comparison. */ > + (cond^ (gt:c (minus@2 @0 @1) @0) integer_zerop @2)) > + (match (unsigned_integer_sat_sub @0 @1) > + /* SAT_U_SUB = (X - Y) <= X ? X - Y : 0, and the commuted comparison. */ > + (cond^ (le:c (minus@2 @0 @1) @0) @2 integer_zerop)) > (match (unsigned_integer_sat_sub @0 @1) > /* SAT_U_SUB = X - MIN (X, Y). The MIN has to be single use: while it > stays live the saturating subtract is computed beside it instead of > diff --git a/gcc/testsuite/gcc.target/aarch64/sat_u_sub_borrow-1.c b/gcc/testsuite/gcc.target/aarch64/sat_u_sub_borrow-1.c > new file mode 100644 > index 00000000000..8b86cb92922 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/aarch64/sat_u_sub_borrow-1.c > @@ -0,0 +1,22 @@ > +/* { 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 wrapped difference is larger than the minuend exactly when the > + subtraction borrowed, so each of these is a saturating subtract. */ > + > +#define DEF(N, T) \ > + T f1_##N (T a, T b) { T r = a - b; return r > a ? 0 : r; } \ > + T f2_##N (T a, T b) { T r = a - b; if (r > a) r = 0; return r; } \ > + T f3_##N (T a, T b) { T r = a - b; return r <= a ? r : 0; } > + > +DEF (8, u8) > +DEF (16, u16) > +DEF (32, u32) > +DEF (64, u64) > + > +/* { dg-final { scan-tree-dump-times "\\.SAT_SUB " 12 "optimized" } } */ > diff --git a/gcc/testsuite/gcc.target/aarch64/saturating_arithmetic_1.c b/gcc/testsuite/gcc.target/aarch64/saturating_arithmetic_1.c > index 8fc1569845b..c73a024edec 100644 > --- a/gcc/testsuite/gcc.target/aarch64/saturating_arithmetic_1.c > +++ b/gcc/testsuite/gcc.target/aarch64/saturating_arithmetic_1.c > @@ -19,7 +19,7 @@ > ** ret > */ > /* > -** usub: { xfail *-*-* } > +** usub: > ** dup v([0-9]+).8b, w[01] > ** dup v([0-9]+).8b, w[01] > ** uqsub b([0-9]+), b\1, b\2 > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_1.c b/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_1.c > index 6936e9a2704..4cfab740c40 100644 > --- a/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_1.c > +++ b/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_1.c > @@ -39,7 +39,7 @@ > ** ... > */ > /* > -** usubq: { xfail *-*-* } > +** usubq: > ** ... > ** ld1b\tz([0-9]+)\.b, .* > ** ld1b\tz([0-9]+)\.b, .* > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_3.c b/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_3.c > index 14e2de59b1e..def7c82e256 100644 > --- a/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_3.c > +++ b/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_3.c > @@ -40,7 +40,7 @@ > ** ... > */ > /* > -** usubq: { xfail *-*-* } > +** usubq: > ** ... > ** ld1w\tz([0-9]+)\.s, .* > ** ld1w\tz([0-9]+)\.s, .* > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_4.c b/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_4.c > index 05a5786b4ab..5b9f8740ed4 100644 > --- a/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_4.c > +++ b/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_4.c > @@ -40,7 +40,7 @@ > ** ... > */ > /* > -** usubq: { xfail *-*-* } > +** usubq: > ** ... > ** ld1d\tz([0-9]+)\.d, .* > ** ld1d\tz([0-9]+)\.d, .* > -- > 2.50.1 (Apple Git-155) >
diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd index 8c7bc0782a0..dfb2eb9800f 100644 --- a/gcc/match-sat-alu.pd +++ b/gcc/match-sat-alu.pd @@ -131,6 +131,15 @@ along with GCC; see the file COPYING3. If not see /* SAT_U_SUB = (X - Y) * (X >= Y) */ (mult:c (minus @0 @1) (convert (ge @0 @1))) (if (types_match (type, @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. */ + (match (unsigned_integer_sat_sub @0 @1) + /* SAT_U_SUB = (X - Y) > X ? 0 : X - Y, and the commuted comparison. */ + (cond^ (gt:c (minus@2 @0 @1) @0) integer_zerop @2)) + (match (unsigned_integer_sat_sub @0 @1) + /* SAT_U_SUB = (X - Y) <= X ? X - Y : 0, and the commuted comparison. */ + (cond^ (le:c (minus@2 @0 @1) @0) @2 integer_zerop)) (match (unsigned_integer_sat_sub @0 @1) /* SAT_U_SUB = X - MIN (X, Y). The MIN has to be single use: while it stays live the saturating subtract is computed beside it instead of diff --git a/gcc/testsuite/gcc.target/aarch64/sat_u_sub_borrow-1.c b/gcc/testsuite/gcc.target/aarch64/sat_u_sub_borrow-1.c new file mode 100644 index 00000000000..8b86cb92922 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sat_u_sub_borrow-1.c @@ -0,0 +1,22 @@ +/* { 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 wrapped difference is larger than the minuend exactly when the + subtraction borrowed, so each of these is a saturating subtract. */ + +#define DEF(N, T) \ + T f1_##N (T a, T b) { T r = a - b; return r > a ? 0 : r; } \ + T f2_##N (T a, T b) { T r = a - b; if (r > a) r = 0; return r; } \ + T f3_##N (T a, T b) { T r = a - b; return r <= a ? r : 0; } + +DEF (8, u8) +DEF (16, u16) +DEF (32, u32) +DEF (64, u64) + +/* { dg-final { scan-tree-dump-times "\\.SAT_SUB " 12 "optimized" } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/saturating_arithmetic_1.c b/gcc/testsuite/gcc.target/aarch64/saturating_arithmetic_1.c index 8fc1569845b..c73a024edec 100644 --- a/gcc/testsuite/gcc.target/aarch64/saturating_arithmetic_1.c +++ b/gcc/testsuite/gcc.target/aarch64/saturating_arithmetic_1.c @@ -19,7 +19,7 @@ ** ret */ /* -** usub: { xfail *-*-* } +** usub: ** dup v([0-9]+).8b, w[01] ** dup v([0-9]+).8b, w[01] ** uqsub b([0-9]+), b\1, b\2 diff --git a/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_1.c b/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_1.c index 6936e9a2704..4cfab740c40 100644 --- a/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_1.c +++ b/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_1.c @@ -39,7 +39,7 @@ ** ... */ /* -** usubq: { xfail *-*-* } +** usubq: ** ... ** ld1b\tz([0-9]+)\.b, .* ** ld1b\tz([0-9]+)\.b, .* diff --git a/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_3.c b/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_3.c index 14e2de59b1e..def7c82e256 100644 --- a/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_3.c +++ b/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_3.c @@ -40,7 +40,7 @@ ** ... */ /* -** usubq: { xfail *-*-* } +** usubq: ** ... ** ld1w\tz([0-9]+)\.s, .* ** ld1w\tz([0-9]+)\.s, .* diff --git a/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_4.c b/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_4.c index 05a5786b4ab..5b9f8740ed4 100644 --- a/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_4.c +++ b/gcc/testsuite/gcc.target/aarch64/sve/saturating_arithmetic_4.c @@ -40,7 +40,7 @@ ** ... */ /* -** usubq: { xfail *-*-* } +** usubq: ** ... ** ld1d\tz([0-9]+)\.d, .* ** ld1d\tz([0-9]+)\.d, .*