diff mbox series

Introduce -fwrapp and make -fno-strict-overflow imply it (PR middle-end/82694)

Message ID 20180112205144.GK2063@tucnak
State New
Headers show
Series Introduce -fwrapp and make -fno-strict-overflow imply it (PR middle-end/82694) | expand

Commit Message

Jakub Jelinek Jan. 12, 2018, 8:51 p.m. UTC
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?

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.


	Jakub

Comments

Marc Glisse Jan. 12, 2018, 10:32 p.m. UTC | #1
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.
Martin Sebor Jan. 13, 2018, 6:36 p.m. UTC | #2
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
>
Richard Biener Jan. 15, 2018, 8:41 a.m. UTC | #3
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
> 
>
Richard Biener Jan. 15, 2018, 8:42 a.m. UTC | #4
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.
diff mbox series

Patch

--- 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" } } */