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" } } */
