diff mbox

Convert manual unsigned +/- overflow checking into {ADD,SUB}_OVERFLOW (PR target/67089)

Message ID 20151124205352.GS5675@tucnak.redhat.com
State New
Headers show

Commit Message

Jakub Jelinek Nov. 24, 2015, 8:53 p.m. UTC
Hi!

This is the GIMPLE side of Richard's i?86 uadd/usub overflow
testing improvements.  If unsigned addition or subtraction
result is used both normally and in a GIMPLE_COND/COND_EXPR/tcc_comparison
that tests if unsigned overflow happened, the patch replaces it shortly
before expansion with {ADD,SUB}_OVERFLOW, so that RTL expansion can generate
better code on it.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2015-11-24  Jakub Jelinek  <jakub@redhat.com>

	PR target/67089
	* tree-ssa-math-opts.c (uaddsub_overflow_check_p,
	match_uaddsub_overflow): New functions.
	(pass_optimize_widening_mul::execute): Call match_uaddsub_overflow.

	* gcc.dg/pr67089-1.c: New test.
	* gcc.dg/pr67089-2.c: New test.
	* gcc.dg/pr67089-3.c: New test.
	* gcc.dg/pr67089-4.c: New test.
	* gcc.dg/pr67089-5.c: New test.
	* gcc.dg/pr67089-6.c: New test.
	* gcc.dg/pr67089-7.c: New test.


	Jakub

Comments

Marc Glisse Nov. 25, 2015, 7:56 a.m. UTC | #1
On Tue, 24 Nov 2015, Jakub Jelinek wrote:

> This is the GIMPLE side of Richard's i?86 uadd/usub overflow
> testing improvements.  If unsigned addition or subtraction
> result is used both normally and in a GIMPLE_COND/COND_EXPR/tcc_comparison
> that tests if unsigned overflow happened, the patch replaces it shortly
> before expansion with {ADD,SUB}_OVERFLOW, so that RTL expansion can generate
> better code on it.

If I test a+b<a and don't use a+b anywhere else, don't we also want to use 
the OVERFLOW things so we can expand to test the carry flag? That is, I am 
not convinced we want to punt on has_single_use for add_overflow. For 
sub_overflow with a single use of y-z, I guess y-z>y should become z>y, 
and going through a rewrite with sub_overflow neither helps nor hinders 
that. Actually, writing z>y is something the user is not unlikely to have 
done himself, and walking through the uses of y or z should not be hard, 
so I guess it could make sense to rewrite y-z>y to z>y always in match.pd 
and only look for the second form in math-opts.

I was thinking more match.pd to transform a+b<a and sccvn to somehow CSE 
a+b with add_overflow(a,b), but your patch seems to work well with simpler 
code, that's cool :-)

And it shouldn't be too hard to add a few more later, to detect widening 
operations that are only used for overflow testing, although the form of 
such tests is much less universal among users.
Richard Biener Nov. 25, 2015, 8:40 a.m. UTC | #2
On Tue, 24 Nov 2015, Jakub Jelinek wrote:

> Hi!
> 
> This is the GIMPLE side of Richard's i?86 uadd/usub overflow
> testing improvements.  If unsigned addition or subtraction
> result is used both normally and in a GIMPLE_COND/COND_EXPR/tcc_comparison
> that tests if unsigned overflow happened, the patch replaces it shortly
> before expansion with {ADD,SUB}_OVERFLOW, so that RTL expansion can generate
> better code on it.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

Ok.

Thanks,
Richard.

> 2015-11-24  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR target/67089
> 	* tree-ssa-math-opts.c (uaddsub_overflow_check_p,
> 	match_uaddsub_overflow): New functions.
> 	(pass_optimize_widening_mul::execute): Call match_uaddsub_overflow.
> 
> 	* gcc.dg/pr67089-1.c: New test.
> 	* gcc.dg/pr67089-2.c: New test.
> 	* gcc.dg/pr67089-3.c: New test.
> 	* gcc.dg/pr67089-4.c: New test.
> 	* gcc.dg/pr67089-5.c: New test.
> 	* gcc.dg/pr67089-6.c: New test.
> 	* gcc.dg/pr67089-7.c: New test.
> 
> --- gcc/tree-ssa-math-opts.c.jj	2015-11-18 11:19:23.000000000 +0100
> +++ gcc/tree-ssa-math-opts.c	2015-11-24 17:00:10.825900958 +0100
> @@ -3491,6 +3491,189 @@ convert_mult_to_fma (gimple *mul_stmt, t
>    return true;
>  }
>  
> +
> +/* Helper function of match_uaddsub_overflow.  Return 1
> +   if USE_STMT is unsigned overflow check ovf != 0 for
> +   STMT, -1 if USE_STMT is unsigned overflow check ovf == 0
> +   and 0 otherwise.  */
> +
> +static int
> +uaddsub_overflow_check_p (gimple *stmt, gimple *use_stmt)
> +{
> +  enum tree_code ccode = ERROR_MARK;
> +  tree crhs1 = NULL_TREE, crhs2 = NULL_TREE;
> +  if (gimple_code (use_stmt) == GIMPLE_COND)
> +    {
> +      ccode = gimple_cond_code (use_stmt);
> +      crhs1 = gimple_cond_lhs (use_stmt);
> +      crhs2 = gimple_cond_rhs (use_stmt);
> +    }
> +  else if (is_gimple_assign (use_stmt))
> +    {
> +      if (gimple_assign_rhs_class (use_stmt) == GIMPLE_BINARY_RHS)
> +	{
> +	  ccode = gimple_assign_rhs_code (use_stmt);
> +	  crhs1 = gimple_assign_rhs1 (use_stmt);
> +	  crhs2 = gimple_assign_rhs2 (use_stmt);
> +	}
> +      else if (gimple_assign_rhs_code (use_stmt) == COND_EXPR)
> +	{
> +	  tree cond = gimple_assign_rhs1 (use_stmt);
> +	  if (COMPARISON_CLASS_P (cond))
> +	    {
> +	      ccode = TREE_CODE (cond);
> +	      crhs1 = TREE_OPERAND (cond, 0);
> +	      crhs2 = TREE_OPERAND (cond, 1);
> +	    }
> +	  else
> +	    return 0;
> +	}
> +      else
> +	return 0;
> +    }
> +  else
> +    return 0;
> +
> +  if (TREE_CODE_CLASS (ccode) != tcc_comparison)
> +    return 0;
> +
> +  enum tree_code code = gimple_assign_rhs_code (stmt);
> +  tree lhs = gimple_assign_lhs (stmt);
> +  tree rhs1 = gimple_assign_rhs1 (stmt);
> +  tree rhs2 = gimple_assign_rhs2 (stmt);
> +
> +  switch (ccode)
> +    {
> +    case GT_EXPR:
> +    case LE_EXPR:
> +      /* r = a - b; r > a or r <= a
> +	 r = a + b; a > r or a <= r or b > r or b <= r.  */
> +      if ((code == MINUS_EXPR && crhs1 == lhs && crhs2 == rhs1)
> +	  || (code == PLUS_EXPR && (crhs1 == rhs1 || crhs1 == rhs2)
> +	      && crhs2 == lhs))
> +	return ccode == GT_EXPR ? 1 : -1;
> +      break;
> +    case LT_EXPR:
> +    case GE_EXPR:
> +      /* r = a - b; a < r or a >= r
> +	 r = a + b; r < a or r >= a or r < b or r >= b.  */
> +      if ((code == MINUS_EXPR && crhs1 == rhs1 && crhs2 == lhs)
> +	  || (code == PLUS_EXPR && crhs1 == lhs
> +	      && (crhs2 == rhs1 || crhs2 == rhs2)))
> +	return ccode == LT_EXPR ? 1 : -1;
> +      break;
> +    default:
> +      break;
> +    }
> +  return 0;
> +}
> +
> +/* Recognize for unsigned x
> +   x = y - z;
> +   if (x > y)
> +   where there are other uses of x and replace it with
> +   _7 = SUB_OVERFLOW (y, z);
> +   x = REALPART_EXPR <_7>;
> +   _8 = IMAGPART_EXPR <_7>;
> +   if (_8)
> +   and similarly for addition.  */
> +
> +static bool
> +match_uaddsub_overflow (gimple_stmt_iterator *gsi, gimple *stmt,
> +			enum tree_code code)
> +{
> +  tree lhs = gimple_assign_lhs (stmt);
> +  tree type = TREE_TYPE (lhs);
> +  use_operand_p use_p;
> +  imm_use_iterator iter;
> +  bool use_seen = false;
> +  bool ovf_use_seen = false;
> +  gimple *use_stmt;
> +
> +  gcc_checking_assert (code == PLUS_EXPR || code == MINUS_EXPR);
> +  if (!INTEGRAL_TYPE_P (type)
> +      || !TYPE_UNSIGNED (type)
> +      || has_zero_uses (lhs)
> +      || has_single_use (lhs)
> +      || optab_handler (code == PLUS_EXPR ? uaddv4_optab : usubv4_optab,
> +			TYPE_MODE (type)) == CODE_FOR_nothing)
> +    return false;
> +
> +  FOR_EACH_IMM_USE_FAST (use_p, iter, lhs)
> +    {
> +      use_stmt = USE_STMT (use_p);
> +      if (is_gimple_debug (use_stmt))
> +	continue;
> +
> +      if (uaddsub_overflow_check_p (stmt, use_stmt))
> +	ovf_use_seen = true;
> +      else
> +	use_seen = true;
> +      if (ovf_use_seen && use_seen)
> +	break;
> +    }
> +
> +  if (!ovf_use_seen || !use_seen)
> +    return false;
> +
> +  tree ctype = build_complex_type (type);
> +  tree rhs1 = gimple_assign_rhs1 (stmt);
> +  tree rhs2 = gimple_assign_rhs2 (stmt);
> +  gcall *g = gimple_build_call_internal (code == PLUS_EXPR
> +					 ? IFN_ADD_OVERFLOW : IFN_SUB_OVERFLOW,
> +					 2, rhs1, rhs2);
> +  tree ctmp = make_ssa_name (ctype);
> +  gimple_call_set_lhs (g, ctmp);
> +  gsi_insert_before (gsi, g, GSI_SAME_STMT);
> +  gassign *g2 = gimple_build_assign (lhs, REALPART_EXPR,
> +				     build1 (REALPART_EXPR, type, ctmp));
> +  gsi_replace (gsi, g2, true);
> +  tree ovf = make_ssa_name (type);
> +  g2 = gimple_build_assign (ovf, IMAGPART_EXPR,
> +			    build1 (IMAGPART_EXPR, type, ctmp));
> +  gsi_insert_after (gsi, g2, GSI_NEW_STMT);
> +
> +  FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
> +    {
> +      if (is_gimple_debug (use_stmt))
> +	continue;
> +
> +      int ovf_use = uaddsub_overflow_check_p (stmt, use_stmt);
> +      if (ovf_use == 0)
> +	continue;
> +      if (gimple_code (use_stmt) == GIMPLE_COND)
> +	{
> +	  gcond *cond_stmt = as_a <gcond *> (use_stmt);
> +	  gimple_cond_set_lhs (cond_stmt, ovf);
> +	  gimple_cond_set_rhs (cond_stmt, build_int_cst (type, 0));
> +	  gimple_cond_set_code (cond_stmt, ovf_use == 1 ? NE_EXPR : EQ_EXPR);
> +	}
> +      else
> +	{
> +	  gcc_checking_assert (is_gimple_assign (use_stmt));
> +	  if (gimple_assign_rhs_class (use_stmt) == GIMPLE_BINARY_RHS)
> +	    {
> +	      gimple_assign_set_rhs1 (use_stmt, ovf);
> +	      gimple_assign_set_rhs2 (use_stmt, build_int_cst (type, 0));
> +	      gimple_assign_set_rhs_code (use_stmt,
> +					  ovf_use == 1 ? NE_EXPR : EQ_EXPR);
> +	    }
> +	  else
> +	    {
> +	      gcc_checking_assert (gimple_assign_rhs_code (use_stmt)
> +				   == COND_EXPR);
> +	      tree cond = build2 (ovf_use == 1 ? NE_EXPR : EQ_EXPR,
> +				  boolean_type_node, ovf,
> +				  build_int_cst (type, 0));
> +	      gimple_assign_set_rhs1 (use_stmt, cond);
> +	    }
> +	}
> +      update_stmt (use_stmt);
> +    }
> +  return true;
> +}
> +
> +
>  /* Find integer multiplications where the operands are extended from
>     smaller types, and replace the MULT_EXPR with a WIDEN_MULT_EXPR
>     where appropriate.  */
> @@ -3563,7 +3746,8 @@ pass_optimize_widening_mul::execute (fun
>  
>  		case PLUS_EXPR:
>  		case MINUS_EXPR:
> -		  convert_plusminus_to_widen (&gsi, stmt, code);
> +		  if (!convert_plusminus_to_widen (&gsi, stmt, code))
> +		    match_uaddsub_overflow (&gsi, stmt, code);
>  		  break;
>  
>  		default:;
> --- gcc/testsuite/gcc.dg/pr67089-1.c.jj	2015-11-24 18:16:30.817446026 +0100
> +++ gcc/testsuite/gcc.dg/pr67089-1.c	2015-11-24 19:03:41.302284096 +0100
> @@ -0,0 +1,112 @@
> +/* PR target/67089 */
> +/* { dg-do run } */
> +/* { dg-options "-O2" } */
> +
> +extern void abort (void);
> +
> +int cnt, d;
> +
> +__attribute__((noinline, noclone))
> +void foo (int x)
> +{
> +  asm volatile ("" : "+m" (d) : "g" (x) : "memory");
> +  cnt++;
> +}
> +
> +#define T(n, type, op, cond) \
> +__attribute__((noinline, noclone))	\
> +type					\
> +f##n (type x, type y)			\
> +{					\
> +  type r = op;				\
> +  cond;					\
> +  return r;				\
> +}
> +
> +T (1, unsigned int, x - y, if (r > x) foo (0))
> +T (2, unsigned long, x - y, if (r <= x) foo (0))
> +T (3, unsigned short, x - y, if (x < r) foo (r))
> +T (4, unsigned long long, x - y, if (x >= r) foo (0))
> +T (5, unsigned int, x - y, if (r >= x) foo (0))
> +T (6, unsigned long, x - y, if (r < x) foo (0))
> +T (7, unsigned short, x - y, if (x <= r) foo (r))
> +T (8, unsigned long long, x - y, if (d || x > r) foo (0))
> +T (9, unsigned int, x - y, if (d || r > x) foo (0))
> +T (10, unsigned long, x - y, if (d || r <= x) foo (0))
> +T (11, unsigned char, x - y, if (d || x < r) foo (0))
> +T (12, unsigned long long, x - y, if (d || x >= r) foo (0))
> +T (13, unsigned int, x - y, if (d || r >= x) foo (0))
> +T (14, unsigned long, x - y, if (d || r < x) foo (0))
> +T (15, unsigned short, x - y, if (d || x <= r) foo (0))
> +T (16, unsigned long long, x - y, if (d || x > r) foo (0))
> +
> +int
> +main ()
> +{
> +  if (f1 (5, 3) != 2U || cnt != 0) abort ();
> +  if (f1 (5, 7) != -2U || cnt != 1) abort ();
> +  if (f1 (5, 5) != 0U || cnt != 1) abort ();
> +  if (f1 (5, 0) != 5U || cnt != 1) abort ();
> +  if (f2 (7, 1) != 6UL || cnt != 2) abort ();
> +  if (f2 (7, 8) != -1UL || cnt != 2) abort ();
> +  if (f2 (9, 9) != 0UL || cnt != 3) abort ();
> +  if (f2 (9, 0) != 9UL || cnt != 4) abort ();
> +  if (f3 (15, 14) != 1 || cnt != 4) abort ();
> +  if (f3 (15, 25) != (unsigned short) -10 || cnt != 5) abort ();
> +  if (f3 (15, 15) != 0 || cnt != 5) abort ();
> +  if (f3 (15, 0) != 15 || cnt != 5) abort ();
> +  if (f4 (9132, 9127) != 5ULL || cnt != 6) abort ();
> +  if (f4 (9132, 9137) != -5ULL || cnt != 6) abort ();
> +  if (f4 (9132, 9132) != 0 || cnt != 7) abort ();
> +  if (f4 (9132, 0) != 9132ULL || cnt != 8) abort ();
> +  if (f5 (5, 3) != 2U || cnt != 8) abort ();
> +  if (f5 (5, 7) != -2U || cnt != 9) abort ();
> +  if (f5 (5, 5) != 0U || cnt != 9) abort ();
> +  if (f5 (5, 0) != 5U || cnt != 10) abort ();
> +  if (f6 (7, 1) != 6UL || cnt != 11) abort ();
> +  if (f6 (7, 8) != -1UL || cnt != 11) abort ();
> +  if (f6 (9, 9) != 0UL || cnt != 12) abort ();
> +  if (f6 (9, 0) != 9UL || cnt != 12) abort ();
> +  if (f7 (15, 14) != 1 || cnt != 12) abort ();
> +  if (f7 (15, 25) != (unsigned short) -10 || cnt != 13) abort ();
> +  if (f7 (15, 15) != 0 || cnt != 13) abort ();
> +  if (f7 (15, 0) != 15 || cnt != 14) abort ();
> +  if (f8 (9132, 9127) != 5ULL || cnt != 15) abort ();
> +  if (f8 (9132, 9137) != -5ULL || cnt != 15) abort ();
> +  if (f8 (9132, 9132) != 0 || cnt != 16) abort ();
> +  if (f8 (9132, 0) != 9132ULL || cnt != 16) abort ();
> +  cnt = 0;
> +  if (f9 (5, 3) != 2U || cnt != 0) abort ();
> +  if (f9 (5, 7) != -2U || cnt != 1) abort ();
> +  if (f9 (5, 5) != 0U || cnt != 1) abort ();
> +  if (f9 (5, 0) != 5U || cnt != 1) abort ();
> +  if (f10 (7, 1) != 6UL || cnt != 2) abort ();
> +  if (f10 (7, 8) != -1UL || cnt != 2) abort ();
> +  if (f10 (9, 9) != 0UL || cnt != 3) abort ();
> +  if (f10 (9, 0) != 9UL || cnt != 4) abort ();
> +  if (f11 (15, 14) != 1 || cnt != 4) abort ();
> +  if (f11 (15, 25) != (unsigned char) -10 || cnt != 5) abort ();
> +  if (f11 (15, 15) != 0 || cnt != 5) abort ();
> +  if (f11 (15, 0) != 15 || cnt != 5) abort ();
> +  if (f12 (9132, 9127) != 5ULL || cnt != 6) abort ();
> +  if (f12 (9132, 9137) != -5ULL || cnt != 6) abort ();
> +  if (f12 (9132, 9132) != 0 || cnt != 7) abort ();
> +  if (f12 (9132, 0) != 9132ULL || cnt != 8) abort ();
> +  if (f13 (5, 3) != 2U || cnt != 8) abort ();
> +  if (f13 (5, 7) != -2U || cnt != 9) abort ();
> +  if (f13 (5, 5) != 0U || cnt != 9) abort ();
> +  if (f13 (5, 0) != 5U || cnt != 10) abort ();
> +  if (f14 (7, 1) != 6UL || cnt != 11) abort ();
> +  if (f14 (7, 8) != -1UL || cnt != 11) abort ();
> +  if (f14 (9, 9) != 0UL || cnt != 12) abort ();
> +  if (f14 (9, 0) != 9UL || cnt != 12) abort ();
> +  if (f15 (15, 14) != 1 || cnt != 12) abort ();
> +  if (f15 (15, 25) != (unsigned short) -10 || cnt != 13) abort ();
> +  if (f15 (15, 15) != 0 || cnt != 13) abort ();
> +  if (f15 (15, 0) != 15 || cnt != 14) abort ();
> +  if (f16 (9132, 9127) != 5ULL || cnt != 15) abort ();
> +  if (f16 (9132, 9137) != -5ULL || cnt != 15) abort ();
> +  if (f16 (9132, 9132) != 0 || cnt != 16) abort ();
> +  if (f16 (9132, 0) != 9132ULL || cnt != 16) abort ();
> +  return 0;
> +}
> --- gcc/testsuite/gcc.dg/pr67089-2.c.jj	2015-11-24 18:18:51.804434548 +0100
> +++ gcc/testsuite/gcc.dg/pr67089-2.c	2015-11-24 19:03:44.769234628 +0100
> @@ -0,0 +1,112 @@
> +/* PR target/67089 */
> +/* { dg-do run } */
> +/* { dg-options "-O2" } */
> +
> +extern void abort (void);
> +
> +int cnt, d;
> +
> +__attribute__((noinline, noclone))
> +void foo (int x)
> +{
> +  asm volatile ("" : "+m" (d) : "g" (x) : "memory");
> +  cnt++;
> +}
> +
> +#define T(n, type, op, cond) \
> +__attribute__((noinline, noclone))	\
> +type					\
> +f##n (type x, type y)			\
> +{					\
> +  type r = op;				\
> +  cond;					\
> +  return r;				\
> +}
> +
> +T (1, unsigned int, x - y, if (r > y) foo (0))
> +T (2, unsigned long, x - y, if (r <= y) foo (0))
> +T (3, unsigned short, x - y, if (y < r) foo (r))
> +T (4, unsigned long long, x - y, if (y >= r) foo (0))
> +T (5, unsigned int, x - y, if (r >= y) foo (0))
> +T (6, unsigned long, x - y, if (r < y) foo (0))
> +T (7, unsigned short, x - y, if (y <= r) foo (r))
> +T (8, unsigned long long, x - y, if (d || y > r) foo (0))
> +T (9, unsigned int, x - y, if (d || r > y) foo (0))
> +T (10, unsigned long, x - y, if (d || r <= y) foo (0))
> +T (11, unsigned char, x - y, if (d || y < r) foo (0))
> +T (12, unsigned long long, x - y, if (d || y >= r) foo (0))
> +T (13, unsigned int, x - y, if (d || r >= y) foo (0))
> +T (14, unsigned long, x - y, if (d || r < y) foo (0))
> +T (15, unsigned short, x - y, if (d || y <= r) foo (0))
> +T (16, unsigned long long, x - y, if (d || y > r) foo (0))
> +
> +int
> +main ()
> +{
> +  if (f1 (5, 3) != 2U || cnt != 0) abort ();
> +  if (f1 (5, 7) != -2U || cnt != 1) abort ();
> +  if (f1 (5, 5) != 0U || cnt != 1) abort ();
> +  if (f1 (5, 0) != 5U || cnt != 2) abort ();
> +  if (f2 (7, 1) != 6UL || cnt != 2) abort ();
> +  if (f2 (7, 8) != -1UL || cnt != 2) abort ();
> +  if (f2 (9, 9) != 0UL || cnt != 3) abort ();
> +  if (f2 (9, 0) != 9UL || cnt != 3) abort ();
> +  if (f3 (15, 14) != 1 || cnt != 3) abort ();
> +  if (f3 (15, 25) != (unsigned short) -10 || cnt != 4) abort ();
> +  if (f3 (15, 15) != 0 || cnt != 4) abort ();
> +  if (f3 (15, 0) != 15 || cnt != 5) abort ();
> +  if (f4 (9132, 9127) != 5ULL || cnt != 6) abort ();
> +  if (f4 (9132, 9137) != -5ULL || cnt != 6) abort ();
> +  if (f4 (9132, 9132) != 0 || cnt != 7) abort ();
> +  if (f4 (9132, 0) != 9132ULL || cnt != 7) abort ();
> +  if (f5 (5, 3) != 2U || cnt != 7) abort ();
> +  if (f5 (5, 7) != -2U || cnt != 8) abort ();
> +  if (f5 (5, 5) != 0U || cnt != 8) abort ();
> +  if (f5 (5, 0) != 5U || cnt != 9) abort ();
> +  if (f6 (7, 1) != 6UL || cnt != 9) abort ();
> +  if (f6 (7, 8) != -1UL || cnt != 9) abort ();
> +  if (f6 (9, 9) != 0UL || cnt != 10) abort ();
> +  if (f6 (9, 0) != 9UL || cnt != 10) abort ();
> +  if (f7 (15, 14) != 1 || cnt != 10) abort ();
> +  if (f7 (15, 25) != (unsigned short) -10 || cnt != 11) abort ();
> +  if (f7 (15, 15) != 0 || cnt != 11) abort ();
> +  if (f7 (15, 0) != 15 || cnt != 12) abort ();
> +  if (f8 (9132, 9127) != 5ULL || cnt != 13) abort ();
> +  if (f8 (9132, 9137) != -5ULL || cnt != 13) abort ();
> +  if (f8 (9132, 9132) != 0 || cnt != 14) abort ();
> +  if (f8 (9132, 0) != 9132ULL || cnt != 14) abort ();
> +  cnt = 0;
> +  if (f9 (5, 3) != 2U || cnt != 0) abort ();
> +  if (f9 (5, 7) != -2U || cnt != 1) abort ();
> +  if (f9 (5, 5) != 0U || cnt != 1) abort ();
> +  if (f9 (5, 0) != 5U || cnt != 2) abort ();
> +  if (f10 (7, 1) != 6UL || cnt != 2) abort ();
> +  if (f10 (7, 8) != -1UL || cnt != 2) abort ();
> +  if (f10 (9, 9) != 0UL || cnt != 3) abort ();
> +  if (f10 (9, 0) != 9UL || cnt != 3) abort ();
> +  if (f11 (15, 14) != 1 || cnt != 3) abort ();
> +  if (f11 (15, 25) != (unsigned char) -10 || cnt != 4) abort ();
> +  if (f11 (15, 15) != 0 || cnt != 4) abort ();
> +  if (f11 (15, 0) != 15 || cnt != 5) abort ();
> +  if (f12 (9132, 9127) != 5ULL || cnt != 6) abort ();
> +  if (f12 (9132, 9137) != -5ULL || cnt != 6) abort ();
> +  if (f12 (9132, 9132) != 0 || cnt != 7) abort ();
> +  if (f12 (9132, 0) != 9132ULL || cnt != 7) abort ();
> +  if (f13 (5, 3) != 2U || cnt != 7) abort ();
> +  if (f13 (5, 7) != -2U || cnt != 8) abort ();
> +  if (f13 (5, 5) != 0U || cnt != 8) abort ();
> +  if (f13 (5, 0) != 5U || cnt != 9) abort ();
> +  if (f14 (7, 1) != 6UL || cnt != 9) abort ();
> +  if (f14 (7, 8) != -1UL || cnt != 9) abort ();
> +  if (f14 (9, 9) != 0UL || cnt != 10) abort ();
> +  if (f14 (9, 0) != 9UL || cnt != 10) abort ();
> +  if (f15 (15, 14) != 1 || cnt != 10) abort ();
> +  if (f15 (15, 25) != (unsigned short) -10 || cnt != 11) abort ();
> +  if (f15 (15, 15) != 0 || cnt != 11) abort ();
> +  if (f15 (15, 0) != 15 || cnt != 12) abort ();
> +  if (f16 (9132, 9127) != 5ULL || cnt != 13) abort ();
> +  if (f16 (9132, 9137) != -5ULL || cnt != 13) abort ();
> +  if (f16 (9132, 9132) != 0 || cnt != 14) abort ();
> +  if (f16 (9132, 0) != 9132ULL || cnt != 14) abort ();
> +  return 0;
> +}
> --- gcc/testsuite/gcc.dg/pr67089-3.c.jj	2015-11-24 18:28:05.788530792 +0100
> +++ gcc/testsuite/gcc.dg/pr67089-3.c	2015-11-24 19:03:48.375183177 +0100
> @@ -0,0 +1,112 @@
> +/* PR target/67089 */
> +/* { dg-do run } */
> +/* { dg-options "-O2" } */
> +
> +extern void abort (void);
> +
> +int cnt, d;
> +
> +__attribute__((noinline, noclone))
> +void foo (int x)
> +{
> +  asm volatile ("" : "+m" (d) : "g" (x) : "memory");
> +  cnt++;
> +}
> +
> +#define T(n, type, op, cond) \
> +__attribute__((noinline, noclone))	\
> +type					\
> +f##n (type x, type y)			\
> +{					\
> +  type r = op;				\
> +  cond;					\
> +  return r;				\
> +}
> +
> +T (1, unsigned int, x + y, if (r > x) foo (0))
> +T (2, unsigned long, x + y, if (r <= x) foo (0))
> +T (3, unsigned short, x + y, if (x < r) foo (r))
> +T (4, unsigned long long, x + y, if (x >= r) foo (0))
> +T (5, unsigned int, x + y, if (r >= x) foo (0))
> +T (6, unsigned long, x + y, if (r < x) foo (0))
> +T (7, unsigned short, x + y, if (x <= r) foo (r))
> +T (8, unsigned long long, x + y, if (d || x > r) foo (0))
> +T (9, unsigned int, x + y, if (d || r > x) foo (0))
> +T (10, unsigned long, x + y, if (d || r <= x) foo (0))
> +T (11, unsigned char, x + y, if (d || x < r) foo (0))
> +T (12, unsigned long long, x + y, if (d || x >= r) foo (0))
> +T (13, unsigned int, x + y, if (d || r >= x) foo (0))
> +T (14, unsigned long, x + y, if (d || r < x) foo (0))
> +T (15, unsigned short, x + y, if (d || x <= r) foo (0))
> +T (16, unsigned long long, x + y, if (d || x > r) foo (0))
> +
> +int
> +main ()
> +{
> +  if (f1 (-7U, 0) != -7U || cnt != 0) abort ();
> +  if (f1 (-7U, 6) != -1U || cnt != 1) abort ();
> +  if (f1 (-7U, 7) != 0U || cnt != 1) abort ();
> +  if (f1 (-7U, 8) != 1U || cnt != 1) abort ();
> +  if (f2 (-9UL, 0) != -9UL || cnt != 2) abort ();
> +  if (f2 (-9UL, 8) != -1UL || cnt != 2) abort ();
> +  if (f2 (-9UL, 9) != 0UL || cnt != 3) abort ();
> +  if (f2 (-9UL, 10) != 1UL || cnt != 4) abort ();
> +  if (f3 (-15, 0) != (unsigned short) -15 || cnt != 4) abort ();
> +  if (f3 (-15, 14) != (unsigned short) -1 || cnt != 5) abort ();
> +  if (f3 (-15, 15) != 0 || cnt != 5) abort ();
> +  if (f3 (-15, 16) != 1 || cnt != 5) abort ();
> +  if (f4 (-9132ULL, 0) != -9132ULL || cnt != 6) abort ();
> +  if (f4 (-9132ULL, 9131) != -1ULL || cnt != 6) abort ();
> +  if (f4 (-9132ULL, 9132) != 0 || cnt != 7) abort ();
> +  if (f4 (-9132ULL, 9133) != 1ULL || cnt != 8) abort ();
> +  if (f5 (-7U, 0) != -7U || cnt != 9) abort ();
> +  if (f5 (-7U, 6) != -1U || cnt != 10) abort ();
> +  if (f5 (-7U, 7) != 0U || cnt != 10) abort ();
> +  if (f5 (-7U, 8) != 1U || cnt != 10) abort ();
> +  if (f6 (-9UL, 0) != -9UL || cnt != 10) abort ();
> +  if (f6 (-9UL, 8) != -1UL || cnt != 10) abort ();
> +  if (f6 (-9UL, 9) != 0UL || cnt != 11) abort ();
> +  if (f6 (-9UL, 10) != 1UL || cnt != 12) abort ();
> +  if (f7 (-15, 0) != (unsigned short) -15 || cnt != 13) abort ();
> +  if (f7 (-15, 14) != (unsigned short) -1 || cnt != 14) abort ();
> +  if (f7 (-15, 15) != 0 || cnt != 14) abort ();
> +  if (f7 (-15, 16) != 1 || cnt != 14) abort ();
> +  if (f8 (-9132ULL, 0) != -9132ULL || cnt != 14) abort ();
> +  if (f8 (-9132ULL, 9131) != -1ULL || cnt != 14) abort ();
> +  if (f8 (-9132ULL, 9132) != 0 || cnt != 15) abort ();
> +  if (f8 (-9132ULL, 9133) != 1ULL || cnt != 16) abort ();
> +  cnt = 0;
> +  if (f9 (-7U, 0) != -7U || cnt != 0) abort ();
> +  if (f9 (-7U, 6) != -1U || cnt != 1) abort ();
> +  if (f9 (-7U, 7) != 0U || cnt != 1) abort ();
> +  if (f9 (-7U, 8) != 1U || cnt != 1) abort ();
> +  if (f10 (-9UL, 0) != -9UL || cnt != 2) abort ();
> +  if (f10 (-9UL, 8) != -1UL || cnt != 2) abort ();
> +  if (f10 (-9UL, 9) != 0UL || cnt != 3) abort ();
> +  if (f10 (-9UL, 10) != 1UL || cnt != 4) abort ();
> +  if (f11 (-15, 0) != (unsigned char) -15 || cnt != 4) abort ();
> +  if (f11 (-15, 14) != (unsigned char) -1 || cnt != 5) abort ();
> +  if (f11 (-15, 15) != 0 || cnt != 5) abort ();
> +  if (f11 (-15, 16) != 1 || cnt != 5) abort ();
> +  if (f12 (-9132ULL, 0) != -9132ULL || cnt != 6) abort ();
> +  if (f12 (-9132ULL, 9131) != -1ULL || cnt != 6) abort ();
> +  if (f12 (-9132ULL, 9132) != 0 || cnt != 7) abort ();
> +  if (f12 (-9132ULL, 9133) != 1ULL || cnt != 8) abort ();
> +  if (f13 (-7U, 0) != -7U || cnt != 9) abort ();
> +  if (f13 (-7U, 6) != -1U || cnt != 10) abort ();
> +  if (f13 (-7U, 7) != 0U || cnt != 10) abort ();
> +  if (f13 (-7U, 8) != 1U || cnt != 10) abort ();
> +  if (f14 (-9UL, 0) != -9UL || cnt != 10) abort ();
> +  if (f14 (-9UL, 8) != -1UL || cnt != 10) abort ();
> +  if (f14 (-9UL, 9) != 0UL || cnt != 11) abort ();
> +  if (f14 (-9UL, 10) != 1UL || cnt != 12) abort ();
> +  if (f15 (-15, 0) != (unsigned short) -15 || cnt != 13) abort ();
> +  if (f15 (-15, 14) != (unsigned short) -1 || cnt != 14) abort ();
> +  if (f15 (-15, 15) != 0 || cnt != 14) abort ();
> +  if (f15 (-15, 16) != 1 || cnt != 14) abort ();
> +  if (f16 (-9132ULL, 0) != -9132ULL || cnt != 14) abort ();
> +  if (f16 (-9132ULL, 9131) != -1ULL || cnt != 14) abort ();
> +  if (f16 (-9132ULL, 9132) != 0 || cnt != 15) abort ();
> +  if (f16 (-9132ULL, 9133) != 1ULL || cnt != 16) abort ();
> +  return 0;
> +}
> --- gcc/testsuite/gcc.dg/pr67089-4.c.jj	2015-11-24 18:42:04.482600934 +0100
> +++ gcc/testsuite/gcc.dg/pr67089-4.c	2015-11-24 19:15:19.155412082 +0100
> @@ -0,0 +1,112 @@
> +/* PR target/67089 */
> +/* { dg-do run } */
> +/* { dg-options "-O2" } */
> +
> +extern void abort (void);
> +
> +int cnt, d;
> +
> +__attribute__((noinline, noclone))
> +void foo (int x)
> +{
> +  asm volatile ("" : "+m" (d) : "g" (x) : "memory");
> +  cnt++;
> +}
> +
> +#define T(n, type, op, cond) \
> +__attribute__((noinline, noclone))	\
> +type					\
> +f##n (type x, type y)			\
> +{					\
> +  type r = op;				\
> +  cond;					\
> +  return r;				\
> +}
> +
> +T (1, unsigned int, x + y, if (r > y) foo (0))
> +T (2, unsigned long, x + y, if (r <= y) foo (0))
> +T (3, unsigned short, x + y, if (y < r) foo (r))
> +T (4, unsigned long long, x + y, if (y >= r) foo (0))
> +T (5, unsigned int, x + y, if (r >= y) foo (0))
> +T (6, unsigned long, x + y, if (r < y) foo (0))
> +T (7, unsigned short, x + y, if (y <= r) foo (r))
> +T (8, unsigned long long, x + y, if (d || y > r) foo (0))
> +T (9, unsigned int, x + y, if (d || r > y) foo (0))
> +T (10, unsigned long, x + y, if (d || r <= y) foo (0))
> +T (11, unsigned char, x + y, if (d || y < r) foo (0))
> +T (12, unsigned long long, x + y, if (d || y >= r) foo (0))
> +T (13, unsigned int, x + y, if (d || r >= y) foo (0))
> +T (14, unsigned long, x + y, if (d || r < y) foo (0))
> +T (15, unsigned short, x + y, if (d || y <= r) foo (0))
> +T (16, unsigned long long, x + y, if (d || y > r) foo (0))
> +
> +int
> +main ()
> +{
> +  if (f1 (-7U, 0) != -7U || cnt != 1) abort ();
> +  if (f1 (-7U, 6) != -1U || cnt != 2) abort ();
> +  if (f1 (-7U, 7) != 0U || cnt != 2) abort ();
> +  if (f1 (-7U, 8) != 1U || cnt != 2) abort ();
> +  if (f2 (-9UL, 0) != -9UL || cnt != 2) abort ();
> +  if (f2 (-9UL, 8) != -1UL || cnt != 2) abort ();
> +  if (f2 (-9UL, 9) != 0UL || cnt != 3) abort ();
> +  if (f2 (-9UL, 10) != 1UL || cnt != 4) abort ();
> +  if (f3 (-15, 0) != (unsigned short) -15 || cnt != 5) abort ();
> +  if (f3 (-15, 14) != (unsigned short) -1 || cnt != 6) abort ();
> +  if (f3 (-15, 15) != 0 || cnt != 6) abort ();
> +  if (f3 (-15, 16) != 1 || cnt != 6) abort ();
> +  if (f4 (-9132ULL, 0) != -9132ULL || cnt != 6) abort ();
> +  if (f4 (-9132ULL, 9131) != -1ULL || cnt != 6) abort ();
> +  if (f4 (-9132ULL, 9132) != 0 || cnt != 7) abort ();
> +  if (f4 (-9132ULL, 9133) != 1ULL || cnt != 8) abort ();
> +  if (f5 (-7U, 0) != -7U || cnt != 9) abort ();
> +  if (f5 (-7U, 6) != -1U || cnt != 10) abort ();
> +  if (f5 (-7U, 7) != 0U || cnt != 10) abort ();
> +  if (f5 (-7U, 8) != 1U || cnt != 10) abort ();
> +  if (f6 (-9UL, 0) != -9UL || cnt != 10) abort ();
> +  if (f6 (-9UL, 8) != -1UL || cnt != 10) abort ();
> +  if (f6 (-9UL, 9) != 0UL || cnt != 11) abort ();
> +  if (f6 (-9UL, 10) != 1UL || cnt != 12) abort ();
> +  if (f7 (-15, 0) != (unsigned short) -15 || cnt != 13) abort ();
> +  if (f7 (-15, 14) != (unsigned short) -1 || cnt != 14) abort ();
> +  if (f7 (-15, 15) != 0 || cnt != 14) abort ();
> +  if (f7 (-15, 16) != 1 || cnt != 14) abort ();
> +  if (f8 (-9132ULL, 0) != -9132ULL || cnt != 14) abort ();
> +  if (f8 (-9132ULL, 9131) != -1ULL || cnt != 14) abort ();
> +  if (f8 (-9132ULL, 9132) != 0 || cnt != 15) abort ();
> +  if (f8 (-9132ULL, 9133) != 1ULL || cnt != 16) abort ();
> +  cnt = 0;
> +  if (f9 (-7U, 0) != -7U || cnt != 1) abort ();
> +  if (f9 (-7U, 6) != -1U || cnt != 2) abort ();
> +  if (f9 (-7U, 7) != 0U || cnt != 2) abort ();
> +  if (f9 (-7U, 8) != 1U || cnt != 2) abort ();
> +  if (f10 (-9UL, 0) != -9UL || cnt != 2) abort ();
> +  if (f10 (-9UL, 8) != -1UL || cnt != 2) abort ();
> +  if (f10 (-9UL, 9) != 0UL || cnt != 3) abort ();
> +  if (f10 (-9UL, 10) != 1UL || cnt != 4) abort ();
> +  if (f11 (-15, 0) != (unsigned char) -15 || cnt != 5) abort ();
> +  if (f11 (-15, 14) != (unsigned char) -1 || cnt != 6) abort ();
> +  if (f11 (-15, 15) != 0 || cnt != 6) abort ();
> +  if (f11 (-15, 16) != 1 || cnt != 6) abort ();
> +  if (f12 (-9132ULL, 0) != -9132ULL || cnt != 6) abort ();
> +  if (f12 (-9132ULL, 9131) != -1ULL || cnt != 6) abort ();
> +  if (f12 (-9132ULL, 9132) != 0 || cnt != 7) abort ();
> +  if (f12 (-9132ULL, 9133) != 1ULL || cnt != 8) abort ();
> +  if (f13 (-7U, 0) != -7U || cnt != 9) abort ();
> +  if (f13 (-7U, 6) != -1U || cnt != 10) abort ();
> +  if (f13 (-7U, 7) != 0U || cnt != 10) abort ();
> +  if (f13 (-7U, 8) != 1U || cnt != 10) abort ();
> +  if (f14 (-9UL, 0) != -9UL || cnt != 10) abort ();
> +  if (f14 (-9UL, 8) != -1UL || cnt != 10) abort ();
> +  if (f14 (-9UL, 9) != 0UL || cnt != 11) abort ();
> +  if (f14 (-9UL, 10) != 1UL || cnt != 12) abort ();
> +  if (f15 (-15, 0) != (unsigned short) -15 || cnt != 13) abort ();
> +  if (f15 (-15, 14) != (unsigned short) -1 || cnt != 14) abort ();
> +  if (f15 (-15, 15) != 0 || cnt != 14) abort ();
> +  if (f15 (-15, 16) != 1 || cnt != 14) abort ();
> +  if (f16 (-9132ULL, 0) != -9132ULL || cnt != 14) abort ();
> +  if (f16 (-9132ULL, 9131) != -1ULL || cnt != 14) abort ();
> +  if (f16 (-9132ULL, 9132) != 0 || cnt != 15) abort ();
> +  if (f16 (-9132ULL, 9133) != 1ULL || cnt != 16) abort ();
> +  return 0;
> +}
> --- gcc/testsuite/gcc.dg/pr67089-5.c.jj	2015-11-24 19:03:19.571594157 +0100
> +++ gcc/testsuite/gcc.dg/pr67089-5.c	2015-11-24 19:31:29.707645365 +0100
> @@ -0,0 +1,82 @@
> +/* PR target/67089 */
> +/* { dg-do run } */
> +/* { dg-options "-O2 -ftree-loop-if-convert" } */
> +
> +extern void abort (void);
> +
> +int cnt;
> +unsigned int a[16], b[16], c[16];
> +
> +__attribute__((noinline, noclone))
> +void foo (int x)
> +{
> +  asm volatile ("" : : "g" (x) : "memory");
> +  cnt++;
> +}
> +
> +__attribute__((noinline, noclone)) void
> +f0 (unsigned int x)
> +{
> +  for (int i = 0; i < 16; i++)
> +    {
> +      unsigned int r = x - a[i];
> +      b[i] = r;
> +      c[i] = r > x ? 7 : x;
> +    }
> +}
> +
> +#define T(n, type, op, cond) \
> +__attribute__((noinline, noclone))	\
> +type					\
> +f##n (type x)				\
> +{					\
> +  type r = op;				\
> +  cond;					\
> +  return r;				\
> +}
> +
> +T (1, unsigned int, x - 2U, if (r > x) foo (0))
> +T (2, unsigned long, x - 2U, if (r <= x) foo (0))
> +T (3, unsigned short, 2U - x, if (r > 2U) foo (0))
> +T (4, unsigned char, 2U - x, if (r <= 2U) foo (0))
> +T (5, unsigned int, x + -2U, if (r > x) foo (0))
> +T (6, unsigned long, x + -2UL, if (r <= x) foo (0))
> +T (7, unsigned short, (unsigned short) -2 + x, if (r > (unsigned short) -2) foo (0))
> +T (8, unsigned char, (unsigned char) -2 + x, if (r <= (unsigned char) -2) foo (0))
> +
> +int
> +main ()
> +{
> +  int i;
> +  for (i = 0; i < 16; i++)
> +    a[i] = i - 7;
> +  f0 (5);
> +  for (i = 0; i < 16; i++)
> +    if (b[i] != 12U - i || c[i] != 7 - 2 * (i >= 7 && i < 13))
> +      abort ();
> +  if (f1 (3) != 1 || cnt != 0) abort ();
> +  if (f1 (2) != 0 || cnt != 0) abort ();
> +  if (f1 (1) != -1U || cnt != 1) abort ();
> +  if (f2 (3) != 1 || cnt != 2) abort ();
> +  if (f2 (2) != 0 || cnt != 3) abort ();
> +  if (f2 (1) != -1UL || cnt != 3) abort ();
> +  if (f3 (3) != (unsigned short) -1 || cnt != 4) abort ();
> +  if (f3 (2) != 0 || cnt != 4) abort ();
> +  if (f3 (1) != 1 || cnt != 4) abort ();
> +  if (f4 (3) != (unsigned char) -1 || cnt != 4) abort ();
> +  if (f4 (2) != 0 || cnt != 5) abort ();
> +  if (f4 (1) != 1 || cnt != 6) abort ();
> +  if (f5 (3) != 1 || cnt != 6) abort ();
> +  if (f5 (2) != 0 || cnt != 6) abort ();
> +  if (f5 (1) != -1U || cnt != 7) abort ();
> +  if (f6 (3) != 1 || cnt != 8) abort ();
> +  if (f6 (2) != 0 || cnt != 9) abort ();
> +  if (f6 (1) != -1UL || cnt != 9) abort ();
> +  if (f7 (3) != 1 || cnt != 9) abort ();
> +  if (f7 (2) != 0 || cnt != 9) abort ();
> +  if (f7 (1) != (unsigned short) -1 || cnt != 10) abort ();
> +  if (f8 (3) != 1 || cnt != 11) abort ();
> +  if (f8 (2) != 0 || cnt != 12) abort ();
> +  if (f8 (1) != (unsigned char) -1 || cnt != 12) abort ();
> +  return 0;
> +}
> --- gcc/testsuite/gcc.dg/pr67089-6.c.jj	2015-11-24 19:16:02.898794422 +0100
> +++ gcc/testsuite/gcc.dg/pr67089-6.c	2015-11-24 19:32:09.928077054 +0100
> @@ -0,0 +1,62 @@
> +/* PR target/67089 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -ftree-loop-if-convert -fdump-tree-widening_mul" } */
> +
> +extern void abort (void);
> +
> +int cnt;
> +unsigned int a[16], b[16], c[16], d;
> +void foo (int x);
> +
> +__attribute__((noinline, noclone)) void
> +f0 (unsigned int x)
> +{
> +  for (int i = 0; i < 16; i++)
> +    {
> +      unsigned int r = x - a[i];
> +      b[i] = r;
> +      c[i] = r > x ? 7 : x;
> +    }
> +}
> +
> +#define T(n, type, op, cond) \
> +__attribute__((noinline, noclone))	\
> +type					\
> +f##n (type x, type y)			\
> +{					\
> +  type r = op;				\
> +  cond;					\
> +  return r;				\
> +}
> +
> +T (1, unsigned int, x - y, if (r > x) foo (0))
> +T (2, unsigned long, x - y, if (r <= x) foo (0))
> +T (3, unsigned short, x - y, if (x < r) foo (r))
> +T (4, unsigned long long, x - y, if (x >= r) foo (0))
> +T (5, unsigned int, x - y, if (d || r > x) foo (0))
> +T (6, unsigned long, x - y, if (d || r <= x) foo (0))
> +T (7, unsigned char, x - y, if (d || x < r) foo (0))
> +T (8, unsigned long long, x - y, if (d || x >= r) foo (0))
> +T (9, unsigned int, x + y, if (r >= x) foo (0))
> +T (10, unsigned long, x + y, if (r < x) foo (0))
> +T (11, unsigned short, x + y, if (x <= r) foo (r))
> +T (12, unsigned long long, x + y, if (d || x > r) foo (0))
> +T (13, unsigned int, x + y, if (d || r >= x) foo (0))
> +T (14, unsigned long, x + y, if (d || r < x) foo (0))
> +T (15, unsigned short, x + y, if (d || x <= r) foo (0))
> +T (16, unsigned long long, x + y, if (d || x > r) foo (0))
> +T (17, unsigned int, x + y, if (r >= y) foo (0))
> +T (18, unsigned long, x + y, if (r < y) foo (0))
> +T (19, unsigned short, x + y, if (y <= r) foo (r))
> +T (20, unsigned long long, x + y, if (d || y > r) foo (0))
> +T (21, unsigned int, x + y, if (d || r >= y) foo (0))
> +T (22, unsigned long, x + y, if (d || r < y) foo (0))
> +T (23, unsigned short, x + y, if (d || y <= r) foo (0))
> +T (24, unsigned long long, x + y, if (d || y > r) foo (0))
> +T (25, unsigned short, 2U - x, if (r > 2U) foo (0))
> +T (26, unsigned char, 2U - x, if (r <= 2U) foo (0))
> +
> +/* { dg-final { scan-tree-dump-times "ADD_OVERFLOW" 16 "widening_mul" { target { { i?86-*-* x86_64-*-* } && { ! ia32 } } } } } */
> +/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 11 "widening_mul" { target { { i?86-*-* x86_64-*-* } && { ! ia32 } } } } } */
> +/* { dg-final { scan-tree-dump-times "ADD_OVERFLOW" 12 "widening_mul" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */
> +/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 9 "widening_mul" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */
> --- gcc/testsuite/gcc.dg/pr67089-7.c.jj	2015-11-24 19:32:30.332788737 +0100
> +++ gcc/testsuite/gcc.dg/pr67089-7.c	2015-11-24 19:37:12.304804499 +0100
> @@ -0,0 +1,62 @@
> +/* PR target/67089 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -ftree-loop-if-convert -fdump-tree-widening_mul" } */
> +
> +extern void abort (void);
> +
> +int cnt, d;
> +void foo (int x);
> +
> +#define T(n, type, op, cond) \
> +__attribute__((noinline, noclone))	\
> +type					\
> +f##n (type x, type y)			\
> +{					\
> +  type r = op;				\
> +  cond;					\
> +  return r;				\
> +}
> +
> +T (1, unsigned int, x - y, if (r >= x) foo (0))
> +T (2, unsigned long, x - y, if (r < x) foo (0))
> +T (3, unsigned short, x - y, if (x <= r) foo (r))
> +T (4, unsigned long long, x - y, if (d || x > r) foo (0))
> +T (5, unsigned int, x - y, if (d || r >= x) foo (0))
> +T (6, unsigned long, x - y, if (d || r < x) foo (0))
> +T (7, unsigned short, x - y, if (d || x <= r) foo (0))
> +T (8, unsigned long long, x - y, if (d || x > r) foo (0))
> +T (9, unsigned int, x - y, if (r > y) foo (0))
> +T (10, unsigned long, x - y, if (r <= y) foo (0))
> +T (11, unsigned short, x - y, if (y < r) foo (r))
> +T (12, unsigned long long, x - y, if (y >= r) foo (0))
> +T (13, unsigned int, x - y, if (r >= y) foo (0))
> +T (14, unsigned long, x - y, if (r < y) foo (0))
> +T (15, unsigned short, x - y, if (y <= r) foo (r))
> +T (16, unsigned long long, x - y, if (d || y > r) foo (0))
> +T (17, unsigned int, x - y, if (d || r > y) foo (0))
> +T (18, unsigned long, x - y, if (d || r <= y) foo (0))
> +T (19, unsigned char, x - y, if (d || y < r) foo (0))
> +T (20, unsigned long long, x - y, if (d || y >= r) foo (0))
> +T (21, unsigned int, x - y, if (d || r >= y) foo (0))
> +T (22, unsigned long, x - y, if (d || r < y) foo (0))
> +T (23, unsigned short, x - y, if (d || y <= r) foo (0))
> +T (24, unsigned long long, x - y, if (d || y > r) foo (0))
> +T (25, unsigned int, x + y, if (r > x) foo (0))
> +T (26, unsigned long, x + y, if (r <= x) foo (0))
> +T (27, unsigned short, x + y, if (x < r) foo (r))
> +T (28, unsigned long long, x + y, if (x >= r) foo (0))
> +T (29, unsigned int, x + y, if (d || r > x) foo (0))
> +T (30, unsigned long, x + y, if (d || r <= x) foo (0))
> +T (31, unsigned char, x + y, if (d || x < r) foo (0))
> +T (32, unsigned long long, x + y, if (d || x >= r) foo (0))
> +T (33, unsigned int, x + y, if (r > y) foo (0))
> +T (34, unsigned long, x + y, if (r <= y) foo (0))
> +T (35, unsigned short, x + y, if (y < r) foo (r))
> +T (36, unsigned long long, x + y, if (y >= r) foo (0))
> +T (37, unsigned int, x + y, if (d || r > y) foo (0))
> +T (38, unsigned long, x + y, if (d || r <= y) foo (0))
> +T (39, unsigned char, x + y, if (d || y < r) foo (0))
> +T (40, unsigned long long, x + y, if (d || y >= r) foo (0))
> +
> +/* { dg-final { scan-tree-dump-not "ADD_OVERFLOW" "widening_mul" } } */
> +/* { dg-final { scan-tree-dump-not "SUB_OVERFLOW" "widening_mul" } } */
> 
> 	Jakub
> 
>
diff mbox

Patch

--- gcc/tree-ssa-math-opts.c.jj	2015-11-18 11:19:23.000000000 +0100
+++ gcc/tree-ssa-math-opts.c	2015-11-24 17:00:10.825900958 +0100
@@ -3491,6 +3491,189 @@  convert_mult_to_fma (gimple *mul_stmt, t
   return true;
 }
 
+
+/* Helper function of match_uaddsub_overflow.  Return 1
+   if USE_STMT is unsigned overflow check ovf != 0 for
+   STMT, -1 if USE_STMT is unsigned overflow check ovf == 0
+   and 0 otherwise.  */
+
+static int
+uaddsub_overflow_check_p (gimple *stmt, gimple *use_stmt)
+{
+  enum tree_code ccode = ERROR_MARK;
+  tree crhs1 = NULL_TREE, crhs2 = NULL_TREE;
+  if (gimple_code (use_stmt) == GIMPLE_COND)
+    {
+      ccode = gimple_cond_code (use_stmt);
+      crhs1 = gimple_cond_lhs (use_stmt);
+      crhs2 = gimple_cond_rhs (use_stmt);
+    }
+  else if (is_gimple_assign (use_stmt))
+    {
+      if (gimple_assign_rhs_class (use_stmt) == GIMPLE_BINARY_RHS)
+	{
+	  ccode = gimple_assign_rhs_code (use_stmt);
+	  crhs1 = gimple_assign_rhs1 (use_stmt);
+	  crhs2 = gimple_assign_rhs2 (use_stmt);
+	}
+      else if (gimple_assign_rhs_code (use_stmt) == COND_EXPR)
+	{
+	  tree cond = gimple_assign_rhs1 (use_stmt);
+	  if (COMPARISON_CLASS_P (cond))
+	    {
+	      ccode = TREE_CODE (cond);
+	      crhs1 = TREE_OPERAND (cond, 0);
+	      crhs2 = TREE_OPERAND (cond, 1);
+	    }
+	  else
+	    return 0;
+	}
+      else
+	return 0;
+    }
+  else
+    return 0;
+
+  if (TREE_CODE_CLASS (ccode) != tcc_comparison)
+    return 0;
+
+  enum tree_code code = gimple_assign_rhs_code (stmt);
+  tree lhs = gimple_assign_lhs (stmt);
+  tree rhs1 = gimple_assign_rhs1 (stmt);
+  tree rhs2 = gimple_assign_rhs2 (stmt);
+
+  switch (ccode)
+    {
+    case GT_EXPR:
+    case LE_EXPR:
+      /* r = a - b; r > a or r <= a
+	 r = a + b; a > r or a <= r or b > r or b <= r.  */
+      if ((code == MINUS_EXPR && crhs1 == lhs && crhs2 == rhs1)
+	  || (code == PLUS_EXPR && (crhs1 == rhs1 || crhs1 == rhs2)
+	      && crhs2 == lhs))
+	return ccode == GT_EXPR ? 1 : -1;
+      break;
+    case LT_EXPR:
+    case GE_EXPR:
+      /* r = a - b; a < r or a >= r
+	 r = a + b; r < a or r >= a or r < b or r >= b.  */
+      if ((code == MINUS_EXPR && crhs1 == rhs1 && crhs2 == lhs)
+	  || (code == PLUS_EXPR && crhs1 == lhs
+	      && (crhs2 == rhs1 || crhs2 == rhs2)))
+	return ccode == LT_EXPR ? 1 : -1;
+      break;
+    default:
+      break;
+    }
+  return 0;
+}
+
+/* Recognize for unsigned x
+   x = y - z;
+   if (x > y)
+   where there are other uses of x and replace it with
+   _7 = SUB_OVERFLOW (y, z);
+   x = REALPART_EXPR <_7>;
+   _8 = IMAGPART_EXPR <_7>;
+   if (_8)
+   and similarly for addition.  */
+
+static bool
+match_uaddsub_overflow (gimple_stmt_iterator *gsi, gimple *stmt,
+			enum tree_code code)
+{
+  tree lhs = gimple_assign_lhs (stmt);
+  tree type = TREE_TYPE (lhs);
+  use_operand_p use_p;
+  imm_use_iterator iter;
+  bool use_seen = false;
+  bool ovf_use_seen = false;
+  gimple *use_stmt;
+
+  gcc_checking_assert (code == PLUS_EXPR || code == MINUS_EXPR);
+  if (!INTEGRAL_TYPE_P (type)
+      || !TYPE_UNSIGNED (type)
+      || has_zero_uses (lhs)
+      || has_single_use (lhs)
+      || optab_handler (code == PLUS_EXPR ? uaddv4_optab : usubv4_optab,
+			TYPE_MODE (type)) == CODE_FOR_nothing)
+    return false;
+
+  FOR_EACH_IMM_USE_FAST (use_p, iter, lhs)
+    {
+      use_stmt = USE_STMT (use_p);
+      if (is_gimple_debug (use_stmt))
+	continue;
+
+      if (uaddsub_overflow_check_p (stmt, use_stmt))
+	ovf_use_seen = true;
+      else
+	use_seen = true;
+      if (ovf_use_seen && use_seen)
+	break;
+    }
+
+  if (!ovf_use_seen || !use_seen)
+    return false;
+
+  tree ctype = build_complex_type (type);
+  tree rhs1 = gimple_assign_rhs1 (stmt);
+  tree rhs2 = gimple_assign_rhs2 (stmt);
+  gcall *g = gimple_build_call_internal (code == PLUS_EXPR
+					 ? IFN_ADD_OVERFLOW : IFN_SUB_OVERFLOW,
+					 2, rhs1, rhs2);
+  tree ctmp = make_ssa_name (ctype);
+  gimple_call_set_lhs (g, ctmp);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+  gassign *g2 = gimple_build_assign (lhs, REALPART_EXPR,
+				     build1 (REALPART_EXPR, type, ctmp));
+  gsi_replace (gsi, g2, true);
+  tree ovf = make_ssa_name (type);
+  g2 = gimple_build_assign (ovf, IMAGPART_EXPR,
+			    build1 (IMAGPART_EXPR, type, ctmp));
+  gsi_insert_after (gsi, g2, GSI_NEW_STMT);
+
+  FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
+    {
+      if (is_gimple_debug (use_stmt))
+	continue;
+
+      int ovf_use = uaddsub_overflow_check_p (stmt, use_stmt);
+      if (ovf_use == 0)
+	continue;
+      if (gimple_code (use_stmt) == GIMPLE_COND)
+	{
+	  gcond *cond_stmt = as_a <gcond *> (use_stmt);
+	  gimple_cond_set_lhs (cond_stmt, ovf);
+	  gimple_cond_set_rhs (cond_stmt, build_int_cst (type, 0));
+	  gimple_cond_set_code (cond_stmt, ovf_use == 1 ? NE_EXPR : EQ_EXPR);
+	}
+      else
+	{
+	  gcc_checking_assert (is_gimple_assign (use_stmt));
+	  if (gimple_assign_rhs_class (use_stmt) == GIMPLE_BINARY_RHS)
+	    {
+	      gimple_assign_set_rhs1 (use_stmt, ovf);
+	      gimple_assign_set_rhs2 (use_stmt, build_int_cst (type, 0));
+	      gimple_assign_set_rhs_code (use_stmt,
+					  ovf_use == 1 ? NE_EXPR : EQ_EXPR);
+	    }
+	  else
+	    {
+	      gcc_checking_assert (gimple_assign_rhs_code (use_stmt)
+				   == COND_EXPR);
+	      tree cond = build2 (ovf_use == 1 ? NE_EXPR : EQ_EXPR,
+				  boolean_type_node, ovf,
+				  build_int_cst (type, 0));
+	      gimple_assign_set_rhs1 (use_stmt, cond);
+	    }
+	}
+      update_stmt (use_stmt);
+    }
+  return true;
+}
+
+
 /* Find integer multiplications where the operands are extended from
    smaller types, and replace the MULT_EXPR with a WIDEN_MULT_EXPR
    where appropriate.  */
@@ -3563,7 +3746,8 @@  pass_optimize_widening_mul::execute (fun
 
 		case PLUS_EXPR:
 		case MINUS_EXPR:
-		  convert_plusminus_to_widen (&gsi, stmt, code);
+		  if (!convert_plusminus_to_widen (&gsi, stmt, code))
+		    match_uaddsub_overflow (&gsi, stmt, code);
 		  break;
 
 		default:;
--- gcc/testsuite/gcc.dg/pr67089-1.c.jj	2015-11-24 18:16:30.817446026 +0100
+++ gcc/testsuite/gcc.dg/pr67089-1.c	2015-11-24 19:03:41.302284096 +0100
@@ -0,0 +1,112 @@ 
+/* PR target/67089 */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+extern void abort (void);
+
+int cnt, d;
+
+__attribute__((noinline, noclone))
+void foo (int x)
+{
+  asm volatile ("" : "+m" (d) : "g" (x) : "memory");
+  cnt++;
+}
+
+#define T(n, type, op, cond) \
+__attribute__((noinline, noclone))	\
+type					\
+f##n (type x, type y)			\
+{					\
+  type r = op;				\
+  cond;					\
+  return r;				\
+}
+
+T (1, unsigned int, x - y, if (r > x) foo (0))
+T (2, unsigned long, x - y, if (r <= x) foo (0))
+T (3, unsigned short, x - y, if (x < r) foo (r))
+T (4, unsigned long long, x - y, if (x >= r) foo (0))
+T (5, unsigned int, x - y, if (r >= x) foo (0))
+T (6, unsigned long, x - y, if (r < x) foo (0))
+T (7, unsigned short, x - y, if (x <= r) foo (r))
+T (8, unsigned long long, x - y, if (d || x > r) foo (0))
+T (9, unsigned int, x - y, if (d || r > x) foo (0))
+T (10, unsigned long, x - y, if (d || r <= x) foo (0))
+T (11, unsigned char, x - y, if (d || x < r) foo (0))
+T (12, unsigned long long, x - y, if (d || x >= r) foo (0))
+T (13, unsigned int, x - y, if (d || r >= x) foo (0))
+T (14, unsigned long, x - y, if (d || r < x) foo (0))
+T (15, unsigned short, x - y, if (d || x <= r) foo (0))
+T (16, unsigned long long, x - y, if (d || x > r) foo (0))
+
+int
+main ()
+{
+  if (f1 (5, 3) != 2U || cnt != 0) abort ();
+  if (f1 (5, 7) != -2U || cnt != 1) abort ();
+  if (f1 (5, 5) != 0U || cnt != 1) abort ();
+  if (f1 (5, 0) != 5U || cnt != 1) abort ();
+  if (f2 (7, 1) != 6UL || cnt != 2) abort ();
+  if (f2 (7, 8) != -1UL || cnt != 2) abort ();
+  if (f2 (9, 9) != 0UL || cnt != 3) abort ();
+  if (f2 (9, 0) != 9UL || cnt != 4) abort ();
+  if (f3 (15, 14) != 1 || cnt != 4) abort ();
+  if (f3 (15, 25) != (unsigned short) -10 || cnt != 5) abort ();
+  if (f3 (15, 15) != 0 || cnt != 5) abort ();
+  if (f3 (15, 0) != 15 || cnt != 5) abort ();
+  if (f4 (9132, 9127) != 5ULL || cnt != 6) abort ();
+  if (f4 (9132, 9137) != -5ULL || cnt != 6) abort ();
+  if (f4 (9132, 9132) != 0 || cnt != 7) abort ();
+  if (f4 (9132, 0) != 9132ULL || cnt != 8) abort ();
+  if (f5 (5, 3) != 2U || cnt != 8) abort ();
+  if (f5 (5, 7) != -2U || cnt != 9) abort ();
+  if (f5 (5, 5) != 0U || cnt != 9) abort ();
+  if (f5 (5, 0) != 5U || cnt != 10) abort ();
+  if (f6 (7, 1) != 6UL || cnt != 11) abort ();
+  if (f6 (7, 8) != -1UL || cnt != 11) abort ();
+  if (f6 (9, 9) != 0UL || cnt != 12) abort ();
+  if (f6 (9, 0) != 9UL || cnt != 12) abort ();
+  if (f7 (15, 14) != 1 || cnt != 12) abort ();
+  if (f7 (15, 25) != (unsigned short) -10 || cnt != 13) abort ();
+  if (f7 (15, 15) != 0 || cnt != 13) abort ();
+  if (f7 (15, 0) != 15 || cnt != 14) abort ();
+  if (f8 (9132, 9127) != 5ULL || cnt != 15) abort ();
+  if (f8 (9132, 9137) != -5ULL || cnt != 15) abort ();
+  if (f8 (9132, 9132) != 0 || cnt != 16) abort ();
+  if (f8 (9132, 0) != 9132ULL || cnt != 16) abort ();
+  cnt = 0;
+  if (f9 (5, 3) != 2U || cnt != 0) abort ();
+  if (f9 (5, 7) != -2U || cnt != 1) abort ();
+  if (f9 (5, 5) != 0U || cnt != 1) abort ();
+  if (f9 (5, 0) != 5U || cnt != 1) abort ();
+  if (f10 (7, 1) != 6UL || cnt != 2) abort ();
+  if (f10 (7, 8) != -1UL || cnt != 2) abort ();
+  if (f10 (9, 9) != 0UL || cnt != 3) abort ();
+  if (f10 (9, 0) != 9UL || cnt != 4) abort ();
+  if (f11 (15, 14) != 1 || cnt != 4) abort ();
+  if (f11 (15, 25) != (unsigned char) -10 || cnt != 5) abort ();
+  if (f11 (15, 15) != 0 || cnt != 5) abort ();
+  if (f11 (15, 0) != 15 || cnt != 5) abort ();
+  if (f12 (9132, 9127) != 5ULL || cnt != 6) abort ();
+  if (f12 (9132, 9137) != -5ULL || cnt != 6) abort ();
+  if (f12 (9132, 9132) != 0 || cnt != 7) abort ();
+  if (f12 (9132, 0) != 9132ULL || cnt != 8) abort ();
+  if (f13 (5, 3) != 2U || cnt != 8) abort ();
+  if (f13 (5, 7) != -2U || cnt != 9) abort ();
+  if (f13 (5, 5) != 0U || cnt != 9) abort ();
+  if (f13 (5, 0) != 5U || cnt != 10) abort ();
+  if (f14 (7, 1) != 6UL || cnt != 11) abort ();
+  if (f14 (7, 8) != -1UL || cnt != 11) abort ();
+  if (f14 (9, 9) != 0UL || cnt != 12) abort ();
+  if (f14 (9, 0) != 9UL || cnt != 12) abort ();
+  if (f15 (15, 14) != 1 || cnt != 12) abort ();
+  if (f15 (15, 25) != (unsigned short) -10 || cnt != 13) abort ();
+  if (f15 (15, 15) != 0 || cnt != 13) abort ();
+  if (f15 (15, 0) != 15 || cnt != 14) abort ();
+  if (f16 (9132, 9127) != 5ULL || cnt != 15) abort ();
+  if (f16 (9132, 9137) != -5ULL || cnt != 15) abort ();
+  if (f16 (9132, 9132) != 0 || cnt != 16) abort ();
+  if (f16 (9132, 0) != 9132ULL || cnt != 16) abort ();
+  return 0;
+}
--- gcc/testsuite/gcc.dg/pr67089-2.c.jj	2015-11-24 18:18:51.804434548 +0100
+++ gcc/testsuite/gcc.dg/pr67089-2.c	2015-11-24 19:03:44.769234628 +0100
@@ -0,0 +1,112 @@ 
+/* PR target/67089 */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+extern void abort (void);
+
+int cnt, d;
+
+__attribute__((noinline, noclone))
+void foo (int x)
+{
+  asm volatile ("" : "+m" (d) : "g" (x) : "memory");
+  cnt++;
+}
+
+#define T(n, type, op, cond) \
+__attribute__((noinline, noclone))	\
+type					\
+f##n (type x, type y)			\
+{					\
+  type r = op;				\
+  cond;					\
+  return r;				\
+}
+
+T (1, unsigned int, x - y, if (r > y) foo (0))
+T (2, unsigned long, x - y, if (r <= y) foo (0))
+T (3, unsigned short, x - y, if (y < r) foo (r))
+T (4, unsigned long long, x - y, if (y >= r) foo (0))
+T (5, unsigned int, x - y, if (r >= y) foo (0))
+T (6, unsigned long, x - y, if (r < y) foo (0))
+T (7, unsigned short, x - y, if (y <= r) foo (r))
+T (8, unsigned long long, x - y, if (d || y > r) foo (0))
+T (9, unsigned int, x - y, if (d || r > y) foo (0))
+T (10, unsigned long, x - y, if (d || r <= y) foo (0))
+T (11, unsigned char, x - y, if (d || y < r) foo (0))
+T (12, unsigned long long, x - y, if (d || y >= r) foo (0))
+T (13, unsigned int, x - y, if (d || r >= y) foo (0))
+T (14, unsigned long, x - y, if (d || r < y) foo (0))
+T (15, unsigned short, x - y, if (d || y <= r) foo (0))
+T (16, unsigned long long, x - y, if (d || y > r) foo (0))
+
+int
+main ()
+{
+  if (f1 (5, 3) != 2U || cnt != 0) abort ();
+  if (f1 (5, 7) != -2U || cnt != 1) abort ();
+  if (f1 (5, 5) != 0U || cnt != 1) abort ();
+  if (f1 (5, 0) != 5U || cnt != 2) abort ();
+  if (f2 (7, 1) != 6UL || cnt != 2) abort ();
+  if (f2 (7, 8) != -1UL || cnt != 2) abort ();
+  if (f2 (9, 9) != 0UL || cnt != 3) abort ();
+  if (f2 (9, 0) != 9UL || cnt != 3) abort ();
+  if (f3 (15, 14) != 1 || cnt != 3) abort ();
+  if (f3 (15, 25) != (unsigned short) -10 || cnt != 4) abort ();
+  if (f3 (15, 15) != 0 || cnt != 4) abort ();
+  if (f3 (15, 0) != 15 || cnt != 5) abort ();
+  if (f4 (9132, 9127) != 5ULL || cnt != 6) abort ();
+  if (f4 (9132, 9137) != -5ULL || cnt != 6) abort ();
+  if (f4 (9132, 9132) != 0 || cnt != 7) abort ();
+  if (f4 (9132, 0) != 9132ULL || cnt != 7) abort ();
+  if (f5 (5, 3) != 2U || cnt != 7) abort ();
+  if (f5 (5, 7) != -2U || cnt != 8) abort ();
+  if (f5 (5, 5) != 0U || cnt != 8) abort ();
+  if (f5 (5, 0) != 5U || cnt != 9) abort ();
+  if (f6 (7, 1) != 6UL || cnt != 9) abort ();
+  if (f6 (7, 8) != -1UL || cnt != 9) abort ();
+  if (f6 (9, 9) != 0UL || cnt != 10) abort ();
+  if (f6 (9, 0) != 9UL || cnt != 10) abort ();
+  if (f7 (15, 14) != 1 || cnt != 10) abort ();
+  if (f7 (15, 25) != (unsigned short) -10 || cnt != 11) abort ();
+  if (f7 (15, 15) != 0 || cnt != 11) abort ();
+  if (f7 (15, 0) != 15 || cnt != 12) abort ();
+  if (f8 (9132, 9127) != 5ULL || cnt != 13) abort ();
+  if (f8 (9132, 9137) != -5ULL || cnt != 13) abort ();
+  if (f8 (9132, 9132) != 0 || cnt != 14) abort ();
+  if (f8 (9132, 0) != 9132ULL || cnt != 14) abort ();
+  cnt = 0;
+  if (f9 (5, 3) != 2U || cnt != 0) abort ();
+  if (f9 (5, 7) != -2U || cnt != 1) abort ();
+  if (f9 (5, 5) != 0U || cnt != 1) abort ();
+  if (f9 (5, 0) != 5U || cnt != 2) abort ();
+  if (f10 (7, 1) != 6UL || cnt != 2) abort ();
+  if (f10 (7, 8) != -1UL || cnt != 2) abort ();
+  if (f10 (9, 9) != 0UL || cnt != 3) abort ();
+  if (f10 (9, 0) != 9UL || cnt != 3) abort ();
+  if (f11 (15, 14) != 1 || cnt != 3) abort ();
+  if (f11 (15, 25) != (unsigned char) -10 || cnt != 4) abort ();
+  if (f11 (15, 15) != 0 || cnt != 4) abort ();
+  if (f11 (15, 0) != 15 || cnt != 5) abort ();
+  if (f12 (9132, 9127) != 5ULL || cnt != 6) abort ();
+  if (f12 (9132, 9137) != -5ULL || cnt != 6) abort ();
+  if (f12 (9132, 9132) != 0 || cnt != 7) abort ();
+  if (f12 (9132, 0) != 9132ULL || cnt != 7) abort ();
+  if (f13 (5, 3) != 2U || cnt != 7) abort ();
+  if (f13 (5, 7) != -2U || cnt != 8) abort ();
+  if (f13 (5, 5) != 0U || cnt != 8) abort ();
+  if (f13 (5, 0) != 5U || cnt != 9) abort ();
+  if (f14 (7, 1) != 6UL || cnt != 9) abort ();
+  if (f14 (7, 8) != -1UL || cnt != 9) abort ();
+  if (f14 (9, 9) != 0UL || cnt != 10) abort ();
+  if (f14 (9, 0) != 9UL || cnt != 10) abort ();
+  if (f15 (15, 14) != 1 || cnt != 10) abort ();
+  if (f15 (15, 25) != (unsigned short) -10 || cnt != 11) abort ();
+  if (f15 (15, 15) != 0 || cnt != 11) abort ();
+  if (f15 (15, 0) != 15 || cnt != 12) abort ();
+  if (f16 (9132, 9127) != 5ULL || cnt != 13) abort ();
+  if (f16 (9132, 9137) != -5ULL || cnt != 13) abort ();
+  if (f16 (9132, 9132) != 0 || cnt != 14) abort ();
+  if (f16 (9132, 0) != 9132ULL || cnt != 14) abort ();
+  return 0;
+}
--- gcc/testsuite/gcc.dg/pr67089-3.c.jj	2015-11-24 18:28:05.788530792 +0100
+++ gcc/testsuite/gcc.dg/pr67089-3.c	2015-11-24 19:03:48.375183177 +0100
@@ -0,0 +1,112 @@ 
+/* PR target/67089 */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+extern void abort (void);
+
+int cnt, d;
+
+__attribute__((noinline, noclone))
+void foo (int x)
+{
+  asm volatile ("" : "+m" (d) : "g" (x) : "memory");
+  cnt++;
+}
+
+#define T(n, type, op, cond) \
+__attribute__((noinline, noclone))	\
+type					\
+f##n (type x, type y)			\
+{					\
+  type r = op;				\
+  cond;					\
+  return r;				\
+}
+
+T (1, unsigned int, x + y, if (r > x) foo (0))
+T (2, unsigned long, x + y, if (r <= x) foo (0))
+T (3, unsigned short, x + y, if (x < r) foo (r))
+T (4, unsigned long long, x + y, if (x >= r) foo (0))
+T (5, unsigned int, x + y, if (r >= x) foo (0))
+T (6, unsigned long, x + y, if (r < x) foo (0))
+T (7, unsigned short, x + y, if (x <= r) foo (r))
+T (8, unsigned long long, x + y, if (d || x > r) foo (0))
+T (9, unsigned int, x + y, if (d || r > x) foo (0))
+T (10, unsigned long, x + y, if (d || r <= x) foo (0))
+T (11, unsigned char, x + y, if (d || x < r) foo (0))
+T (12, unsigned long long, x + y, if (d || x >= r) foo (0))
+T (13, unsigned int, x + y, if (d || r >= x) foo (0))
+T (14, unsigned long, x + y, if (d || r < x) foo (0))
+T (15, unsigned short, x + y, if (d || x <= r) foo (0))
+T (16, unsigned long long, x + y, if (d || x > r) foo (0))
+
+int
+main ()
+{
+  if (f1 (-7U, 0) != -7U || cnt != 0) abort ();
+  if (f1 (-7U, 6) != -1U || cnt != 1) abort ();
+  if (f1 (-7U, 7) != 0U || cnt != 1) abort ();
+  if (f1 (-7U, 8) != 1U || cnt != 1) abort ();
+  if (f2 (-9UL, 0) != -9UL || cnt != 2) abort ();
+  if (f2 (-9UL, 8) != -1UL || cnt != 2) abort ();
+  if (f2 (-9UL, 9) != 0UL || cnt != 3) abort ();
+  if (f2 (-9UL, 10) != 1UL || cnt != 4) abort ();
+  if (f3 (-15, 0) != (unsigned short) -15 || cnt != 4) abort ();
+  if (f3 (-15, 14) != (unsigned short) -1 || cnt != 5) abort ();
+  if (f3 (-15, 15) != 0 || cnt != 5) abort ();
+  if (f3 (-15, 16) != 1 || cnt != 5) abort ();
+  if (f4 (-9132ULL, 0) != -9132ULL || cnt != 6) abort ();
+  if (f4 (-9132ULL, 9131) != -1ULL || cnt != 6) abort ();
+  if (f4 (-9132ULL, 9132) != 0 || cnt != 7) abort ();
+  if (f4 (-9132ULL, 9133) != 1ULL || cnt != 8) abort ();
+  if (f5 (-7U, 0) != -7U || cnt != 9) abort ();
+  if (f5 (-7U, 6) != -1U || cnt != 10) abort ();
+  if (f5 (-7U, 7) != 0U || cnt != 10) abort ();
+  if (f5 (-7U, 8) != 1U || cnt != 10) abort ();
+  if (f6 (-9UL, 0) != -9UL || cnt != 10) abort ();
+  if (f6 (-9UL, 8) != -1UL || cnt != 10) abort ();
+  if (f6 (-9UL, 9) != 0UL || cnt != 11) abort ();
+  if (f6 (-9UL, 10) != 1UL || cnt != 12) abort ();
+  if (f7 (-15, 0) != (unsigned short) -15 || cnt != 13) abort ();
+  if (f7 (-15, 14) != (unsigned short) -1 || cnt != 14) abort ();
+  if (f7 (-15, 15) != 0 || cnt != 14) abort ();
+  if (f7 (-15, 16) != 1 || cnt != 14) abort ();
+  if (f8 (-9132ULL, 0) != -9132ULL || cnt != 14) abort ();
+  if (f8 (-9132ULL, 9131) != -1ULL || cnt != 14) abort ();
+  if (f8 (-9132ULL, 9132) != 0 || cnt != 15) abort ();
+  if (f8 (-9132ULL, 9133) != 1ULL || cnt != 16) abort ();
+  cnt = 0;
+  if (f9 (-7U, 0) != -7U || cnt != 0) abort ();
+  if (f9 (-7U, 6) != -1U || cnt != 1) abort ();
+  if (f9 (-7U, 7) != 0U || cnt != 1) abort ();
+  if (f9 (-7U, 8) != 1U || cnt != 1) abort ();
+  if (f10 (-9UL, 0) != -9UL || cnt != 2) abort ();
+  if (f10 (-9UL, 8) != -1UL || cnt != 2) abort ();
+  if (f10 (-9UL, 9) != 0UL || cnt != 3) abort ();
+  if (f10 (-9UL, 10) != 1UL || cnt != 4) abort ();
+  if (f11 (-15, 0) != (unsigned char) -15 || cnt != 4) abort ();
+  if (f11 (-15, 14) != (unsigned char) -1 || cnt != 5) abort ();
+  if (f11 (-15, 15) != 0 || cnt != 5) abort ();
+  if (f11 (-15, 16) != 1 || cnt != 5) abort ();
+  if (f12 (-9132ULL, 0) != -9132ULL || cnt != 6) abort ();
+  if (f12 (-9132ULL, 9131) != -1ULL || cnt != 6) abort ();
+  if (f12 (-9132ULL, 9132) != 0 || cnt != 7) abort ();
+  if (f12 (-9132ULL, 9133) != 1ULL || cnt != 8) abort ();
+  if (f13 (-7U, 0) != -7U || cnt != 9) abort ();
+  if (f13 (-7U, 6) != -1U || cnt != 10) abort ();
+  if (f13 (-7U, 7) != 0U || cnt != 10) abort ();
+  if (f13 (-7U, 8) != 1U || cnt != 10) abort ();
+  if (f14 (-9UL, 0) != -9UL || cnt != 10) abort ();
+  if (f14 (-9UL, 8) != -1UL || cnt != 10) abort ();
+  if (f14 (-9UL, 9) != 0UL || cnt != 11) abort ();
+  if (f14 (-9UL, 10) != 1UL || cnt != 12) abort ();
+  if (f15 (-15, 0) != (unsigned short) -15 || cnt != 13) abort ();
+  if (f15 (-15, 14) != (unsigned short) -1 || cnt != 14) abort ();
+  if (f15 (-15, 15) != 0 || cnt != 14) abort ();
+  if (f15 (-15, 16) != 1 || cnt != 14) abort ();
+  if (f16 (-9132ULL, 0) != -9132ULL || cnt != 14) abort ();
+  if (f16 (-9132ULL, 9131) != -1ULL || cnt != 14) abort ();
+  if (f16 (-9132ULL, 9132) != 0 || cnt != 15) abort ();
+  if (f16 (-9132ULL, 9133) != 1ULL || cnt != 16) abort ();
+  return 0;
+}
--- gcc/testsuite/gcc.dg/pr67089-4.c.jj	2015-11-24 18:42:04.482600934 +0100
+++ gcc/testsuite/gcc.dg/pr67089-4.c	2015-11-24 19:15:19.155412082 +0100
@@ -0,0 +1,112 @@ 
+/* PR target/67089 */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+extern void abort (void);
+
+int cnt, d;
+
+__attribute__((noinline, noclone))
+void foo (int x)
+{
+  asm volatile ("" : "+m" (d) : "g" (x) : "memory");
+  cnt++;
+}
+
+#define T(n, type, op, cond) \
+__attribute__((noinline, noclone))	\
+type					\
+f##n (type x, type y)			\
+{					\
+  type r = op;				\
+  cond;					\
+  return r;				\
+}
+
+T (1, unsigned int, x + y, if (r > y) foo (0))
+T (2, unsigned long, x + y, if (r <= y) foo (0))
+T (3, unsigned short, x + y, if (y < r) foo (r))
+T (4, unsigned long long, x + y, if (y >= r) foo (0))
+T (5, unsigned int, x + y, if (r >= y) foo (0))
+T (6, unsigned long, x + y, if (r < y) foo (0))
+T (7, unsigned short, x + y, if (y <= r) foo (r))
+T (8, unsigned long long, x + y, if (d || y > r) foo (0))
+T (9, unsigned int, x + y, if (d || r > y) foo (0))
+T (10, unsigned long, x + y, if (d || r <= y) foo (0))
+T (11, unsigned char, x + y, if (d || y < r) foo (0))
+T (12, unsigned long long, x + y, if (d || y >= r) foo (0))
+T (13, unsigned int, x + y, if (d || r >= y) foo (0))
+T (14, unsigned long, x + y, if (d || r < y) foo (0))
+T (15, unsigned short, x + y, if (d || y <= r) foo (0))
+T (16, unsigned long long, x + y, if (d || y > r) foo (0))
+
+int
+main ()
+{
+  if (f1 (-7U, 0) != -7U || cnt != 1) abort ();
+  if (f1 (-7U, 6) != -1U || cnt != 2) abort ();
+  if (f1 (-7U, 7) != 0U || cnt != 2) abort ();
+  if (f1 (-7U, 8) != 1U || cnt != 2) abort ();
+  if (f2 (-9UL, 0) != -9UL || cnt != 2) abort ();
+  if (f2 (-9UL, 8) != -1UL || cnt != 2) abort ();
+  if (f2 (-9UL, 9) != 0UL || cnt != 3) abort ();
+  if (f2 (-9UL, 10) != 1UL || cnt != 4) abort ();
+  if (f3 (-15, 0) != (unsigned short) -15 || cnt != 5) abort ();
+  if (f3 (-15, 14) != (unsigned short) -1 || cnt != 6) abort ();
+  if (f3 (-15, 15) != 0 || cnt != 6) abort ();
+  if (f3 (-15, 16) != 1 || cnt != 6) abort ();
+  if (f4 (-9132ULL, 0) != -9132ULL || cnt != 6) abort ();
+  if (f4 (-9132ULL, 9131) != -1ULL || cnt != 6) abort ();
+  if (f4 (-9132ULL, 9132) != 0 || cnt != 7) abort ();
+  if (f4 (-9132ULL, 9133) != 1ULL || cnt != 8) abort ();
+  if (f5 (-7U, 0) != -7U || cnt != 9) abort ();
+  if (f5 (-7U, 6) != -1U || cnt != 10) abort ();
+  if (f5 (-7U, 7) != 0U || cnt != 10) abort ();
+  if (f5 (-7U, 8) != 1U || cnt != 10) abort ();
+  if (f6 (-9UL, 0) != -9UL || cnt != 10) abort ();
+  if (f6 (-9UL, 8) != -1UL || cnt != 10) abort ();
+  if (f6 (-9UL, 9) != 0UL || cnt != 11) abort ();
+  if (f6 (-9UL, 10) != 1UL || cnt != 12) abort ();
+  if (f7 (-15, 0) != (unsigned short) -15 || cnt != 13) abort ();
+  if (f7 (-15, 14) != (unsigned short) -1 || cnt != 14) abort ();
+  if (f7 (-15, 15) != 0 || cnt != 14) abort ();
+  if (f7 (-15, 16) != 1 || cnt != 14) abort ();
+  if (f8 (-9132ULL, 0) != -9132ULL || cnt != 14) abort ();
+  if (f8 (-9132ULL, 9131) != -1ULL || cnt != 14) abort ();
+  if (f8 (-9132ULL, 9132) != 0 || cnt != 15) abort ();
+  if (f8 (-9132ULL, 9133) != 1ULL || cnt != 16) abort ();
+  cnt = 0;
+  if (f9 (-7U, 0) != -7U || cnt != 1) abort ();
+  if (f9 (-7U, 6) != -1U || cnt != 2) abort ();
+  if (f9 (-7U, 7) != 0U || cnt != 2) abort ();
+  if (f9 (-7U, 8) != 1U || cnt != 2) abort ();
+  if (f10 (-9UL, 0) != -9UL || cnt != 2) abort ();
+  if (f10 (-9UL, 8) != -1UL || cnt != 2) abort ();
+  if (f10 (-9UL, 9) != 0UL || cnt != 3) abort ();
+  if (f10 (-9UL, 10) != 1UL || cnt != 4) abort ();
+  if (f11 (-15, 0) != (unsigned char) -15 || cnt != 5) abort ();
+  if (f11 (-15, 14) != (unsigned char) -1 || cnt != 6) abort ();
+  if (f11 (-15, 15) != 0 || cnt != 6) abort ();
+  if (f11 (-15, 16) != 1 || cnt != 6) abort ();
+  if (f12 (-9132ULL, 0) != -9132ULL || cnt != 6) abort ();
+  if (f12 (-9132ULL, 9131) != -1ULL || cnt != 6) abort ();
+  if (f12 (-9132ULL, 9132) != 0 || cnt != 7) abort ();
+  if (f12 (-9132ULL, 9133) != 1ULL || cnt != 8) abort ();
+  if (f13 (-7U, 0) != -7U || cnt != 9) abort ();
+  if (f13 (-7U, 6) != -1U || cnt != 10) abort ();
+  if (f13 (-7U, 7) != 0U || cnt != 10) abort ();
+  if (f13 (-7U, 8) != 1U || cnt != 10) abort ();
+  if (f14 (-9UL, 0) != -9UL || cnt != 10) abort ();
+  if (f14 (-9UL, 8) != -1UL || cnt != 10) abort ();
+  if (f14 (-9UL, 9) != 0UL || cnt != 11) abort ();
+  if (f14 (-9UL, 10) != 1UL || cnt != 12) abort ();
+  if (f15 (-15, 0) != (unsigned short) -15 || cnt != 13) abort ();
+  if (f15 (-15, 14) != (unsigned short) -1 || cnt != 14) abort ();
+  if (f15 (-15, 15) != 0 || cnt != 14) abort ();
+  if (f15 (-15, 16) != 1 || cnt != 14) abort ();
+  if (f16 (-9132ULL, 0) != -9132ULL || cnt != 14) abort ();
+  if (f16 (-9132ULL, 9131) != -1ULL || cnt != 14) abort ();
+  if (f16 (-9132ULL, 9132) != 0 || cnt != 15) abort ();
+  if (f16 (-9132ULL, 9133) != 1ULL || cnt != 16) abort ();
+  return 0;
+}
--- gcc/testsuite/gcc.dg/pr67089-5.c.jj	2015-11-24 19:03:19.571594157 +0100
+++ gcc/testsuite/gcc.dg/pr67089-5.c	2015-11-24 19:31:29.707645365 +0100
@@ -0,0 +1,82 @@ 
+/* PR target/67089 */
+/* { dg-do run } */
+/* { dg-options "-O2 -ftree-loop-if-convert" } */
+
+extern void abort (void);
+
+int cnt;
+unsigned int a[16], b[16], c[16];
+
+__attribute__((noinline, noclone))
+void foo (int x)
+{
+  asm volatile ("" : : "g" (x) : "memory");
+  cnt++;
+}
+
+__attribute__((noinline, noclone)) void
+f0 (unsigned int x)
+{
+  for (int i = 0; i < 16; i++)
+    {
+      unsigned int r = x - a[i];
+      b[i] = r;
+      c[i] = r > x ? 7 : x;
+    }
+}
+
+#define T(n, type, op, cond) \
+__attribute__((noinline, noclone))	\
+type					\
+f##n (type x)				\
+{					\
+  type r = op;				\
+  cond;					\
+  return r;				\
+}
+
+T (1, unsigned int, x - 2U, if (r > x) foo (0))
+T (2, unsigned long, x - 2U, if (r <= x) foo (0))
+T (3, unsigned short, 2U - x, if (r > 2U) foo (0))
+T (4, unsigned char, 2U - x, if (r <= 2U) foo (0))
+T (5, unsigned int, x + -2U, if (r > x) foo (0))
+T (6, unsigned long, x + -2UL, if (r <= x) foo (0))
+T (7, unsigned short, (unsigned short) -2 + x, if (r > (unsigned short) -2) foo (0))
+T (8, unsigned char, (unsigned char) -2 + x, if (r <= (unsigned char) -2) foo (0))
+
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 16; i++)
+    a[i] = i - 7;
+  f0 (5);
+  for (i = 0; i < 16; i++)
+    if (b[i] != 12U - i || c[i] != 7 - 2 * (i >= 7 && i < 13))
+      abort ();
+  if (f1 (3) != 1 || cnt != 0) abort ();
+  if (f1 (2) != 0 || cnt != 0) abort ();
+  if (f1 (1) != -1U || cnt != 1) abort ();
+  if (f2 (3) != 1 || cnt != 2) abort ();
+  if (f2 (2) != 0 || cnt != 3) abort ();
+  if (f2 (1) != -1UL || cnt != 3) abort ();
+  if (f3 (3) != (unsigned short) -1 || cnt != 4) abort ();
+  if (f3 (2) != 0 || cnt != 4) abort ();
+  if (f3 (1) != 1 || cnt != 4) abort ();
+  if (f4 (3) != (unsigned char) -1 || cnt != 4) abort ();
+  if (f4 (2) != 0 || cnt != 5) abort ();
+  if (f4 (1) != 1 || cnt != 6) abort ();
+  if (f5 (3) != 1 || cnt != 6) abort ();
+  if (f5 (2) != 0 || cnt != 6) abort ();
+  if (f5 (1) != -1U || cnt != 7) abort ();
+  if (f6 (3) != 1 || cnt != 8) abort ();
+  if (f6 (2) != 0 || cnt != 9) abort ();
+  if (f6 (1) != -1UL || cnt != 9) abort ();
+  if (f7 (3) != 1 || cnt != 9) abort ();
+  if (f7 (2) != 0 || cnt != 9) abort ();
+  if (f7 (1) != (unsigned short) -1 || cnt != 10) abort ();
+  if (f8 (3) != 1 || cnt != 11) abort ();
+  if (f8 (2) != 0 || cnt != 12) abort ();
+  if (f8 (1) != (unsigned char) -1 || cnt != 12) abort ();
+  return 0;
+}
--- gcc/testsuite/gcc.dg/pr67089-6.c.jj	2015-11-24 19:16:02.898794422 +0100
+++ gcc/testsuite/gcc.dg/pr67089-6.c	2015-11-24 19:32:09.928077054 +0100
@@ -0,0 +1,62 @@ 
+/* PR target/67089 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -ftree-loop-if-convert -fdump-tree-widening_mul" } */
+
+extern void abort (void);
+
+int cnt;
+unsigned int a[16], b[16], c[16], d;
+void foo (int x);
+
+__attribute__((noinline, noclone)) void
+f0 (unsigned int x)
+{
+  for (int i = 0; i < 16; i++)
+    {
+      unsigned int r = x - a[i];
+      b[i] = r;
+      c[i] = r > x ? 7 : x;
+    }
+}
+
+#define T(n, type, op, cond) \
+__attribute__((noinline, noclone))	\
+type					\
+f##n (type x, type y)			\
+{					\
+  type r = op;				\
+  cond;					\
+  return r;				\
+}
+
+T (1, unsigned int, x - y, if (r > x) foo (0))
+T (2, unsigned long, x - y, if (r <= x) foo (0))
+T (3, unsigned short, x - y, if (x < r) foo (r))
+T (4, unsigned long long, x - y, if (x >= r) foo (0))
+T (5, unsigned int, x - y, if (d || r > x) foo (0))
+T (6, unsigned long, x - y, if (d || r <= x) foo (0))
+T (7, unsigned char, x - y, if (d || x < r) foo (0))
+T (8, unsigned long long, x - y, if (d || x >= r) foo (0))
+T (9, unsigned int, x + y, if (r >= x) foo (0))
+T (10, unsigned long, x + y, if (r < x) foo (0))
+T (11, unsigned short, x + y, if (x <= r) foo (r))
+T (12, unsigned long long, x + y, if (d || x > r) foo (0))
+T (13, unsigned int, x + y, if (d || r >= x) foo (0))
+T (14, unsigned long, x + y, if (d || r < x) foo (0))
+T (15, unsigned short, x + y, if (d || x <= r) foo (0))
+T (16, unsigned long long, x + y, if (d || x > r) foo (0))
+T (17, unsigned int, x + y, if (r >= y) foo (0))
+T (18, unsigned long, x + y, if (r < y) foo (0))
+T (19, unsigned short, x + y, if (y <= r) foo (r))
+T (20, unsigned long long, x + y, if (d || y > r) foo (0))
+T (21, unsigned int, x + y, if (d || r >= y) foo (0))
+T (22, unsigned long, x + y, if (d || r < y) foo (0))
+T (23, unsigned short, x + y, if (d || y <= r) foo (0))
+T (24, unsigned long long, x + y, if (d || y > r) foo (0))
+T (25, unsigned short, 2U - x, if (r > 2U) foo (0))
+T (26, unsigned char, 2U - x, if (r <= 2U) foo (0))
+
+/* { dg-final { scan-tree-dump-times "ADD_OVERFLOW" 16 "widening_mul" { target { { i?86-*-* x86_64-*-* } && { ! ia32 } } } } } */
+/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 11 "widening_mul" { target { { i?86-*-* x86_64-*-* } && { ! ia32 } } } } } */
+/* { dg-final { scan-tree-dump-times "ADD_OVERFLOW" 12 "widening_mul" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */
+/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 9 "widening_mul" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */
--- gcc/testsuite/gcc.dg/pr67089-7.c.jj	2015-11-24 19:32:30.332788737 +0100
+++ gcc/testsuite/gcc.dg/pr67089-7.c	2015-11-24 19:37:12.304804499 +0100
@@ -0,0 +1,62 @@ 
+/* PR target/67089 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -ftree-loop-if-convert -fdump-tree-widening_mul" } */
+
+extern void abort (void);
+
+int cnt, d;
+void foo (int x);
+
+#define T(n, type, op, cond) \
+__attribute__((noinline, noclone))	\
+type					\
+f##n (type x, type y)			\
+{					\
+  type r = op;				\
+  cond;					\
+  return r;				\
+}
+
+T (1, unsigned int, x - y, if (r >= x) foo (0))
+T (2, unsigned long, x - y, if (r < x) foo (0))
+T (3, unsigned short, x - y, if (x <= r) foo (r))
+T (4, unsigned long long, x - y, if (d || x > r) foo (0))
+T (5, unsigned int, x - y, if (d || r >= x) foo (0))
+T (6, unsigned long, x - y, if (d || r < x) foo (0))
+T (7, unsigned short, x - y, if (d || x <= r) foo (0))
+T (8, unsigned long long, x - y, if (d || x > r) foo (0))
+T (9, unsigned int, x - y, if (r > y) foo (0))
+T (10, unsigned long, x - y, if (r <= y) foo (0))
+T (11, unsigned short, x - y, if (y < r) foo (r))
+T (12, unsigned long long, x - y, if (y >= r) foo (0))
+T (13, unsigned int, x - y, if (r >= y) foo (0))
+T (14, unsigned long, x - y, if (r < y) foo (0))
+T (15, unsigned short, x - y, if (y <= r) foo (r))
+T (16, unsigned long long, x - y, if (d || y > r) foo (0))
+T (17, unsigned int, x - y, if (d || r > y) foo (0))
+T (18, unsigned long, x - y, if (d || r <= y) foo (0))
+T (19, unsigned char, x - y, if (d || y < r) foo (0))
+T (20, unsigned long long, x - y, if (d || y >= r) foo (0))
+T (21, unsigned int, x - y, if (d || r >= y) foo (0))
+T (22, unsigned long, x - y, if (d || r < y) foo (0))
+T (23, unsigned short, x - y, if (d || y <= r) foo (0))
+T (24, unsigned long long, x - y, if (d || y > r) foo (0))
+T (25, unsigned int, x + y, if (r > x) foo (0))
+T (26, unsigned long, x + y, if (r <= x) foo (0))
+T (27, unsigned short, x + y, if (x < r) foo (r))
+T (28, unsigned long long, x + y, if (x >= r) foo (0))
+T (29, unsigned int, x + y, if (d || r > x) foo (0))
+T (30, unsigned long, x + y, if (d || r <= x) foo (0))
+T (31, unsigned char, x + y, if (d || x < r) foo (0))
+T (32, unsigned long long, x + y, if (d || x >= r) foo (0))
+T (33, unsigned int, x + y, if (r > y) foo (0))
+T (34, unsigned long, x + y, if (r <= y) foo (0))
+T (35, unsigned short, x + y, if (y < r) foo (r))
+T (36, unsigned long long, x + y, if (y >= r) foo (0))
+T (37, unsigned int, x + y, if (d || r > y) foo (0))
+T (38, unsigned long, x + y, if (d || r <= y) foo (0))
+T (39, unsigned char, x + y, if (d || y < r) foo (0))
+T (40, unsigned long long, x + y, if (d || y >= r) foo (0))
+
+/* { dg-final { scan-tree-dump-not "ADD_OVERFLOW" "widening_mul" } } */
+/* { dg-final { scan-tree-dump-not "SUB_OVERFLOW" "widening_mul" } } */