On Tue, Sep 29, 2026 at 11:52 PM Dominic P <[email protected]> wrote:
>
> 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;

Since it is only 3 lines, either extract this out into its own
function (even a lambda) or just do it inline with arg1 and arg2
directly.  I am not a fan of using "range for" with just 2 elements
like this.

> +
>    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