@@ -23,6 +23,16 @@ along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
+/* Clamp @0 between the constant bounds @1 and @2. The source can put the
+ MIN or the MAX first. The outer operation must be single use so that
+ replacing its conversion also removes the complete clamp. */
+(match (integer_constant_clamp @0 @1 @2)
+ (max (min @0 INTEGER_CST@2) INTEGER_CST@1)
+ (if (single_use (t))))
+(match (integer_constant_clamp @0 @1 @2)
+ (min (max @0 INTEGER_CST@1) INTEGER_CST@2)
+ (if (single_use (t))))
+
/* Saturation add for unsigned integer. */
(if (INTEGRAL_TYPE_P (type) && TYPE_UNSIGNED (type))
(match (usadd_overflow_mask @0 @1)
@@ -323,7 +333,22 @@ along with GCC; see the file COPYING3. If not see
&& wi::eq_p (trunc_max, int_cst_1)
&& cmp >= 0
&& expr_not_equal_to (@0, wi::min_value (itype_precision,
- SIGNED))))))))
+ SIGNED)))))))
+ (match (unsigned_integer_narrow_clip @0)
+ /* SAT_US_TRUNC = (NT)MAX (MIN (X, NT_MAX), 0). */
+ (convert (integer_constant_clamp @0 @1 @2))
+ (if (!TYPE_UNSIGNED (TREE_TYPE (@0)))
+ (with
+ {
+ unsigned itype_precision = TYPE_PRECISION (TREE_TYPE (@0));
+ unsigned otype_precision = TYPE_PRECISION (type);
+ wide_int trunc_max = wi::mask (otype_precision, false, itype_precision);
+ wide_int lo_cst = wi::to_wide (@1, itype_precision);
+ wide_int hi_cst = wi::to_wide (@2, itype_precision);
+ }
+ (if (otype_precision < itype_precision
+ && wi::eq_p (lo_cst, 0)
+ && wi::eq_p (hi_cst, trunc_max)))))))
/* Saturation truncate for unsigned integer. */
(if (INTEGRAL_TYPE_P (type) && TYPE_UNSIGNED (type))
new file mode 100644
@@ -0,0 +1,121 @@
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3 -fvect-cost-model=unlimited" } */
+
+#include "tree-vect.h"
+
+typedef __UINT16_TYPE__ u16;
+typedef __UINT32_TYPE__ u32;
+typedef __UINT64_TYPE__ u64;
+typedef __INT32_TYPE__ i32;
+typedef __INT64_TYPE__ i64;
+
+#define N 259
+
+static i32 in32[N];
+static i64 in64[N];
+static u16 out16[N];
+static u32 out32[N];
+
+__attribute__((noipa)) static void
+clamp_u16_lo (u16 *__restrict out, const i32 *__restrict in, int n)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ i32 x = in[i];
+ out[i] = x < 0 ? 0 : (x > 65535 ? 65535 : x);
+ }
+}
+
+__attribute__((noipa)) static void
+clamp_u16_hi (u16 *__restrict out, const i32 *__restrict in, int n)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ i32 x = in[i];
+ out[i] = x > 65535 ? 65535 : (x < 0 ? 0 : x);
+ }
+}
+
+__attribute__((noipa)) static void
+clamp_u32_lo (u32 *__restrict out, const i64 *__restrict in, int n)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ i64 x = in[i];
+ out[i] = x < 0 ? 0 : (x > 4294967295LL ? 4294967295LL : x);
+ }
+}
+
+__attribute__((noipa)) static void
+clamp_u32_hi (u32 *__restrict out, const i64 *__restrict in, int n)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ i64 x = in[i];
+ out[i] = x > 4294967295LL ? 4294967295LL : (x < 0 ? 0 : x);
+ }
+}
+
+static u16
+ref_u16 (i32 x)
+{
+ return x < 0 ? 0 : (x > 65535 ? 65535 : x);
+}
+
+static u32
+ref_u32 (i64 x)
+{
+ return x < 0 ? 0 : (x > 4294967295LL ? 4294967295LL : x);
+}
+
+static void
+check_u16 (void)
+{
+#pragma GCC novector
+ for (int i = 0; i < N; ++i)
+ if (out16[i] != ref_u16 (in32[i]))
+ __builtin_abort ();
+}
+
+static void
+check_u32 (void)
+{
+#pragma GCC novector
+ for (int i = 0; i < N; ++i)
+ if (out32[i] != ref_u32 (in64[i]))
+ __builtin_abort ();
+}
+
+int
+main (void)
+{
+ check_vect ();
+
+ for (int i = 0; i < N; ++i)
+ {
+ in32[i] = (i32) ((u32) i * 2654435761U + 1013904223U);
+ in64[i] = (i64) ((u64) i * 11400714819323198485ULL
+ + 13787848793156543929ULL);
+ }
+
+ in32[0] = -1;
+ in32[1] = 0;
+ in32[2] = 65535;
+ in32[3] = 65536;
+ in64[0] = -1;
+ in64[1] = 0;
+ in64[2] = 4294967295LL;
+ in64[3] = 4294967296LL;
+
+ clamp_u16_lo (out16, in32, N);
+ check_u16 ();
+ clamp_u16_hi (out16, in32, N);
+ check_u16 ();
+ clamp_u32_lo (out32, in64, N);
+ check_u32 ();
+ clamp_u32_hi (out32, in64, N);
+ check_u32 ();
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=armv8-a" } */
+/* { dg-additional-options "-mmax-vectorization --param=vect-epilogues-nomask=0 -fdump-tree-vect-details" } */
+
+typedef __UINT16_TYPE__ u16;
+typedef __INT32_TYPE__ i32;
+
+void
+clip (u16 *__restrict out, i32 *__restrict copy,
+ const i32 *__restrict in, int n)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ i32 x = in[i];
+ i32 inner = x > 65535 ? 65535 : x;
+ i32 outer = inner < 0 ? 0 : inner;
+ out[i] = outer;
+ copy[i] = inner;
+ }
+}
+
+/* { dg-final { scan-tree-dump-times "sat_trunc pattern recognized" 1 "vect" } } */
new file mode 100644
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=armv8-a" } */
+/* { dg-additional-options "-mmax-vectorization --param=vect-epilogues-nomask=0 -fdump-tree-vect-details" } */
+
+typedef __UINT16_TYPE__ u16;
+typedef __INT32_TYPE__ i32;
+
+void
+clip (u16 *__restrict out, i32 *__restrict copy,
+ const i32 *__restrict in, int n)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ i32 x = in[i];
+ i32 clamped = x < 0 ? 0 : (x > 65535 ? 65535 : x);
+ out[i] = clamped;
+ copy[i] = clamped;
+ }
+}
+
+/* { dg-final { scan-tree-dump-not "sat_trunc pattern recognized" "vect" } } */
new file mode 100644
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=armv8-a" } */
+/* { dg-additional-options "-mmax-vectorization --param=vect-epilogues-nomask=0 -fdump-tree-vect-details" } */
+
+#include "../../gcc.dg/vect/vect-sat-trunc-clamp-1.c"
+
+/* { dg-final { scan-tree-dump-times "sat_trunc pattern recognized" 4 "vect" } } */
new file mode 100644
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=armv8-a" } */
+/* { dg-additional-options "-mmax-vectorization --param=vect-epilogues-nomask=0 -fdump-tree-vect-details" } */
+
+typedef __UINT16_TYPE__ u16;
+typedef __INT32_TYPE__ i32;
+
+__attribute__((noipa))
+void
+bad_low (u16 *__restrict out, const i32 *__restrict in, int n)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ i32 x = in[i];
+ out[i] = x < 1 ? 1 : (x > 65535 ? 65535 : x);
+ }
+}
+
+__attribute__((noipa))
+void
+bad_high (u16 *__restrict out, const i32 *__restrict in, int n)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ i32 x = in[i];
+ out[i] = x > 65534 ? 65534 : (x < 0 ? 0 : x);
+ }
+}
+
+/* { dg-final { scan-tree-dump-not "sat_trunc pattern recognized" "vect" } } */