can_min_p compares POLY_INT_CSTs as poly_widest_ints, which zero-extends
the coefficients of an unsigned type.  33 - svcntb () in uint64_t is
POLY_INT_CST [17, 18446744073709551600] (17 - 16x), so
MIN (33 - svcntb (), svcntb ()) folds to svcntb () although the result
is 1 at VL 256.  Likewise (uint8_t) svcntb () is POLY_INT_CST [16, 16],
so MIN of it and 5 folds to 5 although it is 0 at VL 2048.  Punt when
an unsigned POLY_INT_CST may exceed its type for the bounds given by
TARGET_POLY_INT_INDETERMINATE_BOUND.  Without the hook, as on RISC-V,
punt only when a coefficient is negative when read as signed, so
wraps past the top of the type still fold there.

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): New function.
        (maybe_wrapping_poly_p): Likewise.
        (can_min_p): Use maybe_wrapping_poly_p to punt on unsigned
        POLY_INT_CST operands that may wrap.

gcc/testsuite/ChangeLog:

        * gcc.target/aarch64/sve/acle/general/min_poly_1.c: New test.
        * gcc.target/aarch64/sve/acle/general/min_poly_4.c: New test.

Signed-off-by: Dominic P <[email protected]>
---
Changes in v2: the check is now a static helper, as you suggested.  It
also catches unsigned POLY_INT_CSTs that wrap past the top of their
type, such as (uint8_t) svcntb (), using
TARGET_POLY_INT_INDETERMINATE_BOUND (poly_int_cst_bounds also returns
the lower bound, for 2/2); without the hook it behaves as v1.
min_poly_4.c checks the code at any VL; min_poly_1.c runs it.  GCC 16
is affected too, but it lacks the hook, so a backport would catch only
the negative coefficients.

 gcc/fold-const.cc                             | 41 ++++++++++++++
 .../aarch64/sve/acle/general/min_poly_1.c     | 55 +++++++++++++++++++
 .../aarch64/sve/acle/general/min_poly_4.c     | 51 +++++++++++++++++
 3 files changed, 147 insertions(+)
 create mode 100644 
gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_1.c
 create mode 100644 
gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_4.c

diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index 113558973f6..6795c15b906 100644
--- a/gcc/fold-const.cc
+++ b/gcc/fold-const.cc
@@ -1144,11 +1144,52 @@ wide_int_binop (wide_int &res,
   return true;
 }
 
+/* If the target defines TARGET_POLY_INT_INDETERMINATE_BOUND, set *LO and
+   *HI to the smallest and largest values of POLY_INT_CST ARG over those
+   bounds, computed in infinite precision, and return true.  Otherwise
+   return false.  */
+
+static bool
+poly_int_cst_bounds (const_tree arg, widest_int *lo, widest_int *hi)
+{
+  if (!targetm.poly_int_indeterminate_bound)
+    return false;
+  poly_uint64 bound = targetm.poly_int_indeterminate_bound ();
+  *lo = *hi = wi::to_widest (POLY_INT_CST_COEFF (arg, 0));
+  for (unsigned int i = 1; i < NUM_POLY_INT_COEFFS; ++i)
+    {
+      auto term = (wi::to_widest (POLY_INT_CST_COEFF (arg, i))
+                  * bound.coeffs[i]);
+      (wi::neg_p (term) ? *lo : *hi) += term;
+    }
+  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.  */
+
+static bool
+maybe_wrapping_poly_p (const_tree arg)
+{
+  if (!POLY_INT_CST_P (arg) || !TYPE_UNSIGNED (TREE_TYPE (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);
+}
+
 /* Returns true if we know who is smaller or equal, ARG1 or ARG2, and set the
    min value to RES.  */
 bool
 can_min_p (const_tree arg1, const_tree arg2, poly_wide_int &res)
 {
+  if (maybe_wrapping_poly_p (arg1) || maybe_wrapping_poly_p (arg2))
+    return false;
+
   if (known_le (wi::to_poly_widest (arg1), wi::to_poly_widest (arg2)))
     {
       res = wi::to_poly_wide (arg1);
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_1.c 
b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_1.c
new file mode 100644
index 00000000000..896c6169c02
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_1.c
@@ -0,0 +1,55 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O2" } */
+
+#include <arm_sve.h>
+
+#define TEST(NAME, T, K, CNT)                                  \
+  T __attribute__ ((noipa))                                    \
+  NAME (void)                                                  \
+  {                                                            \
+    T n = (T) K - (T) CNT ();                                  \
+    return n < (T) CNT () ? n : (T) CNT ();                    \
+  }
+
+#define TEST_MUL(NAME, T, K, CNT)                              \
+  T __attribute__ ((noipa))                                    \
+  NAME (void)                                                  \
+  {                                                            \
+    T n = CNT () * K;                                          \
+    return n < 5 ? n : 5;                                      \
+  }
+
+TEST (f1, uint64_t, 33, svcntb)
+TEST (f2, uint32_t, 33, svcnth)
+TEST (f3, uint64_t, 130, svcntb)
+TEST (f4, uint64_t, 300, svcntb)
+TEST_MUL (g1, uint16_t, 1024, svcntb)
+TEST_MUL (g2, uint8_t, 4, svcnth)
+TEST_MUL (g3, uint32_t, 0x4000000, svcntb)
+TEST_MUL (g4, uint8_t, 1, svcntb)
+
+int __attribute__ ((optimize (0)))
+main (void)
+{
+#define CHECK(NAME, T, K, CNT)                                 \
+  {                                                            \
+    T c = CNT (), n = (T) K - c;                               \
+    if (NAME () != (n < c ? n : c))                            \
+      __builtin_abort ();                                      \
+  }
+#define CHECK_MUL(NAME, T, K, CNT)                             \
+  {                                                            \
+    T n = CNT () * K;                                          \
+    if (NAME () != (n < 5 ? n : 5))                            \
+      __builtin_abort ();                                      \
+  }
+  CHECK (f1, uint64_t, 33, svcntb)
+  CHECK (f2, uint32_t, 33, svcnth)
+  CHECK (f3, uint64_t, 130, svcntb)
+  CHECK (f4, uint64_t, 300, svcntb)
+  CHECK_MUL (g1, uint16_t, 1024, svcntb)
+  CHECK_MUL (g2, uint8_t, 4, svcnth)
+  CHECK_MUL (g3, uint32_t, 0x4000000, svcntb)
+  CHECK_MUL (g4, uint8_t, 1, svcntb)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_4.c 
b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_4.c
new file mode 100644
index 00000000000..7ff76a29bb6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_4.c
@@ -0,0 +1,51 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include <arm_sve.h>
+
+#define TEST(NAME, T, K, CNT)                                  \
+  T NAME (void)                                                        \
+  {                                                            \
+    T n = (T) K - (T) CNT ();                                  \
+    return n < (T) CNT () ? n : (T) CNT ();                    \
+  }
+
+#define TEST_MUL(NAME, T, K, CNT)                              \
+  T NAME (void)                                                        \
+  {                                                            \
+    T n = CNT () * K;                                          \
+    return n < 5 ? n : 5;                                      \
+  }
+
+/*
+** f1:
+**     ...
+**     csel    [^\n]+
+**     ret
+*/
+TEST (f1, uint64_t, 33, svcntb)
+
+/*
+** f2:
+**     ...
+**     csel    [^\n]+
+**     ret
+*/
+TEST (f2, uint32_t, 33, svcnth)
+
+/*
+** g1:
+**     ...
+**     csel    [^\n]+
+**     ret
+*/
+TEST_MUL (g1, uint16_t, 1024, svcntb)
+
+/*
+** g4:
+**     ...
+**     csel    [^\n]+
+**     ret
+*/
+TEST_MUL (g4, uint8_t, 1, svcntb)
-- 
2.56.0

Reply via email to