diff mbox series

widening_mul: Pattern recognize also signed multiplication with overflow check [PR95852]

Message ID 20210109094646.GD725145@tucnak
State New
Headers show
Series widening_mul: Pattern recognize also signed multiplication with overflow check [PR95852] | expand

Commit Message

Jakub Jelinek Jan. 9, 2021, 9:46 a.m. UTC
Hi!

On top of the previous widening_mul patch, this one recognizes also
(non-perfect) signed multiplication with overflow, like:
int
f5 (int x, int y, int *res)
{
  *res = (unsigned) x * y;
  return x && (*res / x) != y;
}
The problem with such checks is that they invoke UB if x is -1 and
y is INT_MIN during the division, but perhaps the code knows that
those values won't appear.  As that case is UB, we can do for that
case whatever we want and handling that case as signed overflow
is the best option.  If x is a constant not equal to -1, then the checks
are 100% correct though.
Haven't tried to pattern match bullet-proof checks, because I really don't
know if users would write it in real-world code like that,
perhaps
  *res = (unsigned) x * y;
  return x && (x == -1 ? (*res / y) != x : (*res / x) != y);
?

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

https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
suggests to use twice as wide multiplication (perhaps we should handle that
too, for both signed and unsigned), or some very large code
with 4 different divisions nested in many conditionals, no way one can
match all the possible variants thereof.

2021-01-08  Jakub Jelinek  <jakub@redhat.com>

	PR tree-optimization/95852
	* tree-ssa-math-opts.c (maybe_optimize_guarding_check): Change
	mul_stmts parameter type to vec<gimple *> &.  Before cond_stmt
	allow in the bb any of the stmts in that vector, div_stmt and
	up to 3 cast stmts.
	(arith_cast_equal_p): New function.
	(arith_overflow_check_p): Add cast_stmt argument, handle signed
	multiply overflow checks.
	(match_arith_overflow): Adjust caller.  Handle signed multiply
	overflow checks.

	* gcc.target/i386/pr95852-3.c: New test.
	* gcc.target/i386/pr95852-4.c: New test.


	Jakub

Comments

Richard Biener Jan. 11, 2021, 8:26 a.m. UTC | #1
On Sat, 9 Jan 2021, Jakub Jelinek wrote:

> Hi!
>
> On top of the previous widening_mul patch, this one recognizes also
> (non-perfect) signed multiplication with overflow, like:
> int
> f5 (int x, int y, int *res)
> {
>  *res = (unsigned) x * y;
>  return x && (*res / x) != y;
> }
> The problem with such checks is that they invoke UB if x is -1 and
> y is INT_MIN during the division, but perhaps the code knows that
> those values won't appear.  As that case is UB, we can do for that
> case whatever we want and handling that case as signed overflow
> is the best option.  If x is a constant not equal to -1, then the checks
> are 100% correct though.
> Haven't tried to pattern match bullet-proof checks, because I really don't
> know if users would write it in real-world code like that,
> perhaps
>  *res = (unsigned) x * y;
>  return x && (x == -1 ? (*res / y) != x : (*res / x) != y);
> ?
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

Thanks,
Richard.

> https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
> suggests to use twice as wide multiplication (perhaps we should handle that
> too, for both signed and unsigned), or some very large code
> with 4 different divisions nested in many conditionals, no way one can
> match all the possible variants thereof.
>
> 2021-01-08  Jakub Jelinek  <jakub@redhat.com>
>
> 	PR tree-optimization/95852
> 	* tree-ssa-math-opts.c (maybe_optimize_guarding_check): Change
> 	mul_stmts parameter type to vec<gimple *> &.  Before cond_stmt
> 	allow in the bb any of the stmts in that vector, div_stmt and
> 	up to 3 cast stmts.
> 	(arith_cast_equal_p): New function.
> 	(arith_overflow_check_p): Add cast_stmt argument, handle signed
> 	multiply overflow checks.
> 	(match_arith_overflow): Adjust caller.  Handle signed multiply
> 	overflow checks.
>
> 	* gcc.target/i386/pr95852-3.c: New test.
> 	* gcc.target/i386/pr95852-4.c: New test.
>
> --- gcc/tree-ssa-math-opts.c.jj	2021-01-08 17:45:47.251561681 +0100
> +++ gcc/tree-ssa-math-opts.c	2021-01-08 20:42:54.350664472 +0100
> @@ -3471,7 +3471,7 @@ convert_mult_to_fma (gimple *mul_stmt, t
>    optimize the x_4(D) != 0 condition to 1.  */
>
> static void
> -maybe_optimize_guarding_check (gimple **mul_stmts, gimple *cond_stmt,
> +maybe_optimize_guarding_check (vec<gimple *> &mul_stmts, gimple *cond_stmt,
> 			       gimple *div_stmt, bool *cfg_changed)
> {
>   basic_block bb = gimple_bb (cond_stmt);
> @@ -3525,37 +3525,34 @@ maybe_optimize_guarding_check (gimple **
> 	      != TYPE_PRECISION (TREE_TYPE (rhs2))))
> 	return;
>     }
> -  gimple_stmt_iterator gsi = gsi_for_stmt (div_stmt);
> -  gsi_prev_nondebug (&gsi);
> -  if (!gsi_end_p (gsi))
> +  gimple_stmt_iterator gsi = gsi_after_labels (bb);
> +  mul_stmts.quick_push (div_stmt);
> +  if (is_gimple_debug (gsi_stmt (gsi)))
> +    gsi_next_nondebug (&gsi);
> +  unsigned cast_count = 0;
> +  while (gsi_stmt (gsi) != cond_stmt)
>     {
>       /* If original mul_stmt has a single use, allow it in the same bb,
> 	 we are looking then just at __builtin_mul_overflow_p.
> 	 Though, in that case the original mul_stmt will be replaced
> 	 by .MUL_OVERFLOW, REALPART_EXPR and IMAGPART_EXPR stmts.  */
> -      for (int i = 2; i >= 0; --i)
> +      gimple *mul_stmt;
> +      unsigned int i;
> +      bool ok = false;
> +      FOR_EACH_VEC_ELT (mul_stmts, i, mul_stmt)
> 	{
> -	  if (gsi_stmt (gsi) != mul_stmts[i])
> -	    return;
> -	  gsi_prev_nondebug (&gsi);
> -	}
> -      /* Allow up to 2 extra casts.  Given the way we check PHIs,
> -	 nothing from this bb should be consumed by any other bb
> -	 anyway.  */
> -      for (int i = 0; i < 2 && !gsi_end_p (gsi); i++)
> -	{
> -	  gimple *g = gsi_stmt (gsi);
> -	  if (!gimple_assign_cast_p (g))
> -	    return;
> -	  gsi_prev_nondebug (&gsi);
> +	  if (gsi_stmt (gsi) == mul_stmt)
> +	    {
> +	      ok = true;
> +	      break;
> +	    }
> 	}
> -      if (!gsi_end_p (gsi))
> +      if (!ok && gimple_assign_cast_p (gsi_stmt (gsi)) && ++cast_count < 4)
> +	ok = true;
> +      if (!ok)
> 	return;
> +      gsi_next_nondebug (&gsi);
>     }
> -  gsi = gsi_for_stmt (div_stmt);
> -  gsi_next_nondebug (&gsi);
> -  if (gsi_stmt (gsi) != cond_stmt)
> -    return;
>   if (gimple_code (cond_stmt) == GIMPLE_COND)
>     {
>       basic_block succ_bb = other_edge->dest;
> @@ -3633,19 +3630,39 @@ maybe_optimize_guarding_check (gimple **
>   *cfg_changed = true;
> }
>
> +/* Helper function for arith_overflow_check_p.  Return true
> +   if VAL1 is equal to VAL2 cast to corresponding integral type
> +   with other signedness or vice versa.  */
> +
> +static bool
> +arith_cast_equal_p (tree val1, tree val2)
> +{
> +  if (TREE_CODE (val1) == INTEGER_CST && TREE_CODE (val2) == INTEGER_CST)
> +    return wi::eq_p (wi::to_wide (val1), wi::to_wide (val2));
> +  else if (TREE_CODE (val1) != SSA_NAME || TREE_CODE (val2) != SSA_NAME)
> +    return false;
> +  if (gimple_assign_cast_p (SSA_NAME_DEF_STMT (val1))
> +      && gimple_assign_rhs1 (SSA_NAME_DEF_STMT (val1)) == val2)
> +    return true;
> +  if (gimple_assign_cast_p (SSA_NAME_DEF_STMT (val2))
> +      && gimple_assign_rhs1 (SSA_NAME_DEF_STMT (val2)) == val1)
> +    return true;
> +  return false;
> +}
> +
> /* Helper function of match_arith_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
> -arith_overflow_check_p (gimple *stmt, gimple *&use_stmt, tree maxval,
> -			tree *other)
> +arith_overflow_check_p (gimple *stmt, gimple *cast_stmt, gimple *&use_stmt,
> +			tree maxval, tree *other)
> {
>   enum tree_code ccode = ERROR_MARK;
>   tree crhs1 = NULL_TREE, crhs2 = NULL_TREE;
>   enum tree_code code = gimple_assign_rhs_code (stmt);
> -  tree lhs = gimple_assign_lhs (stmt);
> +  tree lhs = gimple_assign_lhs (cast_stmt ? cast_stmt : stmt);
>   tree rhs1 = gimple_assign_rhs1 (stmt);
>   tree rhs2 = gimple_assign_rhs2 (stmt);
>   tree multop = NULL_TREE, divlhs = NULL_TREE;
> @@ -3658,7 +3675,16 @@ arith_overflow_check_p (gimple *stmt, gi
> 	return 0;
>       if (gimple_assign_rhs1 (use_stmt) != lhs)
> 	return 0;
> -      if (gimple_assign_rhs2 (use_stmt) == rhs1)
> +      if (cast_stmt)
> +	{
> +	  if (arith_cast_equal_p (gimple_assign_rhs2 (use_stmt), rhs1))
> +	    multop = rhs2;
> +	  else if (arith_cast_equal_p (gimple_assign_rhs2 (use_stmt), rhs2))
> +	    multop = rhs1;
> +	  else
> +	    return 0;
> +	}
> +      else if (gimple_assign_rhs2 (use_stmt) == rhs1)
> 	multop = rhs2;
>       else if (operand_equal_p (gimple_assign_rhs2 (use_stmt), rhs2, 0))
> 	multop = rhs1;
> @@ -3759,10 +3785,18 @@ arith_overflow_check_p (gimple *stmt, gi
> 	 r = a * b; _1 = r / b; _1 == a
> 	 r = a * b; _1 = r / a; _1 != b
> 	 r = a * b; _1 = r / b; _1 != a.  */
> -      if (code == MULT_EXPR
> -	  && ((crhs1 == divlhs && operand_equal_p (crhs2, multop, 0))
> -	      || (crhs2 == divlhs && crhs1 == multop)))
> -	return ccode == NE_EXPR ? 1 : -1;
> +      if (code == MULT_EXPR)
> +	{
> +	  if (cast_stmt)
> +	    {
> +	      if ((crhs1 == divlhs && arith_cast_equal_p (crhs2, multop))
> +		  || (crhs2 == divlhs && arith_cast_equal_p (crhs1, multop)))
> +		return ccode == NE_EXPR ? 1 : -1;
> +	    }
> +	  else if ((crhs1 == divlhs && operand_equal_p (crhs2, multop, 0))
> +		   || (crhs2 == divlhs && crhs1 == multop))
> +	    return ccode == NE_EXPR ? 1 : -1;
> +	}
>       break;
>     default:
>       break;
> @@ -3837,6 +3871,8 @@ match_arith_overflow (gimple_stmt_iterat
>   gimple *add_stmt = NULL;
>   bool add_first = false;
>   gimple *cond_stmt = NULL;
> +  gimple *cast_stmt = NULL;
> +  tree cast_lhs = NULL_TREE;
>
>   gcc_checking_assert (code == PLUS_EXPR
> 		       || code == MINUS_EXPR
> @@ -3860,7 +3896,7 @@ match_arith_overflow (gimple_stmt_iterat
> 	continue;
>
>       tree other = NULL_TREE;
> -      if (arith_overflow_check_p (stmt, use_stmt, NULL_TREE, &other))
> +      if (arith_overflow_check_p (stmt, NULL, use_stmt, NULL_TREE, &other))
> 	{
> 	  if (code == BIT_NOT_EXPR)
> 	    {
> @@ -3875,12 +3911,51 @@ match_arith_overflow (gimple_stmt_iterat
> 	    }
> 	  ovf_use_seen = true;
> 	}
> -      else
> -	use_seen = true;
> +      else
> +	{
> +	  use_seen = true;
> +	  if (code == MULT_EXPR
> +	      && cast_stmt == NULL
> +	      && gimple_assign_cast_p (use_stmt))
> +	    {
> +	      cast_lhs = gimple_assign_lhs (use_stmt);
> +	      if (INTEGRAL_TYPE_P (TREE_TYPE (cast_lhs))
> +		  && !TYPE_UNSIGNED (TREE_TYPE (cast_lhs))
> +		  && (TYPE_PRECISION (TREE_TYPE (cast_lhs))
> +		      == TYPE_PRECISION (TREE_TYPE (lhs))))
> +		cast_stmt = use_stmt;
> +	      else
> +		cast_lhs = NULL_TREE;
> +	    }
> +	}
>       if (ovf_use_seen && use_seen)
> 	break;
>     }
>
> +  if (!ovf_use_seen
> +      && code == MULT_EXPR
> +      && cast_stmt)
> +    {
> +      if (TREE_CODE (rhs1) != SSA_NAME
> +	  || (TREE_CODE (rhs2) != SSA_NAME && TREE_CODE (rhs2) != INTEGER_CST))
> +	return false;
> +      FOR_EACH_IMM_USE_FAST (use_p, iter, cast_lhs)
> +	{
> +	  use_stmt = USE_STMT (use_p);
> +	  if (is_gimple_debug (use_stmt))
> +	    continue;
> +
> +	  if (arith_overflow_check_p (stmt, cast_stmt, use_stmt,
> +				      NULL_TREE, NULL))
> +	    ovf_use_seen = true;
> +	}
> +    }
> +  else
> +    {
> +      cast_stmt = NULL;
> +      cast_lhs = NULL_TREE;
> +    }
> +
>   tree maxval = NULL_TREE;
>   if (!ovf_use_seen
>       || (code != MULT_EXPR && (code == BIT_NOT_EXPR ? use_seen : !use_seen))
> @@ -3888,7 +3963,7 @@ match_arith_overflow (gimple_stmt_iterat
> 	  && optab_handler (uaddv4_optab,
> 			    TYPE_MODE (type)) == CODE_FOR_nothing)
>       || (code == MULT_EXPR
> -	  && optab_handler (umulv4_optab,
> +	  && optab_handler (cast_stmt ? mulv4_optab : umulv4_optab,
> 			    TYPE_MODE (type)) == CODE_FOR_nothing))
>     {
>       if (code != PLUS_EXPR)
> @@ -3947,7 +4022,7 @@ match_arith_overflow (gimple_stmt_iterat
> 	  if (is_gimple_debug (use_stmt))
> 	    continue;
>
> -	  if (arith_overflow_check_p (stmt, use_stmt, maxval, NULL))
> +	  if (arith_overflow_check_p (stmt, NULL, use_stmt, maxval, NULL))
> 	    {
> 	      ovf_use_seen = true;
> 	      use_bb = gimple_bb (use_stmt);
> @@ -4066,6 +4141,41 @@ match_arith_overflow (gimple_stmt_iterat
>   if (code == BIT_NOT_EXPR)
>     *gsi = gsi_for_stmt (cond_stmt);
>
> +  auto_vec<gimple *, 8> mul_stmts;
> +  if (code == MULT_EXPR && cast_stmt)
> +    {
> +      type = TREE_TYPE (cast_lhs);
> +      gimple *g = SSA_NAME_DEF_STMT (rhs1);
> +      if (gimple_assign_cast_p (g)
> +	  && useless_type_conversion_p (type,
> +					TREE_TYPE (gimple_assign_rhs1 (g)))
> +	  && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs1 (g)))
> +	rhs1 = gimple_assign_rhs1 (g);
> +      else
> +	{
> +	  g = gimple_build_assign (make_ssa_name (type), NOP_EXPR, rhs1);
> +	  gsi_insert_before (gsi, g, GSI_SAME_STMT);
> +	  rhs1 = gimple_assign_lhs (g);
> +	  mul_stmts.quick_push (g);
> +	}
> +      if (TREE_CODE (rhs2) == INTEGER_CST)
> +	rhs2 = fold_convert (type, rhs2);
> +      else
> +	{
> +	  if (gimple_assign_cast_p (g)
> +	      && useless_type_conversion_p (type,
> +					    TREE_TYPE (gimple_assign_rhs1 (g)))
> +	      && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs1 (g)))
> +	    rhs2 = gimple_assign_rhs1 (g);
> +	  else
> +	    {
> +	      g = gimple_build_assign (make_ssa_name (type), NOP_EXPR, rhs2);
> +	      gsi_insert_before (gsi, g, GSI_SAME_STMT);
> +	      rhs2 = gimple_assign_lhs (g);
> +	      mul_stmts.quick_push (g);
> +	    }
> +	}
> +    }
>   tree ctype = build_complex_type (type);
>   gcall *g = gimple_build_call_internal (code == MULT_EXPR
> 					 ? IFN_MUL_OVERFLOW
> @@ -4075,14 +4185,13 @@ match_arith_overflow (gimple_stmt_iterat
>   tree ctmp = make_ssa_name (ctype);
>   gimple_call_set_lhs (g, ctmp);
>   gsi_insert_before (gsi, g, GSI_SAME_STMT);
> -  tree new_lhs = maxval ? make_ssa_name (type) : lhs;
> -  gimple *mul_stmts[3] = { NULL, NULL, NULL };
> +  tree new_lhs = (maxval || cast_stmt) ? make_ssa_name (type) : lhs;
>   gassign *g2;
>   if (code != BIT_NOT_EXPR)
>     {
>       g2 = gimple_build_assign (new_lhs, REALPART_EXPR,
> 				build1 (REALPART_EXPR, type, ctmp));
> -      if (maxval)
> +      if (maxval || cast_stmt)
> 	{
> 	  gsi_insert_before (gsi, g2, GSI_SAME_STMT);
> 	  if (add_first)
> @@ -4092,8 +4201,14 @@ match_arith_overflow (gimple_stmt_iterat
> 	gsi_replace (gsi, g2, true);
>       if (code == MULT_EXPR)
> 	{
> -	  mul_stmts[0] = g;
> -	  mul_stmts[1] = g2;
> +	  mul_stmts.quick_push (g);
> +	  mul_stmts.quick_push (g2);
> +	  if (cast_stmt)
> +	    {
> +	      g2 = gimple_build_assign (lhs, NOP_EXPR, new_lhs);
> +	      gsi_replace (gsi, g2, true);
> +	      mul_stmts.quick_push (g2);
> +	    }
> 	}
>     }
>   tree ovf = make_ssa_name (type);
> @@ -4104,15 +4219,16 @@ match_arith_overflow (gimple_stmt_iterat
>   else
>     gsi_insert_before (gsi, g2, GSI_SAME_STMT);
>   if (code == MULT_EXPR)
> -    mul_stmts[2] = g2;
> +    mul_stmts.quick_push (g2);
>
> -  FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
> +  FOR_EACH_IMM_USE_STMT (use_stmt, iter, cast_lhs ? cast_lhs : lhs)
>     {
>       if (is_gimple_debug (use_stmt))
> 	continue;
>
>       gimple *orig_use_stmt = use_stmt;
> -      int ovf_use = arith_overflow_check_p (stmt, use_stmt, maxval, NULL);
> +      int ovf_use = arith_overflow_check_p (stmt, cast_stmt, use_stmt,
> +					    maxval, NULL);
>       if (ovf_use == 0)
> 	{
> 	  gcc_assert (code != BIT_NOT_EXPR);
> --- gcc/testsuite/gcc.target/i386/pr95852-3.c.jj	2021-01-08 20:45:15.153076655 +0100
> +++ gcc/testsuite/gcc.target/i386/pr95852-3.c	2021-01-08 20:07:52.772379490 +0100
> @@ -0,0 +1,266 @@
> +/* PR tree-optimization/95852 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized -masm=att" } */
> +/* { dg-final { scan-tree-dump-times " = \.MUL_OVERFLOW " 32 "optimized" } } */
> +/* { dg-final { scan-assembler-times "\timull\t" 32 } } */
> +/* { dg-final { scan-assembler-times "\tseto\t" 8 } } */
> +/* { dg-final { scan-assembler-times "\tsetno\t" 8 } } */
> +/* { dg-final { scan-assembler-times "\tjn\?o\t" 16 } } */
> +
> +unsigned fn (void);
> +
> +int
> +f1 (unsigned x, unsigned y, unsigned *res)
> +{
> +  *res = x * y;
> +  return x && ((int) *res / (int) x) != (int) y;
> +}
> +
> +unsigned
> +f2 (unsigned x, unsigned y)
> +{
> +  unsigned int r = x * y;
> +  if (x && ((int) r / (int) x) != (int) y)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f3 (unsigned x, unsigned y, unsigned *res)
> +{
> +  *res = x * y;
> +  return !x || ((int) *res / (int) x) == (int) y;
> +}
> +
> +unsigned
> +f4 (unsigned x, unsigned y)
> +{
> +  unsigned int r = x * y;
> +  if (!x || ((int) r / (int) x) == (int) y)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f5 (int x, int y, int *res)
> +{
> +  *res = (unsigned) x * y;
> +  return x && (*res / x) != y;
> +}
> +
> +int
> +f6 (int x, int y)
> +{
> +  int r = (unsigned) x * y;
> +  if (x && (r / x) != y)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f7 (int x, int y, int *res)
> +{
> +  *res = (unsigned) x * y;
> +  return !x || (*res / x) == y;
> +}
> +
> +int
> +f8 (int x, int y)
> +{
> +  int r = (unsigned) x * y;
> +  if (!x || (r / x) == y)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f9 (unsigned x, unsigned y, unsigned *res)
> +{
> +  *res = x * y;
> +  return y && ((int) *res / (int) y) != (int) x;
> +}
> +
> +unsigned
> +f10 (unsigned x, unsigned y)
> +{
> +  unsigned int r = x * y;
> +  if (y && ((int) r / (int) y) != (int) x)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f11 (unsigned x, unsigned y, unsigned *res)
> +{
> +  *res = x * y;
> +  return !y || ((int) *res / (int) y) == (int) x;
> +}
> +
> +unsigned
> +f12 (unsigned x, unsigned y)
> +{
> +  unsigned int r = x * y;
> +  if (!y || ((int) r / (int) y) == (int) x)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f13 (int x, int y, int *res)
> +{
> +  *res = (unsigned) x * y;
> +  return y && (*res / y) != x;
> +}
> +
> +int
> +f14 (int x, int y)
> +{
> +  int r = (unsigned) x * y;
> +  if (y && (r / y) != x)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f15 (int x, int y, int *res)
> +{
> +  *res = (unsigned) x * y;
> +  return !y || (*res / y) == x;
> +}
> +
> +int
> +f16 (int x, int y)
> +{
> +  int r = (unsigned) x * y;
> +  if (!y || (r / y) == x)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f17 (unsigned x, unsigned *res)
> +{
> +  *res = x * 35U;
> +  return x && ((int) *res / (int) x) != 35;
> +}
> +
> +unsigned
> +f18 (unsigned x)
> +{
> +  unsigned int r = x * 35U;
> +  if (x && ((int) r / (int) x) != 35)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f19 (unsigned x, unsigned *res)
> +{
> +  *res = x * 35U;
> +  return !x || ((int) *res / (int) x) == 35;
> +}
> +
> +unsigned
> +f20 (unsigned x)
> +{
> +  unsigned int r = x * 35U;
> +  if (!x || ((int) r / (int) x) == 35)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f21 (int x, int *res)
> +{
> +  *res = (unsigned) x * 35;
> +  return x && (*res / x) != 35;
> +}
> +
> +int
> +f22 (int x)
> +{
> +  int r = (unsigned) x * 35;
> +  if (x && (r / x) != 35)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f23 (int x, int *res)
> +{
> +  *res = (unsigned) x * 35;
> +  return !x || (*res / x) == 35;
> +}
> +
> +int
> +f24 (int x)
> +{
> +  int r = (unsigned) x * 35;
> +  if (!x || (r / x) == 35)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f25 (unsigned x, unsigned *res)
> +{
> +  *res = x * 35U;
> +  return ((int) *res / 35) != (int) x;
> +}
> +
> +unsigned
> +f26 (unsigned x)
> +{
> +  unsigned int r = x * 35U;
> +  if (((int) r / 35) != (int) x)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f27 (unsigned x, unsigned *res)
> +{
> +  *res = x * 35U;
> +  return ((int) *res / 35) == (int) x;
> +}
> +
> +unsigned
> +f28 (unsigned x)
> +{
> +  unsigned int r = x * 35U;
> +  if (((int) r / 35) == (int) x)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f29 (int x, int *res)
> +{
> +  *res = (unsigned) x * 35;
> +  return 35 && (*res / 35) != x;
> +}
> +
> +int
> +f30 (int x)
> +{
> +  int r = (unsigned) x * 35;
> +  if ((r / 35) != x)
> +    return fn ();
> +  return r;
> +}
> +
> +int
> +f31 (int x, int *res)
> +{
> +  *res = (unsigned) x * 35;
> +  return (*res / 35) == x;
> +}
> +
> +int
> +f32 (int x)
> +{
> +  int r = (unsigned) x * 35;
> +  if ((r / 35) == x)
> +    return fn ();
> +  return r;
> +}
> --- gcc/testsuite/gcc.target/i386/pr95852-4.c.jj	2021-01-08 21:02:39.627299338 +0100
> +++ gcc/testsuite/gcc.target/i386/pr95852-4.c	2021-01-08 21:02:34.731354546 +0100
> @@ -0,0 +1,266 @@
> +/* PR tree-optimization/95852 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized -masm=att" } */
> +/* { dg-final { scan-tree-dump-times " = \.MUL_OVERFLOW " 32 "optimized" } } */
> +/* { dg-final { scan-assembler-times "\timull\t" 32 } } */
> +/* { dg-final { scan-assembler-times "\tseto\t" 8 } } */
> +/* { dg-final { scan-assembler-times "\tsetno\t" 8 } } */
> +/* { dg-final { scan-assembler-times "\tjn\?o\t" 16 } } */
> +
> +unsigned fn (void);
> +
> +int
> +f1 (unsigned x, unsigned y)
> +{
> +  unsigned int r = x * y;
> +  return x && ((int) r / (int) x) != (int) y;
> +}
> +
> +unsigned
> +f2 (unsigned x, unsigned y)
> +{
> +  unsigned int r = x * y;
> +  if (x && ((int) r / (int) x) != (int) y)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f3 (unsigned x, unsigned y)
> +{
> +  unsigned int r = x * y;
> +  return !x || ((int) r / (int) x) == (int) y;
> +}
> +
> +unsigned
> +f4 (unsigned x, unsigned y)
> +{
> +  unsigned int r = x * y;
> +  if (!x || ((int) r / (int) x) == (int) y)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f5 (int x, int y)
> +{
> +  int r = (unsigned) x * y;
> +  return x && (r / x) != y;
> +}
> +
> +int
> +f6 (int x, int y)
> +{
> +  int r = (unsigned) x * y;
> +  if (x && (r / x) != y)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f7 (int x, int y)
> +{
> +  int r = (unsigned) x * y;
> +  return !x || (r / x) == y;
> +}
> +
> +int
> +f8 (int x, int y)
> +{
> +  int r = (unsigned) x * y;
> +  if (!x || (r / x) == y)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f9 (unsigned x, unsigned y)
> +{
> +  unsigned r = x * y;
> +  return y && ((int) r / (int) y) != (int) x;
> +}
> +
> +unsigned
> +f10 (unsigned x, unsigned y)
> +{
> +  unsigned int r = x * y;
> +  if (y && ((int) r / (int) y) != (int) x)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f11 (unsigned x, unsigned y)
> +{
> +  unsigned r = x * y;
> +  return !y || ((int) r / (int) y) == (int) x;
> +}
> +
> +unsigned
> +f12 (unsigned x, unsigned y)
> +{
> +  unsigned int r = x * y;
> +  if (!y || ((int) r / (int) y) == (int) x)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f13 (int x, int y)
> +{
> +  int r = (unsigned) x * y;
> +  return y && (r / y) != x;
> +}
> +
> +int
> +f14 (int x, int y)
> +{
> +  int r = (unsigned) x * y;
> +  if (y && (r / y) != x)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f15 (int x, int y)
> +{
> +  int r = (unsigned) x * y;
> +  return !y || (r / y) == x;
> +}
> +
> +int
> +f16 (int x, int y)
> +{
> +  int r = (unsigned) x * y;
> +  if (!y || (r / y) == x)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f17 (unsigned x)
> +{
> +  unsigned r = x * 35U;
> +  return x && ((int) r / (int) x) != 35;
> +}
> +
> +unsigned
> +f18 (unsigned x)
> +{
> +  unsigned int r = x * 35U;
> +  if (x && ((int) r / (int) x) != 35)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f19 (unsigned x)
> +{
> +  unsigned r = x * 35U;
> +  return !x || ((int) r / (int) x) == 35;
> +}
> +
> +unsigned
> +f20 (unsigned x)
> +{
> +  unsigned int r = x * 35U;
> +  if (!x || ((int) r / (int) x) == 35)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f21 (int x)
> +{
> +  int r = (unsigned) x * 35;
> +  return x && (r / x) != 35;
> +}
> +
> +int
> +f22 (int x)
> +{
> +  int r = (unsigned) x * 35;
> +  if (x && (r / x) != 35)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f23 (int x)
> +{
> +  int r = (unsigned) x * 35;
> +  return !x || (r / x) == 35;
> +}
> +
> +int
> +f24 (int x)
> +{
> +  int r = (unsigned) x * 35;
> +  if (!x || (r / x) == 35)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f25 (unsigned x)
> +{
> +  unsigned r = x * 35U;
> +  return ((int) r / 35) != (int) x;
> +}
> +
> +unsigned
> +f26 (unsigned x)
> +{
> +  unsigned int r = x * 35U;
> +  if (((int) r / 35) != (int) x)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f27 (unsigned x)
> +{
> +  unsigned r = x * 35U;
> +  return !35 || ((int) r / 35) == (int) x;
> +}
> +
> +unsigned
> +f28 (unsigned x)
> +{
> +  unsigned int r = x * 35U;
> +  if (((int) r / 35) == (int) x)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f29 (int x)
> +{
> +  int r = (unsigned) x * 35;
> +  return 35 && (r / 35) != x;
> +}
> +
> +int
> +f30 (int x)
> +{
> +  int r = (unsigned) x * 35;
> +  if ((r / 35) != x)
> +    return fn ();
> +  return 0;
> +}
> +
> +int
> +f31 (int x)
> +{
> +  int r = (unsigned) x * 35;
> +  return (r / 35) == x;
> +}
> +
> +int
> +f32 (int x)
> +{
> +  int r = (unsigned) x * 35;
> +  if ((r / 35) == x)
> +    return fn ();
> +  return 0;
> +}
>
> 	Jakub
>
>
diff mbox series

Patch

--- gcc/tree-ssa-math-opts.c.jj	2021-01-08 17:45:47.251561681 +0100
+++ gcc/tree-ssa-math-opts.c	2021-01-08 20:42:54.350664472 +0100
@@ -3471,7 +3471,7 @@  convert_mult_to_fma (gimple *mul_stmt, t
    optimize the x_4(D) != 0 condition to 1.  */
 
 static void
-maybe_optimize_guarding_check (gimple **mul_stmts, gimple *cond_stmt,
+maybe_optimize_guarding_check (vec<gimple *> &mul_stmts, gimple *cond_stmt,
 			       gimple *div_stmt, bool *cfg_changed)
 {
   basic_block bb = gimple_bb (cond_stmt);
@@ -3525,37 +3525,34 @@  maybe_optimize_guarding_check (gimple **
 	      != TYPE_PRECISION (TREE_TYPE (rhs2))))
 	return;
     }
-  gimple_stmt_iterator gsi = gsi_for_stmt (div_stmt);
-  gsi_prev_nondebug (&gsi);
-  if (!gsi_end_p (gsi))
+  gimple_stmt_iterator gsi = gsi_after_labels (bb);
+  mul_stmts.quick_push (div_stmt);
+  if (is_gimple_debug (gsi_stmt (gsi)))
+    gsi_next_nondebug (&gsi);
+  unsigned cast_count = 0;
+  while (gsi_stmt (gsi) != cond_stmt)
     {
       /* If original mul_stmt has a single use, allow it in the same bb,
 	 we are looking then just at __builtin_mul_overflow_p.
 	 Though, in that case the original mul_stmt will be replaced
 	 by .MUL_OVERFLOW, REALPART_EXPR and IMAGPART_EXPR stmts.  */
-      for (int i = 2; i >= 0; --i)
+      gimple *mul_stmt;
+      unsigned int i;
+      bool ok = false;
+      FOR_EACH_VEC_ELT (mul_stmts, i, mul_stmt)
 	{
-	  if (gsi_stmt (gsi) != mul_stmts[i])
-	    return;
-	  gsi_prev_nondebug (&gsi);
-	}
-      /* Allow up to 2 extra casts.  Given the way we check PHIs,
-	 nothing from this bb should be consumed by any other bb
-	 anyway.  */
-      for (int i = 0; i < 2 && !gsi_end_p (gsi); i++)
-	{
-	  gimple *g = gsi_stmt (gsi);
-	  if (!gimple_assign_cast_p (g))
-	    return;
-	  gsi_prev_nondebug (&gsi);
+	  if (gsi_stmt (gsi) == mul_stmt)
+	    {
+	      ok = true;
+	      break;
+	    }
 	}
-      if (!gsi_end_p (gsi))
+      if (!ok && gimple_assign_cast_p (gsi_stmt (gsi)) && ++cast_count < 4)
+	ok = true;
+      if (!ok)
 	return;
+      gsi_next_nondebug (&gsi);
     }
-  gsi = gsi_for_stmt (div_stmt);
-  gsi_next_nondebug (&gsi);
-  if (gsi_stmt (gsi) != cond_stmt)
-    return;
   if (gimple_code (cond_stmt) == GIMPLE_COND)
     {
       basic_block succ_bb = other_edge->dest;
@@ -3633,19 +3630,39 @@  maybe_optimize_guarding_check (gimple **
   *cfg_changed = true;
 }
 
+/* Helper function for arith_overflow_check_p.  Return true
+   if VAL1 is equal to VAL2 cast to corresponding integral type
+   with other signedness or vice versa.  */
+
+static bool
+arith_cast_equal_p (tree val1, tree val2)
+{
+  if (TREE_CODE (val1) == INTEGER_CST && TREE_CODE (val2) == INTEGER_CST)
+    return wi::eq_p (wi::to_wide (val1), wi::to_wide (val2));
+  else if (TREE_CODE (val1) != SSA_NAME || TREE_CODE (val2) != SSA_NAME)
+    return false;
+  if (gimple_assign_cast_p (SSA_NAME_DEF_STMT (val1))
+      && gimple_assign_rhs1 (SSA_NAME_DEF_STMT (val1)) == val2)
+    return true;
+  if (gimple_assign_cast_p (SSA_NAME_DEF_STMT (val2))
+      && gimple_assign_rhs1 (SSA_NAME_DEF_STMT (val2)) == val1)
+    return true;
+  return false;
+}
+
 /* Helper function of match_arith_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
-arith_overflow_check_p (gimple *stmt, gimple *&use_stmt, tree maxval,
-			tree *other)
+arith_overflow_check_p (gimple *stmt, gimple *cast_stmt, gimple *&use_stmt,
+			tree maxval, tree *other)
 {
   enum tree_code ccode = ERROR_MARK;
   tree crhs1 = NULL_TREE, crhs2 = NULL_TREE;
   enum tree_code code = gimple_assign_rhs_code (stmt);
-  tree lhs = gimple_assign_lhs (stmt);
+  tree lhs = gimple_assign_lhs (cast_stmt ? cast_stmt : stmt);
   tree rhs1 = gimple_assign_rhs1 (stmt);
   tree rhs2 = gimple_assign_rhs2 (stmt);
   tree multop = NULL_TREE, divlhs = NULL_TREE;
@@ -3658,7 +3675,16 @@  arith_overflow_check_p (gimple *stmt, gi
 	return 0;
       if (gimple_assign_rhs1 (use_stmt) != lhs)
 	return 0;
-      if (gimple_assign_rhs2 (use_stmt) == rhs1)
+      if (cast_stmt)
+	{
+	  if (arith_cast_equal_p (gimple_assign_rhs2 (use_stmt), rhs1))
+	    multop = rhs2;
+	  else if (arith_cast_equal_p (gimple_assign_rhs2 (use_stmt), rhs2))
+	    multop = rhs1;
+	  else
+	    return 0;
+	}
+      else if (gimple_assign_rhs2 (use_stmt) == rhs1)
 	multop = rhs2;
       else if (operand_equal_p (gimple_assign_rhs2 (use_stmt), rhs2, 0))
 	multop = rhs1;
@@ -3759,10 +3785,18 @@  arith_overflow_check_p (gimple *stmt, gi
 	 r = a * b; _1 = r / b; _1 == a
 	 r = a * b; _1 = r / a; _1 != b
 	 r = a * b; _1 = r / b; _1 != a.  */
-      if (code == MULT_EXPR
-	  && ((crhs1 == divlhs && operand_equal_p (crhs2, multop, 0))
-	      || (crhs2 == divlhs && crhs1 == multop)))
-	return ccode == NE_EXPR ? 1 : -1;
+      if (code == MULT_EXPR)
+	{
+	  if (cast_stmt)
+	    {
+	      if ((crhs1 == divlhs && arith_cast_equal_p (crhs2, multop))
+		  || (crhs2 == divlhs && arith_cast_equal_p (crhs1, multop)))
+		return ccode == NE_EXPR ? 1 : -1;
+	    }
+	  else if ((crhs1 == divlhs && operand_equal_p (crhs2, multop, 0))
+		   || (crhs2 == divlhs && crhs1 == multop))
+	    return ccode == NE_EXPR ? 1 : -1;
+	}
       break;
     default:
       break;
@@ -3837,6 +3871,8 @@  match_arith_overflow (gimple_stmt_iterat
   gimple *add_stmt = NULL;
   bool add_first = false;
   gimple *cond_stmt = NULL;
+  gimple *cast_stmt = NULL;
+  tree cast_lhs = NULL_TREE;
 
   gcc_checking_assert (code == PLUS_EXPR
 		       || code == MINUS_EXPR
@@ -3860,7 +3896,7 @@  match_arith_overflow (gimple_stmt_iterat
 	continue;
 
       tree other = NULL_TREE;
-      if (arith_overflow_check_p (stmt, use_stmt, NULL_TREE, &other))
+      if (arith_overflow_check_p (stmt, NULL, use_stmt, NULL_TREE, &other))
 	{
 	  if (code == BIT_NOT_EXPR)
 	    {
@@ -3875,12 +3911,51 @@  match_arith_overflow (gimple_stmt_iterat
 	    }
 	  ovf_use_seen = true;
 	}
-      else
-	use_seen = true;
+      else 
+	{
+	  use_seen = true;
+	  if (code == MULT_EXPR
+	      && cast_stmt == NULL
+	      && gimple_assign_cast_p (use_stmt))
+	    {
+	      cast_lhs = gimple_assign_lhs (use_stmt);
+	      if (INTEGRAL_TYPE_P (TREE_TYPE (cast_lhs))
+		  && !TYPE_UNSIGNED (TREE_TYPE (cast_lhs))
+		  && (TYPE_PRECISION (TREE_TYPE (cast_lhs))
+		      == TYPE_PRECISION (TREE_TYPE (lhs))))
+		cast_stmt = use_stmt;
+	      else
+		cast_lhs = NULL_TREE;
+	    }
+	}
       if (ovf_use_seen && use_seen)
 	break;
     }
 
+  if (!ovf_use_seen
+      && code == MULT_EXPR
+      && cast_stmt)
+    {
+      if (TREE_CODE (rhs1) != SSA_NAME
+	  || (TREE_CODE (rhs2) != SSA_NAME && TREE_CODE (rhs2) != INTEGER_CST))
+	return false;
+      FOR_EACH_IMM_USE_FAST (use_p, iter, cast_lhs)
+	{
+	  use_stmt = USE_STMT (use_p);
+	  if (is_gimple_debug (use_stmt))
+	    continue;
+
+	  if (arith_overflow_check_p (stmt, cast_stmt, use_stmt,
+				      NULL_TREE, NULL))
+	    ovf_use_seen = true;
+	}
+    }
+  else
+    {
+      cast_stmt = NULL;
+      cast_lhs = NULL_TREE;
+    }
+
   tree maxval = NULL_TREE;
   if (!ovf_use_seen
       || (code != MULT_EXPR && (code == BIT_NOT_EXPR ? use_seen : !use_seen))
@@ -3888,7 +3963,7 @@  match_arith_overflow (gimple_stmt_iterat
 	  && optab_handler (uaddv4_optab,
 			    TYPE_MODE (type)) == CODE_FOR_nothing)
       || (code == MULT_EXPR
-	  && optab_handler (umulv4_optab,
+	  && optab_handler (cast_stmt ? mulv4_optab : umulv4_optab,
 			    TYPE_MODE (type)) == CODE_FOR_nothing))
     {
       if (code != PLUS_EXPR)
@@ -3947,7 +4022,7 @@  match_arith_overflow (gimple_stmt_iterat
 	  if (is_gimple_debug (use_stmt))
 	    continue;
 
-	  if (arith_overflow_check_p (stmt, use_stmt, maxval, NULL))
+	  if (arith_overflow_check_p (stmt, NULL, use_stmt, maxval, NULL))
 	    {
 	      ovf_use_seen = true;
 	      use_bb = gimple_bb (use_stmt);
@@ -4066,6 +4141,41 @@  match_arith_overflow (gimple_stmt_iterat
   if (code == BIT_NOT_EXPR)
     *gsi = gsi_for_stmt (cond_stmt);
 
+  auto_vec<gimple *, 8> mul_stmts;
+  if (code == MULT_EXPR && cast_stmt)
+    {
+      type = TREE_TYPE (cast_lhs);
+      gimple *g = SSA_NAME_DEF_STMT (rhs1);
+      if (gimple_assign_cast_p (g)
+	  && useless_type_conversion_p (type,
+					TREE_TYPE (gimple_assign_rhs1 (g)))
+	  && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs1 (g)))
+	rhs1 = gimple_assign_rhs1 (g);
+      else
+	{
+	  g = gimple_build_assign (make_ssa_name (type), NOP_EXPR, rhs1);
+	  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+	  rhs1 = gimple_assign_lhs (g);
+	  mul_stmts.quick_push (g);
+	}
+      if (TREE_CODE (rhs2) == INTEGER_CST)
+	rhs2 = fold_convert (type, rhs2);
+      else
+	{
+	  if (gimple_assign_cast_p (g)
+	      && useless_type_conversion_p (type,
+					    TREE_TYPE (gimple_assign_rhs1 (g)))
+	      && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs1 (g)))
+	    rhs2 = gimple_assign_rhs1 (g);
+	  else
+	    {
+	      g = gimple_build_assign (make_ssa_name (type), NOP_EXPR, rhs2);
+	      gsi_insert_before (gsi, g, GSI_SAME_STMT);
+	      rhs2 = gimple_assign_lhs (g);
+	      mul_stmts.quick_push (g);
+	    }
+	}
+    }
   tree ctype = build_complex_type (type);
   gcall *g = gimple_build_call_internal (code == MULT_EXPR
 					 ? IFN_MUL_OVERFLOW
@@ -4075,14 +4185,13 @@  match_arith_overflow (gimple_stmt_iterat
   tree ctmp = make_ssa_name (ctype);
   gimple_call_set_lhs (g, ctmp);
   gsi_insert_before (gsi, g, GSI_SAME_STMT);
-  tree new_lhs = maxval ? make_ssa_name (type) : lhs;
-  gimple *mul_stmts[3] = { NULL, NULL, NULL };
+  tree new_lhs = (maxval || cast_stmt) ? make_ssa_name (type) : lhs;
   gassign *g2;
   if (code != BIT_NOT_EXPR)
     {
       g2 = gimple_build_assign (new_lhs, REALPART_EXPR,
 				build1 (REALPART_EXPR, type, ctmp));
-      if (maxval)
+      if (maxval || cast_stmt)
 	{
 	  gsi_insert_before (gsi, g2, GSI_SAME_STMT);
 	  if (add_first)
@@ -4092,8 +4201,14 @@  match_arith_overflow (gimple_stmt_iterat
 	gsi_replace (gsi, g2, true);
       if (code == MULT_EXPR)
 	{
-	  mul_stmts[0] = g;
-	  mul_stmts[1] = g2;
+	  mul_stmts.quick_push (g);
+	  mul_stmts.quick_push (g2);
+	  if (cast_stmt)
+	    {
+	      g2 = gimple_build_assign (lhs, NOP_EXPR, new_lhs);
+	      gsi_replace (gsi, g2, true);
+	      mul_stmts.quick_push (g2);
+	    }
 	}
     }
   tree ovf = make_ssa_name (type);
@@ -4104,15 +4219,16 @@  match_arith_overflow (gimple_stmt_iterat
   else
     gsi_insert_before (gsi, g2, GSI_SAME_STMT);
   if (code == MULT_EXPR)
-    mul_stmts[2] = g2;
+    mul_stmts.quick_push (g2);
 
-  FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
+  FOR_EACH_IMM_USE_STMT (use_stmt, iter, cast_lhs ? cast_lhs : lhs)
     {
       if (is_gimple_debug (use_stmt))
 	continue;
 
       gimple *orig_use_stmt = use_stmt;
-      int ovf_use = arith_overflow_check_p (stmt, use_stmt, maxval, NULL);
+      int ovf_use = arith_overflow_check_p (stmt, cast_stmt, use_stmt,
+					    maxval, NULL);
       if (ovf_use == 0)
 	{
 	  gcc_assert (code != BIT_NOT_EXPR);
--- gcc/testsuite/gcc.target/i386/pr95852-3.c.jj	2021-01-08 20:45:15.153076655 +0100
+++ gcc/testsuite/gcc.target/i386/pr95852-3.c	2021-01-08 20:07:52.772379490 +0100
@@ -0,0 +1,266 @@ 
+/* PR tree-optimization/95852 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized -masm=att" } */
+/* { dg-final { scan-tree-dump-times " = \.MUL_OVERFLOW " 32 "optimized" } } */
+/* { dg-final { scan-assembler-times "\timull\t" 32 } } */
+/* { dg-final { scan-assembler-times "\tseto\t" 8 } } */
+/* { dg-final { scan-assembler-times "\tsetno\t" 8 } } */
+/* { dg-final { scan-assembler-times "\tjn\?o\t" 16 } } */
+
+unsigned fn (void);
+
+int
+f1 (unsigned x, unsigned y, unsigned *res)
+{
+  *res = x * y;
+  return x && ((int) *res / (int) x) != (int) y;
+}
+
+unsigned
+f2 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (x && ((int) r / (int) x) != (int) y)
+    return fn ();
+  return r;
+}
+
+int
+f3 (unsigned x, unsigned y, unsigned *res)
+{
+  *res = x * y;
+  return !x || ((int) *res / (int) x) == (int) y;
+}
+
+unsigned
+f4 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (!x || ((int) r / (int) x) == (int) y)
+    return fn ();
+  return r;
+}
+
+int
+f5 (int x, int y, int *res)
+{
+  *res = (unsigned) x * y;
+  return x && (*res / x) != y;
+}
+
+int
+f6 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (x && (r / x) != y)
+    return fn ();
+  return r;
+}
+
+int
+f7 (int x, int y, int *res)
+{
+  *res = (unsigned) x * y;
+  return !x || (*res / x) == y;
+}
+
+int
+f8 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (!x || (r / x) == y)
+    return fn ();
+  return r;
+}
+
+int
+f9 (unsigned x, unsigned y, unsigned *res)
+{
+  *res = x * y;
+  return y && ((int) *res / (int) y) != (int) x;
+}
+
+unsigned
+f10 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (y && ((int) r / (int) y) != (int) x)
+    return fn ();
+  return r;
+}
+
+int
+f11 (unsigned x, unsigned y, unsigned *res)
+{
+  *res = x * y;
+  return !y || ((int) *res / (int) y) == (int) x;
+}
+
+unsigned
+f12 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (!y || ((int) r / (int) y) == (int) x)
+    return fn ();
+  return r;
+}
+
+int
+f13 (int x, int y, int *res)
+{
+  *res = (unsigned) x * y;
+  return y && (*res / y) != x;
+}
+
+int
+f14 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (y && (r / y) != x)
+    return fn ();
+  return r;
+}
+
+int
+f15 (int x, int y, int *res)
+{
+  *res = (unsigned) x * y;
+  return !y || (*res / y) == x;
+}
+
+int
+f16 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (!y || (r / y) == x)
+    return fn ();
+  return r;
+}
+
+int
+f17 (unsigned x, unsigned *res)
+{
+  *res = x * 35U;
+  return x && ((int) *res / (int) x) != 35;
+}
+
+unsigned
+f18 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if (x && ((int) r / (int) x) != 35)
+    return fn ();
+  return r;
+}
+
+int
+f19 (unsigned x, unsigned *res)
+{
+  *res = x * 35U;
+  return !x || ((int) *res / (int) x) == 35;
+}
+
+unsigned
+f20 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if (!x || ((int) r / (int) x) == 35)
+    return fn ();
+  return r;
+}
+
+int
+f21 (int x, int *res)
+{
+  *res = (unsigned) x * 35;
+  return x && (*res / x) != 35;
+}
+
+int
+f22 (int x)
+{
+  int r = (unsigned) x * 35;
+  if (x && (r / x) != 35)
+    return fn ();
+  return r;
+}
+
+int
+f23 (int x, int *res)
+{
+  *res = (unsigned) x * 35;
+  return !x || (*res / x) == 35;
+}
+
+int
+f24 (int x)
+{
+  int r = (unsigned) x * 35;
+  if (!x || (r / x) == 35)
+    return fn ();
+  return r;
+}
+
+int
+f25 (unsigned x, unsigned *res)
+{
+  *res = x * 35U;
+  return ((int) *res / 35) != (int) x;
+}
+
+unsigned
+f26 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if (((int) r / 35) != (int) x)
+    return fn ();
+  return r;
+}
+
+int
+f27 (unsigned x, unsigned *res)
+{
+  *res = x * 35U;
+  return ((int) *res / 35) == (int) x;
+}
+
+unsigned
+f28 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if (((int) r / 35) == (int) x)
+    return fn ();
+  return r;
+}
+
+int
+f29 (int x, int *res)
+{
+  *res = (unsigned) x * 35;
+  return 35 && (*res / 35) != x;
+}
+
+int
+f30 (int x)
+{
+  int r = (unsigned) x * 35;
+  if ((r / 35) != x)
+    return fn ();
+  return r;
+}
+
+int
+f31 (int x, int *res)
+{
+  *res = (unsigned) x * 35;
+  return (*res / 35) == x;
+}
+
+int
+f32 (int x)
+{
+  int r = (unsigned) x * 35;
+  if ((r / 35) == x)
+    return fn ();
+  return r;
+}
--- gcc/testsuite/gcc.target/i386/pr95852-4.c.jj	2021-01-08 21:02:39.627299338 +0100
+++ gcc/testsuite/gcc.target/i386/pr95852-4.c	2021-01-08 21:02:34.731354546 +0100
@@ -0,0 +1,266 @@ 
+/* PR tree-optimization/95852 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized -masm=att" } */
+/* { dg-final { scan-tree-dump-times " = \.MUL_OVERFLOW " 32 "optimized" } } */
+/* { dg-final { scan-assembler-times "\timull\t" 32 } } */
+/* { dg-final { scan-assembler-times "\tseto\t" 8 } } */
+/* { dg-final { scan-assembler-times "\tsetno\t" 8 } } */
+/* { dg-final { scan-assembler-times "\tjn\?o\t" 16 } } */
+
+unsigned fn (void);
+
+int
+f1 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  return x && ((int) r / (int) x) != (int) y;
+}
+
+unsigned
+f2 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (x && ((int) r / (int) x) != (int) y)
+    return fn ();
+  return 0;
+}
+
+int
+f3 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  return !x || ((int) r / (int) x) == (int) y;
+}
+
+unsigned
+f4 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (!x || ((int) r / (int) x) == (int) y)
+    return fn ();
+  return 0;
+}
+
+int
+f5 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  return x && (r / x) != y;
+}
+
+int
+f6 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (x && (r / x) != y)
+    return fn ();
+  return 0;
+}
+
+int
+f7 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  return !x || (r / x) == y;
+}
+
+int
+f8 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (!x || (r / x) == y)
+    return fn ();
+  return 0;
+}
+
+int
+f9 (unsigned x, unsigned y)
+{
+  unsigned r = x * y;
+  return y && ((int) r / (int) y) != (int) x;
+}
+
+unsigned
+f10 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (y && ((int) r / (int) y) != (int) x)
+    return fn ();
+  return 0;
+}
+
+int
+f11 (unsigned x, unsigned y)
+{
+  unsigned r = x * y;
+  return !y || ((int) r / (int) y) == (int) x;
+}
+
+unsigned
+f12 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (!y || ((int) r / (int) y) == (int) x)
+    return fn ();
+  return 0;
+}
+
+int
+f13 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  return y && (r / y) != x;
+}
+
+int
+f14 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (y && (r / y) != x)
+    return fn ();
+  return 0;
+}
+
+int
+f15 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  return !y || (r / y) == x;
+}
+
+int
+f16 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (!y || (r / y) == x)
+    return fn ();
+  return 0;
+}
+
+int
+f17 (unsigned x)
+{
+  unsigned r = x * 35U;
+  return x && ((int) r / (int) x) != 35;
+}
+
+unsigned
+f18 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if (x && ((int) r / (int) x) != 35)
+    return fn ();
+  return 0;
+}
+
+int
+f19 (unsigned x)
+{
+  unsigned r = x * 35U;
+  return !x || ((int) r / (int) x) == 35;
+}
+
+unsigned
+f20 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if (!x || ((int) r / (int) x) == 35)
+    return fn ();
+  return 0;
+}
+
+int
+f21 (int x)
+{
+  int r = (unsigned) x * 35;
+  return x && (r / x) != 35;
+}
+
+int
+f22 (int x)
+{
+  int r = (unsigned) x * 35;
+  if (x && (r / x) != 35)
+    return fn ();
+  return 0;
+}
+
+int
+f23 (int x)
+{
+  int r = (unsigned) x * 35;
+  return !x || (r / x) == 35;
+}
+
+int
+f24 (int x)
+{
+  int r = (unsigned) x * 35;
+  if (!x || (r / x) == 35)
+    return fn ();
+  return 0;
+}
+
+int
+f25 (unsigned x)
+{
+  unsigned r = x * 35U;
+  return ((int) r / 35) != (int) x;
+}
+
+unsigned
+f26 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if (((int) r / 35) != (int) x)
+    return fn ();
+  return 0;
+}
+
+int
+f27 (unsigned x)
+{
+  unsigned r = x * 35U;
+  return !35 || ((int) r / 35) == (int) x;
+}
+
+unsigned
+f28 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if (((int) r / 35) == (int) x)
+    return fn ();
+  return 0;
+}
+
+int
+f29 (int x)
+{
+  int r = (unsigned) x * 35;
+  return 35 && (r / 35) != x;
+}
+
+int
+f30 (int x)
+{
+  int r = (unsigned) x * 35;
+  if ((r / 35) != x)
+    return fn ();
+  return 0;
+}
+
+int
+f31 (int x)
+{
+  int r = (unsigned) x * 35;
+  return (r / 35) == x;
+}
+
+int
+f32 (int x)
+{
+  int r = (unsigned) x * 35;
+  if ((r / 35) == x)
+    return fn ();
+  return 0;
+}