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), which then looks
known to be >= svcntb (), so MIN (33 - svcntb (), svcntb ()) folds to
svcntb () although the result is 1 at VL 256.  Punt when an unsigned
POLY_INT_CST operand may be negative once its coefficients are
sign-extended.  GCC 16 is affected too.  RISC-V V uses the same folding;
no rvv test changes.  Values that wrap past the top of the type, such as
(uint8_t) svcntb () at VL 2048, are a separate issue.

Bootstrapped and regression tested on x86_64-pc-linux-gnu; regression
tested on aarch64-linux-gnu (qemu, VL 128 and SVE VL 2048).

Assisted-by: Claude Opus 5 (Anthropic)

gcc/ChangeLog:

        * fold-const.cc (can_min_p): Punt on unsigned POLY_INT_CST operands
        that may be negative when read as signed.

gcc/testsuite/ChangeLog:

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

Signed-off-by: Dominic P <[email protected]>
---
OK for trunk?

 gcc/fold-const.cc                             |  8 +++++
 .../aarch64/sve/acle/general/min_poly_1.c     | 33 +++++++++++++++++++
 2 files changed, 41 insertions(+)
 create mode 100644 
gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_1.c

diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index 113558973f6..c963a29b265 100644
--- a/gcc/fold-const.cc
+++ b/gcc/fold-const.cc
@@ -1149,6 +1149,14 @@ wide_int_binop (wide_int &res,
 bool
 can_min_p (const_tree arg1, const_tree arg2, poly_wide_int &res)
 {
+  /* An unsigned POLY_INT_CST with a coefficient that is negative when
+     read as signed can wrap.  */
+  for (const_tree arg : { arg1, arg2 })
+    if (POLY_INT_CST_P (arg)
+       && TYPE_UNSIGNED (TREE_TYPE (arg))
+       && maybe_lt (tree_to_poly_int64 (arg), 0))
+      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..99a7139e9f1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/min_poly_1.c
@@ -0,0 +1,33 @@
+/* { 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 ();                    \
+  }
+
+TEST (f1, uint64_t, 33, svcntb)
+TEST (f2, uint32_t, 33, svcnth)
+TEST (f3, uint64_t, 130, svcntb)
+TEST (f4, uint64_t, 300, 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 ();                                      \
+  }
+  CHECK (f1, uint64_t, 33, svcntb)
+  CHECK (f2, uint32_t, 33, svcnth)
+  CHECK (f3, uint64_t, 130, svcntb)
+  CHECK (f4, uint64_t, 300, svcntb)
+  return 0;
+}
-- 
2.55.0

Reply via email to