diff mbox series

[08/13] match-sat-alu.pd: Recognize strict widened unsigned saturation clamps

Message ID 20260902145254.77832-10-ktkachov@nvidia.com
State New
Headers show
Series Saturating arithmetic matching improvements | expand

Commit Message

Kyrylo Tkachov Sept. 2, 2026, 2:52 p.m. UTC
From: Kyrylo Tkachov <ktkachov@nvidia.com>

A widened saturating addition can clamp the sum with either of these forms:

  SUM <= MAX ? SUM : MAX
  SUM < MAX ? SUM : MAX

The strict comparison is canonicalized to SUM <= MAX - 1.  It is equivalent
because equality makes both arms produce MAX.  Accept MAX and MAX - 1 as the
comparison limit.

AArch64 -O2:

before:

	uxtw	x2, w1
	add	x2, x2, w0, uxtw
	add	w0, w0, w1
	mov	w1, 4294967294
	cmp	x2, x1
	csinv	w0, w0, wzr, ls

after:

	adds	w0, w0, w1
	csinv	w0, w0, wzr, cc

Bootstrapped and tested on aarch64-none-linux-gnu.

Ok for trunk?

gcc/ChangeLog:

	* match-sat-alu.pd (unsigned_integer_sat_add): Accept a compare
	against MAX - 1 in the widened form.

gcc/testsuite/ChangeLog:

	* gcc.target/aarch64/sat_u_add_widen_lt-1.c: New test.

Signed-off-by: Kyrylo Tkachov <ktkachov@nvidia.com>
---
 gcc/match-sat-alu.pd                          |  6 +++-
 .../gcc.target/aarch64/sat_u_add_widen_lt-1.c | 32 +++++++++++++++++++
 2 files changed, 37 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sat_u_add_widen_lt-1.c
diff mbox series

Patch

diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd
index b01989c3a1c..477ae366661 100644
--- a/gcc/match-sat-alu.pd
+++ b/gcc/match-sat-alu.pd
@@ -148,8 +148,12 @@  along with GCC; see the file COPYING3.  If not see
      unsigned widen_precision = TYPE_PRECISION (TREE_TYPE (@2));
      wide_int max = wi::mask (precision, false, widen_precision);
      wide_int c4 = wi::to_wide (@4);
+     /* A source written with < rather than <= puts MAX - 1 in the compare.
+	The sum equal to MAX then takes the other arm, which is also MAX.  */
+     wide_int c4_add_1 = wi::add (c4, wi::uhwi (1, widen_precision));
     }
-    (if (wi::eq_p (c4, max) && widen_precision > precision))))))
+    (if ((wi::eq_p (c4, max) || wi::eq_p (c4_add_1, max))
+	 && widen_precision > precision))))))
 
 /* Saturation sub for unsigned integer.  */
 (if (INTEGRAL_TYPE_P (type) && TYPE_UNSIGNED (type))
diff --git a/gcc/testsuite/gcc.target/aarch64/sat_u_add_widen_lt-1.c b/gcc/testsuite/gcc.target/aarch64/sat_u_add_widen_lt-1.c
new file mode 100644
index 00000000000..114602e2f8f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sat_u_add_widen_lt-1.c
@@ -0,0 +1,32 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+typedef unsigned int u32;
+typedef unsigned short u16;
+typedef unsigned long long u64;
+
+/* Written with < rather than <=, so the compare holds MAX - 1.  The sum
+   equal to MAX takes the other arm, which is MAX as well.  */
+
+u32
+lt32 (u32 a, u32 b)
+{
+  u64 t = (u64) a + b;
+  return (u32) (t < 0xffffffffull ? t : 0xffffffffull);
+}
+
+u32
+le32 (u32 a, u32 b)
+{
+  u64 t = (u64) a + b;
+  return (u32) (t <= 0xffffffffull ? t : 0xffffffffull);
+}
+
+u16
+lt16 (u16 a, u16 b)
+{
+  u32 t = (u32) a + b;
+  return (u16) (t < 0xffffu ? t : 0xffffu);
+}
+
+/* { dg-final { scan-tree-dump-times "\\.SAT_ADD " 3 "optimized" } } */