https://gcc.gnu.org/g:d7f4fc514a7f5d951e9738fb3c23a14cf49bfd8d
commit r17-3945-gd7f4fc514a7f5d951e9738fb3c23a14cf49bfd8d Author: Kyrylo Tkachov <[email protected]> Date: Thu Jul 30 18:13:05 2026 +0200 match-sat-alu.pd: Recognize borrow-test saturating subtraction 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. 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 <[email protected]> Diff: --- gcc/match-sat-alu.pd | 9 +++++++++ .../gcc.target/aarch64/sat_u_sub_borrow-1.c | 22 ++++++++++++++++++++++ .../gcc.target/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(-) diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd index 22855726d90a..bfba3f64d8e0 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) /* DIFF = SUB_OVERFLOW (X, Y) SAT_U_SUB = REALPART (DIFF) | (IMAGPART (DIFF) + (-1)) */ 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 000000000000..8b86cb929228 --- /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 8fc1569845b5..c73a024edec6 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 6936e9a27044..4cfab740c408 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 14e2de59b1ed..def7c82e2564 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 05a5786b4abd..5b9f8740ed41 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, .*
