> On 21 Aug 2026, at 20:45, Andrea Pinski <[email protected]>
> wrote:
>
> On Fri, Aug 21, 2026 at 4:30 AM Kyrylo Tkachov <[email protected]> wrote:
>>
>>
>>
>>> On 20 Aug 2026, at 21:32, Andrea Pinski <[email protected]>
>>> wrote:
>>>
>>> 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.
>>
>> I think we still need it. A VECTOR_CST can also contain fixed-point lanes.
>> Such a vector is not ANY_INTEGRAL_TYPE_P.
>> e.g. typedef _Fract v2hq __attribute__ ((vector_size (4)))
>
> Hmm, I always forget about fixed point since they are conditional
> types in the first place (only arm, mips and avr enables them; I
> wonder if we can enable them in a more major target).
> It would be a good idea to add many more fixed point testcases because
> I noticed they are lacking and folks forget about them too.
I don’t have easy access to those targets to test a lot of testcases
unfortunately.
Even if I get an LLM to generate them it’d be pointless without a way to run
them.
I suppose qemu is an option. But where does that leave this patch (and 3/4,
4/4)?
Shall I leave in this check for now?
Thanks,
Kyrill
>
>
>> Thanks,
>> Kyrill
>>
>>>
>>>>
>>>> - /* 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)