On Thu, Jul 28, 2016 at 11:46 PM, Patrick Palka <patr...@parcs.ath.cx> wrote: > This patch improves the forward jump threader's ability to thread > GIMPLE_SWITCHes by making the VRP simplification callback attempt to > determine which case label will be taken. > > For example, if the index operand of a switch has a value range ~[5,6] > along some edge and the switch statement has no "case 5" or "case 6" > label then this patch recognizes such a scenario as an opportunity for > threading through the switch and to the switch's default bb. > > This improvement is necessary for the code in comment #1 of PR18046 to > get threaded. There we have (in the VRP2 dump): > > bar () > { > int i.0_1; > int i.0_2; > int pretmp_8; > int prephitmp_9; > int i.0_10; > > <bb 2>: > i.0_1 = i; > switch (i.0_1) <default: <L12>, case 0: <L0>> > > <L12>: > i.0_10 = ASSERT_EXPR <i.0_1, i.0_1 != 0>; > goto <bb 4> (<L2>); // <-- this can be threaded to <L6> > > <L0>: > i.0_2 = ASSERT_EXPR <i.0_1, i.0_1 == 0>; > foo (); > pretmp_8 = i; > > # prephitmp_9 = PHI <i.0_10(7), pretmp_8(3)> > <L2>: > switch (prephitmp_9) <default: <L6>, case 0: <L4>> > > <L4>: > foo (); > > <L6>: > return; > > } > > The FSM threader can't thread the default edge of the 1st switch through > to the default edge of the 2nd switch because i.0_1 doesn't have a known > constant value along that path -- it could have any non-zero value. For > this scenario and others like it, it is necessary to consider value > ranges. > > During bootstrap this simplification triggered about 1000 times in > total. It's not an impressive amount but the simplification itself is > cheap. > > Bootstrap + regtest in progress on x86_64-pc-linux-gnu. Does this look > OK to commit if there are no new regressions? > > 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. > --- > gcc/testsuite/gcc.dg/tree-ssa/vrp105.c | 37 +++++++++++++++++++++ > gcc/testsuite/gcc.dg/tree-ssa/vrp106.c | 27 +++++++++++++++ > gcc/tree-ssa-threadedge.c | 40 ++++++++++++++++++---- > gcc/tree-vrp.c | 61 > ++++++++++++++++++++++++++++++++++ > 4 files changed, 159 insertions(+), 6 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp105.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp106.c > > 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 0000000..7cdd4dd > --- /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 0000000..e2e48d8 > --- /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 de671b9..170e456 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<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 > diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c > index 41c870f..cb316b0 100644 > --- a/gcc/tree-vrp.c > +++ b/gcc/tree-vrp.c > @@ -10092,6 +10092,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; > -- > 2.9.2.512.gd7cab81 >
Ping.