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)
>