On Sat, Aug 22, 2026 at 12:32 AM Kyrylo Tkachov <[email protected]> wrote:
>
>
>
> > 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.

That is NOT true. You can use a simulator like qemu and build a cross.
Both MIPS and ARM cross compilers are easy to build for running with
qemu with glibc.


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

Reply via email to