https://gcc.gnu.org/g:76466425366d0f30effcdc1cfe729be28db4b88e
commit r17-3946-g76466425366d0f30effcdc1cfe729be28db4b88e Author: Kyrylo Tkachov <[email protected]> Date: Thu Jul 30 18:13:04 2026 +0200 match-sat-alu.pd: Recognize X + MIN (~X, Y) as unsigned saturating addition For unsigned X, ~X is MAX - X. MIN (~X, Y) limits the addend to the largest value that cannot overflow. X + MIN (~X, Y) is therefore saturating addition. Recognize this form. Require the MIN to have one use so that the replacement removes it. Add a negative test where the MIN remains live. AArch64 -O2: before: mvn w2, w0 cmp w2, w1 csel w2, w2, w1, ls add w0, w2, w0 after: adds w0, w0, w1 csinv w0, w0, wzr, cc Four instructions become two. Bootstrapped and tested on aarch64-none-linux-gnu. Changes since v1: - Keep the explicit `single_use` test because `:s` is not enforced in predicate-only `match` rules. - Add a negative test in which the MIN result remains live. gcc/ChangeLog: * match-sat-alu.pd (unsigned_integer_sat_add): Add the X + MIN (~X, Y) form. gcc/testsuite/ChangeLog: * gcc.target/aarch64/sat_u_add_min_not-1.c: New test. * gcc.target/aarch64/sat_u_add_min_not-2.c: Likewise. Signed-off-by: Kyrylo Tkachov <[email protected]> Diff: --- gcc/match-sat-alu.pd | 7 +++++++ gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-1.c | 18 ++++++++++++++++++ gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-2.c | 17 +++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd index bfba3f64d8e0..83cd8963147c 100644 --- a/gcc/match-sat-alu.pd +++ b/gcc/match-sat-alu.pd @@ -68,6 +68,13 @@ along with GCC; see the file COPYING3. If not see wide_int sum = wi::add (cst_1, cst_2); } (if (wi::eq_p (max, sum)))))) + (match (unsigned_integer_sat_add @0 @1) + /* SAT_U_ADD = X + MIN (~X, Y). ~X is MAX - X, so the MIN caps Y at the + largest addend that does not overflow. The MIN has to be single use: + while it stays live the saturating add is computed beside it instead of + replacing it, which costs an instruction. */ + (plus:c (min:c@2 (bit_not @0) @1) @0) + (if (single_use (@2)))) (match (unsigned_integer_sat_add @0 @1) /* SUM = ADD_OVERFLOW (X, Y) SAT_U_ADD = REALPART (SUM) | -IMAGPART (SUM) */ diff --git a/gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-1.c b/gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-1.c new file mode 100644 index 000000000000..58ed36897abd --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-1.c @@ -0,0 +1,18 @@ +/* { 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; + +#define DEF(T) \ + T f_##T (T a, T b) { T t = ~a; return a + (b < t ? b : t); } \ + T g_##T (T a, T b) { T t = ~a; return (t < b ? t : b) + a; } + +DEF (u8) +DEF (u16) +DEF (u32) +DEF (u64) + +/* { dg-final { scan-tree-dump-times "\\.SAT_ADD " 8 "optimized" } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-2.c b/gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-2.c new file mode 100644 index 000000000000..9e1e6ffa19e5 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sat_u_add_min_not-2.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +typedef unsigned int u32; + +/* Keep the MIN live so that replacing the addition would add work. */ + +u32 +add_min_live (u32 a, u32 b, u32 *out) +{ + u32 t = ~a; + u32 m = b < t ? b : t; + *out = m; + return a + m; +} + +/* { dg-final { scan-tree-dump-not "\\.SAT_ADD " "optimized" } } */
