On Mon, 8 Jun 2026, Abhishek Kaushik wrote:

> determine_value_range only queried ranger when VAR was an SSA_NAME.
> However, number_of_iterations_exit_assumptions calls
> expand_simple_operations on the IV bases before asking for the range.
> This can expose simple GENERIC expressions, including conversions, even
> when the original value had an SSA_NAME with useful range information.
> 
> Ranger can analyze such expressions at a program point or edge, so query
> it for integral bound expressions on the loop preheader edge rather than
> restricting the query to SSA_NAMEs.  This lets niter analysis recover the
> narrower range for value-preserving conversions such as uint8_t to int,
> while still handling non-value-preserving conversions and wrapping
> expressions conservatively.
> 
> The new test covers a direct converted uint8_t bound, a masked uint16_t
> bound whose useful range comes from ranger, a guarded uint16_t bound
> whose range is context-sensitive, a non-value-preserving uint8_t to
> int8_t conversion, and a wrapping unsigned expression.
> 
> Bootstrapped and regression tested on aarch64-unknown-linux-gnu.

OK.

Sorry for the delay,
Richard.

> gcc/ChangeLog:
> 
>       * tree-ssa-loop-niter.cc (determine_value_range): Query ranger
>       for integral expressions, not only SSA_NAMEs.  Query ranges on the
>       loop preheader edge.
> 
> gcc/testsuite/ChangeLog:
> 
>       * gcc.target/aarch64/sve2/niter-convert-range.c: New test.
> ---
>  .../aarch64/sve2/niter-convert-range.c        | 68 +++++++++++++++++++
>  gcc/tree-ssa-loop-niter.cc                    | 15 ++--
>  2 files changed, 76 insertions(+), 7 deletions(-)
>  create mode 100644 
> gcc/testsuite/gcc.target/aarch64/sve2/niter-convert-range.c
> 
> diff --git a/gcc/testsuite/gcc.target/aarch64/sve2/niter-convert-range.c 
> b/gcc/testsuite/gcc.target/aarch64/sve2/niter-convert-range.c
> new file mode 100644
> index 00000000000..c96b2f3999c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/sve2/niter-convert-range.c
> @@ -0,0 +1,68 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -march=armv8.3-a+sve2 -fdump-tree-vect-details" } */
> +
> +#include <stdint.h>
> +
> +int
> +foo (char *buf, uint8_t len)
> +{
> +  int x = 0;
> +
> +  for (int16_t i = 0, y = 0; i < len; i++, y = i * 10)
> +    x += (int) y * (buf[i] - '0');
> +
> +  return x;
> +}
> +
> +int
> +masked_len_foo (unsigned char *buf, uint16_t len)
> +{
> +  uint16_t n = len & 255;
> +  int x = 0;
> +
> +  for (int16_t i = 0, y = 0; i < n; i++, y = i * 10)
> +    x += (int) y * (buf[i] - '0');
> +
> +  return x;
> +}
> +
> +int
> +guarded_len_foo (char *buf, uint16_t len)
> +{
> +  if (len > 255)
> +    return 0;
> +
> +  int x = 0;
> +
> +  for (int16_t i = 0, y = 0; i < len; i++, y = i * 10)
> +    x += (int) y * (buf[i] - '0');
> +
> +  return x;
> +}
> +
> +int
> +non_value_preserving (unsigned char *buf, uint8_t len)
> +{
> +  int x = 0;
> +
> +  for (int16_t i = 0, y = 0; i < (int) (int8_t) len; i++, y = i * 10)
> +    x += (int) y * (buf[i] - '0');
> +
> +  return x;
> +}
> +
> +unsigned
> +niter_convert_range_wrap (unsigned char *buf, uint8_t len)
> +{
> +  unsigned x = 0;
> +
> +  for (unsigned i = 0, y = 0; i < (unsigned) len - 1; i++, y = i * 10)
> +    x += y * (buf[i] - '0');
> +
> +  return x;
> +}
> +
> +/* { dg-final { scan-tree-dump-times {bounds on difference of bases: 0 
> [.][.][.] 126} 3 "vect" } } */
> +/* { dg-final { scan-tree-dump-times {bounds on difference of bases: 0 
> [.][.][.] 254} 9 "vect" } } */
> +/* { dg-final { scan-tree-dump-times {bounds on difference of bases: -1 
> [.][.][.] 4294967294} 3 "vect" } } */
> +/* { dg-final { scan-tree-dump-times "loop vectorized using variable length 
> vectors" 5 "vect" } } */
> diff --git a/gcc/tree-ssa-loop-niter.cc b/gcc/tree-ssa-loop-niter.cc
> index f179be1ffc7..43177be5d56 100644
> --- a/gcc/tree-ssa-loop-niter.cc
> +++ b/gcc/tree-ssa-loop-niter.cc
> @@ -332,8 +332,9 @@ end:
>    mpz_clear (offc1);
>  }
>  
> -/* Stores estimate on the minimum/maximum value of the expression VAR + OFF
> -   in TYPE to MIN and MAX.  */
> +/* Stores estimates of the minimum and maximum values of the expression
> +   VAR + OFF in TYPE to MIN and MAX.  The estimates are valid on entry
> +   to LOOP, i.e. on the loop preheader edge.  */
>  
>  static void
>  determine_value_range (class loop *loop, tree type, tree var, mpz_t off,
> @@ -356,7 +357,7 @@ determine_value_range (class loop *loop, tree type, tree 
> var, mpz_t off,
>    get_type_static_bounds (type, min, max);
>  
>    /* See if we have some range info from VRP.  */
> -  if (TREE_CODE (var) == SSA_NAME && INTEGRAL_TYPE_P (type))
> +  if (INTEGRAL_TYPE_P (type))
>      {
>        edge e = loop_preheader_edge (loop);
>        signop sgn = TYPE_SIGN (type);
> @@ -364,7 +365,7 @@ determine_value_range (class loop *loop, tree type, tree 
> var, mpz_t off,
>  
>        /* Either for VAR itself...  */
>        int_range_max var_range (TREE_TYPE (var));
> -      get_range_query (cfun)->range_of_expr (var_range, var);
> +      get_range_query (cfun)->range_on_edge (var_range, e, var);
>        if (var_range.varying_p () || var_range.undefined_p ())
>       rtype = VR_VARYING;
>        else
> @@ -382,8 +383,8 @@ determine_value_range (class loop *loop, tree type, tree 
> var, mpz_t off,
>       {
>         gphi *phi = gsi.phi ();
>         if (PHI_ARG_DEF_FROM_EDGE (phi, e) == var
> -           && get_range_query (cfun)->range_of_expr (phi_range,
> -                                                 gimple_phi_result (phi))
> +           && get_range_query (cfun)->range_on_edge (phi_range,
> +                                                 e, gimple_phi_result (phi))
>             && !phi_range.varying_p ()
>             && !phi_range.undefined_p ())
>           {
> @@ -404,7 +405,7 @@ determine_value_range (class loop *loop, tree type, tree 
> var, mpz_t off,
>                 if (wi::gt_p (minv, maxv, sgn))
>                   {
>                     int_range_max vr (TREE_TYPE (var));
> -                   get_range_query (cfun)->range_of_expr (vr, var);
> +                   get_range_query (cfun)->range_on_edge (vr, e, var);
>                     if (vr.varying_p () || vr.undefined_p ())
>                       rtype = VR_VARYING;
>                     else
> 

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

Reply via email to