determine_value_range used ranger information only when the expression
being bounded was itself an SSA_NAME. This missed useful range
information when the expression was a value-preserving conversion of an
SSA_NAME, such as a conversion from uint8_t to int.
This can make niter analysis fall back to the full range of the converted
type. In turn, vectorization can fail to prove that related induction
variables do not wrap.
Look through chains of integral conversions while each conversion
preserves every value of the source type. Use ranger information for the
peeled SSA_NAME when available, and otherwise use the static bounds of the
peeled type. Stop at the first non-value-preserving conversion so that
cases such as uint8_t -> int8_t -> int are not treated as having the
original uint8_t range.
This patch was bootstrapped and regression tested on aarch64-linux-gnu.
gcc/ChangeLog:
* tree-ssa-loop-niter.cc (determine_value_range): Look through
value-preserving integral conversions when determining the range
of VAR. Use the peeled SSA_NAME for ranger and PHI range queries.
Use the peeled type bounds when no ranger range is available.
gcc/testsuite/ChangeLog:
* gcc.target/aarch64/sve2/niter-convert-range.c: New test.
---
.../aarch64/sve2/niter-convert-range.c | 52 ++++++
gcc/tree-ssa-loop-niter.cc | 166 ++++++++++++------
2 files changed, 166 insertions(+), 52 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..b1abfb68375
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve2/niter-convert-range.c
@@ -0,0 +1,52 @@
+/* { dg-options "-O2 -march=armv8.3-a+sve2 -fdump-tree-vect-details" } */
+
+#include <stdint.h>
+
+int modified_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
+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 int
+niter_convert_range_wrap (unsigned char *buf, uint8_t len)
+{
+ unsigned int x = 0;
+
+ for (unsigned int i = 0; i < (unsigned int) len - 1; ++i)
+ x += buf[i];
+
+ return x;
+}
+
+/* { dg-final { scan-tree-dump-times {bounds on difference of bases: -1
[.][.][.] 254} 6 "vect" } } */
+/* { dg-final { scan-tree-dump-times {bounds on difference of bases: -129
[.][.][.] 126} 3 "vect" } } */
+/* { dg-final { scan-tree-dump {bounds on difference of bases: -1 [.][.][.]
4294967294} "vect" } } */
+/* { dg-final { scan-tree-dump-times "loop vectorized using variable length
vectors" 4 "vect" } } */
diff --git a/gcc/tree-ssa-loop-niter.cc b/gcc/tree-ssa-loop-niter.cc
index f179be1ffc7..94330a2e60c 100644
--- a/gcc/tree-ssa-loop-niter.cc
+++ b/gcc/tree-ssa-loop-niter.cc
@@ -344,6 +344,8 @@ determine_value_range (class loop *loop, tree type, tree
var, mpz_t off,
basic_block bb;
wide_int minv, maxv;
enum value_range_kind rtype = VR_VARYING;
+ tree range_var = var;
+ bool use_range_type_bounds_p = false;
/* If the expression is a constant, we know its value exactly. */
if (integer_zerop (var))
@@ -355,76 +357,136 @@ 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))
+ /* Look through a chain of integral conversions, but only while each
+ conversion preserves every value of the source type.
+
+ For example, it is safe to use the range of LEN for
+
+ (int) LEN
+
+ when LEN has type uint8_t.
+
+ But it is not safe to look through the whole chain in
+
+ (int) (int8_t) LEN
+
+ when LEN has type uint8_t, since the uint8_t -> int8_t conversion
+ is not value-preserving. */
+ if (INTEGRAL_TYPE_P (type))
+ while (CONVERT_EXPR_P (range_var))
+ {
+ tree outer_type = TREE_TYPE (range_var);
+ tree inner = TREE_OPERAND (range_var, 0);
+ tree inner_type = TREE_TYPE (inner);
+ mpz_t outer_min, outer_max, inner_min, inner_max;
+ bool value_preserving_p;
+
+ if (!INTEGRAL_TYPE_P (outer_type)
+ || !INTEGRAL_TYPE_P (inner_type))
+ break;
+
+ mpz_inits (outer_min, outer_max, inner_min, inner_max, NULL);
+
+ get_type_static_bounds (outer_type, outer_min, outer_max);
+ get_type_static_bounds (inner_type, inner_min, inner_max);
+
+ value_preserving_p = (mpz_cmp (inner_min, outer_min) >= 0
+ && mpz_cmp (inner_max, outer_max) <= 0);
+
+ mpz_clears (outer_min, outer_max, inner_min, inner_max, NULL);
+
+ if (!value_preserving_p)
+ break;
+
+ range_var = inner;
+ use_range_type_bounds_p = true;
+ }
+
+ /* See if we have some range info from VRP, or if we managed to look
+ through a value-preserving conversion to a narrower static type. */
+ if ((TREE_CODE (range_var) == SSA_NAME || use_range_type_bounds_p)
+ && INTEGRAL_TYPE_P (type))
{
- edge e = loop_preheader_edge (loop);
- signop sgn = TYPE_SIGN (type);
+ signop sgn = TYPE_SIGN (TREE_TYPE (range_var));
gphi_iterator gsi;
- /* Either for VAR itself... */
- int_range_max var_range (TREE_TYPE (var));
- get_range_query (cfun)->range_of_expr (var_range, var);
- if (var_range.varying_p () || var_range.undefined_p ())
- rtype = VR_VARYING;
- else
- rtype = VR_RANGE;
- if (!var_range.undefined_p ())
+ if (TREE_CODE (range_var) == SSA_NAME)
{
- minv = var_range.lower_bound ();
- maxv = var_range.upper_bound ();
- }
+ edge e = loop_preheader_edge (loop);
- /* Or for PHI results in loop->header where VAR is used as
- PHI argument from the loop preheader edge. */
- int_range_max phi_range (TREE_TYPE (var));
- for (gsi = gsi_start_phis (loop->header); !gsi_end_p (gsi); gsi_next
(&gsi))
- {
- 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))
- && !phi_range.varying_p ()
- && !phi_range.undefined_p ())
+ /* Either for RANGE_VAR itself... */
+ int_range_max var_range (TREE_TYPE (range_var));
+ get_range_query (cfun)->range_of_expr (var_range, range_var);
+ if (var_range.varying_p () || var_range.undefined_p ())
+ rtype = VR_VARYING;
+ else
+ rtype = VR_RANGE;
+ if (!var_range.undefined_p ())
{
- if (rtype != VR_RANGE)
- {
- rtype = VR_RANGE;
- minv = phi_range.lower_bound ();
- maxv = phi_range.upper_bound ();
- }
- else
+ minv = var_range.lower_bound ();
+ maxv = var_range.upper_bound ();
+ }
+
+ /* Or for PHI results in loop->header where RANGE_VAR is used as
+ PHI argument from the loop preheader edge. */
+ int_range_max phi_range (TREE_TYPE (range_var));
+ for (gsi = gsi_start_phis (loop->header);
+ !gsi_end_p (gsi);
+ gsi_next (&gsi))
+ {
+ gphi *phi = gsi.phi ();
+ if (PHI_ARG_DEF_FROM_EDGE (phi, e) == range_var
+ && get_range_query (cfun)->range_of_expr
+ (phi_range, gimple_phi_result (phi))
+ && !phi_range.varying_p ()
+ && !phi_range.undefined_p ())
{
- minv = wi::max (minv, phi_range.lower_bound (), sgn);
- maxv = wi::min (maxv, phi_range.upper_bound (), sgn);
- /* If the PHI result range are inconsistent with
- the VAR range, give up on looking at the PHI
- results. This can happen if VR_UNDEFINED is
- involved. */
- if (wi::gt_p (minv, maxv, sgn))
+ if (rtype != VR_RANGE)
+ {
+ rtype = VR_RANGE;
+ minv = phi_range.lower_bound ();
+ maxv = phi_range.upper_bound ();
+ }
+ else
{
- int_range_max vr (TREE_TYPE (var));
- get_range_query (cfun)->range_of_expr (vr, var);
- if (vr.varying_p () || vr.undefined_p ())
- rtype = VR_VARYING;
- else
- rtype = VR_RANGE;
- if (!vr.undefined_p ())
+ minv = wi::max (minv, phi_range.lower_bound (), sgn);
+ maxv = wi::min (maxv, phi_range.upper_bound (), sgn);
+
+ /* If the PHI result ranges are inconsistent with
+ the RANGE_VAR range, give up on looking at the PHI
+ results. This can happen if VR_UNDEFINED is
+ involved. */
+ if (wi::gt_p (minv, maxv, sgn))
{
- minv = vr.lower_bound ();
- maxv = vr.upper_bound ();
+ int_range_max vr (TREE_TYPE (range_var));
+ get_range_query (cfun)->range_of_expr (vr, range_var);
+ if (vr.varying_p () || vr.undefined_p ())
+ rtype = VR_VARYING;
+ else
+ rtype = VR_RANGE;
+ if (!vr.undefined_p ())
+ {
+ minv = vr.lower_bound ();
+ maxv = vr.upper_bound ();
+ }
+ break;
}
- break;
}
}
}
}
+
mpz_init (minm);
mpz_init (maxm);
if (rtype != VR_RANGE)
{
- mpz_set (minm, min);
- mpz_set (maxm, max);
+ if (use_range_type_bounds_p)
+ get_type_static_bounds (TREE_TYPE (range_var), minm, maxm);
+ else
+ {
+ mpz_set (minm, min);
+ mpz_set (maxm, max);
+ }
}
else
{
--
2.43.0