Improve forward jump threading of switch statements (PR18046)
authorPatrick Palka <ppalka@gcc.gnu.org>
Fri, 5 Aug 2016 23:29:53 +0000 (23:29 +0000)
committerPatrick Palka <ppalka@gcc.gnu.org>
Fri, 5 Aug 2016 23:29:53 +0000 (23:29 +0000)
gcc/ChangeLog:

PR tree-optimization/18046
* tree-ssa-threadedge.c: Include cfganal.h.
(simplify_control_statement_condition): If simplifying a
GIMPLE_SWITCH, replace the index operand of the GIMPLE_SWITCH
with the dominating ASSERT_EXPR before handing it off to VRP.
Mention that a CASE_LABEL_EXPR may be returned.
(thread_around_empty_blocks): Adjust to handle
simplify_control_statement_condition() returning a
CASE_LABEL_EXPR.
(thread_through_normal_block): Likewise.
* tree-vrp.c (simplify_stmt_for_jump_threading): Simplify
a switch statement by trying to determine which case label
will be taken.

gcc/testsuite/ChangeLog:

PR tree-optimization/18046
* gcc.dg/tree-ssa/vrp105.c: New test.
* gcc.dg/tree-ssa/vrp106.c: New test.

From-SVN: r239181

gcc/ChangeLog
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/tree-ssa/vrp105.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/vrp106.c [new file with mode: 0644]
gcc/tree-ssa-threadedge.c
gcc/tree-vrp.c

index 67ec6224a8afd32b4813c967e8e9ff5cdfeb0bcf..f6a62d1ffc12c8522a8c896f7ba74268c668a891 100644 (file)
@@ -1,3 +1,19 @@
+2016-08-05  Patrick Palka  <ppalka@gcc.gnu.org>
+
+       PR tree-optimization/18046
+       * tree-ssa-threadedge.c: Include cfganal.h.
+       (simplify_control_statement_condition): If simplifying a
+       GIMPLE_SWITCH, replace the index operand of the GIMPLE_SWITCH
+       with the dominating ASSERT_EXPR before handing it off to VRP.
+       Mention that a CASE_LABEL_EXPR may be returned.
+       (thread_around_empty_blocks): Adjust to handle
+       simplify_control_statement_condition() returning a
+       CASE_LABEL_EXPR.
+       (thread_through_normal_block): Likewise.
+       * tree-vrp.c (simplify_stmt_for_jump_threading): Simplify
+       a switch statement by trying to determine which case label
+       will be taken.
+
 2016-08-05  Vladimir Makarov  <vmakarov@redhat.com>
 
        PR rtl-optimization/69847
index 3c1fbf012cdbd4cc89ce95ea71a8d2ac826035b1..e9792962c62d80bcac629077173210c7d0815d77 100644 (file)
@@ -1,3 +1,9 @@
+2016-08-05  Patrick Palka  <ppalka@gcc.gnu.org>
+
+       PR tree-optimization/18046
+       * gcc.dg/tree-ssa/vrp105.c: New test.
+       * gcc.dg/tree-ssa/vrp106.c: New test.
+
 2016-08-05 Martin Sebor  <msebor@redhat.com>
 
        * g++.dg/cpp0x/constexpr-cast.C: Avoid assuming (void*)1 is spelled
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp105.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp105.c
new file mode 100644 (file)
index 0000000..7cdd4dd
--- /dev/null
@@ -0,0 +1,37 @@
+/* PR tree-optimization/18046  */
+/* { dg-options "-O2 -fdump-tree-vrp2-details" }  */
+/* { dg-final { scan-tree-dump-times "Threaded jump" 1 "vrp2" } }  */
+/* In the 2nd VRP pass (after PRE) we expect to thread the default label of the
+   1st switch straight to that of the 2nd switch.  */
+
+extern void foo (void);
+extern void bar (void);
+
+extern int i;
+void
+test (void)
+{
+  switch (i)
+    {
+    case 0:
+      foo ();
+      break;
+    case 1:
+      bar ();
+      break;
+    default:
+      break;
+    }
+
+  switch (i)
+    {
+    case 0:
+      foo ();
+      break;
+    case 1:
+      bar ();
+      break;
+    default:
+      break;
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp106.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp106.c
new file mode 100644 (file)
index 0000000..e2e48d8
--- /dev/null
@@ -0,0 +1,27 @@
+/* PR tree-optimization/18046  */
+/* { dg-options "-O2 -fdump-tree-vrp1-details" }  */
+/* { dg-final { scan-tree-dump-times "Threaded jump" 1 "vrp1" } }  */
+/* During VRP we expect to thread the true arm of the conditional through the switch
+   and to the BB that corresponds to the 7 ... 9 case label.  */
+extern void foo (void);
+extern void bar (void);
+extern void baz (void);
+
+void
+test (int i)
+{
+  if (i >= 7 && i <= 8)
+    foo ();
+
+  switch (i)
+  {
+    case 1:
+      bar ();
+      break;
+    case 7:
+    case 8:
+    case 9:
+      baz ();
+      break;
+  }
+}
index de671b9463773e8a5d22807a8824670e4dc3f705..170e4564b559a09a86eb32318d6f62bc86fd3ad7 100644 (file)
@@ -36,6 +36,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-ssa-threadedge.h"
 #include "tree-ssa-dom.h"
 #include "gimple-fold.h"
+#include "cfganal.h"
 
 /* To avoid code explosion due to jump threading, we limit the
    number of statements we are going to copy.  This variable
@@ -390,7 +391,8 @@ static tree simplify_control_stmt_condition_1 (edge, gimple *,
    a condition using pass specific information.
 
    Return the simplified condition or NULL if simplification could
-   not be performed.
+   not be performed.  When simplifying a GIMPLE_SWITCH, we may return
+   the CASE_LABEL_EXPR that will be taken.
 
    The available expression table is referenced via AVAIL_EXPRS_STACK.  */
 
@@ -513,7 +515,21 @@ simplify_control_stmt_condition (edge e,
       /* If we haven't simplified to an invariant yet, then use the
         pass specific callback to try and simplify it further.  */
       if (cached_lhs && ! is_gimple_min_invariant (cached_lhs))
-        cached_lhs = (*simplify) (stmt, stmt, avail_exprs_stack);
+       {
+         if (handle_dominating_asserts && code == GIMPLE_SWITCH)
+           {
+             /* Replace the index operand of the GIMPLE_SWITCH with the
+                dominating ASSERT_EXPR before handing it off to VRP.  If
+                simplification is possible, the simplified value will be a
+                CASE_LABEL_EXPR of the label that is proven to be taken.  */
+             gswitch *dummy_switch = as_a<gswitch *> (gimple_copy (stmt));
+             gimple_switch_set_index (dummy_switch, cached_lhs);
+             cached_lhs = (*simplify) (dummy_switch, stmt, avail_exprs_stack);
+             ggc_free (dummy_switch);
+           }
+         else
+           cached_lhs = (*simplify) (stmt, stmt, avail_exprs_stack);
+       }
 
       /* We couldn't find an invariant.  But, callers of this
         function may be able to do something useful with the
@@ -938,9 +954,14 @@ thread_around_empty_blocks (edge taken_edge,
   /* If the condition can be statically computed and we have not already
      visited the destination edge, then add the taken edge to our thread
      path.  */
-  if (cond && is_gimple_min_invariant (cond))
+  if (cond != NULL_TREE
+      && (is_gimple_min_invariant (cond)
+         || TREE_CODE (cond) == CASE_LABEL_EXPR))
     {
-      taken_edge = find_taken_edge (bb, cond);
+      if (TREE_CODE (cond) == CASE_LABEL_EXPR)
+       taken_edge = find_edge (bb, label_to_block (CASE_LABEL (cond)));
+      else
+       taken_edge = find_taken_edge (bb, cond);
 
       if ((taken_edge->flags & EDGE_DFS_BACK) != 0)
        return false;
@@ -1069,9 +1090,16 @@ thread_through_normal_block (edge e,
       if (!cond)
        return 0;
 
-      if (is_gimple_min_invariant (cond))
+      if (is_gimple_min_invariant (cond)
+         || TREE_CODE (cond) == CASE_LABEL_EXPR)
        {
-         edge taken_edge = find_taken_edge (e->dest, cond);
+         edge taken_edge;
+         if (TREE_CODE (cond) == CASE_LABEL_EXPR)
+           taken_edge = find_edge (e->dest,
+                                   label_to_block (CASE_LABEL (cond)));
+         else
+           taken_edge = find_taken_edge (e->dest, cond);
+
          basic_block dest = (taken_edge ? taken_edge->dest : NULL);
 
          /* DEST could be NULL for a computed jump to an absolute
index 5573023da76f4b1ba2514efddc267e4268fb0b91..44dfc84b268278d9e61efed59681c6cbe5266edf 100644 (file)
@@ -10183,6 +10183,67 @@ simplify_stmt_for_jump_threading (gimple *stmt, gimple *within_stmt,
                                     gimple_cond_rhs (cond_stmt),
                                     within_stmt);
 
+  /* We simplify a switch statement by trying to determine which case label
+     will be taken.  If we are successful then we return the corresponding
+     CASE_LABEL_EXPR.  */
+  if (gswitch *switch_stmt = dyn_cast <gswitch *> (stmt))
+    {
+      tree op = gimple_switch_index (switch_stmt);
+      if (TREE_CODE (op) != SSA_NAME)
+       return NULL_TREE;
+
+      value_range *vr = get_value_range (op);
+      if ((vr->type != VR_RANGE && vr->type != VR_ANTI_RANGE)
+         || symbolic_range_p (vr))
+       return NULL_TREE;
+
+      if (vr->type == VR_RANGE)
+       {
+         size_t i, j;
+         /* Get the range of labels that contain a part of the operand's
+            value range.  */
+         find_case_label_range (switch_stmt, vr->min, vr->max, &i, &j);
+
+         /* Is there only one such label?  */
+         if (i == j)
+           {
+             tree label = gimple_switch_label (switch_stmt, i);
+
+             /* The i'th label will be taken only if the value range of the
+                operand is entirely within the bounds of this label.  */
+             if (CASE_HIGH (label) != NULL_TREE
+                 ? (tree_int_cst_compare (CASE_LOW (label), vr->min) <= 0
+                    && tree_int_cst_compare (CASE_HIGH (label), vr->max) >= 0)
+                 : (tree_int_cst_equal (CASE_LOW (label), vr->min)
+                    && tree_int_cst_equal (vr->min, vr->max)))
+               return label;
+           }
+
+         /* If there are no such labels then the default label will be
+            taken.  */
+         if (i > j)
+           return gimple_switch_label (switch_stmt, 0);
+       }
+
+      if (vr->type == VR_ANTI_RANGE)
+       {
+         unsigned n = gimple_switch_num_labels (switch_stmt);
+         tree min_label = gimple_switch_label (switch_stmt, 1);
+         tree max_label = gimple_switch_label (switch_stmt, n - 1);
+
+         /* The default label will be taken only if the anti-range of the
+            operand is entirely outside the bounds of all the (non-default)
+            case labels.  */
+         if (tree_int_cst_compare (vr->min, CASE_LOW (min_label)) <= 0
+             && (CASE_HIGH (max_label) != NULL_TREE
+                 ? tree_int_cst_compare (vr->max, CASE_HIGH (max_label)) >= 0
+                 : tree_int_cst_compare (vr->max, CASE_LOW (max_label)) >= 0))
+         return gimple_switch_label (switch_stmt, 0);
+       }
+
+      return NULL_TREE;
+    }
+
   if (gassign *assign_stmt = dyn_cast <gassign *> (stmt))
     {
       value_range new_vr = VR_INITIALIZER;