Re: [PATCH 2/4] fold-const: do not fold unsafe stepped vector negations

Andrea Pinski <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <CALvbMcBSJX86mOYAWVL8-n+sonsbci2nbABwUJRKJf0rj0DMEw@mail.gmail.com>
On Thu, Aug 20, 2026 at 4:57 AM <[email protected]> wrote:
>
> From: Kyrylo Tkachov <[email protected]>
>
> A VECTOR_CST can encode a stepped series without storing every lane.  The
> fold-const negate_expr_p checks only the encoded elements.  It can therefore
> approve a signed vector whose implicit final lane is INT_MIN.
>
>   typedef int v4si __attribute__ ((vector_size (16)));
>
>   const v4si c = { 2147483645, 2147483646, 2147483647,
>                    (-2147483647 - 1) };
>
>   v4si f (v4si x) { return (-x) - c; }
>   v4si g (v4si x) { return -(x + c); }
>   v4si h () { return -c; }
>
> The old predicate lets f and g negate c.  It represents -INT_MIN as INT_MIN,
> which adds a false signed overflow for defined inputs.  fold_negate_expr_1
> can also negate the encoded elements of c directly.  This removes the
> required overflow diagnostic from h when overflow is instrumented.
>
> aarch64 -O2 -fsanitize=signed-integer-overflow
>         -fsanitize-trap=signed-integer-overflow before:
>
>   h:
>           adrp    x0, .LANCHOR0
>           ldr     q0, [x0, #:lo12:.LANCHOR0]
>           ret
>
> After:
>
>   h:
>           brk     #1000
>
> Reject a non-wrapping integral stepped vector in negate_expr_p.  Also keep a
> direct stepped integral negation when sanitizer instrumentation makes overflow
> observable.  Use vector-aware predicates directly.  Keep the integral type
> check before TYPE_OVERFLOW_WRAPS because fixed-point vectors can reach the
> predicate, but TYPE_OVERFLOW_WRAPS does not accept them.  The preceding patch
> makes TYPE_OVERFLOW_SANITIZED accept integral vectors.
>
> The two stepped checks have different conditions.  negate_expr_p is a proof
> for moving a negation.  Moving an implicit INT_MIN negation can introduce
> undefined overflow into f or g even when no overflow option is enabled, so
> that predicate rejects every non-wrapping integral stepped constant.
> fold_negate_expr_1 handles a direct source negation whose INT_MIN lane already
> overflows.  GCC normally folds that constant overflow, and -ftrapv does not
> trap vector constant negation.  Only sanitizer instrumentation makes the
> missing operation observable, so the direct-fold check is conditional on
> TYPE_OVERFLOW_SANITIZED.
>
> The test checks that f and g do not report overflow for defined inputs.  It
> also checks that h reports its real INT_MIN negation.
>
> Bootstrapped and tested on aarch64-none-linux-gnu and x86_64-pc-linux-gnu.
> Ok for trunk?
> Thanks,
> Kyrill
>
> gcc/ChangeLog:
>
>         * fold-const.cc (negate_expr_p): Reject non-wrapping stepped vector
>         constants.
>         (fold_negate_expr_1): Preserve sanitized stepped integral negations.
>
> gcc/testsuite/ChangeLog:
>
>         * g++.dg/ubsan/fold-negate-vector-1.C: New test.
>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
>  gcc/fold-const.cc                             | 12 ++++-
>  .../g++.dg/ubsan/fold-negate-vector-1.C       | 46 +++++++++++++++++++
>  2 files changed, 56 insertions(+), 2 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/ubsan/fold-negate-vector-1.C
>
> diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
> index 420e3185a2a..b2fc63ce5aa 100644
> --- a/gcc/fold-const.cc
> +++ b/gcc/fold-const.cc
> @@ -406,10 +406,14 @@ negate_expr_p (tree t)
>
>      case VECTOR_CST:
>        {
> -       if (FLOAT_TYPE_P (TREE_TYPE (type)) || TYPE_OVERFLOW_WRAPS (type))
> +       if (FLOAT_TYPE_P (type)
> +           || (ANY_INTEGRAL_TYPE_P (type) && TYPE_OVERFLOW_WRAPS (type)))
>           return true;

I think you don't need to change this part any more.  Otherwise ok.


>
> -       /* Steps don't prevent negation.  */
> +       /* An implicit element of a stepped vector can be the minimum
> +          value.  */
> +       if (VECTOR_CST_STEPPED_P (t))
> +         return false;
>         unsigned int count = vector_cst_encoded_nelts (t);
>         for (unsigned int i = 0; i < count; ++i)
>           if (!negate_expr_p (VECTOR_CST_ENCODED_ELT (t, i)))
> @@ -566,6 +570,10 @@ fold_negate_expr_1 (location_t loc, tree t)
>
>      case VECTOR_CST:
>        {
> +       if (VECTOR_CST_STEPPED_P (t)
> +           && TYPE_OVERFLOW_SANITIZED (type))
> +         return NULL_TREE;
> +
>         tree_vector_builder elts;
>         elts.new_unary_operation (type, t, true);
>         unsigned int count = elts.encoded_nelts ();
> diff --git a/gcc/testsuite/g++.dg/ubsan/fold-negate-vector-1.C b/gcc/testsuite/g++.dg/ubsan/fold-negate-vector-1.C
> new file mode 100644
> index 00000000000..cda0fd9817f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ubsan/fold-negate-vector-1.C
> @@ -0,0 +1,46 @@
> +// { dg-do run { target int32 } }
> +// { dg-options "-O2 -Wno-psabi -fsanitize=signed-integer-overflow" }
> +
> +#define INT_MAX __INT_MAX__
> +#define INT_MIN (-INT_MAX - 1)
> +
> +typedef int v4si __attribute__ ((vector_size (16)));
> +
> +const v4si c = { INT_MAX - 2, INT_MAX - 1, INT_MAX, INT_MIN };
> +
> +v4si __attribute__ ((noipa))
> +f (v4si x)
> +{
> +  return (-x) - c;
> +}
> +
> +v4si __attribute__ ((noipa))
> +g (v4si x)
> +{
> +  return -(x + c);
> +}
> +
> +v4si __attribute__ ((noipa))
> +h ()
> +{
> +  return -c;
> +}
> +
> +int
> +main ()
> +{
> +  v4si x = { 0, 0, 0, 1 };
> +  v4si y = f (x);
> +  if (y[3] != INT_MAX)
> +    __builtin_abort ();
> +
> +  y = g (x);
> +  if (y[3] != INT_MAX)
> +    __builtin_abort ();
> +
> +  volatile v4si z = h ();
> +  if (z[3] != INT_MIN)
> +    __builtin_abort ();
> +}
> +
> +// { dg-output "negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself" }
> --
> 2.50.1 (Apple Git-155)
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.