Hi!

The following patch pattern recognizes some forms of multiplication followed
by overflow check through division by one of the operands compared to the
other one, with optional removal of guarding non-zero check for that operand
if possible.  The patterns are replaced with effectively
__builtin_mul_overflow or __builtin_mul_overflow_p.  The testcases cover 64
different forms of that.

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

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

        PR tree-optimization/95852
        * tree-ssa-math-opts.c (maybe_optimize_guarding_check): New function.
        (uaddsub_overflow_check_p): Renamed to ...
        (arith_overflow_check_p): ... this.  Handle also multiplication
        with overflow check.
        (match_uaddsub_overflow): Renamed to ...
        (match_arith_overflow): ... this.  Add cfg_changed argument.  Handle
        also multiplication with overflow check.  Adjust function comment.
        (math_opts_dom_walker::after_dom_children): Adjust callers.  Call
        match_arith_overflow also for MULT_EXPR.

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

--- gcc/tree-ssa-math-opts.c.jj 2021-01-08 12:27:46.066177063 +0100
+++ gcc/tree-ssa-math-opts.c    2021-01-08 14:39:11.872358339 +0100
@@ -3451,17 +3451,228 @@ convert_mult_to_fma (gimple *mul_stmt, t
 }
 
 
-/* Helper function of match_uaddsub_overflow.  Return 1
+/* Helper function of match_arith_overflow.  For MUL_OVERFLOW, if we have
+   a check for non-zero like:
+   _1 = x_4(D) * y_5(D);
+   *res_7(D) = _1;
+   if (x_4(D) != 0)
+     goto <bb 3>; [50.00%]
+   else
+     goto <bb 4>; [50.00%]
+
+   <bb 3> [local count: 536870913]:
+   _2 = _1 / x_4(D);
+   _9 = _2 != y_5(D);
+   _10 = (int) _9;
+
+   <bb 4> [local count: 1073741824]:
+   # iftmp.0_3 = PHI <_10(3), 0(2)>
+   then in addition to using .MUL_OVERFLOW (x_4(D), y_5(D)) we can also
+   optimize the x_4(D) != 0 condition to 1.  */
+
+static void
+maybe_optimize_guarding_check (gimple **mul_stmts, gimple *cond_stmt,
+                              gimple *div_stmt, bool *cfg_changed)
+{
+  basic_block bb = gimple_bb (cond_stmt);
+  if (gimple_bb (div_stmt) != bb || !single_pred_p (bb))
+    return;
+  edge pred_edge = single_pred_edge (bb);
+  basic_block pred_bb = pred_edge->src;
+  if (EDGE_COUNT (pred_bb->succs) != 2)
+    return;
+  edge other_edge = EDGE_SUCC (pred_bb, EDGE_SUCC (pred_bb, 0) == pred_edge);
+  edge other_succ_edge = NULL;
+  if (gimple_code (cond_stmt) == GIMPLE_COND)
+    {
+      if (EDGE_COUNT (bb->succs) != 2)
+       return;
+      other_succ_edge = EDGE_SUCC (bb, 0);
+      if (gimple_cond_code (cond_stmt) == NE_EXPR)
+       {
+         if (other_succ_edge->flags & EDGE_TRUE_VALUE)
+           other_succ_edge = EDGE_SUCC (bb, 1);
+       }
+      else if (other_succ_edge->flags & EDGE_FALSE_VALUE)
+       other_succ_edge = EDGE_SUCC (bb, 0);
+      if (other_edge->dest != other_succ_edge->dest)
+       return;
+    }
+  else if (!single_succ_p (bb) || other_edge->dest != single_succ (bb))
+    return;
+  gimple *zero_cond = last_stmt (pred_bb);
+  if (zero_cond == NULL
+      || gimple_code (zero_cond) != GIMPLE_COND
+      || (gimple_cond_code (zero_cond)
+         != ((pred_edge->flags & EDGE_TRUE_VALUE) ? NE_EXPR : EQ_EXPR))
+      || !integer_zerop (gimple_cond_rhs (zero_cond)))
+    return;
+  tree zero_cond_lhs = gimple_cond_lhs (zero_cond);
+  if (TREE_CODE (zero_cond_lhs) != SSA_NAME)
+    return;
+  if (gimple_assign_rhs2 (div_stmt) != zero_cond_lhs)
+    {
+      /* Allow the divisor to be result of a same precision cast
+        from zero_cond_lhs.  */
+      tree rhs2 = gimple_assign_rhs2 (div_stmt);
+      if (TREE_CODE (rhs2) != SSA_NAME)
+       return;
+      gimple *g = SSA_NAME_DEF_STMT (rhs2);
+      if (!gimple_assign_cast_p (g)
+         || gimple_assign_rhs1 (g) != gimple_cond_lhs (zero_cond)
+         || !INTEGRAL_TYPE_P (TREE_TYPE (zero_cond_lhs))
+         || (TYPE_PRECISION (TREE_TYPE (zero_cond_lhs))
+             != TYPE_PRECISION (TREE_TYPE (rhs2))))
+       return;
+    }
+  gimple_stmt_iterator gsi = gsi_for_stmt (div_stmt);
+  gsi_prev_nondebug (&gsi);
+  if (!gsi_end_p (gsi))
+    {
+      /* 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)
+       {
+         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_end_p (gsi))
+       return;
+    }
+  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;
+      for (gphi_iterator gpi = gsi_start_phis (succ_bb); !gsi_end_p (gpi);
+          gsi_next (&gpi))
+       {
+         gphi *phi = gpi.phi ();
+         tree v1 = gimple_phi_arg_def (phi, other_edge->dest_idx);
+         tree v2 = gimple_phi_arg_def (phi, other_succ_edge->dest_idx);
+         if (!operand_equal_p (v1, v2, 0))
+           return;
+       }
+    }
+  else
+    {
+      tree lhs = gimple_assign_lhs (cond_stmt);
+      if (!lhs || !INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+       return;
+      gsi_next_nondebug (&gsi);
+      if (!gsi_end_p (gsi))
+       {
+         if (gimple_assign_rhs_code (cond_stmt) == COND_EXPR)
+           return;
+         gimple *cast_stmt = gsi_stmt (gsi);
+         if (!gimple_assign_cast_p (cast_stmt))
+           return;
+         tree new_lhs = gimple_assign_lhs (cast_stmt);
+         gsi_next_nondebug (&gsi);
+         if (!gsi_end_p (gsi)
+             || !new_lhs
+             || !INTEGRAL_TYPE_P (TREE_TYPE (new_lhs))
+             || TYPE_PRECISION (TREE_TYPE (new_lhs)) <= 1)
+           return;
+         lhs = new_lhs;
+       }
+      edge succ_edge = single_succ_edge (bb);
+      basic_block succ_bb = succ_edge->dest;
+      gsi = gsi_start_phis (succ_bb);
+      if (gsi_end_p (gsi))
+       return;
+      gphi *phi = as_a <gphi *> (gsi_stmt (gsi));
+      gsi_next (&gsi);
+      if (!gsi_end_p (gsi))
+       return;
+      if (gimple_phi_arg_def (phi, succ_edge->dest_idx) != lhs)
+       return;
+      tree other_val = gimple_phi_arg_def (phi, other_edge->dest_idx);
+      if (gimple_assign_rhs_code (cond_stmt) == COND_EXPR)
+       {
+         tree cond = gimple_assign_rhs1 (cond_stmt);
+         if (TREE_CODE (cond) == NE_EXPR)
+           {
+             if (!operand_equal_p (other_val,
+                                   gimple_assign_rhs3 (cond_stmt), 0))
+               return;
+           }
+         else if (!operand_equal_p (other_val,
+                                    gimple_assign_rhs2 (cond_stmt), 0))
+           return;
+       }
+      else if (gimple_assign_rhs_code (cond_stmt) == NE_EXPR)
+       {
+         if (!integer_zerop (other_val))
+           return;
+       }
+      else if (!integer_onep (other_val))
+       return;
+    }
+  gcond *zero_gcond = as_a <gcond *> (zero_cond);
+  if (pred_edge->flags & EDGE_TRUE_VALUE)
+    gimple_cond_make_true (zero_gcond);
+  else
+    gimple_cond_make_false (zero_gcond);
+  update_stmt (zero_cond);
+  *cfg_changed = true;
+}
+
+/* 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
-uaddsub_overflow_check_p (gimple *stmt, gimple *use_stmt, tree maxval,
-                         tree *other)
+arith_overflow_check_p (gimple *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 rhs1 = gimple_assign_rhs1 (stmt);
+  tree rhs2 = gimple_assign_rhs2 (stmt);
+  tree multop = NULL_TREE, divlhs = NULL_TREE;
+
+  if (code == MULT_EXPR)
+    {
+      if (!is_gimple_assign (use_stmt))
+       return 0;
+      if (gimple_assign_rhs_code (use_stmt) != TRUNC_DIV_EXPR)
+       return 0;
+      if (gimple_assign_rhs1 (use_stmt) != lhs)
+       return 0;
+      if (gimple_assign_rhs2 (use_stmt) == rhs1)
+       multop = rhs2;
+      else if (operand_equal_p (gimple_assign_rhs2 (use_stmt), rhs2, 0))
+       multop = rhs1;
+      else
+       return 0;
+      if (stmt_ends_bb_p (use_stmt))
+       return 0;
+      divlhs = gimple_assign_lhs (use_stmt);
+      if (!divlhs)
+       return 0;
+      use_operand_p use;
+      if (!single_imm_use (divlhs, &use, &use_stmt))
+       return 0;
+    }
   if (gimple_code (use_stmt) == GIMPLE_COND)
     {
       ccode = gimple_cond_code (use_stmt);
@@ -3497,11 +3708,6 @@ uaddsub_overflow_check_p (gimple *stmt,
   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:
@@ -3547,6 +3753,17 @@ uaddsub_overflow_check_p (gimple *stmt,
          return ccode == LT_EXPR ? 1 : -1;
        }
       break;
+    case EQ_EXPR:
+    case NE_EXPR:
+      /* r = a * b; _1 = r / a; _1 == b
+        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;
+      break;
     default:
       break;
     }
@@ -3557,7 +3774,7 @@ uaddsub_overflow_check_p (gimple *stmt,
    x = y - z;
    if (x > y)
    where there are other uses of x and replace it with
-   _7 = SUB_OVERFLOW (y, z);
+   _7 = .SUB_OVERFLOW (y, z);
    x = REALPART_EXPR <_7>;
    _8 = IMAGPART_EXPR <_7>;
    if (_8)
@@ -3571,9 +3788,9 @@ uaddsub_overflow_check_p (gimple *stmt,
    where y and z have unsigned types with maximum max
    and there are other uses of x and all of those cast x
    back to that unsigned type and again replace it with
-   _7 = ADD_OVERFLOW (y, z);
+   _7 = .ADD_OVERFLOW (y, z);
    _9 = REALPART_EXPR <_7>;
-   _8 = IMAGPART_EXPR <_8>;
+   _8 = IMAGPART_EXPR <_7>;
    if (_8)
    and replace (utype) x with _9.
 
@@ -3581,13 +3798,34 @@ uaddsub_overflow_check_p (gimple *stmt,
    x = ~z;
    if (y > x)
    and replace it with
-   _7 = ADD_OVERFLOW (y, z);
-   _8 = IMAGPART_EXPR <_8>;
-   if (_8)  */
+   _7 = .ADD_OVERFLOW (y, z);
+   _8 = IMAGPART_EXPR <_7>;
+   if (_8)
+
+   And also recognize:
+   z = x * y;
+   if (x != 0)
+     goto <bb 3>; [50.00%]
+   else
+     goto <bb 4>; [50.00%]
+
+   <bb 3> [local count: 536870913]:
+   _2 = z / x;
+   _9 = _2 != y;
+   _10 = (int) _9;
+
+   <bb 4> [local count: 1073741824]:
+   # iftmp.0_3 = PHI <_10(3), 0(2)>
+   and replace it with
+   _7 = .MUL_OVERFLOW (x, y);
+   z = IMAGPART_EXPR <_7>;
+   _8 = IMAGPART_EXPR <_7>;
+   _9 = _8 != 0;
+   iftmp.0_3 = (int) _9;  */
 
 static bool
-match_uaddsub_overflow (gimple_stmt_iterator *gsi, gimple *stmt,
-                       enum tree_code code)
+match_arith_overflow (gimple_stmt_iterator *gsi, gimple *stmt,
+                     enum tree_code code, bool *cfg_changed)
 {
   tree lhs = gimple_assign_lhs (stmt);
   tree type = TREE_TYPE (lhs);
@@ -3602,11 +3840,13 @@ match_uaddsub_overflow (gimple_stmt_iter
 
   gcc_checking_assert (code == PLUS_EXPR
                       || code == MINUS_EXPR
+                      || code == MULT_EXPR
                       || code == BIT_NOT_EXPR);
   if (!INTEGRAL_TYPE_P (type)
       || !TYPE_UNSIGNED (type)
       || has_zero_uses (lhs)
       || (code != PLUS_EXPR
+         && code != MULT_EXPR
          && optab_handler (code == MINUS_EXPR ? usubv4_optab : uaddv4_optab,
                            TYPE_MODE (type)) == CODE_FOR_nothing))
     return false;
@@ -3620,7 +3860,7 @@ match_uaddsub_overflow (gimple_stmt_iter
        continue;
 
       tree other = NULL_TREE;
-      if (uaddsub_overflow_check_p (stmt, use_stmt, NULL_TREE, &other))
+      if (arith_overflow_check_p (stmt, use_stmt, NULL_TREE, &other))
        {
          if (code == BIT_NOT_EXPR)
            {
@@ -3643,9 +3883,12 @@ match_uaddsub_overflow (gimple_stmt_iter
 
   tree maxval = NULL_TREE;
   if (!ovf_use_seen
-      || (code == BIT_NOT_EXPR ? use_seen : !use_seen)
+      || (code != MULT_EXPR && (code == BIT_NOT_EXPR ? use_seen : !use_seen))
       || (code == PLUS_EXPR
          && optab_handler (uaddv4_optab,
+                           TYPE_MODE (type)) == CODE_FOR_nothing)
+      || (code == MULT_EXPR
+         && optab_handler (umulv4_optab,
                            TYPE_MODE (type)) == CODE_FOR_nothing))
     {
       if (code != PLUS_EXPR)
@@ -3704,7 +3947,7 @@ match_uaddsub_overflow (gimple_stmt_iter
          if (is_gimple_debug (use_stmt))
            continue;
 
-         if (uaddsub_overflow_check_p (stmt, use_stmt, maxval, NULL))
+         if (arith_overflow_check_p (stmt, use_stmt, maxval, NULL))
            {
              ovf_use_seen = true;
              use_bb = gimple_bb (use_stmt);
@@ -3824,13 +4067,16 @@ match_uaddsub_overflow (gimple_stmt_iter
     *gsi = gsi_for_stmt (cond_stmt);
 
   tree ctype = build_complex_type (type);
-  gcall *g = gimple_build_call_internal (code != MINUS_EXPR
+  gcall *g = gimple_build_call_internal (code == MULT_EXPR
+                                        ? IFN_MUL_OVERFLOW
+                                        : code != MINUS_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);
   tree new_lhs = maxval ? make_ssa_name (type) : lhs;
+  gimple *mul_stmts[3] = { NULL, NULL, NULL };
   gassign *g2;
   if (code != BIT_NOT_EXPR)
     {
@@ -3844,6 +4090,11 @@ match_uaddsub_overflow (gimple_stmt_iter
        }
       else
        gsi_replace (gsi, g2, true);
+      if (code == MULT_EXPR)
+       {
+         mul_stmts[0] = g;
+         mul_stmts[1] = g2;
+       }
     }
   tree ovf = make_ssa_name (type);
   g2 = gimple_build_assign (ovf, IMAGPART_EXPR,
@@ -3852,13 +4103,16 @@ match_uaddsub_overflow (gimple_stmt_iter
     gsi_insert_after (gsi, g2, GSI_NEW_STMT);
   else
     gsi_insert_before (gsi, g2, GSI_SAME_STMT);
+  if (code == MULT_EXPR)
+    mul_stmts[2] = g2;
 
   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, maxval, NULL);
+      gimple *orig_use_stmt = use_stmt;
+      int ovf_use = arith_overflow_check_p (stmt, use_stmt, maxval, NULL);
       if (ovf_use == 0)
        {
          gcc_assert (code != BIT_NOT_EXPR);
@@ -3901,6 +4155,14 @@ match_uaddsub_overflow (gimple_stmt_iter
            }
        }
       update_stmt (use_stmt);
+      if (code == MULT_EXPR && use_stmt != orig_use_stmt)
+       {
+         gimple_stmt_iterator gsi2 = gsi_for_stmt (orig_use_stmt);
+         maybe_optimize_guarding_check (mul_stmts, use_stmt, orig_use_stmt,
+                                        cfg_changed);
+         gsi_remove (&gsi2, true);
+         release_ssa_name (gimple_assign_lhs (orig_use_stmt));
+       }
     }
   if (maxval)
     {
@@ -4238,16 +4500,17 @@ math_opts_dom_walker::after_dom_children
                  release_defs (stmt);
                  continue;
                }
+             match_arith_overflow (&gsi, stmt, code, m_cfg_changed_p);
              break;
 
            case PLUS_EXPR:
            case MINUS_EXPR:
              if (!convert_plusminus_to_widen (&gsi, stmt, code))
-               match_uaddsub_overflow (&gsi, stmt, code);
+               match_arith_overflow (&gsi, stmt, code, m_cfg_changed_p);
              break;
 
            case BIT_NOT_EXPR:
-             if (match_uaddsub_overflow (&gsi, stmt, code))
+             if (match_arith_overflow (&gsi, stmt, code, m_cfg_changed_p))
                continue;
              break;
 
--- gcc/testsuite/gcc.target/i386/pr95852-1.c.jj        2021-01-08 
13:52:54.222694377 +0100
+++ gcc/testsuite/gcc.target/i386/pr95852-1.c   2021-01-08 13:52:30.924957853 
+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 "\tmull\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 && (*res / x) != y;
+}
+
+unsigned
+f2 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (x && (r / x) != y)
+    return fn ();
+  return r;
+}
+
+int
+f3 (unsigned x, unsigned y, unsigned *res)
+{
+  *res = x * y;
+  return !x || (*res / x) == y;
+}
+
+unsigned
+f4 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (!x || (r / x) == y)
+    return fn ();
+  return r;
+}
+
+int
+f5 (int x, int y, int *res)
+{
+  *res = (unsigned) x * y;
+  return x && ((unsigned) *res / x) != (unsigned) y;
+}
+
+int
+f6 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (x && ((unsigned) r / x) != (unsigned) y)
+    return fn ();
+  return r;
+}
+
+int
+f7 (int x, int y, int *res)
+{
+  *res = (unsigned) x * y;
+  return !x || ((unsigned) *res / x) == (unsigned) y;
+}
+
+int
+f8 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (!x || ((unsigned) r / x) == (unsigned) y)
+    return fn ();
+  return r;
+}
+
+int
+f9 (unsigned x, unsigned y, unsigned *res)
+{
+  *res = x * y;
+  return y && (*res / y) != x;
+}
+
+unsigned
+f10 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (y && (r / y) != x)
+    return fn ();
+  return r;
+}
+
+int
+f11 (unsigned x, unsigned y, unsigned *res)
+{
+  *res = x * y;
+  return !y || (*res / y) == x;
+}
+
+unsigned
+f12 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (!y || (r / y) == x)
+    return fn ();
+  return r;
+}
+
+int
+f13 (int x, int y, int *res)
+{
+  *res = (unsigned) x * y;
+  return y && ((unsigned) *res / y) != (unsigned) x;
+}
+
+int
+f14 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (y && ((unsigned) r / y) != (unsigned) x)
+    return fn ();
+  return r;
+}
+
+int
+f15 (int x, int y, int *res)
+{
+  *res = (unsigned) x * y;
+  return !y || ((unsigned) *res / y) == (unsigned) x;
+}
+
+int
+f16 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (!y || ((unsigned) r / y) == (unsigned) x)
+    return fn ();
+  return r;
+}
+
+int
+f17 (unsigned x, unsigned *res)
+{
+  *res = x * 35U;
+  return x && (*res / x) != 35U;
+}
+
+unsigned
+f18 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if (x && (r / x) != 35U)
+    return fn ();
+  return r;
+}
+
+int
+f19 (unsigned x, unsigned *res)
+{
+  *res = x * 35U;
+  return !x || (*res / x) == 35U;
+}
+
+unsigned
+f20 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if (!x || (r / x) == 35U)
+    return fn ();
+  return r;
+}
+
+int
+f21 (int x, int *res)
+{
+  *res = (unsigned) x * 35;
+  return x && ((unsigned) *res / x) != 35U;
+}
+
+int
+f22 (int x)
+{
+  int r = (unsigned) x * 35;
+  if (x && ((unsigned) r / x) != 35U)
+    return fn ();
+  return r;
+}
+
+int
+f23 (int x, int *res)
+{
+  *res = (unsigned) x * 35;
+  return !x || ((unsigned) *res / x) == 35U;
+}
+
+int
+f24 (int x)
+{
+  int r = (unsigned) x * 35;
+  if (!x || ((unsigned) r / x) == 35U)
+    return fn ();
+  return r;
+}
+
+int
+f25 (unsigned x, unsigned *res)
+{
+  *res = x * 35U;
+  return (*res / 35U) != x;
+}
+
+unsigned
+f26 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if ((r / 35U) != x)
+    return fn ();
+  return r;
+}
+
+int
+f27 (unsigned x, unsigned *res)
+{
+  *res = x * 35U;
+  return !35U || (*res / 35U) == x;
+}
+
+unsigned
+f28 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if ((r / 35U) == x)
+    return fn ();
+  return r;
+}
+
+int
+f29 (int x, int *res)
+{
+  *res = (unsigned) x * 35;
+  return 35 && ((unsigned) *res / 35) != (unsigned) x;
+}
+
+int
+f30 (int x)
+{
+  int r = (unsigned) x * 35;
+  if (((unsigned) r / 35) != (unsigned) x)
+    return fn ();
+  return r;
+}
+
+int
+f31 (int x, int *res)
+{
+  *res = (unsigned) x * 35;
+  return ((unsigned) *res / 35) == (unsigned) x;
+}
+
+int
+f32 (int x)
+{
+  int r = (unsigned) x * 35;
+  if (((unsigned) r / 35) == (unsigned) x)
+    return fn ();
+  return r;
+}
--- gcc/testsuite/gcc.target/i386/pr95852-2.c.jj        2021-01-08 
14:40:06.438745208 +0100
+++ gcc/testsuite/gcc.target/i386/pr95852-2.c   2021-01-08 14:12:59.416071946 
+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 "\tmull\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 && (r / x) != y;
+}
+
+unsigned
+f2 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (x && (r / x) != y)
+    return fn ();
+  return 0;
+}
+
+int
+f3 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  return !x || (r / x) == y;
+}
+
+unsigned
+f4 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (!x || (r / x) == y)
+    return fn ();
+  return 0;
+}
+
+int
+f5 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  return x && ((unsigned) r / x) != (unsigned) y;
+}
+
+int
+f6 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (x && ((unsigned) r / x) != (unsigned) y)
+    return fn ();
+  return 0;
+}
+
+int
+f7 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  return !x || ((unsigned) r / x) == (unsigned) y;
+}
+
+int
+f8 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (!x || ((unsigned) r / x) == (unsigned) y)
+    return fn ();
+  return 0;
+}
+
+int
+f9 (unsigned x, unsigned y)
+{
+  unsigned r = x * y;
+  return y && (r / y) != x;
+}
+
+unsigned
+f10 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (y && (r / y) != x)
+    return fn ();
+  return 0;
+}
+
+int
+f11 (unsigned x, unsigned y)
+{
+  unsigned r = x * y;
+  return !y || (r / y) == x;
+}
+
+unsigned
+f12 (unsigned x, unsigned y)
+{
+  unsigned int r = x * y;
+  if (!y || (r / y) == x)
+    return fn ();
+  return 0;
+}
+
+int
+f13 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  return y && ((unsigned) r / y) != (unsigned) x;
+}
+
+int
+f14 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (y && ((unsigned) r / y) != (unsigned) x)
+    return fn ();
+  return 0;
+}
+
+int
+f15 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  return !y || ((unsigned) r / y) == (unsigned) x;
+}
+
+int
+f16 (int x, int y)
+{
+  int r = (unsigned) x * y;
+  if (!y || ((unsigned) r / y) == (unsigned) x)
+    return fn ();
+  return 0;
+}
+
+int
+f17 (unsigned x)
+{
+  unsigned r = x * 35U;
+  return x && (r / x) != 35U;
+}
+
+unsigned
+f18 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if (x && (r / x) != 35U)
+    return fn ();
+  return 0;
+}
+
+int
+f19 (unsigned x)
+{
+  unsigned r = x * 35U;
+  return !x || (r / x) == 35U;
+}
+
+unsigned
+f20 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if (!x || (r / x) == 35U)
+    return fn ();
+  return 0;
+}
+
+int
+f21 (int x)
+{
+  int r = (unsigned) x * 35;
+  return x && ((unsigned) r / x) != 35U;
+}
+
+int
+f22 (int x)
+{
+  int r = (unsigned) x * 35;
+  if (x && ((unsigned) r / x) != 35U)
+    return fn ();
+  return 0;
+}
+
+int
+f23 (int x)
+{
+  int r = (unsigned) x * 35;
+  return !x || ((unsigned) r / x) == 35U;
+}
+
+int
+f24 (int x)
+{
+  int r = (unsigned) x * 35;
+  if (!x || ((unsigned) r / x) == 35U)
+    return fn ();
+  return 0;
+}
+
+int
+f25 (unsigned x)
+{
+  unsigned r = x * 35U;
+  return (r / 35U) != x;
+}
+
+unsigned
+f26 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if ((r / 35U) != x)
+    return fn ();
+  return 0;
+}
+
+int
+f27 (unsigned x)
+{
+  unsigned r = x * 35U;
+  return !35U || (r / 35U) == x;
+}
+
+unsigned
+f28 (unsigned x)
+{
+  unsigned int r = x * 35U;
+  if ((r / 35U) == x)
+    return fn ();
+  return 0;
+}
+
+int
+f29 (int x)
+{
+  int r = (unsigned) x * 35;
+  return 35 && ((unsigned) r / 35) != (unsigned) x;
+}
+
+int
+f30 (int x)
+{
+  int r = (unsigned) x * 35;
+  if (((unsigned) r / 35) != (unsigned) x)
+    return fn ();
+  return 0;
+}
+
+int
+f31 (int x)
+{
+  int r = (unsigned) x * 35;
+  return ((unsigned) r / 35) == (unsigned) x;
+}
+
+int
+f32 (int x)
+{
+  int r = (unsigned) x * 35;
+  if (((unsigned) r / 35) == (unsigned) x)
+    return fn ();
+  return 0;
+}

        Jakub

Reply via email to