[08/13] match-sat-alu.pd: Recognize strict widened unsigned saturation clamps
Commit Message
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
@@ -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))
new file mode 100644
@@ -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" } } */