A signed POLY_INT_CST can wrap without undefined behaviour:
fold_convert_const narrows coefficient-wise, so (int8_t) svcntb () is
POLY_INT_CST [16, 16] but -128 for 1024-bit vectors, and GCC does not
treat signed << overflow as undefined. get_tree_range saturates a
signed bound that overflows, as Richard Sandiford asked in the review of
the POLY_INT_CST range support (<[email protected]>); that
is right for a value that cannot wrap, but here it gives [16, 127], so
(int8_t) svcntb () < 5 folds to false. Use poly_int_cst_bounds there
and give VARYING when the target has no bound or a bound does not fit
the type, and let can_min_p punt on signed POLY_INT_CSTs whose bounds
do not fit.
Pointer-typed POLY_INT_CSTs, such as (char *) svcntb (), now get VARYING
instead of an ICE.
Bootstrapped and regression tested on x86_64-pc-linux-gnu; regression
tested on aarch64-linux-gnu under qemu (the SVE ACLE, vect, aarch64-sve
and execute tests at VL 128, and at VL 2048 with -march=armv8.2-a+sve).
Assisted-by: Claude Opus 5 (Anthropic)
gcc/ChangeLog:
* fold-const.cc (poly_int_cst_bounds): Export.
(maybe_wrapping_poly_p): Handle signed POLY_INT_CSTs.
* fold-const.h (poly_int_cst_bounds): Declare.
* value-query.cc: Do not include target.h.
(range_query::get_tree_range): Use poly_int_cst_bounds. Give
VARYING rather than a saturated range when the bounds are unknown
or do not fit, and for pointer types.
gcc/testsuite/ChangeLog:
* gcc.target/aarch64/sve/acle/general/cntb_ptr_1.c: New test.
* gcc.target/aarch64/sve/acle/general/min_poly_2.c: New test.
* gcc.target/aarch64/sve/acle/general/min_poly_2_run.c: New test.
* gcc.target/aarch64/sve/acle/general/min_poly_3.c: New test.
* gcc.target/aarch64/sve/acle/general/min_poly_3_run.c: New test.
Signed-off-by: Dominic P <[email protected]>
---
gcc/fold-const.cc | 16 ++--
gcc/fold-const.h | 1 +
.../aarch64/sve/acle/general/cntb_ptr_1.c | 11 +++
.../aarch64/sve/acle/general/min_poly_2.c | 88 +++++++++++++++++
.../aarch64/sve/acle/general/min_poly_2_run.c | 24 +++++
.../aarch64/sve/acle/general/min_poly_3.c | 50 ++++++++++
.../aarch64/sve/acle/general/min_poly_3_run.c | 20 ++++
gcc/value-query.cc | 96 +++----------------
8 files changed, 214 insertions(+), 92 deletions(-)
create mode 100644
gcc/testsuite/gcc.target/aarch64/sve/acle/general/cntb_ptr_1.c
create mode 100644
gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_2.c
create mode 100644
gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_2_run.c
create mode 100644
gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_3.c
create mode 100644
gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_3_run.c
diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index 6795c15b906..5e2a06c71d1 100644
--- a/gcc/fold-const.cc
+++ b/gcc/fold-const.cc
@@ -1149,7 +1149,7 @@ wide_int_binop (wide_int &res,
bounds, computed in infinite precision, and return true. Otherwise
return false. */
-static bool
+bool
poly_int_cst_bounds (const_tree arg, widest_int *lo, widest_int *hi)
{
if (!targetm.poly_int_indeterminate_bound)
@@ -1165,21 +1165,21 @@ poly_int_cst_bounds (const_tree arg, widest_int *lo,
widest_int *hi)
return true;
}
-/* Return true if ARG is an unsigned POLY_INT_CST that may wrap for some
- runtime value of its indeterminates. Without
- TARGET_POLY_INT_INDETERMINATE_BOUND, only catch coefficients that are
- negative when read as signed. */
+/* Return true if ARG is a POLY_INT_CST that may wrap for some runtime
+ value of its indeterminates. Without TARGET_POLY_INT_INDETERMINATE_BOUND,
+ only catch unsigned POLY_INT_CSTs that may be negative when read as
+ signed. */
static bool
maybe_wrapping_poly_p (const_tree arg)
{
- if (!POLY_INT_CST_P (arg) || !TYPE_UNSIGNED (TREE_TYPE (arg)))
+ if (!POLY_INT_CST_P (arg))
return false;
tree type = TREE_TYPE (arg);
widest_int lo, hi;
if (!poly_int_cst_bounds (arg, &lo, &hi))
- return maybe_lt (tree_to_poly_int64 (arg), 0);
- return !wi::fits_to_tree_p (hi, type);
+ return TYPE_UNSIGNED (type) && maybe_lt (tree_to_poly_int64 (arg), 0);
+ return !wi::fits_to_tree_p (lo, type) || !wi::fits_to_tree_p (hi, type);
}
/* Returns true if we know who is smaller or equal, ARG1 or ARG2, and set the
diff --git a/gcc/fold-const.h b/gcc/fold-const.h
index ab03bf0aa35..d9f0c373e39 100644
--- a/gcc/fold-const.h
+++ b/gcc/fold-const.h
@@ -125,6 +125,7 @@ extern tree fold_vec_perm (tree, tree, tree, const
vec_perm_indices &);
extern bool wide_int_binop (wide_int &res, enum tree_code,
const wide_int &arg1, const wide_int &arg2,
signop, wi::overflow_type *);
+extern bool poly_int_cst_bounds (const_tree, widest_int *, widest_int *);
extern bool poly_int_binop (poly_wide_int &res, enum tree_code,
const_tree, const_tree, signop,
wi::overflow_type *);
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/acle/general/cntb_ptr_1.c
b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/cntb_ptr_1.c
new file mode 100644
index 00000000000..d208920096e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/cntb_ptr_1.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+#include <arm_sve.h>
+
+int
+f (void)
+{
+ char *p = (char *) svcntb ();
+ return p != 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_2.c
b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_2.c
new file mode 100644
index 00000000000..bface1b739a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_2.c
@@ -0,0 +1,88 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include <arm_sve.h>
+
+/*
+** min_b:
+** (cnt[bhwd]|rdvl) [^\n]+
+** ...
+** csel [^\n]+
+** ret
+*/
+int8_t __attribute__ ((noipa))
+min_b (void)
+{
+ int8_t n = svcntb ();
+ return n < 5 ? n : 5;
+}
+
+/*
+** max_h:
+** (cnt[bhwd]|rdvl) [^\n]+
+** ...
+** csel [^\n]+
+** ...
+** ret
+*/
+int16_t __attribute__ ((noipa))
+max_h (void)
+{
+ int16_t n = svcnth () * 1024;
+ return n > 7 ? n : 7;
+}
+
+/*
+** gt_w:
+** (cnt[bhwd]|rdvl) [^\n]+
+** ...
+** cset [^\n]+
+** ret
+*/
+int __attribute__ ((noipa))
+gt_w (void)
+{
+ int32_t n = svcntw () * 0x2000000;
+ return n > 0;
+}
+
+/*
+** ne_d:
+** (cnt[bhwd]|rdvl) [^\n]+
+** ...
+** cset [^\n]+
+** ret
+*/
+int __attribute__ ((noipa))
+ne_d (void)
+{
+ int8_t n = svcntd () * 16;
+ return n != 0;
+}
+
+/*
+** lt_neg:
+** (cnt[bhwd]|rdvl) [^\n]+
+** ...
+** ret
+*/
+int __attribute__ ((noipa))
+lt_neg (void)
+{
+ int8_t n = -svcntb ();
+ return n < 0;
+}
+
+/*
+** ge_d:
+** (cnt[bhwd]|rdvl) [^\n]+
+** ...
+** ret
+*/
+int __attribute__ ((noipa))
+ge_d (void)
+{
+ int64_t n = (int64_t) svcntd () << 58;
+ return n >= 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_2_run.c
b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_2_run.c
new file mode 100644
index 00000000000..c3129f591ea
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_2_run.c
@@ -0,0 +1,24 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O2" } */
+
+#include "min_poly_2.c"
+
+volatile uint64_t vcnt;
+
+int
+main (void)
+{
+ vcnt = svcntd ();
+ int8_t b = vcnt * 8, d = vcnt * 16, neg = -(vcnt * 8);
+ int16_t h = vcnt * 4 * 1024;
+ int32_t w = vcnt * 2 * 0x2000000;
+ int64_t d58 = vcnt << 58;
+ if (min_b () != (b < 5 ? b : 5)
+ || max_h () != (h > 7 ? h : 7)
+ || gt_w () != (w > 0)
+ || ne_d () != (d != 0)
+ || lt_neg () != (neg < 0)
+ || ge_d () != (d58 >= 0))
+ __builtin_abort ();
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_3.c
b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_3.c
new file mode 100644
index 00000000000..ff5a0ea82a7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_3.c
@@ -0,0 +1,50 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fwrapv" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include <arm_sve.h>
+
+/*
+** min_16:
+** (cnt[bhwd]|rdvl) [^\n]+
+** ...
+** csel [^\n]+
+** ret
+*/
+int16_t __attribute__ ((noipa))
+min_16 (void)
+{
+ int16_t n = svcntb ();
+ n *= 0x100;
+ return n < 5 ? n : 5;
+}
+
+/*
+** min_32:
+** (cnt[bhwd]|rdvl) [^\n]+
+** ...
+** csel [^\n]+
+** ret
+*/
+int32_t __attribute__ ((noipa))
+min_32 (void)
+{
+ int32_t n = svcntb ();
+ n *= 0x1000000;
+ return n < 5 ? n : 5;
+}
+
+/*
+** min_64:
+** (cnt[bhwd]|rdvl) [^\n]+
+** ...
+** csel [^\n]+
+** ret
+*/
+int64_t __attribute__ ((noipa))
+min_64 (void)
+{
+ int64_t n = svcntb ();
+ n *= 0x80000000000000;
+ return n < 5 ? n : 5;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_3_run.c
b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_3_run.c
new file mode 100644
index 00000000000..a2677567c39
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_3_run.c
@@ -0,0 +1,20 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O2 -fwrapv" } */
+
+#include "min_poly_3.c"
+
+volatile int64_t vcnt;
+
+int
+main (void)
+{
+ vcnt = svcntb ();
+ int16_t h = vcnt * 0x100;
+ int32_t w = vcnt * 0x1000000;
+ int64_t d = vcnt * 0x80000000000000;
+ if (min_16 () != (h < 5 ? h : 5)
+ || min_32 () != (w < 5 ? w : 5)
+ || min_64 () != (d < 5 ? d : 5))
+ __builtin_abort ();
+ return 0;
+}
diff --git a/gcc/value-query.cc b/gcc/value-query.cc
index 78976e21f25..425afa4982e 100644
--- a/gcc/value-query.cc
+++ b/gcc/value-query.cc
@@ -32,7 +32,6 @@ along with GCC; see the file COPYING3. If not see
#include "alloc-pool.h"
#include "gimple-range.h"
#include "value-range-storage.h"
-#include "target.h"
// Attempt to make Q the current range query for FUN. If it is already the
// current range query, return NULL.
@@ -442,94 +441,23 @@ range_query::get_tree_range (vrange &r, tree expr, gimple
*stmt,
if (POLY_INT_CST_P (expr))
{
unsigned int precision = TYPE_PRECISION (type);
- signop sign = TYPE_SIGN (type);
- bool have_poly_bound = targetm.poly_int_indeterminate_bound;
- poly_uint64 indeterminate_bound;
-
- if (have_poly_bound)
- indeterminate_bound = targetm.poly_int_indeterminate_bound ();
-
- auto val = wi::to_poly_wide (expr);
- auto type_min = wi::to_wide (TYPE_MIN_VALUE (type));
- auto type_max = wi::to_wide (TYPE_MAX_VALUE (type));
-
- /* Start with the invariant part of the poly-int, then account
- for each coefficient below.
-
- The target hook gives a per-coefficient upper bound for the
- indeterminate. Since those indeterminates are unsigned and
- nonnegative, a positive coefficient can only increase the upper
- bound and a negative coefficient can only decrease the lower
- bound. The opposite bound is unaffected by that coefficient:
-
- [A, +C] with C >= 0 => max += C * bound
- [A, -C] with C >= 0 => min -= C * bound. */
- wide_int bounds[2] = { val.coeffs[0], val.coeffs[0] };
- bool ovf[2] = { false, false };
-
- for (unsigned int i = 1; i < NUM_POLY_INT_COEFFS; ++i)
- {
- const auto &coeff = val.coeffs[i];
- if (wi::eq_p (coeff, 0))
- continue;
-
- /* Select the only bound affected by this coefficient. A
- negative coefficient contributes to the minimum and a positive
- coefficient contributes to the maximum. */
- bool coeff_neg = wi::neg_p (coeff, sign);
- wide_int &bound = bounds[coeff_neg ? 0 : 1];
- bool &bound_ovf = ovf[coeff_neg ? 0 : 1];
-
- if (bound_ovf)
- continue;
-
- /* A missing hook, or a -1 bound for this coefficient, means the
- indeterminate has no finite target-specific limit. Treat that
- like an overflow of the affected bound. */
- if (!have_poly_bound
- || indeterminate_bound.coeffs[i] == HOST_WIDE_INT_M1U)
- bound_ovf = true;
- else
- {
- auto indeterminate
- = wi::uhwi (indeterminate_bound.coeffs[i], precision);
- wi::overflow_type mul_ovf = wi::OVF_NONE;
- auto term = wi::mul (coeff, indeterminate, sign, &mul_ovf);
- wi::overflow_type add_ovf = wi::OVF_NONE;
- bound = wi::add (bound, term, sign, &add_ovf);
- bound_ovf = (mul_ovf != wi::OVF_NONE
- || add_ovf != wi::OVF_NONE);
- }
-
- if (TYPE_OVERFLOW_WRAPS (type) && bound_ovf)
- {
- r.set_varying (type);
- r.update_bitmask (irange_bitmask (wi::zero (precision),
- get_nonzero_bits (expr)));
- return true;
- }
-
- if (bound_ovf)
- {
- if (coeff_neg)
- bounds[0] = type_min;
- else
- bounds[1] = type_max;
- }
- }
-
- /* Check that the target filled in sensible bounds information. */
- gcc_assert (wi::le_p (bounds[0], bounds[1], sign));
-
- irange &ir = as_a <irange> (r);
- ir.set (type, bounds[0], bounds[1]);
+ widest_int lo, hi;
+ if (is_a <irange> (r)
+ && poly_int_cst_bounds (expr, &lo, &hi)
+ && wi::fits_to_tree_p (lo, type)
+ && wi::fits_to_tree_p (hi, type))
+ as_a <irange> (r).set (type,
+ wide_int::from (lo, precision, SIGNED),
+ wide_int::from (hi, precision, SIGNED));
+ else
+ r.set_varying (type);
/* Preserve alignment/step information that is not visible in the
intervals. For example, a poly-int like [8, 8] can
only produce multiples of 8, but the interval range might be
[8, 136], which also contains values with low bits set. */
- ir.update_bitmask (irange_bitmask (wi::zero (precision),
- get_nonzero_bits (expr)));
+ r.update_bitmask (irange_bitmask (wi::zero (precision),
+ get_nonzero_bits (expr)));
return true;
}
break;
--
2.56.0