@@ -334,6 +334,30 @@ along with GCC; see the file COPYING3. If not see
&& cmp >= 0
&& expr_not_equal_to (@0, wi::min_value (itype_precision,
SIGNED)))))))
+ (match (unsigned_integer_narrow_clip @0)
+ /* Use the sign of ~X for a source that avoids negating the minimum signed
+ value. For a negative X, ~X is nonnegative and gives zero. For a
+ positive X above the result range, ~X is negative and gives all ones. */
+ (convert (cond^ (gt (nop_convert@3 @0) INTEGER_CST@1)
+ (rshift:s (bit_not @0) INTEGER_CST@2)
+ @0))
+ (if (!TYPE_UNSIGNED (TREE_TYPE (@0))
+ && TYPE_UNSIGNED (TREE_TYPE (@3)))
+ (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 int_cst_1 = wi::to_wide (@1, itype_precision);
+ wide_int int_cst_2 = wi::to_wide (@2, itype_precision);
+ wide_int shift_amount = wi::uhwi (itype_precision - 1,
+ itype_precision);
+ int cmp = wi::cmp (int_cst_2, shift_amount,
+ TYPE_SIGN (TREE_TYPE (@0)));
+ }
+ (if (otype_precision < itype_precision
+ && wi::eq_p (trunc_max, int_cst_1)
+ && cmp >= 0)))))
(match (unsigned_integer_narrow_clip @0)
/* SAT_US_TRUNC = (NT)MAX (MIN (X, NT_MAX), 0). */
(convert (integer_constant_clamp @0 @1 @2))
new file mode 100644
@@ -0,0 +1,42 @@
+/* The complement spelling of the narrow-clip idiom is only a clamp when the
+ comparison that selects the saturated arm is unsigned. With a signed
+ comparison a negative value takes the fall-through arm and keeps its low
+ bits, so it must not be turned into a saturating truncation. */
+
+/* { dg-do run } */
+/* { dg-options "-O1 -ftree-vectorize -fdump-tree-vect-details" } */
+
+typedef __UINT16_TYPE__ u16;
+typedef __INT32_TYPE__ i32;
+
+#define N 32
+
+static i32 a[N];
+static u16 r[N];
+
+__attribute__((noipa)) static void
+clip (u16 *__restrict d, const i32 *__restrict s, int n)
+{
+ for (int i = 0; i < n; i++)
+ {
+ i32 v = s[i];
+ d[i] = v > 65535 ? (~v) >> 31 : v;
+ }
+}
+
+int
+main (void)
+{
+ for (int i = 0; i < N; i++)
+ a[i] = -1;
+
+ clip (r, a, N);
+
+ for (int i = 0; i < N; i++)
+ if (r[i] != (u16) -1)
+ __builtin_abort ();
+
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "sat_trunc pattern recognized" "vect" } } */
new file mode 100644
@@ -0,0 +1,70 @@
+/* { 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 __INT32_TYPE__ i32;
+typedef __INT64_TYPE__ i64;
+
+#define N 96
+
+static i32 in32[N];
+static i64 in64[N];
+static u16 out16[N];
+static u32 out32[N];
+
+__attribute__((noipa)) static void
+clip_u16 (u16 *__restrict out, const i32 *__restrict in, int n)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ i32 x = in[i];
+ out[i] = (x & ~65535) ? (~x) >> 31 : x;
+ }
+}
+
+__attribute__((noipa)) static void
+clip_u32 (u32 *__restrict out, const i64 *__restrict in, int n)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ i64 x = in[i];
+ out[i] = (x & ~(i64) 0xffffffff) ? (~x) >> 63 : x;
+ }
+}
+
+int
+main (void)
+{
+ static const i32 values32[6] =
+ { (-2147483647 - 1), -1, 0, 65535, 65536, 2147483647 };
+ static const u16 expected16[6] =
+ { 0, 0, 0, 65535, 65535, 65535 };
+ static const i64 values64[6] =
+ { (-9223372036854775807LL - 1), -1, 0, 4294967295LL,
+ 4294967296LL, 9223372036854775807LL };
+ static const u32 expected32[6] =
+ { 0, 0, 0, (u32) -1, (u32) -1, (u32) -1 };
+
+ check_vect ();
+
+ for (int i = 0; i < N; ++i)
+ {
+ in32[i] = values32[i % 6];
+ in64[i] = values64[i % 6];
+ }
+
+ clip_u16 (out16, in32, N);
+ clip_u32 (out32, in64, N);
+
+#pragma GCC novector
+ for (int i = 0; i < N; ++i)
+ if (out16[i] != expected16[i % 6]
+ || out32[i] != expected32[i % 6])
+ __builtin_abort ();
+
+ return 0;
+}
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 __UINT32_TYPE__ u32;
+typedef __INT32_TYPE__ i32;
+typedef __INT64_TYPE__ i64;
+
+void
+clip_u16 (u16 *__restrict out, const i32 *__restrict in, int n)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ i32 x = in[i];
+ out[i] = (x & ~65535) ? (~x) >> 31 : x;
+ }
+}
+
+void
+clip_u32 (u32 *__restrict out, const i64 *__restrict in, int n)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ i64 x = in[i];
+ out[i] = (x & ~(i64) 4294967295LL) ? (~x) >> 63 : x;
+ }
+}
+
+/* { dg-final { scan-tree-dump-times "sat_trunc pattern recognized" 2 "vect" } } */