On Wed, Sep 2, 2026 at 8:02 AM <[email protected]> wrote:
>
> From: Kyrylo Tkachov <[email protected]>
>
> An arithmetic right shift preserves its input sign.  The range test and
> conversion can therefore use the shifted value while the saturation value
> uses the original input:
>
>   shifted = x >> 8;
>   range = (unsigned int) shifted + 32768;
>   out = range > 65535
>         ? (short int) (x >> 31) ^ 32767 : (short int) shifted;
>
> This is equivalent to .SAT_TRUNC (shifted).  Recognize a precision-minus-one
> sign shift and allow the saturation value to use the original input sign.
>
> Require the data shift to be a constant smaller than the input precision.
> Require the range type to be at least as wide as the shifted value and the
> output type, so the conversion and clamp constants do not lose bits.
>
> With AArch64 -O2 -march=armv8-a -fopenmp-simd:
>
> before:
>
> shifted_clip4:
>         ldp     d0, d30, [x1]
>         movi    v27.2s, 0x80, lsl 8
>         movi    v25.2s, 0xff, msl 8
>         mvni    v31.4h, 0x80, lsl 8
>         sshr    v29.2s, v0.2s, 8
>         sshr    v28.2s, v30.2s, 8
>         cmlt    v0.2s, v0.2s, #0
>         cmlt    v30.2s, v30.2s, #0
>         add     v26.2s, v29.2s, v27.2s
>         add     v27.2s, v28.2s, v27.2s
>         uzp1    v30.4h, v0.4h, v30.4h
>         uzp1    v28.4h, v29.4h, v28.4h
>         cmhi    v26.2s, v26.2s, v25.2s
>         cmhi    v27.2s, v27.2s, v25.2s
>         eor     v31.8b, v30.8b, v31.8b
>         uzp1    v27.4h, v26.4h, v27.4h
>         bit     v28.8b, v31.8b, v27.8b
>         str     d28, [x0]
>         ret
>
> after:
>
> shifted_clip4:
>         ldr     q31, [x1]
>         sqshrn  v31.4h, v31.4s, 8
>         str     d31, [x0]
>         ret
>
> The vector sequence becomes one SQSHRN.  Runtime tests cover valid and
> wrapping forms.  Target tests cover valid forms, rejected forms, and the
> related signed saturating addition and subtraction forms.
>
> Bootstrapped and tested on aarch64-none-linux-gnu.
> Tested on x86_64-pc-linux-gnu.
>
> Ok for trunk?
>
> gcc/ChangeLog:
>
>         * match-sat-alu.pd (signed_integer_sat_val): Recognize an arithmetic
>         sign shift.
>         (signed_integer_sat_trunc): Use signed_integer_sat_val in the existing
>         form.  Recognize a shifted value with an equivalent pre-shift sign
>         source.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.dg/vect/vect-sat-trunc-shift-sign-source-1.c: New test.
>         * gcc.target/aarch64/sat_s_val_ashr-1.c: Likewise.
>         * gcc.target/aarch64/vect-sat-trunc-shift-sign-source-1.c: Likewise.
>         * gcc.target/aarch64/vect-sat-trunc-shift-sign-source-negative-1.c:
>         Likewise.
>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
>  gcc/match-sat-alu.pd                          |  69 +++++-
>  .../vect/vect-sat-trunc-shift-sign-source-1.c | 221 ++++++++++++++++++
>  .../gcc.target/aarch64/sat_s_val_ashr-1.c     |  30 +++
>  .../vect-sat-trunc-shift-sign-source-1.c      |   7 +
>  ...t-sat-trunc-shift-sign-source-negative-1.c | 122 ++++++++++
>  5 files changed, 438 insertions(+), 11 deletions(-)
>  create mode 100644 
> gcc/testsuite/gcc.dg/vect/vect-sat-trunc-shift-sign-source-1.c
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/sat_s_val_ashr-1.c
>  create mode 100644 
> gcc/testsuite/gcc.target/aarch64/vect-sat-trunc-shift-sign-source-1.c
>  create mode 100644 
> gcc/testsuite/gcc.target/aarch64/vect-sat-trunc-shift-sign-source-negative-1.c
>
> diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd
> index 477ae366661..b141c87be03 100644
> --- a/gcc/match-sat-alu.pd
> +++ b/gcc/match-sat-alu.pd
> @@ -383,7 +383,15 @@ along with GCC; see the file COPYING3.  If not see
>   (match (signed_integer_sat_val @0)
>    (bit_xor:c (nop_convert? (negate
>                             (nop_convert? (convert (lt @0 integer_zerop)))))
> -            max_value)))
> +            max_value))
> + /* SAT_VAL = ((T)(X >> (PRECISION (X) - 1)) ^ MAX)  */
> + (match (signed_integer_sat_val @0)
> +  (bit_xor:c (convert? (rshift @0 INTEGER_CST@1)) max_value)
> +  (if (!TYPE_UNSIGNED (TREE_TYPE (@0))
> +       && tree_fits_uhwi_p (@1)
> +       && (tree_to_uhwi (@1)
> +           == (unsigned HOST_WIDE_INT)
> +              TYPE_PRECISION (TREE_TYPE (@0)) - 1)))))

Just:
wi::to_wide (@1) ==  (TYPE_PRECISION (TREE_TYPE (@0)) - 1)

Should be enough.
Should there be a check on precision of the conversion here; is
extending ok and what about truncating?  What about the signedness of
`type`?

>
>  /* Saturation add for signed integer.  */
>  (if (INTEGRAL_TYPE_P (type) && !TYPE_UNSIGNED (type))
> @@ -603,9 +611,7 @@ along with GCC; see the file COPYING3.  If not see
>   (match (signed_integer_sat_trunc @0)
>    /* SAT_S_TRUNC(X) = (unsigned)X + NT_MAX + 1  > Unsigned_MAX ? (NT)X  */
>    (cond^ (gt (plus:c (convert@4 @0) INTEGER_CST@1) INTEGER_CST@2)
> -        (bit_xor:c (nop_convert?
> -                    (negate (nop_convert? (convert (lt @0 integer_zerop)))))
> -                   INTEGER_CST@3)
> +        (signed_integer_sat_val @0)
>          (convert @0))
>    /* The comparison type has to be unsigned and at least as wide as X.  A
>       narrower type would examine only the low bits of X.  The result
> @@ -623,17 +629,58 @@ along with GCC; see the file COPYING3.  If not see
>       wide_int limit_0 = wi::mask (otype_prec, false, range_prec); // Aka 255
>       wide_int limit_1 = limit_0 - 2; // Aka 253
>       wide_int limit_2 = limit_0 - 1; // Aka 254
> -     wide_int otype_max = wi::mask (otype_prec - 1, false, otype_prec);
> -     wide_int itype_max = wi::mask (otype_prec - 1, false, range_prec);
> +     wide_int otype_max_in_range
> +       = wi::mask (otype_prec - 1, false, range_prec);
>       wide_int int_cst_1 = wi::to_wide (@1);
>       wide_int int_cst_2 = wi::to_wide (@2);
> -     wide_int int_cst_3 = wi::to_wide (@3);
>      }
> -    (if (((wi::eq_p (int_cst_1, offset) && wi::eq_p (int_cst_2, limit_0))
> -        || (wi::eq_p (int_cst_1, itype_max) && wi::eq_p (int_cst_2, limit_2))
> +    (if ((wi::eq_p (int_cst_1, offset)
> +         && wi::eq_p (int_cst_2, limit_0))
> +        || (wi::eq_p (int_cst_1, otype_max_in_range)
> +            && wi::eq_p (int_cst_2, limit_2))
>          || (wi::eq_p (int_cst_1, offset) && wi::eq_p (int_cst_2, limit_2))
> -        || (wi::eq_p (int_cst_1, itype_max) && wi::eq_p (int_cst_2, 
> limit_1)))
> -        && wi::eq_p (int_cst_3, otype_max)))))))
> +        || (wi::eq_p (int_cst_1, otype_max_in_range)
> +            && wi::eq_p (int_cst_2, limit_1))))))))
> +
> +/* An arithmetic right shift preserves the sign of its signed input.  The
> +   saturation value can therefore use the input sign while the range check
> +   and truncated result use the shifted value.  */
> +(if (INTEGRAL_TYPE_P (type) && !TYPE_UNSIGNED (type))
> + (match (signed_integer_sat_trunc @0)
> +  (cond^ (gt (plus:c (convert@4
> +                      (rshift@0 @5 INTEGER_CST@6)) INTEGER_CST@1)
> +            INTEGER_CST@2)
> +        (signed_integer_sat_val @5)
> +        (convert @0))
> +  (if (!TYPE_UNSIGNED (TREE_TYPE (@0))
> +       && TYPE_UNSIGNED (TREE_TYPE (@4))
> +       && tree_fits_uhwi_p (@6)
> +       && TYPE_PRECISION (type) < TYPE_PRECISION (TREE_TYPE (@0))
> +       && tree_to_uhwi (@6) < TYPE_PRECISION (TREE_TYPE (@0))
> +       && TYPE_PRECISION (TREE_TYPE (@4)) >= TYPE_PRECISION (type)
> +       && (TYPE_PRECISION (TREE_TYPE (@4))
> +           >= TYPE_PRECISION (TREE_TYPE (@0)) - tree_to_uhwi (@6)))
> +   (with
> +    {
> +     unsigned otype_prec = TYPE_PRECISION (type);
> +     unsigned range_prec = TYPE_PRECISION (TREE_TYPE (@4));
> +     wide_int offset = wi::set_bit_in_zero (otype_prec - 1, range_prec);
> +     wide_int limit_0 = wi::mask (otype_prec, false, range_prec); // Aka 255
> +     wide_int limit_1 = limit_0 - 2; // Aka 253
> +     wide_int limit_2 = limit_0 - 1; // Aka 254
> +     wide_int otype_max_in_range
> +       = wi::mask (otype_prec - 1, false, range_prec);
> +     wide_int int_cst_1 = wi::to_wide (@1);
> +     wide_int int_cst_2 = wi::to_wide (@2);
> +    }
> +    (if ((wi::eq_p (int_cst_1, offset)
> +             && wi::eq_p (int_cst_2, limit_0))
> +            || (wi::eq_p (int_cst_1, otype_max_in_range)
> +                && wi::eq_p (int_cst_2, limit_2))
> +            || (wi::eq_p (int_cst_1, offset)
> +                && wi::eq_p (int_cst_2, limit_2))
> +            || (wi::eq_p (int_cst_1, otype_max_in_range)
> +                && wi::eq_p (int_cst_2, limit_1))))))))
>
>  /* Saturation mult for unsigned integer.  */
>  (if (INTEGRAL_TYPE_P (type) && TYPE_UNSIGNED (type))
> diff --git a/gcc/testsuite/gcc.dg/vect/vect-sat-trunc-shift-sign-source-1.c 
> b/gcc/testsuite/gcc.dg/vect/vect-sat-trunc-shift-sign-source-1.c
> new file mode 100644
> index 00000000000..f68e503b6c0
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vect/vect-sat-trunc-shift-sign-source-1.c
> @@ -0,0 +1,221 @@
> +/* { dg-do run { target bitint } } */
> +/* { dg-additional-options "-O3 -fvect-cost-model=unlimited" } */
> +
> +typedef __INT8_TYPE__ int8_t;
> +typedef __INT16_TYPE__ int16_t;
> +typedef __INT32_TYPE__ int32_t;
> +typedef __INT64_TYPE__ int64_t;
> +typedef __UINT32_TYPE__ uint32_t;
> +typedef __UINT64_TYPE__ uint64_t;
> +typedef signed _BitInt(16) int16b_t;
> +typedef unsigned _BitInt(12) uint12b_t;
> +typedef unsigned _BitInt(16) uint16b_t;
> +typedef unsigned _BitInt(17) uint17b_t;
> +typedef unsigned _BitInt(24) uint24b_t;
> +
> +#define N 259
> +
> +#define SHIFT_SAT_VALUE(OUT, X, SHIFT, MAX) \
> +  ((OUT) ((OUT) ((X) >> (SHIFT)) ^ (OUT) (MAX)))
> +#define COMPARE_SAT_VALUE(OUT, X, SHIFT, MAX) \
> +  ((OUT) (-((OUT) ((X) < 0)) ^ (OUT) (MAX)))
> +
> +#define DEF_CLIP(NAME, SAT, OUT, IN, UIN, SHIFT, SIGN_SHIFT, OFFSET, LIMIT, \
> +                MAX)                                                        \
> +  __attribute__((noipa))                                                   \
> +  static void                                                              \
> +  NAME (OUT *__restrict out, const IN *__restrict in, int n)               \
> +  {                                                                        \
> +    for (int i = 0; i < n; ++i)                                           \
> +      {                                                                    \
> +       IN source = in[i];                                                  \
> +       IN shifted = source >> SHIFT;                                       \
> +       UIN range = (UIN) shifted + (UIN) OFFSET;                            \
> +       out[i] = (range > (UIN) LIMIT                                       \
> +                 ? SAT (OUT, source, SIGN_SHIFT, MAX) : (OUT) shifted);      
> \
> +      }                                                                    \
> +  }
> +
> +#define DEF_REF(NAME, OUT, IN, SHIFT, MIN, MAX)                           \
> +  __attribute__((noipa, optimize ("O0")))                                \
> +  static void                                                             \
> +  NAME (OUT *out, const IN *in, int n)                                    \
> +  {                                                                       \
> +    for (int i = 0; i < n; ++i)                                          \
> +      {                                                                   \
> +       IN shifted = in[i] >> SHIFT;                                       \
> +       if (shifted < (IN) MIN)                                            \
> +         out[i] = (OUT) MIN;                                              \
> +       else if (shifted > (IN) MAX)                                      \
> +         out[i] = (OUT) MAX;                                              \
> +       else                                                               \
> +         out[i] = (OUT) shifted;                                          \
> +      }                                                                   \
> +  }
> +
> +DEF_CLIP (clip_s16_s8, SHIFT_SAT_VALUE, int8_t, int16b_t, uint16b_t,
> +         3, 15, 128, 255, 127)
> +DEF_CLIP (clip_s32_s16, COMPARE_SAT_VALUE, int16_t, int32_t, uint32_t,
> +         8, 31, 32768U, 65535U, 32767)
> +DEF_CLIP (clip_s64_s32, SHIFT_SAT_VALUE, int32_t, int64_t, uint64_t,
> +         13, 63, 2147483648ULL, 4294967295ULL, 2147483647)
> +DEF_CLIP (clip_s16_s8_wide_unshifted, SHIFT_SAT_VALUE, int8_t, int16b_t,
> +         uint32_t, 0, 15, 128, 255, 127)
> +DEF_CLIP (clip_s16_s8_wide_shifted, SHIFT_SAT_VALUE, int8_t, int16b_t,
> +         uint32_t, 3, 15, 128, 255, 127)
> +DEF_CLIP (clip_s32_s16_reduced_range, SHIFT_SAT_VALUE, int16_t, int32_t,
> +         uint24b_t, 8, 31, 32768, 65535, 32767)
> +
> +DEF_REF (ref_s16_s8, int8_t, int16b_t, 3, -128, 127)
> +DEF_REF (ref_s32_s16, int16_t, int32_t, 8, -32768, 32767)
> +DEF_REF (ref_s64_s32, int32_t, int64_t, 13,
> +        -2147483647 - 1, 2147483647)
> +DEF_REF (ref_s16_s8_wide_unshifted, int8_t, int16b_t, 0, -128, 127)
> +DEF_REF (ref_s16_s8_wide_shifted, int8_t, int16b_t, 3, -128, 127)
> +DEF_REF (ref_s32_s16_reduced_range, int16_t, int32_t, 8, -32768, 32767)
> +
> +/* This range calculation can wrap a shifted value that still fits in the
> +   shifted input type.  */
> +
> +__attribute__((noipa))
> +static void
> +narrow_range (int16_t *__restrict out, const int32_t *__restrict in, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int32_t source = in[i];
> +      int32_t shifted = source >> 1;
> +      uint17b_t range = (uint17b_t) shifted + (uint17b_t) 32768;
> +      out[i] = (range > (uint17b_t) 65535
> +               ? (int16_t) (source >> 31) ^ 32767 : (int16_t) shifted);
> +    }
> +}
> +
> +__attribute__((noipa, optimize ("O0")))
> +static void
> +ref_narrow_range (int16_t *out, const int32_t *in, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int32_t source = in[i];
> +      int32_t shifted = source >> 1;
> +      uint17b_t range = (uint17b_t) shifted + (uint17b_t) 32768;
> +      out[i] = (range > (uint17b_t) 65535
> +               ? (int16_t) (source >> 31) ^ 32767 : (int16_t) shifted);
> +    }
> +}
> +
> +/* These wrapped constants do not describe a saturating truncation.  */
> +
> +__attribute__((noipa))
> +static void
> +wrapped_output_range (int16_t *__restrict out, const int32_t *__restrict in,
> +                     int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int32_t source = in[i];
> +      int32_t shifted = source >> 20;
> +      uint12b_t range = (uint12b_t) shifted + (uint12b_t) 32767;
> +      out[i] = (range > (uint12b_t) 65534
> +               ? (int16_t) (source >> 31) ^ 32767 : (int16_t) shifted);
> +    }
> +}
> +
> +__attribute__((noipa, optimize ("O0")))
> +static void
> +ref_wrapped_output_range (int16_t *out, const int32_t *in, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int32_t source = in[i];
> +      int32_t shifted = source >> 20;
> +      uint12b_t range = (uint12b_t) shifted + (uint12b_t) 32767;
> +      out[i] = (range > (uint12b_t) 65534
> +               ? (int16_t) (source >> 31) ^ 32767 : (int16_t) shifted);
> +    }
> +}
> +
> +typedef void (*clip_fn) (int16_t *, const int32_t *, int);
> +
> +static void
> +check_invalid (clip_fn fn, clip_fn ref_fn, int32_t special)
> +{
> +  int16_t out[N];
> +  int16_t ref[N];
> +  int32_t in[N];
> +
> +  for (int i = 0; i < N; ++i)
> +    in[i] = (int32_t) ((uint32_t) i * 2654435761U + 1013904223U);
> +  in[0] = special;
> +
> +  for (int n = 0; n <= N; ++n)
> +    {
> +      for (int i = 0; i < N; ++i)
> +       out[i] = ref[i] = 23;
> +      fn (out, in, n);
> +      ref_fn (ref, in, n);
> +      for (int i = 0; i < N; ++i)
> +       if (out[i] != ref[i])
> +         __builtin_abort ();
> +    }
> +}
> +
> +#define CHECK(NAME, REF, OUT, IN, INIT, SHIFT, MIN, MAX)                  \
> +  do                                                                     \
> +    {                                                                    \
> +      OUT out[N];                                                        \
> +      OUT ref[N];                                                        \
> +      IN in[N];                                                          \
> +      IN scale = (IN) 1 << (SHIFT);                                      \
> +      for (int i = 0; i < N; ++i)                                      \
> +       in[i] = (IN) (INIT);                                               \
> +      in[0] = ((IN) (MIN) - 1) * scale;                                 \
> +      in[1] = (IN) (MIN) * scale;                                       \
> +      in[2] = (IN) -1 * scale;                                          \
> +      in[3] = 0;                                                        \
> +      in[4] = (IN) (MAX) * scale;                                       \
> +      in[5] = ((IN) (MAX) + 1) * scale;                                 \
> +      for (int n = 0; n <= N; ++n)                                     \
> +       {                                                                 \
> +         for (int i = 0; i < N; ++i)                                    \
> +           out[i] = ref[i] = (OUT) 23;                                   \
> +         NAME (out, in, n);                                              \
> +         REF (ref, in, n);                                               \
> +         for (int i = 0; i < N; ++i)                                    \
> +           if (out[i] != ref[i])                                         \
> +             __builtin_abort ();                                         \
> +       }                                                                 \
> +    }                                                                    \
> +  while (0)
> +
> +int
> +main (void)
> +{
> +  CHECK (clip_s16_s8, ref_s16_s8, int8_t, int16b_t,
> +        (uint16b_t) i * (uint16b_t) 40503 + (uint16b_t) 97,
> +        3, -128, 127);
> +  CHECK (clip_s32_s16, ref_s32_s16, int16_t, int32_t,
> +        (uint32_t) i * 2654435761U + 1013904223U,
> +        8, -32768, 32767);
> +  CHECK (clip_s64_s32, ref_s64_s32, int32_t, int64_t,
> +        (uint64_t) i * 11400714819323198485ULL
> +        + 13787848793156543929ULL,
> +        13, -2147483647 - 1, 2147483647);
> +  CHECK (clip_s16_s8_wide_unshifted, ref_s16_s8_wide_unshifted,
> +        int8_t, int16b_t,
> +        (uint16b_t) i * (uint16b_t) 40503 + (uint16b_t) 97,
> +        0, -128, 127);
> +  CHECK (clip_s16_s8_wide_shifted, ref_s16_s8_wide_shifted,
> +        int8_t, int16b_t,
> +        (uint16b_t) i * (uint16b_t) 40503 + (uint16b_t) 97,
> +        3, -128, 127);
> +  CHECK (clip_s32_s16_reduced_range, ref_s32_s16_reduced_range,
> +        int16_t, int32_t,
> +        (uint32_t) i * 2654435761U + 1013904223U,
> +        8, -32768, 32767);
> +  check_invalid (narrow_range, ref_narrow_range, 196608);
> +  check_invalid (wrapped_output_range, ref_wrapped_output_range, 0);
> +  return 0;
> +}
> +
> diff --git a/gcc/testsuite/gcc.target/aarch64/sat_s_val_ashr-1.c 
> b/gcc/testsuite/gcc.target/aarch64/sat_s_val_ashr-1.c
> new file mode 100644
> index 00000000000..62ce0378d0d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/sat_s_val_ashr-1.c
> @@ -0,0 +1,30 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +#include <stdint.h>
> +
> +/* Write the saturation value of a signed overflow as
> +   (X >> (PREC - 1)) ^ MAX.  */
> +
> +#define DEF(N, T, MX, SH)                                      \
> +  T f1_##N (T x, T y)                                          \
> +  {                                                            \
> +    T s = (T) ((uint64_t) x + (uint64_t) y);                   \
> +    return ((x ^ s) & ~(x ^ y)) < 0 ? ((x >> SH) ^ MX) : s;    \
> +  }                                                            \
> +  T f2_##N (T x, T y)                                          \
> +  {                                                            \
> +    T r;                                                       \
> +    return __builtin_add_overflow (x, y, &r) ? ((x >> SH) ^ MX) : r; \
> +  }                                                            \
> +  T f3_##N (T x, T y)                                          \
> +  {                                                            \
> +    T r;                                                       \
> +    return __builtin_sub_overflow (x, y, &r) ? ((x >> SH) ^ MX) : r; \
> +  }
> +
> +DEF (32, int32_t, INT32_MAX, 31)
> +DEF (64, int64_t, INT64_MAX, 63)
> +
> +/* { dg-final { scan-tree-dump-times "\\.SAT_ADD " 4 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times "\\.SAT_SUB " 2 "optimized" } } */
> diff --git 
> a/gcc/testsuite/gcc.target/aarch64/vect-sat-trunc-shift-sign-source-1.c 
> b/gcc/testsuite/gcc.target/aarch64/vect-sat-trunc-shift-sign-source-1.c
> new file mode 100644
> index 00000000000..b3256db7b6e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/vect-sat-trunc-shift-sign-source-1.c
> @@ -0,0 +1,7 @@
> +/* { dg-do compile { target bitint } } */
> +/* { 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-shift-sign-source-1.c"
> +
> +/* { dg-final { scan-tree-dump-times "sat_trunc pattern recognized" 6 "vect" 
> } } */
> diff --git 
> a/gcc/testsuite/gcc.target/aarch64/vect-sat-trunc-shift-sign-source-negative-1.c
>  
> b/gcc/testsuite/gcc.target/aarch64/vect-sat-trunc-shift-sign-source-negative-1.c
> new file mode 100644
> index 00000000000..25925c96759
> --- /dev/null
> +++ 
> b/gcc/testsuite/gcc.target/aarch64/vect-sat-trunc-shift-sign-source-negative-1.c
> @@ -0,0 +1,122 @@
> +/* { dg-do compile { target bitint } } */
> +/* { dg-options "-O3 -march=armv8-a" } */
> +/* { dg-additional-options "-mmax-vectorization 
> --param=vect-epilogues-nomask=0 -fdump-tree-vect-details" } */
> +
> +typedef __INT16_TYPE__ int16_t;
> +typedef __INT32_TYPE__ int32_t;
> +typedef __UINT16_TYPE__ uint16_t;
> +typedef __UINT32_TYPE__ uint32_t;
> +typedef unsigned _BitInt(12) uint12b_t;
> +typedef unsigned _BitInt(17) uint17b_t;
> +
> +__attribute__((noipa))
> +void
> +different_source (int16_t *__restrict out, const int32_t *__restrict in,
> +                 const int32_t *__restrict signs, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int32_t source = in[i];
> +      int32_t shifted = source >> 8;
> +      uint32_t range = (uint32_t) shifted + 32768U;
> +      int32_t sign_source = signs[i];
> +      out[i] = (range > 65535U
> +               ? (int16_t) (sign_source >> 31) ^ 32767
> +               : (int16_t) shifted);
> +    }
> +}
> +
> +__attribute__((noipa))
> +void
> +logical_shift (int16_t *__restrict out, const uint32_t *__restrict in, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      uint32_t source = in[i];
> +      uint32_t shifted = source >> 8;
> +      uint32_t range = shifted + 32768U;
> +      out[i] = (range > 65535U
> +               ? (int16_t) ((int32_t) source >> 31) ^ 32767
> +               : (int16_t) shifted);
> +    }
> +}
> +
> +__attribute__((noipa))
> +void
> +variable_count (int16_t *__restrict out, const int32_t *__restrict in,
> +               const uint16_t *__restrict counts, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int32_t source = in[i];
> +      int32_t shifted = source >> (counts[i] & 31);
> +      uint32_t range = (uint32_t) shifted + 32768U;
> +      out[i] = (range > 65535U
> +               ? (int16_t) (source >> 31) ^ 32767
> +               : (int16_t) shifted);
> +    }
> +}
> +
> +__attribute__((noipa))
> +void
> +wrong_sign_count (int16_t *__restrict out, const int32_t *__restrict in,
> +                 int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int32_t source = in[i];
> +      int32_t shifted = source >> 8;
> +      uint32_t range = (uint32_t) shifted + 32768U;
> +      out[i] = (range > 65535U
> +               ? (int16_t) (source >> 30) ^ 32767
> +               : (int16_t) shifted);
> +    }
> +}
> +
> +__attribute__((noipa))
> +void
> +wrong_range (int16_t *__restrict out, const int32_t *__restrict in, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int32_t source = in[i];
> +      int32_t shifted = source >> 8;
> +      uint32_t range = (uint32_t) shifted + 32767U;
> +      out[i] = (range > 65535U
> +               ? (int16_t) (source >> 31) ^ 32767
> +               : (int16_t) shifted);
> +    }
> +}
> +
> +__attribute__((noipa))
> +void
> +narrow_range (int16_t *__restrict out, const int32_t *__restrict in, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int32_t source = in[i];
> +      int32_t shifted = source >> 1;
> +      uint17b_t range = (uint17b_t) shifted + (uint17b_t) 32768;
> +      out[i] = (range > (uint17b_t) 65535
> +               ? (int16_t) (source >> 31) ^ 32767
> +               : (int16_t) shifted);
> +    }
> +}
> +
> +__attribute__((noipa))
> +void
> +wrapped_output_range (int16_t *__restrict out,
> +                     const int32_t *__restrict in, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int32_t source = in[i];
> +      int32_t shifted = source >> 20;
> +      uint12b_t range = (uint12b_t) shifted + (uint12b_t) 32767;
> +      out[i] = (range > (uint12b_t) 65534
> +               ? (int16_t) (source >> 31) ^ 32767
> +               : (int16_t) shifted);
> +    }
> +}
> +
> +/* { dg-final { scan-tree-dump-not "sat_trunc pattern recognized" "vect" } } 
> */
> --
> 2.50.1 (Apple Git-155)
>

Reply via email to