From: Abhishek Kaushik Date: Tue, 16 Jun 2026 13:43:50 +0000 (-0600) Subject: [PATCH] tree-optimization: Query ranger on edge for niter bound expressions X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1fb998f8839b31e91fb154bbf052e9470f083ef6;p=thirdparty%2Fgcc.git [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 --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 + +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 13dd1e30fbb..0c1ebd1c7d5 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