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 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 each TYPE_OVERFLOW_WRAPS query with its accepted
integral type domain. VECTOR_CST can also represent fixed-point vectors,
which the wrapping predicate does not accept. TYPE_OVERFLOW_SANITIZED
accepts 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 sanitizer test checks that f and g do not report overflow for defined
inputs. It also checks that h reports its real INT_MIN negation. The Arm
test checks all six fixed-point vector modes in ordinary and saturating forms.
Without the type-domain guards, a checking compiler fails. A release compiler
also changes the results of the six saturating cases under -fwrapv.
Bootstrapped and tested on aarch64-none-linux-gnu.
Tested on arm-linux-gnueabihf with QEMU.
Tested on 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.
* match.pd (negate_expr_p): Guard the wrapping query for vector
constants.
gcc/testsuite/ChangeLog:
* g++.dg/ubsan/fold-negate-vector-1.C: New test.
* gcc.target/arm/fixed-point-vector-negate-1.c: Likewise.
Signed-off-by: Kyrylo Tkachov <[email protected]>
---
gcc/fold-const.cc | 12 ++-
gcc/match.pd | 3 +-
.../g++.dg/ubsan/fold-negate-vector-1.C | 46 +++++++++++
.../arm/fixed-point-vector-negate-1.c | 76 +++++++++++++++++++
4 files changed, 134 insertions(+), 3 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/ubsan/fold-negate-vector-1.C
create mode 100644 gcc/testsuite/gcc.target/arm/fixed-point-vector-negate-1.c
diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index 420e3185a2a..dc324c9cfdb 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 (TREE_TYPE (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/match.pd b/gcc/match.pd
index bcfc7f0e8ce..a969aa20417 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -2430,7 +2430,8 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
ways. */
(match negate_expr_p
VECTOR_CST
- (if (FLOAT_TYPE_P (TREE_TYPE (type)) || TYPE_OVERFLOW_WRAPS (type))))
+ (if (FLOAT_TYPE_P (TREE_TYPE (type))
+ || (ANY_INTEGRAL_TYPE_P (type) && TYPE_OVERFLOW_WRAPS (type)))))
(match negate_expr_p
(minus @0 @1)
(if ((ANY_INTEGRAL_TYPE_P (type) && TYPE_OVERFLOW_WRAPS (type))
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" }
diff --git a/gcc/testsuite/gcc.target/arm/fixed-point-vector-negate-1.c
b/gcc/testsuite/gcc.target/arm/fixed-point-vector-negate-1.c
new file mode 100644
index 00000000000..e34f9633929
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/fixed-point-vector-negate-1.c
@@ -0,0 +1,76 @@
+/* { dg-do run { target fixed_point } } */
+/* { dg-require-effective-target arm_arch_v6_arm_multilib } */
+/* { dg-options "-O2 -std=gnu99 -fwrapv" } */
+/* { dg-add-options arm_arch_v6_arm } */
+
+#include <stdfix.h>
+
+/* Check all Arm fixed-point vector modes and their saturating forms. The
+ saturating cases make an invalid X - C to X + (-C) rewrite observable. */
+
+#define DEF2(NAME, TYPE, X, C, R) \
+ typedef TYPE NAME##_type __attribute__ ((vector_size (4))); \
+ static void __attribute__ ((noipa)) \
+ NAME (NAME##_type *out, const NAME##_type *in) \
+ { \
+ *out = *in - (NAME##_type) { C, C }; \
+ } \
+ static void \
+ check_##NAME (void) \
+ { \
+ NAME##_type in = { X, X }; \
+ NAME##_type out; \
+ NAME (&out, &in); \
+ if (out[0] != R || out[1] != R) \
+ __builtin_abort (); \
+ }
+
+#define DEF4(NAME, TYPE, X, C, R) \
+ typedef TYPE NAME##_type __attribute__ ((vector_size (4))); \
+ static void __attribute__ ((noipa)) \
+ NAME (NAME##_type *out, const NAME##_type *in) \
+ { \
+ *out = *in - (NAME##_type) { C, C, C, C }; \
+ } \
+ static void \
+ check_##NAME (void) \
+ { \
+ NAME##_type in = { X, X, X, X }; \
+ NAME##_type out; \
+ NAME (&out, &in); \
+ if (out[0] != R || out[1] != R || out[2] != R || out[3] != R) \
+ __builtin_abort (); \
+ }
+
+DEF4 (v4qq, short _Fract, 0.5hr, 0.25hr, 0.25hr)
+DEF4 (v4uqq, unsigned short _Fract, 0.5uhr, 0.25uhr, 0.25uhr)
+DEF4 (sat_v4qq, _Sat short _Fract, SFRACT_MIN, SFRACT_MIN, 0.0hr)
+DEF4 (sat_v4uqq, _Sat unsigned short _Fract, 0.5uhr, 0.5uhr, 0.0uhr)
+
+DEF2 (v2hq, _Fract, 0.5r, 0.25r, 0.25r)
+DEF2 (v2uhq, unsigned _Fract, 0.5ur, 0.25ur, 0.25ur)
+DEF2 (sat_v2hq, _Sat _Fract, FRACT_MIN, FRACT_MIN, 0.0r)
+DEF2 (sat_v2uhq, _Sat unsigned _Fract, 0.5ur, 0.5ur, 0.0ur)
+
+DEF2 (v2ha, short _Accum, 0.5hk, 0.25hk, 0.25hk)
+DEF2 (v2uha, unsigned short _Accum, 0.5uhk, 0.25uhk, 0.25uhk)
+DEF2 (sat_v2ha, _Sat short _Accum, SACCUM_MIN, SACCUM_MIN, 0.0hk)
+DEF2 (sat_v2uha, _Sat unsigned short _Accum, 0.5uhk, 0.5uhk, 0.0uhk)
+
+int
+main (void)
+{
+ check_v4qq ();
+ check_v4uqq ();
+ check_sat_v4qq ();
+ check_sat_v4uqq ();
+ check_v2hq ();
+ check_v2uhq ();
+ check_sat_v2hq ();
+ check_sat_v2uhq ();
+ check_v2ha ();
+ check_v2uha ();
+ check_sat_v2ha ();
+ check_sat_v2uha ();
+ return 0;
+}
--
2.50.1 (Apple Git-155)