On Wed, 29 Jul 2026, Tamar Christina wrote:

> The loop
> 
> char b[100];
> int c(int a) {
>   unsigned d = 0;
>   for (; __builtin_expect (d < a, 1); ++d)
>     {
>       if (b[0] + b[d + 1])
>         return 0;
>     }
>  return 1;
> }
> 
> compiled with -march=armv8-a+sve -O3 vectorizes with SVE, however to vectorize
> since we don't support peeling for alignment for
> LOOP_VINFO_EARLY_BREAKS_VECT_PEELED it forces versioning in order to 
> vectorize.
> 
> However versioning will always fail since we know the base access is 
> misaligned.
> i.e. the access to (b+1)[d] is always misaligned.
> 
> This is happening because VLA will always return misalignment unknown from
> dr_misalignment because this requires to known the size of the vector.
> 
> To fix this I added a new helper vector_versioning_alignment_not_reachable_p
> that checks if versioning could ever reach alignment.
> 
> While doing this some unexpected tests started failing. It turns out that the
> DR_SCALAR_KNOWN_BOUNDS check which checks to see that even if misaligned but
> all scalar accesses are in bounds of a known fixed size array then we're OK 
> and
> just need to force masking.
> 
> The loop it was placed in would exit early after the first misaligned access 
> and
> so when you have more than one data access in the loop it wouldn't mark the
> other accesses as safe to speculate.
> 
> This moves it to its own loop.
> 
> This removes all the unreachable loops from the testsuite.  I do have patches
> to enable peeling for alignment for LOOP_VINFO_EARLY_BREAKS_VECT_PEELED but I 
> am
> not yet happy with them (and need to redo prolog peeling LCSSA handling) so 
> will
> submit them later.
> 
> Bootstrapped Regtested on aarch64-none-linux-gnu,
> arm-none-linux-gnueabihf, x86_64-pc-linux-gnu
> -m32, -m64 and no issues.
> 
> Any comments?
> 
> Thanks,
> Tamar
> 
> gcc/ChangeLog:
> 
>       * tree-vect-data-refs.cc (vector_versioning_alignment_not_reachable_p):
>       New.
>       (vect_enhance_data_refs_alignment): Use it.
> 
> gcc/testsuite/ChangeLog:
> 
>       * gcc.dg/vect/vect-early-break-no-epilog_7.c: Fix off by 1 error in test
>       which wasn't noticed before because loop is unreachable.
>       * gcc.target/aarch64/sve/peeled.c: Mark peeling as xfail till PFA for
>       PEELED loops.
>       * gcc.target/aarch64/sve/peeled1.c: Likewise.
> 
> ---
> diff --git a/gcc/testsuite/gcc.dg/vect/vect-early-break-no-epilog_7.c 
> b/gcc/testsuite/gcc.dg/vect/vect-early-break-no-epilog_7.c
> index 
> 6d94312f4a60cb5e519c2eb5ee239732a625045c..6986210f67d4ad29c7e4224178ff679d3b8815cd
>  100644
> --- a/gcc/testsuite/gcc.dg/vect/vect-early-break-no-epilog_7.c
> +++ b/gcc/testsuite/gcc.dg/vect/vect-early-break-no-epilog_7.c
> @@ -10,7 +10,7 @@ int b[N] = {0};
>  
>  int foo (void)
>  {
> -  for (int i = 0; i < (N / 2); i += 2)
> +  for (int i = 0; i < ((N / 2) - 1); i += 2)
>      {
>        if (a[i] > b[i])
>       return 1;
> diff --git a/gcc/testsuite/gcc.target/aarch64/sve/peeled.c 
> b/gcc/testsuite/gcc.target/aarch64/sve/peeled.c
> index 
> f40ffc8f0bcde40aeb754887ed6d5047e9806bc7..1de20ba79bc47cf7e64464a9a1e076884dce0892
>  100644
> --- a/gcc/testsuite/gcc.target/aarch64/sve/peeled.c
> +++ b/gcc/testsuite/gcc.target/aarch64/sve/peeled.c
> @@ -15,6 +15,6 @@ c (int a)
>    return 1;
>  }
>  
> -/* { dg-final { scan-assembler-times {\twhilelo\t} 2 } } */
> +/* { dg-final { scan-assembler-times {\twhilelo\t} 2 { xfail *-*-* } } } */
>  /* { dg-final { scan-assembler-times {\tptest\t} 0 } } */
> -/* { dg-final { scan-assembler {\tld1b\tz[0-9]+\.s, p[0-9]+/z,} } } */
> +/* { dg-final { scan-assembler {\tld1b\tz[0-9]+\.s, p[0-9]+/z,} { xfail 
> *-*-* } } } */
> diff --git a/gcc/testsuite/gcc.target/aarch64/sve/peeled1.c 
> b/gcc/testsuite/gcc.target/aarch64/sve/peeled1.c
> index 
> 92e5bca44c48cefa20e829510a8914e4852145ed..79ca2d8e95ade0bd6510a8eff26083855028e48a
>  100644
> --- a/gcc/testsuite/gcc.target/aarch64/sve/peeled1.c
> +++ b/gcc/testsuite/gcc.target/aarch64/sve/peeled1.c
> @@ -19,6 +19,6 @@ c (int a)
>    return 1;
>  }
>  
> -/* { dg-final { scan-assembler-times {\twhilelo\t} 2 } } */
> -/* { dg-final { scan-assembler-times {\tptest\t} 2 } } */
> -/* { dg-final { scan-assembler-times {\tld1b\tz[0-9]+\.h, p[0-9]+/z,} 2 } } 
> */
> +/* { dg-final { scan-assembler-times {\twhilelo\t} 2 { xfail *-*-* } } } */
> +/* { dg-final { scan-assembler-times {\tptest\t} 2 { xfail *-*-* } } } */
> +/* { dg-final { scan-assembler-times {\tld1b\tz[0-9]+\.h, p[0-9]+/z,} 2 { 
> xfail *-*-* } } } */
> diff --git a/gcc/tree-vect-data-refs.cc b/gcc/tree-vect-data-refs.cc
> index 
> 0e0754769ae441a4d5a505636aa0bb6dbc09ad86..8f7d00ed221be64b6392385dab15a6fa2698061e
>  100644
> --- a/gcc/tree-vect-data-refs.cc
> +++ b/gcc/tree-vect-data-refs.cc
> @@ -1912,6 +1912,56 @@ vector_alignment_reachable_p (dr_vec_info *dr_info, 
> poly_uint64 vf)
>    return true;
>  }
>  
> +/* Return true if DR_INFO is known not to be aligned to its target alignment.
> +
> +  This handles the case where the target alignment is VLA/poly, so
> +  dr_misalignment returns misalignment unknown since it doesn't know the 
> vector
> +  length, but the access is known to be misaligned misaligned wrt all 
> possible
> +  target alignments.  i.e. the base + offset is misaligned vs the base 
> object.
> +  */
> +
> +static bool
> +vector_versioning_alignment_not_reachable_p (vec_info *vinfo,

As we have a vector_alignment_reachable_p please avoid a
similar but negated predicate here, so
vector_versioning_alignment_reachable_p please.

> +                                          dr_vec_info *dr_info,
> +                                          poly_int64 offset)
> +{
> +  if (DR_TARGET_ALIGNMENT (dr_info).is_constant ())
> +    return false;

But that's not true, so better check in the caller if the point
is we've already checked for constant target alignment (did we?).

> +
> +  unsigned HOST_WIDE_INT factor
> +    = known_alignment (DR_TARGET_ALIGNMENT (dr_info));
> +
> +  if (factor <= 1)
> +    return false;
> +
> +  HOST_WIDE_INT diff = 0;
> +  if (STMT_VINFO_GROUPED_ACCESS (dr_info->stmt))
> +    {
> +      dr_vec_info *first_dr
> +     = STMT_VINFO_DR_INFO (DR_GROUP_FIRST_ELEMENT (dr_info->stmt));
> +
> +      diff = (TREE_INT_CST_LOW (DR_INIT (dr_info->dr))
> +           - TREE_INT_CST_LOW (DR_INIT (first_dr->dr)));
> +      gcc_assert (diff >= 0);

We only get here for the first element because of 
vect_relevant_for_alignment_p.  It doesn't make much sense to
ask for any sub-element - there'll always be one that cannot
be aligned.

> +
> +      dr_info = first_dr;
> +    }
> +
> +  innermost_loop_behavior *drb = vect_dr_behavior (vinfo, dr_info);
> +
> +  if (drb->offset_alignment < factor)
> +    return false;

should that have been return true?

> +
> +  poly_offset_int total_misalignment = drb->base_misalignment;
> +  total_misalignment += wi::to_poly_offset (drb->init);
> +  total_misalignment += diff;
> +  total_misalignment += offset;
> +

So I think the point is why

          int misalignment;
          if ((misalignment = dr_misalignment (dr_info, vectype, off)) == 
0)
            continue;

does not compute an appropriate misalignment for VLA vectors?  The
alignment requirement of those is only dependent on the minimal
number of elements, right?  That is, I expected dr_misalignment (...)
to compute a misalignment based on DR_INIT.  Why does that not work?


> +  poly_offset_int misalign;
> +  return (known_misalignment (total_misalignment, factor, &misalign)
> +       && maybe_ne (misalign, 0));
> +}
> +
>  
>  /* Calculate the cost of the memory access represented by DR_INFO.  */
>  
> @@ -2455,6 +2505,30 @@ vect_enhance_data_refs_alignment (loop_vec_info 
> loop_vinfo)
>       }
>      }
>  
> +  /* See if we can relax the flags on speculative reads for early break.  Do
> +     this outside of the other loops below because they can exit early 
> leading
> +     to the flag not being cleared for known in bounds cases.  */
> +  poly_uint64 vf = LOOP_VINFO_VECT_FACTOR (loop_vinfo);
> +  if (LOOP_VINFO_EARLY_BREAKS (loop_vinfo))
> +    for (auto dr : datarefs)
> +      {
> +     dr_vec_info *dr_info = loop_vinfo->lookup_dr (dr);
> +     if (!vect_relevant_for_alignment_p (dr_info))
> +       continue;
> +
> +     stmt_vec_info stmt_info = dr_info->stmt;
> +
> +     /* With variable VF, unsafe speculative read can be avoided for known
> +        inbounds DRs as long as partial vectors are used.  */
> +     if (!vf.is_constant ()
> +         && dr_safe_speculative_read_required (stmt_info)
> +         && DR_SCALAR_KNOWN_BOUNDS (dr_info))
> +       {
> +         dr_set_safe_speculative_read_required (stmt_info, false);
> +         LOOP_VINFO_MUST_USE_PARTIAL_VECTORS_P (loop_vinfo) = true;
> +       }
> +      }
> +

This hunk (and the related below) looks OK, can you split out and
push separately?

>    /* While cost model enhancements are expected in the future, the high level
>       view of the code at this time is as follows:
>  
> @@ -2495,7 +2569,6 @@ vect_enhance_data_refs_alignment (loop_vec_info 
> loop_vinfo)
>       - The cost of peeling (the extra runtime checks, the increase
>         in code size).  */
>  
> -  poly_uint64 vf = LOOP_VINFO_VECT_FACTOR (loop_vinfo);
>    FOR_EACH_VEC_ELT (datarefs, i, dr)
>      {
>        dr_vec_info *dr_info = loop_vinfo->lookup_dr (dr);
> @@ -2505,16 +2578,6 @@ vect_enhance_data_refs_alignment (loop_vec_info 
> loop_vinfo)
>        stmt_vec_info stmt_info = dr_info->stmt;
>        tree vectype = STMT_VINFO_VECTYPE (stmt_info);
>  
> -      /* With variable VF, unsafe speculative read can be avoided for known
> -      inbounds DRs as long as partial vectors are used.  */
> -      if (!vf.is_constant ()
> -       && dr_safe_speculative_read_required (stmt_info)
> -       && DR_SCALAR_KNOWN_BOUNDS (dr_info))
> -     {
> -       dr_set_safe_speculative_read_required (stmt_info, false);
> -       LOOP_VINFO_MUST_USE_PARTIAL_VECTORS_P (loop_vinfo) = true;
> -     }
> -
>        do_peeling = vector_alignment_reachable_p (dr_info, vf);
>        if (do_peeling)
>          {
> @@ -3002,6 +3065,26 @@ vect_enhance_data_refs_alignment (loop_vec_info 
> loop_vinfo)
>                    break;
>                  }
>  
> +           /* For VLA we generally can't know the exact misalignment wrt to
> +              the vector size because we don't know the vector size.  That
> +              means misalignment will always be UNKNOWN.  We do know the
> +              vector size must be a power of two so there are some base
> +              pointers we know would never be aligned.  The check below is
> +              only valid for versioning alone.  For peeling we don't know yet
> +              statically whether we can each alignment.  */
> +           if (!try_peeling_with_versioning
> +               && misalignment == DR_MISALIGNMENT_UNKNOWN
> +               && vector_versioning_alignment_not_reachable_p (loop_vinfo,
> +                                                               dr_info, off))
> +             {
> +               if (dump_enabled_p ())
> +                 dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> +                                  "alignment versioning will never "
> +                                  "succeed.\n");
> +               do_versioning = false;
> +               break;
> +             }
> +
>             /* Forcing alignment in the first iteration is no good if
>                we don't keep it across iterations.  For now, just disable
>                versioning in this case.
> 
> 
> 

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

Reply via email to