Commit 5c3e5002 by Patrick Palka

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
parent b10d44ef
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> 2016-08-05 Vladimir Makarov <vmakarov@redhat.com>
PR rtl-optimization/69847 PR rtl-optimization/69847
......
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> 2016-08-05 Martin Sebor <msebor@redhat.com>
* g++.dg/cpp0x/constexpr-cast.C: Avoid assuming (void*)1 is spelled * g++.dg/cpp0x/constexpr-cast.C: Avoid assuming (void*)1 is spelled
......
/* 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;
}
}
/* 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;
}
}
...@@ -36,6 +36,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -36,6 +36,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-ssa-threadedge.h" #include "tree-ssa-threadedge.h"
#include "tree-ssa-dom.h" #include "tree-ssa-dom.h"
#include "gimple-fold.h" #include "gimple-fold.h"
#include "cfganal.h"
/* To avoid code explosion due to jump threading, we limit the /* To avoid code explosion due to jump threading, we limit the
number of statements we are going to copy. This variable number of statements we are going to copy. This variable
...@@ -390,7 +391,8 @@ static tree simplify_control_stmt_condition_1 (edge, gimple *, ...@@ -390,7 +391,8 @@ static tree simplify_control_stmt_condition_1 (edge, gimple *,
a condition using pass specific information. a condition using pass specific information.
Return the simplified condition or NULL if simplification could 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. */ The available expression table is referenced via AVAIL_EXPRS_STACK. */
...@@ -513,7 +515,21 @@ simplify_control_stmt_condition (edge e, ...@@ -513,7 +515,21 @@ simplify_control_stmt_condition (edge e,
/* If we haven't simplified to an invariant yet, then use the /* If we haven't simplified to an invariant yet, then use the
pass specific callback to try and simplify it further. */ pass specific callback to try and simplify it further. */
if (cached_lhs && ! is_gimple_min_invariant (cached_lhs)) 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 /* We couldn't find an invariant. But, callers of this
function may be able to do something useful with the function may be able to do something useful with the
...@@ -938,9 +954,14 @@ thread_around_empty_blocks (edge taken_edge, ...@@ -938,9 +954,14 @@ thread_around_empty_blocks (edge taken_edge,
/* If the condition can be statically computed and we have not already /* If the condition can be statically computed and we have not already
visited the destination edge, then add the taken edge to our thread visited the destination edge, then add the taken edge to our thread
path. */ 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) if ((taken_edge->flags & EDGE_DFS_BACK) != 0)
return false; return false;
...@@ -1069,9 +1090,16 @@ thread_through_normal_block (edge e, ...@@ -1069,9 +1090,16 @@ thread_through_normal_block (edge e,
if (!cond) if (!cond)
return 0; 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); basic_block dest = (taken_edge ? taken_edge->dest : NULL);
/* DEST could be NULL for a computed jump to an absolute /* DEST could be NULL for a computed jump to an absolute
......
...@@ -10183,6 +10183,67 @@ simplify_stmt_for_jump_threading (gimple *stmt, gimple *within_stmt, ...@@ -10183,6 +10183,67 @@ simplify_stmt_for_jump_threading (gimple *stmt, gimple *within_stmt,
gimple_cond_rhs (cond_stmt), gimple_cond_rhs (cond_stmt),
within_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)) if (gassign *assign_stmt = dyn_cast <gassign *> (stmt))
{ {
value_range new_vr = VR_INITIALIZER; value_range new_vr = VR_INITIALIZER;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment