When op1's range is [N, N + 1] and one of N or N + 1 is a power of
two, split a TRUNC_DIV_EXPR into two divisions by constants selected
by a compare:

  op0 / op1  ->  op1 == N ? op0 / N : op0 / (N + 1)

Each arm divides by a constant, so expansion strength-reduces it (a
shift on the power-of-two arm) and the divide instruction is avoided.

This grows code, so gate it on optimize_bb_for_speed_p.

Bootstrapped and regtested on x86_64-pc-linux-gnu{-m32,}.
Ok for trunk?

        PR middle-end/125708

gcc/ChangeLog:

        * vr-values.cc
        (simplify_using_ranges::simplify_div_or_mod_using_ranges):
        Compute op1's upper bound for all codes and add the
        near-power-of-two TRUNC_DIV_EXPR split, guarded on
        optimize_bb_for_speed_p.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/pr125708-1.c: New test.
        * gcc.target/i386/pr125708-2.c: New test.
---
 gcc/testsuite/gcc.dg/tree-ssa/pr125708-1.c | 42 +++++++++++++++++++++
 gcc/testsuite/gcc.target/i386/pr125708-2.c | 31 +++++++++++++++
 gcc/vr-values.cc                           | 44 +++++++++++++++++++---
 3 files changed, 112 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr125708-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr125708-2.c

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr125708-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr125708-1.c
new file mode 100644
index 00000000000..56b4f8519d5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr125708-1.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+#include <stdbool.h>
+
+/* op1 in [1, 2]: the divisor==1 arm degenerates to op0.  */
+int
+foo (int a, bool b)
+{
+  return a / (2 - b);
+}
+
+/* op1 in [4, 5]: power of two is the lower bound.  */
+int
+foo1 (int a, bool b)
+{
+  return a / (4 + b);
+}
+
+/* op1 in [7, 8]: power of two is the upper bound.  */
+int
+foo2 (int a, bool b)
+{
+  return a / (8 - b);
+}
+
+/* Unsigned dividend.  */
+unsigned
+foo3 (unsigned a, bool b)
+{
+  return a / (4 + b);
+}
+
+/* Negative dividend.  */
+int
+foo4 (int a, bool b)
+{
+  return -a / (8 - b);
+}
+
+/* No division by an SSA name should survive.  */
+/* { dg-final { scan-tree-dump-not " / _" "optimized" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr125708-2.c 
b/gcc/testsuite/gcc.target/i386/pr125708-2.c
new file mode 100644
index 00000000000..853543695ba
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr125708-2.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+#include <stdbool.h>
+
+int
+foo (int a, bool b)
+{
+  return a / (2 - b);
+}
+
+int
+foo1 (int a, bool b)
+{
+  return a / (4 + b);
+}
+
+int
+foo2 (int a, bool b)
+{
+  return a / (8 - b);
+}
+
+unsigned
+foo3 (unsigned a, bool b)
+{
+  return a / (4 + b);
+}
+
+/* { dg-final { scan-assembler-not "idiv" } } */
+/* { dg-final { scan-assembler-not "\tdiv" } } */
diff --git a/gcc/vr-values.cc b/gcc/vr-values.cc
index f0d9a71bd40..f27cf01e485 100644
--- a/gcc/vr-values.cc
+++ b/gcc/vr-values.cc
@@ -594,7 +594,8 @@ simplify_using_ranges::simplify_truth_ops_using_ranges
    constant op1 (op1min = op1) or with op1 in [op1min, op1max] range,
    optimize it into just op0 if op0's range is known to be a subset of
    [-op1min + 1, op1min - 1] for signed and [0, op1min - 1] for unsigned
-   modulo.  */
+   modulo.  Also optimize TRUNC_DIV_EXPR op0 / op1 when op1 is known to be
+   N or N + 1 and one of them is a power of two.  */
 
 bool
 simplify_using_ranges::simplify_div_or_mod_using_ranges
@@ -606,7 +607,7 @@ simplify_using_ranges::simplify_div_or_mod_using_ranges
   tree op0 = gimple_assign_rhs1 (stmt);
   tree op1 = gimple_assign_rhs2 (stmt);
   tree op0min = NULL_TREE, op0max = NULL_TREE;
-  tree op1min = op1;
+  tree op1min = op1, op1max = NULL_TREE;
   int_range_max vr;
 
   if (TREE_CODE (op0) == INTEGER_CST)
@@ -626,15 +627,19 @@ simplify_using_ranges::simplify_div_or_mod_using_ranges
        }
     }
 
-  if (rhs_code == TRUNC_MOD_EXPR
-      && TREE_CODE (op1) == SSA_NAME)
+  if (TREE_CODE (op1) == SSA_NAME)
     {
       int_range_max vr1;
       if (!query->range_of_expr (vr1, op1, stmt))
        vr1.set_varying (TREE_TYPE (op1));
       if (!vr1.varying_p () && !vr1.undefined_p ())
-       op1min = wide_int_to_tree (vr1.type (), vr1.lower_bound ());
+       {
+         op1min = wide_int_to_tree (vr1.type (), vr1.lower_bound ());
+         if (rhs_code == TRUNC_DIV_EXPR)
+           op1max = wide_int_to_tree (vr1.type (), vr1.upper_bound ());
+       }
     }
+
   if (rhs_code == TRUNC_MOD_EXPR
       && TREE_CODE (op1min) == INTEGER_CST
       && tree_int_cst_sgn (op1min) == 1
@@ -653,6 +658,35 @@ simplify_using_ranges::simplify_div_or_mod_using_ranges
        }
     }
 
+  if (rhs_code == TRUNC_DIV_EXPR
+      && TREE_CODE (op1min) == INTEGER_CST
+      && op1max
+      && TREE_CODE (op1max) == INTEGER_CST
+      && tree_int_cst_sgn (op1min) == 1
+      && wi::to_wide (op1max) == wi::to_wide (op1min) + 1
+      && (integer_pow2p (op1min) || integer_pow2p (op1max))
+      && optimize_bb_for_speed_p (gimple_bb (stmt)))
+    {
+      /* op1 == N ? op0 / N : op0 / (N + 1).  Both arms divide by a
+        constant, so expansion avoids the divide instruction.  */
+      location_t loc = gimple_location (stmt);
+      tree div_min = (integer_onep (op1min)
+                     ? op0
+                     : gimple_build (gsi, true, GSI_SAME_STMT, loc,
+                                     TRUNC_DIV_EXPR, TREE_TYPE (op0),
+                                     op0, op1min));
+      tree div_max = gimple_build (gsi, true, GSI_SAME_STMT, loc,
+                                  TRUNC_DIV_EXPR, TREE_TYPE (op0),
+                                  op0, op1max);
+      tree cond = gimple_build (gsi, true, GSI_SAME_STMT, loc,
+                               EQ_EXPR, boolean_type_node, op1, op1min);
+
+      gimple_assign_set_rhs_with_ops (gsi, COND_EXPR, cond, div_min, div_max);
+      update_stmt (gsi_stmt (*gsi));
+      fold_stmt (gsi, follow_single_use_edges);
+      return true;
+    }
+
   if (TREE_CODE (op0) != SSA_NAME)
     return false;
 
-- 
2.34.1

Reply via email to