https://gcc.gnu.org/g:1fb998f8839b31e91fb154bbf052e9470f083ef6

commit r17-1590-g1fb998f8839b31e91fb154bbf052e9470f083ef6
Author: Abhishek Kaushik <[email protected]>
Date:   Tue Jun 16 07:43:50 2026 -0600

    [PATCH] tree-optimization: Query ranger on edge for niter bound expressions
    
    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.
    
    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.

Diff:
---
 .../gcc.target/aarch64/sve2/niter-convert-range.c  | 68 ++++++++++++++++++++++
 gcc/tree-ssa-loop-niter.cc                         | 15 ++---
 2 files changed, 76 insertions(+), 7 deletions(-)

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 000000000000..c96b2f3999c2
--- /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 13dd1e30fbbb..0c1ebd1c7d51 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

Reply via email to