The 09/29/2026 18:35, Dominic P wrote:
> vect_use_grouped_gather computes the offsets { 0, X, X*2, ... } in the
> unsigned element type of the punned vector, so if that is narrower than
> a pointer a negative step is zero-extended and a large one wraps.
> With -msve-vector-bits=256, a grouped load with a negative run-time
> stride faults (ld1w ..., [x4, z31.s, uxtw]).  Ask for a signed offset
> for a negative constant step, and use the gather only if the returned
> offset type is pointer-wide or the constant step's offsets fit it.
> The range of the offsets comes from vect_strided_offset_range, split
> out of vect_truncate_gather_scatter_offset.
> This also fixes vect-alias-check-10.c and -11.c with SVE-256.
> 
> Bootstrapped and regression tested on x86_64-pc-linux-gnu; regression
> tested on aarch64-linux-gnu (qemu, default and SVE-256 boards).
> 
> Assisted-by: Claude Opus 5 (Anthropic)
> 
> gcc/ChangeLog:
> 
>       * tree-vect-stmts.cc (vect_strided_offset_range): New function,
>       split out of...
>       (vect_truncate_gather_scatter_offset): ...here.  Remove the
>       overflow note.
>       (vect_use_grouped_gather): Use a signed offset type for a negative
>       constant step.  Require the offsets not to wrap in the offset type.
> 
> gcc/testsuite/ChangeLog:
> 
>       * gcc.dg/vect/vect-strided-neg-step-1.c: New test.
>       * gcc.target/aarch64/sve/strided_grouped_1.c: New test.
>       * gcc.target/aarch64/sve/strided_grouped_1_run.c: New test.
> 
> Signed-off-by: Dominic P <[email protected]>
> ---
>  .../gcc.dg/vect/vect-strided-neg-step-1.c     | 82 ++++++++++++++++++
>  .../aarch64/sve/strided_grouped_1.c           |  8 ++
>  .../aarch64/sve/strided_grouped_1_run.c       |  4 +
>  gcc/tree-vect-stmts.cc                        | 85 +++++++++++--------
>  4 files changed, 143 insertions(+), 36 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/vect/vect-strided-neg-step-1.c
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/strided_grouped_1.c
>  create mode 100644 
> gcc/testsuite/gcc.target/aarch64/sve/strided_grouped_1_run.c
> 
> diff --git a/gcc/testsuite/gcc.dg/vect/vect-strided-neg-step-1.c 
> b/gcc/testsuite/gcc.dg/vect/vect-strided-neg-step-1.c
> new file mode 100644
> index 00000000000..0d0fe976334
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vect/vect-strided-neg-step-1.c
> @@ -0,0 +1,82 @@
> +/* Grouped strided accesses with a negative step.  */
> +
> +#include "tree-vect.h"
> +
> +typedef unsigned char uc;
> +typedef unsigned short us;
> +
> +#define N 32
> +#define S 40
> +
> +#define TEST(T, G)                                                   \
> +  void __attribute__ ((noipa))                                               
> \
> +  load_##T (T *restrict o, T *restrict p, long stride, int n)                
> \
> +  {                                                                  \
> +    for (int i = 0; i < n; i++)                                              
> \
> +      {                                                                      
> \
> +     for (int k = 0; k < G; k++)                                     \
> +       o[i * G + k] = p[k] * 3;                                      \
> +     p += stride;                                                    \
> +      }                                                                      
> \
> +  }                                                                  \
> +  void __attribute__ ((noipa))                                               
> \
> +  store_##T (T *restrict q, T *restrict a, long stride, int n)               
> \
> +  {                                                                  \
> +    for (int i = 0; i < n; i++)                                              
> \
> +      {                                                                      
> \
> +     for (int k = 0; k < G; k++)                                     \
> +       q[k] = a[i * G + k] * 3;                                      \
> +     q += stride;                                                    \
> +      }                                                                      
> \
> +  }                                                                  \
> +  void __attribute__ ((noipa))                                               
> \
> +  store_down_##T (T *restrict q, T *restrict a, int n)                       
> \
> +  {                                                                  \
> +    for (int i = 0; i < n; i++)                                              
> \
> +      {                                                                      
> \
> +     for (int k = 0; k < G; k++)                                     \
> +       q[k] = a[i * G + k] * 3;                                      \
> +     q -= S;                                                         \
> +      }                                                                      
> \
> +  }                                                                  \
> +  void __attribute__ ((noipa))                                               
> \
> +  check_##T (void)                                                   \
> +  {                                                                  \
> +    static T a[N * G], o[N * G], m[N * S];                           \
> +    T *last = m + (N - 1) * S;                                               
> \
> +    _Pragma ("GCC novector")                                         \
> +    for (int i = 0; i < N * S; i++)                                  \
> +      m[i] = i * 7 + 1;                                                      
> \
> +    _Pragma ("GCC novector")                                         \
> +    for (int i = 0; i < N * G; i++)                                  \
> +      a[i] = i * 5 + 2;                                                      
> \
> +    load_##T (o, last, -S, N);                                               
> \
> +    _Pragma ("GCC novector")                                         \
> +    for (int i = 0; i < N * G; i++)                                  \
> +      if (o[i] != (T) (last[i % G - i / G * S] * 3))                 \
> +     __builtin_abort ();                                             \
> +    store_##T (last, a, -S, N);                                              
> \
> +    _Pragma ("GCC novector")                                         \
> +    for (int i = 0; i < N * G; i++)                                  \
> +      if (last[i % G - i / G * S] != (T) (a[i] * 3))                 \
> +     __builtin_abort ();                                             \
> +    for (int i = 0; i < N * G; i++)                                  \
> +      a[i] += 1;                                                     \
> +    store_down_##T (last, a, N);                                     \
> +    _Pragma ("GCC novector")                                         \
> +    for (int i = 0; i < N * G; i++)                                  \
> +      if (last[i % G - i / G * S] != (T) (a[i] * 3))                 \
> +     __builtin_abort ();                                             \
> +  }
> +
> +TEST (uc, 4)
> +TEST (us, 2)
> +
> +int
> +main (void)
> +{
> +  check_vect ();
> +  check_uc ();
> +  check_us ();
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.target/aarch64/sve/strided_grouped_1.c 
> b/gcc/testsuite/gcc.target/aarch64/sve/strided_grouped_1.c
> new file mode 100644
> index 00000000000..662f9753ecc
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/sve/strided_grouped_1.c
> @@ -0,0 +1,8 @@
> +/* { dg-do compile { target lp64 } } */
> +/* { dg-options "-O3 -fno-vect-cost-model -msve-vector-bits=256" } */
> +
> +#include "../../../gcc.dg/vect/vect-strided-neg-step-1.c"
> +
> +/* { dg-final { scan-assembler-not {\tld1w\tz[0-9]+\.s, p[0-7]/z, \[x[0-9]+, 
> z[0-9]+\.s, uxtw\]} } } */
> +/* { dg-final { scan-assembler-not {\tst1w\tz[0-9]+\.s, p[0-7], \[x[0-9]+, 
> z[0-9]+\.s, uxtw\]} } } */
> +/* { dg-final { scan-assembler-times {\tst1w\tz[0-9]+\.s, p[0-7], \[x[0-9]+, 
> z[0-9]+\.s, sxtw\]} 2 } } */
> diff --git a/gcc/testsuite/gcc.target/aarch64/sve/strided_grouped_1_run.c 
> b/gcc/testsuite/gcc.target/aarch64/sve/strided_grouped_1_run.c
> new file mode 100644
> index 00000000000..12c245c6ec1
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/sve/strided_grouped_1_run.c
> @@ -0,0 +1,4 @@
> +/* { dg-do run { target { aarch64_sve256_hw && lp64 } } } */
> +/* { dg-options "-O3 -fno-vect-cost-model -msve-vector-bits=256" } */
> +
> +#include "../../../gcc.dg/vect/vect-strided-neg-step-1.c"
> diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
> index 1db6786078e..7c96e9c8f55 100644
> --- a/gcc/tree-vect-stmts.cc
> +++ b/gcc/tree-vect-stmts.cc
> @@ -1613,6 +1613,36 @@ prepare_vec_mask (loop_vec_info loop_vinfo, tree 
> mask_type, tree loop_mask,
>    return and_res;
>  }
>  
> +/* Store in *RANGE the last offset of { 0, X, X*2, X*3, ... } for a strided
> +   access in LOOP_VINFO, where X is the constant step STEP divided by SCALE.
> +   Return false if STEP is not a multiple of SCALE or *RANGE overflows.  */
> +
> +static bool
> +vect_strided_offset_range (loop_vec_info loop_vinfo, tree step,
> +                        unsigned int scale, widest_int *range)
> +{
> +  /* Set COUNT to the upper limit on the number of elements - 1.
> +     Start with the maximum vectorization factor.  */
> +  unsigned HOST_WIDE_INT count = vect_max_vf (loop_vinfo) - 1;
> +
> +  /* Try lowering COUNT to the number of scalar latch iterations.
> +     Peeling for alignment by masking shifts the active lanes up.  */
> +  class loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
> +  widest_int max_iters;
> +  if (!LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo)

Needs updating from the previous patch.

> +      && max_loop_iterations (loop, &max_iters)
> +      && max_iters < count)
> +    count = max_iters.to_shwi ();
> +
> +  widest_int factor;
> +  if (!wi::multiple_of_p (wi::to_widest (step), scale, SIGNED, &factor))
> +    return false;
> +
> +  wi::overflow_type overflow;
> +  *range = wi::mul (count, factor, SIGNED, &overflow);
> +  return !overflow;
> +}
> +
>  /* Determine whether we can use a gather load or scatter store to vectorize
>     strided load or store STMT_INFO by truncating the current offset to a
>     smaller width.  We need to be able to construct an offset vector:
> @@ -1645,37 +1675,16 @@ vect_truncate_gather_scatter_offset (stmt_vec_info 
> stmt_info, tree vectype,
>        return false;
>      }
>  
> -  /* Get the number of bits in an element.  */
> -  scalar_mode element_mode = SCALAR_TYPE_MODE (TREE_TYPE (vectype));
> -  unsigned int element_bits = GET_MODE_BITSIZE (element_mode);
> -
> -  /* Set COUNT to the upper limit on the number of elements - 1.
> -     Start with the maximum vectorization factor.  */
> -  unsigned HOST_WIDE_INT count = vect_max_vf (loop_vinfo) - 1;
> -
> -  /* Try lowering COUNT to the number of scalar latch iterations.
> -     Peeling for alignment by masking shifts the active lanes up.  */
> -  class loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
> -  widest_int max_iters;
> -  if (!LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo)
> -      && max_loop_iterations (loop, &max_iters)
> -      && max_iters < count)
> -    count = max_iters.to_shwi ();
> -
>    /* Try scales of 1 and the element size.  */
>    unsigned int scales[] = { 1, vect_get_scalar_dr_size (dr_info) };
> -  wi::overflow_type overflow = wi::OVF_NONE;
>    for (int i = 0; i < 2; ++i)
>      {
>        unsigned int scale = scales[i];
> -      widest_int factor;
> -      if (!wi::multiple_of_p (wi::to_widest (step), scale, SIGNED, &factor))
> +      widest_int range;
> +      if (!vect_strided_offset_range (loop_vinfo, step, scale, &range))
>       continue;
>  
> -      /* Determine the minimum precision of (COUNT - 1) * STEP / SCALE.  */
> -      widest_int range = wi::mul (count, factor, SIGNED, &overflow);
> -      if (overflow)
> -     continue;
> +      /* Determine the minimum precision of RANGE.  */
>        signop sign = range >= 0 ? UNSIGNED : SIGNED;
>        unsigned int min_offset_bits = wi::min_precision (range, sign);
>  
> @@ -1711,11 +1720,6 @@ vect_truncate_gather_scatter_offset (stmt_vec_info 
> stmt_info, tree vectype,
>        return true;
>      }
>  
> -  if (overflow && dump_enabled_p ())
> -    dump_printf_loc (MSG_NOTE, vect_location,
> -                  "truncating gather/scatter offset to %d bits"
> -                  " might change its value.\n", element_bits);
> -

We still want the diagnostics here, likely you want to pass overflow as an 
argument
to vect_strided_offset_range.

Thanks,
Tamar

>    return false;
>  }
>  
> @@ -1762,22 +1766,31 @@ vect_use_grouped_gather (dr_vec_info *dr_info, tree 
> vectype,
>  
>    internal_fn ifn;
>    tree offset_vectype = *pun_vectype;
> +  tree step = DR_STEP (dr);
> +  if (TREE_CODE (step) == INTEGER_CST && tree_int_cst_sgn (step) < 0)
> +    offset_vectype = signed_type_for (offset_vectype);
>  
>    internal_fn strided_ifn = DR_IS_READ (dr)
>      ? IFN_MASK_LEN_STRIDED_LOAD : IFN_MASK_LEN_STRIDED_STORE;
>  
> -  /* Check if we have a gather/scatter with the new type.  We're just trying
> -     with the type itself as offset for now.  If not, check if we have a
> -     strided load/store.  These have fewer constraints (for example no offset
> -     type must exist) so it is possible that even though a gather/scatter is
> -     not available we still have a strided load/store.  */
> +  /* Check if we have a gather/scatter with the new type as offset whose
> +     offsets { 0, X, X*2, ... } for DR_STEP X do not wrap.  If not, check
> +     if we have a strided load/store.  These have fewer constraints (for
> +     example no offset type must exist) so it is possible that even though
> +     a gather/scatter is not available we still have a strided
> +     load/store.  */
>    bool ok = false;
>    tree tmp_vectype;
>    int tmp_scale;
> +  widest_int range;
>    if (vect_gather_scatter_fn_p
>        (loop_vinfo, DR_IS_READ (dr), masked_p, *pun_vectype,
> -       TREE_TYPE (*pun_vectype), *pun_vectype, 1, &tmp_scale, &ifn,
> -       &offset_vectype, &tmp_vectype, elsvals))
> +       TREE_TYPE (*pun_vectype), offset_vectype, 1, &tmp_scale, &ifn,
> +       &offset_vectype, &tmp_vectype, elsvals)
> +      && (TYPE_PRECISION (TREE_TYPE (offset_vectype)) >= POINTER_SIZE
> +       || (TREE_CODE (step) == INTEGER_CST
> +           && vect_strided_offset_range (loop_vinfo, step, 1, &range)
> +           && wi::fits_to_tree_p (range, TREE_TYPE (offset_vectype)))))
>      ok = true;
>    else if (internal_strided_fn_supported_p (strided_ifn, *pun_vectype,
>                                           elsvals))
> -- 
> 2.55.0
> 

-- 

Reply via email to