On 01/12/2018 01:51 PM, Jakub Jelinek wrote:
Hi!
Apparently Linux kernel contains various UB code that has been worked around
through -fno-strict-overflow in 7.x and before, but when
POINTER_TYPE_OVERFLOW_UNDEFINED has been removed it now fails to boot.
The following patch follows the comments in the PR, essentially reverts
Bin's removal of that, except that it is now controlled by a separate option
and is included in TYPE_OVERFLOW_{WRAPS,UNDEFINED} macros.
-fwrapv notwithstanding, I would suggest making the name of the new
option clearer: say -fwrapv-pointer or something that makes it clear
that it's for pointers. (Presumably, the v in -fwrapv comes from
the v in overflow, so retaining it for pointers would make sense
for consistency.)
Martin
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
2018-01-12 Jakub Jelinek <ja...@redhat.com>
PR middle-end/82694
* common.opt (fstrict-overflow): No longer an alias.
(fwrapp): New option.
* tree.h (TYPE_OVERFLOW_WRAPS, TYPE_OVERFLOW_UNDEFINED): Define
also for pointer types based on flag_wrapp.
* opts.c (common_handle_option) <case OPT_fstrict_overflow>: 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.
--- gcc/common.opt.jj 2018-01-03 10:19:54.936533922 +0100
+++ gcc/common.opt 2018-01-12 14:53:28.254485349 +0100
@@ -2411,8 +2411,8 @@ Common Report Var(flag_strict_aliasing)
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 -fwrapp.
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.
+fwrapp
+Common Report Var(flag_wrapp) Optimization
+Assume pointer overflow wraps around.
+
fwrapv
Common Report Var(flag_wrapv) Optimization
Assume signed arithmetic overflow wraps around.
--- gcc/tree.h.jj 2018-01-11 18:58:50.993392760 +0100
+++ gcc/tree.h 2018-01-12 15:04:14.480526788 +0100
@@ -829,13 +829,16 @@ extern void omp_clause_range_check_faile
/* 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_wrapp \
+ : (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_faile
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_wrapp \
+ : (!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. */
--- gcc/opts.c.jj 2018-01-03 10:19:56.142534113 +0100
+++ gcc/opts.c 2018-01-12 14:55:06.670494955 +0100
@@ -2465,6 +2465,13 @@ common_handle_option (struct gcc_options
opts->x_flag_wrapv = 0;
break;
+ case OPT_fstrict_overflow:
+ opts->x_flag_wrapv = !value;
+ opts->x_flag_wrapp = !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;
--- gcc/fold-const.c.jj 2018-01-04 22:08:04.394684734 +0100
+++ gcc/fold-const.c 2018-01-12 15:06:20.040532446 +0100
@@ -8551,9 +8551,13 @@ fold_comparison (location_t loc, enum tr
{
/* 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 tr
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 tr
/* 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 tr
/* 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))
{
--- gcc/match.pd.jj 2018-01-09 21:53:38.366577609 +0100
+++ gcc/match.pd 2018-01-12 16:17:01.677944553 +0100
@@ -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); })
--- gcc/doc/invoke.texi.jj 2018-01-12 11:36:20.115225557 +0100
+++ gcc/doc/invoke.texi 2018-01-12 16:14:11.369911474 +0100
@@ -12576,6 +12576,18 @@ The options @option{-ftrapv} and @option
using @option{-ftrapv} @option{-fwrapv} @option{-fno-wrapv} on the command-line
results in @option{-ftrapv} being effective.
+@item -fwrapp
+@opindex fwrapp
+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-wrapp} and when negated
+implies @option{-fwrapv} @option{-fwrapp}.
+
@item -fexceptions
@opindex fexceptions
Enable exception handling. Generates extra code needed to propagate
--- gcc/testsuite/gcc.dg/tree-ssa/pr81388-1.c.jj 2017-08-01
12:14:27.740533590 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/pr81388-1.c 2018-01-12 16:22:34.427009184
+0100
@@ -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" } } */
--- gcc/testsuite/gcc.dg/no-strict-overflow-7.c.jj 2017-08-01
12:14:27.941531251 +0200
+++ gcc/testsuite/gcc.dg/no-strict-overflow-7.c 2018-01-12 16:19:55.224978262
+0100
@@ -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" } } */
Jakub