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;
 
-       /* 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