Message ID | 20180112205144.GK2063@tucnak |
---|---|
State | New |
Headers | show |
Series | Introduce -fwrapp and make -fno-strict-overflow imply it (PR middle-end/82694) | expand |
On Fri, 12 Jan 2018, Jakub Jelinek wrote: > 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. I am pretty sure there are other patterns in match.pd that need protection now, with pointer_diff. (for op (simple_comparison) (simplify (op (pointer_diff@3 @0 @2) (pointer_diff @1 @2)) (if (!TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@2))) (op @0 @1)))) This is ready for sanitizers but not for wrapping pointers. And there are a few more like it. There were discussions at some point of implementing -fwrapp in the front-end, generating (unsigned)q-(unsigned)p or (unsigned)p<(unsigned)q for pointer operations. It has the advantage that the middle-end doesn't need to know about those variants, but it might have some fallout (and I am not sure what to do when the middle-end creates new pointer operations), and I can understand that in stage 3 you are more interested in an approach that looks like a reversal to a former known-ok state.
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 <jakub@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 >
On Fri, 12 Jan 2018, 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. > > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? This is ok with the name of the option/flag changed as suggested by Martin. Thanks, Richard. > 2018-01-12 Jakub Jelinek <jakub@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 > >
On Fri, 12 Jan 2018, Marc Glisse wrote: > On Fri, 12 Jan 2018, Jakub Jelinek wrote: > > > 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. > > I am pretty sure there are other patterns in match.pd that need protection > now, with pointer_diff. > > (for op (simple_comparison) > (simplify > (op (pointer_diff@3 @0 @2) (pointer_diff @1 @2)) > (if (!TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@2))) > (op @0 @1)))) > > This is ready for sanitizers but not for wrapping pointers. And there are a > few more like it. > > > There were discussions at some point of implementing -fwrapp in the front-end, > generating (unsigned)q-(unsigned)p or (unsigned)p<(unsigned)q for pointer > operations. It has the advantage that the middle-end doesn't need to know > about those variants, but it might have some fallout (and I am not sure what > to do when the middle-end creates new pointer operations), and I can > understand that in stage 3 you are more interested in an approach that looks > like a reversal to a former known-ok state. Yes, I think reversal to previous behavior is the only reasonable thing at the moment. Richard.
--- 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" } } */