From: Patrick Palka Date: Fri, 5 Aug 2016 23:29:53 +0000 (+0000) Subject: Improve forward jump threading of switch statements (PR18046) X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=5c3e5002db1096170c8dd33413842e91bfcb61d8;p=gcc.git Improve forward jump threading of switch statements (PR18046) 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 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 67ec6224a8a..f6a62d1ffc1 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,19 @@ +2016-08-05 Patrick Palka + + 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 PR rtl-optimization/69847 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 3c1fbf012cd..e9792962c62 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2016-08-05 Patrick Palka + + 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 * 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 index 00000000000..7cdd4dd8f3a --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp105.c @@ -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 index 00000000000..e2e48d8deb9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp106.c @@ -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; + } +} diff --git a/gcc/tree-ssa-threadedge.c b/gcc/tree-ssa-threadedge.c index de671b94637..170e4564b55 100644 --- a/gcc/tree-ssa-threadedge.c +++ b/gcc/tree-ssa-threadedge.c @@ -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 (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 diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 5573023da76..44dfc84b268 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -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 (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 (stmt)) { value_range new_vr = VR_INITIALIZER;