From 3fccbb9ecec7daa3b6468f72379c0bd1fb5bb8d9 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Mon, 15 Jan 2018 10:05:59 +0100 Subject: [PATCH] re PR middle-end/82694 (Linux kernel miscompiled since r250765) PR middle-end/82694 * common.opt (fstrict-overflow): No longer an alias. (fwrapv-pointer): New option. * tree.h (TYPE_OVERFLOW_WRAPS, TYPE_OVERFLOW_UNDEFINED): Define also for pointer types based on flag_wrapv_pointer. * opts.c (common_handle_option) : Set opts->x_flag_wrap[pv] to !value, clear opts->x_flag_trapv if opts->x_flag_wrapv got set. * fold-const.c (fold_comparison, fold_binary_loc): Revert 2017-08-01 changes, just use TYPE_OVERFLOW_UNDEFINED on pointer type instead of POINTER_TYPE_OVERFLOW_UNDEFINED. * match.pd: Likewise in address comparison pattern. * doc/invoke.texi: Document -fwrapv and -fstrict-overflow. * gcc.dg/no-strict-overflow-7.c: Revert 2017-08-01 changes. * gcc.dg/tree-ssa/pr81388-1.c: Likewise. From-SVN: r256686 --- gcc/ChangeLog | 16 ++++++++++++++ gcc/common.opt | 8 +++++-- gcc/doc/invoke.texi | 12 +++++++++++ gcc/fold-const.c | 24 ++++++++++++++------- gcc/match.pd | 9 +++++++- gcc/opts.c | 7 ++++++ gcc/testsuite/ChangeLog | 6 ++++++ gcc/testsuite/gcc.dg/no-strict-overflow-7.c | 6 +++--- gcc/testsuite/gcc.dg/tree-ssa/pr81388-1.c | 6 ++---- gcc/tree.h | 17 +++++++++------ 10 files changed, 87 insertions(+), 24 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 43c9cfa0ee8..76977e0d053 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,19 @@ +2018-01-15 Jakub Jelinek + + PR middle-end/82694 + * common.opt (fstrict-overflow): No longer an alias. + (fwrapv-pointer): New option. + * tree.h (TYPE_OVERFLOW_WRAPS, TYPE_OVERFLOW_UNDEFINED): Define + also for pointer types based on flag_wrapv_pointer. + * opts.c (common_handle_option) : Set + opts->x_flag_wrap[pv] to !value, clear opts->x_flag_trapv if + opts->x_flag_wrapv got set. + * fold-const.c (fold_comparison, fold_binary_loc): Revert 2017-08-01 + changes, just use TYPE_OVERFLOW_UNDEFINED on pointer type instead of + POINTER_TYPE_OVERFLOW_UNDEFINED. + * match.pd: Likewise in address comparison pattern. + * doc/invoke.texi: Document -fwrapv and -fstrict-overflow. + 2018-01-15 Richard Biener PR lto/83804 diff --git a/gcc/common.opt b/gcc/common.opt index a07cfdb8409..b20a9aac82e 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -2411,8 +2411,8 @@ Common Report Var(flag_strict_aliasing) Optimization Assume strict aliasing rules apply. fstrict-overflow -Common NegativeAlias Alias(fwrapv) -Treat signed overflow as undefined. Negated as -fwrapv. +Common Report +Treat signed overflow as undefined. Negated as -fwrapv -fwrapv-pointer. fsync-libcalls Common Report Var(flag_sync_libcalls) Init(1) @@ -2860,6 +2860,10 @@ fwhole-program Common Report Var(flag_whole_program) Init(0) Perform whole program optimizations. +fwrapv-pointer +Common Report Var(flag_wrapv_pointer) Optimization +Assume pointer overflow wraps around. + fwrapv Common Report Var(flag_wrapv) Optimization Assume signed arithmetic overflow wraps around. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index f57c0584463..98b73db17af 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -12581,6 +12581,18 @@ The options @option{-ftrapv} and @option{-fwrapv} override each other, so using using @option{-ftrapv} @option{-fwrapv} @option{-fno-wrapv} on the command-line results in @option{-ftrapv} being effective. +@item -fwrapv-pointer +@opindex fwrapv-pointer +This option instructs the compiler to assume that pointer arithmetic +overflow on addition and subtraction wraps around using twos-complement +representation. This flag disables some optimizations which assume +pointer overflow is invalid. + +@item -fstrict-overflow +@opindex fstrict-overflow +This option implies @option{-fno-wrapv} @option{-fno-wrapv-pointer} and when +negated implies @option{-fwrapv} @option{-fwrapv-pointer}. + @item -fexceptions @opindex fexceptions Enable exception handling. Generates extra code needed to propagate diff --git a/gcc/fold-const.c b/gcc/fold-const.c index cfb1b3d0614..f3749db7ed6 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -8551,9 +8551,13 @@ fold_comparison (location_t loc, enum tree_code code, tree type, { /* We can fold this expression to a constant if the non-constant offset parts are equal. */ - if (offset0 == offset1 - || (offset0 && offset1 - && operand_equal_p (offset0, offset1, 0))) + if ((offset0 == offset1 + || (offset0 && offset1 + && operand_equal_p (offset0, offset1, 0))) + && (equality_code + || (indirect_base0 + && (DECL_P (base0) || CONSTANT_CLASS_P (base0))) + || TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (arg0)))) { if (!equality_code && maybe_ne (bitpos0, bitpos1) @@ -8612,7 +8616,11 @@ fold_comparison (location_t loc, enum tree_code code, tree type, because pointer arithmetic is restricted to retain within an object and overflow on pointer differences is undefined as of 6.5.6/8 and /9 with respect to the signed ptrdiff_t. */ - else if (known_eq (bitpos0, bitpos1)) + else if (known_eq (bitpos0, bitpos1) + && (equality_code + || (indirect_base0 + && (DECL_P (base0) || CONSTANT_CLASS_P (base0))) + || TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (arg0)))) { /* By converting to signed sizetype we cover middle-end pointer arithmetic which operates on unsigned pointer types of size @@ -9721,8 +9729,8 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, /* With undefined overflow prefer doing association in a type which wraps on overflow, if that is one of the operand types. */ - if (POINTER_TYPE_P (type) - || (INTEGRAL_TYPE_P (type) && !TYPE_OVERFLOW_WRAPS (type))) + if ((POINTER_TYPE_P (type) || INTEGRAL_TYPE_P (type)) + && !TYPE_OVERFLOW_WRAPS (type)) { if (INTEGRAL_TYPE_P (TREE_TYPE (arg0)) && TYPE_OVERFLOW_WRAPS (TREE_TYPE (arg0))) @@ -9735,8 +9743,8 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, /* With undefined overflow we can only associate constants with one variable, and constants whose association doesn't overflow. */ - if (POINTER_TYPE_P (atype) - || (INTEGRAL_TYPE_P (atype) && !TYPE_OVERFLOW_WRAPS (atype))) + if ((POINTER_TYPE_P (atype) || INTEGRAL_TYPE_P (atype)) + && !TYPE_OVERFLOW_WRAPS (atype)) { if ((var0 && var1) || (minus_var0 && minus_var1)) { diff --git a/gcc/match.pd b/gcc/match.pd index 435125a3172..3f6e0094bf5 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -3610,7 +3610,14 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) || TREE_CODE (base1) == STRING_CST)) equal = (base0 == base1); } - (if (equal == 1) + (if (equal == 1 + && (cmp == EQ_EXPR || cmp == NE_EXPR + /* If the offsets are equal we can ignore overflow. */ + || known_eq (off0, off1) + || TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0)) + /* Or if we compare using pointers to decls or strings. */ + || (POINTER_TYPE_P (TREE_TYPE (@2)) + && (DECL_P (base0) || TREE_CODE (base0) == STRING_CST)))) (switch (if (cmp == EQ_EXPR && (known_eq (off0, off1) || known_ne (off0, off1))) { constant_boolean_node (known_eq (off0, off1), type); }) diff --git a/gcc/opts.c b/gcc/opts.c index e9df6f6a6ae..b1b6a325d09 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -2465,6 +2465,13 @@ common_handle_option (struct gcc_options *opts, opts->x_flag_wrapv = 0; break; + case OPT_fstrict_overflow: + opts->x_flag_wrapv = !value; + opts->x_flag_wrapv_pointer = !value; + if (!value) + opts->x_flag_trapv = 0; + break; + case OPT_fipa_icf: opts->x_flag_ipa_icf_functions = value; opts->x_flag_ipa_icf_variables = value; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 37f54de8146..546fc82c9f0 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2018-01-15 Jakub Jelinek + + PR middle-end/82694 + * gcc.dg/no-strict-overflow-7.c: Revert 2017-08-01 changes. + * gcc.dg/tree-ssa/pr81388-1.c: Likewise. + 2018-01-10 Martin Sebor PR other/83508 diff --git a/gcc/testsuite/gcc.dg/no-strict-overflow-7.c b/gcc/testsuite/gcc.dg/no-strict-overflow-7.c index 0e73d486f22..19e1b55bb12 100644 --- a/gcc/testsuite/gcc.dg/no-strict-overflow-7.c +++ b/gcc/testsuite/gcc.dg/no-strict-overflow-7.c @@ -3,8 +3,8 @@ /* Source: Ian Lance Taylor. Dual of strict-overflow-6.c. */ -/* We can simplify the conditional because pointer overflow always has - undefined semantics. */ +/* We can only simplify the conditional when using strict overflow + semantics. */ int foo (char* p) @@ -12,4 +12,4 @@ foo (char* p) return p + 1000 < p; } -/* { dg-final { scan-tree-dump "return 0" "optimized" } } */ +/* { dg-final { scan-tree-dump "\[+\]\[ \]*1000" "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr81388-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr81388-1.c index 0beb5109d40..85c00e56144 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr81388-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr81388-1.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-strict-overflow -fdump-tree-tailc-details" } */ +/* { dg-options "-O2 -fno-strict-overflow -fdump-tree-ivcanon-details" } */ void bar(); void foo(char *dst) @@ -11,6 +11,4 @@ void foo(char *dst) } while (dst < end); } -/* The loop only iterates once because pointer overflow always has undefined - semantics. As a result, call to bar becomes tail call. */ -/* { dg-final { scan-tree-dump-times "Found tail call " 1 "tailc" } } */ +/* { dg-final { scan-tree-dump " zero if " "ivcanon" } } */ diff --git a/gcc/tree.h b/gcc/tree.h index f47e2338d2d..af8a6fb380c 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -829,13 +829,16 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int, /* Same as TYPE_UNSIGNED but converted to SIGNOP. */ #define TYPE_SIGN(NODE) ((signop) TYPE_UNSIGNED (NODE)) -/* True if overflow wraps around for the given integral type. That +/* True if overflow wraps around for the given integral or pointer type. That is, TYPE_MAX + 1 == TYPE_MIN. */ #define TYPE_OVERFLOW_WRAPS(TYPE) \ - (ANY_INTEGRAL_TYPE_CHECK(TYPE)->base.u.bits.unsigned_flag || flag_wrapv) + (POINTER_TYPE_P (TYPE) \ + ? flag_wrapv_pointer \ + : (ANY_INTEGRAL_TYPE_CHECK(TYPE)->base.u.bits.unsigned_flag \ + || flag_wrapv)) -/* True if overflow is undefined for the given integral type. We may - optimize on the assumption that values in the type never overflow. +/* True if overflow is undefined for the given integral or pointer type. + We may optimize on the assumption that values in the type never overflow. IMPORTANT NOTE: Any optimization based on TYPE_OVERFLOW_UNDEFINED must issue a warning based on warn_strict_overflow. In some cases @@ -843,8 +846,10 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int, other cases it will be appropriate to simply set a flag and let the caller decide whether a warning is appropriate or not. */ #define TYPE_OVERFLOW_UNDEFINED(TYPE) \ - (!ANY_INTEGRAL_TYPE_CHECK(TYPE)->base.u.bits.unsigned_flag \ - && !flag_wrapv && !flag_trapv) + (POINTER_TYPE_P (TYPE) \ + ? !flag_wrapv_pointer \ + : (!ANY_INTEGRAL_TYPE_CHECK(TYPE)->base.u.bits.unsigned_flag \ + && !flag_wrapv && !flag_trapv)) /* True if overflow for the given integral type should issue a trap. */ -- 2.30.2