On Thu, Aug 20, 2026 at 7:33 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. Guard the wrapping query with the integral lane type so
> fixed-point vectors continue to use the element checks.
>
> 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 | 13 +++++-
> .../g++.dg/ubsan/fold-negate-vector-1.C | 46 +++++++++++++++++++
> 2 files changed, 57 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..b23bbb6f9b3 100644
> --- a/gcc/fold-const.cc
> +++ b/gcc/fold-const.cc
> @@ -406,10 +406,15 @@ negate_expr_p (tree t)
>
> case VECTOR_CST:
> {
> - if (FLOAT_TYPE_P (TREE_TYPE (type)) || TYPE_OVERFLOW_WRAPS (type))
> + tree etype = TREE_TYPE (type);
> + if (FLOAT_TYPE_P (etype)
> + || (INTEGRAL_TYPE_P (etype) && TYPE_OVERFLOW_WRAPS (type)))
> return true;
>
> - /* Steps don't prevent negation. */
> + /* An implicit element of a stepped vector can be the minimum
> + value. */
> + if (VECTOR_CST_STEPPED_P (t))
why unconditional here
> + 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 +571,10 @@ fold_negate_expr_1 (location_t loc, tree t)
>
> case VECTOR_CST:
> {
> + if (VECTOR_CST_STEPPED_P (t)
> + && TYPE_OVERFLOW_SANITIZED (TREE_TYPE (type)))
but conditional on TYPE_OVERFLOW_SANITIZED here?
> + 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)
>