On Mon, 28 Sep 2026, Dominic P wrote:

> get_load_store_type elides a consecutive load permutation of a strided
> group and vectorizable_load re-derives the group from subchain_p.
> Release it in remove_redundant_permutations instead, with the BB subchain
> walk kept to power-of-two runs of two or more lanes (other runs load in
> gcd (lanes, nunits) pieces: five of eight shorts ran 1.8 times slower on
> x86_64), and let a node without a load permutation be its own group.
> 
> Runs at lane 0 whose permutation is dropped at VF 1 were costed as
> element loads; the half-pel SAD loop is now 24 to 28% faster on x86_64
> for 64-row blocks.  On a Cortex-A72, 16-byte rows are 0.2 to 2.2% faster
> and 8-byte rows flat or up to 4.1% faster with generic tuning; with
> -mcpu=cortex-a72 they are 3.9% faster for 8-row blocks, flat for 16 and
> 8.5% slower for 64: the cost model now picks a one-row V2SI loop.
> 
> Bootstrapped and regression tested on x86_64-pc-linux-gnu; regression
> tested on aarch64-linux-gnu under qemu.
> 
> Assisted-by: Claude Opus 5 (Anthropic)
> 
> gcc/ChangeLog:
> 
>       * tree-vect-slp.cc
>       (vect_optimize_slp_pass::remove_redundant_permutations): Release
>       the load permutation of a consecutive subchain of a strided group
>       with a power-of-two number of lanes greater than one.
>       * tree-vect-stmts.cc (has_consecutive_load_permutation): Remove.
>       (get_load_store_type): Do not release the load permutation here.
>       Take the group size from the SLP node when the node has none.
>       (vectorizable_load): Take the group size and the first statement
>       of the group from the SLP node when the node has no load
>       permutation.
>       * tree-vectorizer.h (vect_load_store_data): Remove subchain_p.
> 
> gcc/testsuite/ChangeLog:
> 
>       * gcc.dg/vect/vect-strided-subrun-1.c: New test.
> 
> Signed-off-by: Dominic P <[email protected]>
> ---
> Changes in v3: the BB subchain walk replaces v2's own copy of it, as you
> suggested.  With -mcpu=neoverse-v2, neoverse-n2 or cortex-a710 the 8-byte
> loop is now unrolled twice and so gets no vector epilogue, as a plain
> 8-byte SAD already does; odd heights run one row as scalar code.
> 
> OK for trunk?
> 
>  .../gcc.dg/vect/vect-strided-subrun-1.c       | 137 ++++++++++++++++++
>  gcc/tree-vect-slp.cc                          |  13 +-
>  gcc/tree-vect-stmts.cc                        |  67 ++-------
>  gcc/tree-vectorizer.h                         |   2 -
>  4 files changed, 161 insertions(+), 58 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/vect/vect-strided-subrun-1.c
> 
> diff --git a/gcc/testsuite/gcc.dg/vect/vect-strided-subrun-1.c 
> b/gcc/testsuite/gcc.dg/vect/vect-strided-subrun-1.c
> new file mode 100644
> index 00000000000..7b2bf6b2247
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vect/vect-strided-subrun-1.c
> @@ -0,0 +1,137 @@
> +/* { dg-require-effective-target vect_int } */
> +
> +/* Consecutive runs of a strided load group that start at its first
> +   element or later, cover part of it, have an odd length or a single
> +   lane, overlap, run at a negative stride or feed two consumers.  */
> +
> +#include "tree-vect.h"
> +
> +#define N 4096
> +unsigned char buf[N];
> +
> +#define KERNELS(NAME, ATTR)                                          \
> +ATTR int                                                             \
> +NAME##_halfpel (const unsigned char *p1, const unsigned char *p2,    \
> +             long stride, int h)                                     \
> +{                                                                    \
> +  int s = 0;                                                         \
> +  for (int i = 0; i < h; i++)                                                
> \
> +    {                                                                        
> \
> +      for (int k = 0; k < 8; k++)                                    \
> +     s += __builtin_abs (p1[k] - ((p2[k] + p2[k + 1] + 1) >> 1));    \
> +      p1 += stride;                                                  \
> +      p2 += stride;                                                  \
> +    }                                                                        
> \
> +  return s;                                                          \
> +}                                                                    \
> +                                                                     \
> +ATTR int                                                             \
> +NAME##_run_at_5 (const unsigned char *p, long stride, int h)         \
> +{                                                                    \
> +  int s = 0;                                                         \
> +  for (int i = 0; i < h; i++)                                                
> \
> +    {                                                                        
> \
> +      for (int k = 0; k < 4; k++)                                    \
> +     s += p[k + 5] * 3 + p[k + 8];                                   \
> +      s += p[0] + p[11];                                             \
> +      p += stride;                                                   \
> +    }                                                                        
> \
> +  return s;                                                          \
> +}                                                                    \
> +                                                                     \
> +ATTR int                                                             \
> +NAME##_negstep (const unsigned char *p, long stride, int h)          \
> +{                                                                    \
> +  int s = 0;                                                         \
> +  for (int i = 0; i < h; i++)                                                
> \
> +    {                                                                        
> \
> +      for (int k = 0; k < 8; k++)                                    \
> +     s += __builtin_abs (p[k] - ((p[k + 1] + 1) >> 1));              \
> +      p -= stride;                                                   \
> +    }                                                                        
> \
> +  return s;                                                          \
> +}                                                                    \
> +                                                                     \
> +ATTR int                                                             \
> +NAME##_shared (const unsigned char *p, long stride, int h)           \
> +{                                                                    \
> +  int s = 0, t = 0;                                                  \
> +  for (int i = 0; i < h; i++)                                                
> \
> +    {                                                                        
> \
> +      for (int k = 0; k < 8; k++)                                    \
> +     {                                                               \
> +       int v = p[k + 1];                                             \
> +       s += v * 3;                                                   \
> +       t += v ^ p[k];                                                \
> +     }                                                               \
> +      p += stride;                                                   \
> +    }                                                                        
> \
> +  return s - t;                                                              
> \
> +}                                                                    \
> +                                                                     \
> +ATTR int                                                             \
> +NAME##_lane0 (const unsigned char *p, long stride, int h)            \
> +{                                                                    \
> +  int s = 0, t = 0;                                                  \
> +  for (int i = 0; i < h; i++)                                                
> \
> +    {                                                                        
> \
> +      for (int k = 0; k < 4; k++)                                    \
> +     {                                                               \
> +       s += p[k] * 3;                                                \
> +       t ^= p[k + 4];                                                \
> +     }                                                               \
> +      p += stride;                                                   \
> +    }                                                                        
> \
> +  return s - t;                                                              
> \
> +}                                                                    \
> +                                                                     \
> +ATTR int                                                             \
> +NAME##_odd (const unsigned char *p, long stride, int h)                      
> \
> +{                                                                    \
> +  int s = 0;                                                         \
> +  for (int i = 0; i < h; i++)                                                
> \
> +    {                                                                        
> \
> +      for (int k = 0; k < 3; k++)                                    \
> +     s += p[k + 1] * 5;                                              \
> +      s += p[0] + p[5] * 7;                                          \
> +      p += stride;                                                   \
> +    }                                                                        
> \
> +  return s;                                                          \
> +}
> +
> +KERNELS (vec, __attribute__ ((noipa)))
> +KERNELS (ref, __attribute__ ((noipa, optimize ("O0"))))
> +
> +int
> +main (void)
> +{
> +  check_vect ();
> +
> +  unsigned x = 1;
> +  for (int i = 0; i < N; i++)
> +    {
> +      x = x * 1103515245u + 12345u;
> +      buf[i] = x >> 16;
> +    }
> +
> +  for (long stride = 13; stride <= 40; stride += 27)
> +    for (int h = 1; h <= 33; h += 8)
> +      {
> +     if (vec_halfpel (buf + 3, buf + 700, stride, h)
> +         != ref_halfpel (buf + 3, buf + 700, stride, h))
> +       __builtin_abort ();
> +     if (vec_run_at_5 (buf + 1, stride, h)
> +         != ref_run_at_5 (buf + 1, stride, h))
> +       __builtin_abort ();
> +     if (vec_negstep (buf + 2000, stride, h)
> +         != ref_negstep (buf + 2000, stride, h))
> +       __builtin_abort ();
> +     if (vec_shared (buf + 5, stride, h) != ref_shared (buf + 5, stride, h))
> +       __builtin_abort ();
> +     if (vec_lane0 (buf + 7, stride, h) != ref_lane0 (buf + 7, stride, h))
> +       __builtin_abort ();
> +     if (vec_odd (buf + 9, stride, h) != ref_odd (buf + 9, stride, h))
> +       __builtin_abort ();
> +      }
> +  return 0;
> +}
> diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc
> index 12b1abe3d78..8ca291d2bd3 100644
> --- a/gcc/tree-vect-slp.cc
> +++ b/gcc/tree-vect-slp.cc
> @@ -8161,10 +8161,15 @@ vect_optimize_slp_pass::remove_redundant_permutations 
> ()
>        if (!SLP_TREE_LOAD_PERMUTATION (node).exists ())
>       continue;
>  
> -      /* In basic block vectorization we allow any subchain of an 
> interleaving
> -      chain.
> -      FORNOW: not in loop SLP because of realignment complications.  */
> -      if (is_a <bb_vec_info> (m_vinfo))
> +      /* In basic block vectorization we allow any subchain of an
> +      interleaving chain, for strided loop accesses a power-of-two run
> +      of two or more lanes, which is loaded without splitting.
> +      FORNOW: not otherwise in loop SLP because of realignment
> +      complications.  */
> +      if (is_a <bb_vec_info> (m_vinfo)
> +       || (STMT_VINFO_STRIDED_P (SLP_TREE_SCALAR_STMTS (node)[0])
> +           && SLP_TREE_LANES (node) > 1
> +           && pow2p_hwi (SLP_TREE_LANES (node))))

My question from the previous version still stands - what is special
about pow2p_hwi number of lanes?  Having test coverage for the
non-power-of-two case would be nice, of course.  We're probably
filling the vector elementwise (there's room for improvement
in vectorizable_load), but given the whole DR group size can
be non-power-of-two size as well I don't see how this is a
reliable cost thing?  Please at least add a comment here.
I _think_ cost-wide we want to optimize n in

      if (memory_access_type == VMAT_STRIDED_SLP)
        {
          HOST_WIDE_INT n = gcd (group_size, const_nunits);

so n for DR_GROUP_SIZE vs n for SLP_TREE_LANES, esp. we'd like to
preserve n == const_nunits.  I somewhat hate to bring in more
SLP_TREE_VECTYPE uses into permute optimizations, but well ...
we can also chose to defer this to vectorizable_load which
can still decide to load the whole group and permute.

Richard.

>       {
>         bool subchain_p = true;
>         stmt_vec_info next_load_info = NULL;
> diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
> index 244f319d0e4..186228ba23d 100644
> --- a/gcc/tree-vect-stmts.cc
> +++ b/gcc/tree-vect-stmts.cc
> @@ -2033,25 +2033,6 @@ vector_vector_composition_type (tree vtype, 
> poly_uint64 nelts, tree *ptype,
>    return NULL_TREE;
>  }
>  
> -/* Check if the load permutation of NODE only refers to a consecutive
> -   subset of the group indices.  We also require the length of the
> -   permutation to be a power of two.
> -   Such load permutations can be elided in strided access schemes as
> -   we can "jump over" the gap they leave.  */
> -
> -static bool
> -has_consecutive_load_permutation (slp_tree node)
> -{
> -  load_permutation_t perm = SLP_TREE_LOAD_PERMUTATION (node);
> -  if (!perm.exists ()
> -      || perm.length () <= 1
> -      || !pow2p_hwi (perm.length ()))
> -    return false;
> -
> -  return vect_load_perm_consecutive_p (node);
> -}
> -
> -
>  /* Analyze load or store SLP_NODE of type VLS_TYPE.  Return true
>     if there is a memory access type that the vectorized form can use,
>     storing it in *MEMORY_ACCESS_TYPE if so.  If we decide to use gathers
> @@ -2101,7 +2082,6 @@ get_load_store_type (vec_info  *vinfo, stmt_vec_info 
> stmt_info,
>    *slp_perm = false;
>    *n_perms = -1U;
>    *n_loads = -1U;
> -  ls->subchain_p = false;
>  
>    bool perm_ok = true;
>    poly_int64 vf = loop_vinfo ? LOOP_VINFO_VECT_FACTOR (loop_vinfo) : 1;
> @@ -2143,15 +2123,10 @@ get_load_store_type (vec_info  *vinfo, stmt_vec_info 
> stmt_info,
>        Fall back to scalar accesses if that isn't possible.  */
>        *memory_access_type = VMAT_STRIDED_SLP;
>  
> -      /* If the load permutation is consecutive we can reduce the group to
> -      the elements the permutation accesses.  Then we release the
> -      permutation.  */
> -      if (has_consecutive_load_permutation (slp_node))
> -     {
> -       ls->subchain_p = true;
> -       group_size = SLP_TREE_LANES (slp_node);
> -       SLP_TREE_LOAD_PERMUTATION (slp_node).release ();
> -     }
> +      /* A load permutation indexes the whole group, so we have to load
> +      all of it; without one the node's lanes are the group.  */
> +      if (!SLP_TREE_LOAD_PERMUTATION (slp_node).exists ())
> +     group_size = SLP_TREE_LANES (slp_node);
>      }
>    else if (STMT_VINFO_GATHER_SCATTER_P (stmt_info))
>      {
> @@ -10197,16 +10172,14 @@ vectorizable_load (vec_info *vinfo,
>        gcc_assert (!LOOP_VINFO_USING_PARTIAL_VECTORS_P (loop_vinfo));
>        gcc_assert (!nested_in_vect_loop);
>  
> +      /* A load permutation indexes the whole group; without one the
> +      node's own statements are the group.  */
> +      bool perm_p = SLP_TREE_LOAD_PERMUTATION (slp_node).exists ();
>        if (grouped_load)
>       {
> -       /* If we elided a consecutive load permutation, don't
> -          use the original first statement (which could be elided)
> -          but the one the load permutation starts with.
> -          This ensures the stride_base below is correct.  */
> -       if (!ls.subchain_p)
> -         first_stmt_info = DR_GROUP_FIRST_ELEMENT (stmt_info);
> -       else
> -         first_stmt_info = SLP_TREE_SCALAR_STMTS (slp_node)[0];
> +       first_stmt_info = (perm_p
> +                          ? DR_GROUP_FIRST_ELEMENT (stmt_info)
> +                          : SLP_TREE_SCALAR_STMTS (slp_node)[0]);
>         first_dr_info = STMT_VINFO_DR_INFO (first_stmt_info);
>         ref_type = get_group_alias_ptr_type (first_stmt_info);
>       }
> @@ -10217,22 +10190,12 @@ vectorizable_load (vec_info *vinfo,
>         ref_type = reference_alias_ptr_type (DR_REF (dr_info->dr));
>       }
>  
> -      if (grouped_load)
> -     {
> -       if (memory_access_type == VMAT_STRIDED_SLP)
> -         {
> -           /* If we elided a consecutive load permutation, adjust
> -              the group size here.  */
> -           if (!ls.subchain_p)
> -             group_size = DR_GROUP_SIZE (first_stmt_info);
> -           else
> -             group_size = SLP_TREE_LANES (slp_node);
> -         }
> -       else /* VMAT_ELEMENTWISE */
> -         group_size = SLP_TREE_LANES (slp_node);
> -     }
> -      else
> +      if (!grouped_load)
>       group_size = 1;
> +      else if (perm_p && memory_access_type == VMAT_STRIDED_SLP)
> +     group_size = DR_GROUP_SIZE (first_stmt_info);
> +      else
> +     group_size = SLP_TREE_LANES (slp_node);
>  
>        if (!costing_p)
>       {
> diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
> index abd9edc2743..26df0ad526e 100644
> --- a/gcc/tree-vectorizer.h
> +++ b/gcc/tree-vectorizer.h
> @@ -312,8 +312,6 @@ struct vect_load_store_data : vect_data {
>    bool slp_perm;    // SLP_TREE_LOAD_PERMUTATION
>    unsigned n_perms; // SLP_TREE_LOAD_PERMUTATION
>    unsigned n_loads; // SLP_TREE_LOAD_PERMUTATION
> -  /* Whether the load permutation is consecutive and simple.  */
> -  bool subchain_p; // VMAT_STRIDED_SLP and VMAT_GATHER_SCATTER
>  };
>  
>  struct match_elt_t {
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Stefan Gaiser, Jochen Jaser, Abhinav Puri; (HRB 36809, AG Nuernberg)

Reply via email to