re PR tree-optimization/69196 (code size regression with jump threading at -O2)
PR tree-optimization/69196 PR tree-optimization/68398 * tree-ssa-threadupdate.h (enum bb_dom_status): Moved here from tree-ssa-threadupdate.c. (determine_bb_domination_status): Prototype * tree-ssa-threadupdate.c (enum bb_dom_status): Remove (determine_bb_domination_status): No longer static. (valid_jump_thread_path): Remove code to detect characteristics of the jump thread path not associated with correctness. * tree-ssa-threadbackward.c (fsm_find_control_statment_thread_paths): Correct test for thread path length. Count PHIs for real operands as statements that need to be copied. Do not count ASSERT_EXPRs. Look at all the blocks in the thread path. Compute and selectively filter thread paths based on threading through the latch, threading a multiway branch or crossing a multiway branch. PR tree-optimization/69196 PR tree-optimization/68398 * gcc.dg/tree-ssa/pr66752-3.c: Update expected output * gcc.dg/tree-ssa/pr68198.c: Likewise. From-SVN: r232802
This commit is contained in:
parent
2944621e2c
commit
2c89b952c7
7 changed files with 169 additions and 105 deletions
|
@ -1,3 +1,21 @@
|
|||
2016-01-25 Jeff Law <law@redhat.com>
|
||||
|
||||
PR tree-optimization/69196
|
||||
PR tree-optimization/68398
|
||||
* tree-ssa-threadupdate.h (enum bb_dom_status): Moved here from
|
||||
tree-ssa-threadupdate.c.
|
||||
(determine_bb_domination_status): Prototype
|
||||
* tree-ssa-threadupdate.c (enum bb_dom_status): Remove
|
||||
(determine_bb_domination_status): No longer static.
|
||||
(valid_jump_thread_path): Remove code to detect characteristics
|
||||
of the jump thread path not associated with correctness.
|
||||
* tree-ssa-threadbackward.c (fsm_find_control_statment_thread_paths):
|
||||
Correct test for thread path length. Count PHIs for real operands as
|
||||
statements that need to be copied. Do not count ASSERT_EXPRs.
|
||||
Look at all the blocks in the thread path. Compute and selectively
|
||||
filter thread paths based on threading through the latch, threading
|
||||
a multiway branch or crossing a multiway branch.
|
||||
|
||||
2016-01-25 Bill Schmidt <wschmidt@linux.vnet.ibm.com>
|
||||
|
||||
* config/rs6000/rs6000.c (rs6000_keep_leaf_when_profiled): Add
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
2016-01-25 Jeff Law <law@redhat.com>
|
||||
|
||||
PR tree-optimization/69196
|
||||
PR tree-optimization/68398
|
||||
* gcc.dg/tree-ssa/pr66752-3.c: Update expected output
|
||||
* gcc.dg/tree-ssa/pr68198.c: Likewise.
|
||||
|
||||
2016-01-25 David Edelsohn <dje.gcc@gmail.com>
|
||||
|
||||
PR target/69469
|
||||
|
|
|
@ -33,9 +33,9 @@ foo (int N, int c, int b, int *a)
|
|||
}
|
||||
|
||||
/* There are 3 FSM jump threading opportunities, one of which will
|
||||
get cancelled. */
|
||||
/* { dg-final { scan-tree-dump-times "Registering FSM" 3 "vrp1"} } */
|
||||
/* { dg-final { scan-tree-dump-times "Cancelling FSM" 1 "vrp1"} } */
|
||||
get filtered. */
|
||||
/* { dg-final { scan-tree-dump-times "Registering FSM" 2 "vrp1"} } */
|
||||
/* { dg-final { scan-tree-dump-times "FSM would create irreducible loop" 1 "vrp1"} } */
|
||||
|
||||
/* There should be no assignments or references to FLAG. */
|
||||
/* { dg-final { scan-tree-dump-not "flag" "optimized"} } */
|
||||
|
|
|
@ -38,6 +38,6 @@ c_finish_omp_clauses (tree clauses)
|
|||
}
|
||||
|
||||
/* There are 3 FSM jump threading opportunities, two of which will
|
||||
get cancelled. */
|
||||
/* { dg-final { scan-tree-dump-times "Registering FSM" 3 "vrp1"} } */
|
||||
/* { dg-final { scan-tree-dump-times "Cancelling FSM" 2 "vrp1"} } */
|
||||
get filtered out. */
|
||||
/* { dg-final { scan-tree-dump-times "Registering FSM" 1 "vrp1"} } */
|
||||
/* { dg-final { scan-tree-dump-times "FSM Thread through multiway branch without threading a multiway branch" 2 "vrp1"} } */
|
||||
|
|
|
@ -223,8 +223,10 @@ fsm_find_control_statement_thread_paths (tree name,
|
|||
if (TREE_CODE (arg) != INTEGER_CST)
|
||||
continue;
|
||||
|
||||
/* Note BBI is not in the path yet, hence the +1 in the test below
|
||||
to make sure BBI is accounted for in the path length test. */
|
||||
int path_length = path->length ();
|
||||
if (path_length > PARAM_VALUE (PARAM_MAX_FSM_THREAD_LENGTH))
|
||||
if (path_length + 1 > PARAM_VALUE (PARAM_MAX_FSM_THREAD_LENGTH))
|
||||
{
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
fprintf (dump_file, "FSM jump-thread path not considered: "
|
||||
|
@ -251,32 +253,113 @@ fsm_find_control_statement_thread_paths (tree name,
|
|||
int j;
|
||||
loop_p loop = (*path)[0]->loop_father;
|
||||
bool path_crosses_loops = false;
|
||||
bool threaded_through_latch = false;
|
||||
bool multiway_branch_in_path = false;
|
||||
bool threaded_multiway_branch = false;
|
||||
|
||||
/* Count the number of instructions on the path: as these instructions
|
||||
will have to be duplicated, we will not record the path if there are
|
||||
too many instructions on the path. Also check that all the blocks in
|
||||
the path belong to a single loop. */
|
||||
for (j = 1; j < path_length - 1; j++)
|
||||
for (j = 0; j < path_length; j++)
|
||||
{
|
||||
basic_block bb = (*path)[j];
|
||||
|
||||
if (bb->loop_father != loop)
|
||||
/* Remember, blocks in the path are stored in opposite order
|
||||
in the PATH array. The last entry in the array reprensents
|
||||
the block with an outgoing edge that we will redirect to the
|
||||
jump threading path. Thus we don't care about that block's
|
||||
loop father, nor how many statements are in that block because
|
||||
it will not be copied or whether or not it ends in a multiway
|
||||
branch. */
|
||||
if (j < path_length - 1)
|
||||
{
|
||||
path_crosses_loops = true;
|
||||
break;
|
||||
if (bb->loop_father != loop)
|
||||
{
|
||||
path_crosses_loops = true;
|
||||
break;
|
||||
}
|
||||
|
||||
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
|
||||
{
|
||||
gimple *stmt = gsi_stmt (gsi);
|
||||
/* Do not count empty statements and labels. */
|
||||
if (gimple_code (stmt) != GIMPLE_NOP
|
||||
&& gimple_code (stmt) != GIMPLE_LABEL
|
||||
&& !(gimple_code (stmt) == GIMPLE_ASSIGN
|
||||
&& gimple_assign_rhs_code (stmt) == ASSERT_EXPR)
|
||||
&& !is_gimple_debug (stmt))
|
||||
++n_insns;
|
||||
}
|
||||
|
||||
gphi_iterator gsip;
|
||||
for (gsip = gsi_start_phis (bb);
|
||||
!gsi_end_p (gsip);
|
||||
gsi_next (&gsip))
|
||||
{
|
||||
gphi *phi = gsip.phi ();
|
||||
tree dst = gimple_phi_result (phi);
|
||||
|
||||
/* We consider any non-virtual PHI as a statement since it
|
||||
count result in a constant assignment or copy
|
||||
operation. */
|
||||
if (!virtual_operand_p (dst))
|
||||
++n_insns;
|
||||
}
|
||||
|
||||
/* We do not look at the block with the threaded branch
|
||||
in this loop. So if any block with a last statement that
|
||||
is a GIMPLE_SWITCH or GIMPLE_GOTO is seen, then we have a
|
||||
multiway branch on our path.
|
||||
|
||||
The block in PATH[0] is special, it's the block were we're
|
||||
going to be able to eliminate its branch. */
|
||||
gimple *last = last_stmt (bb);
|
||||
if (last && (gimple_code (last) == GIMPLE_SWITCH
|
||||
|| gimple_code (last) == GIMPLE_GOTO))
|
||||
{
|
||||
if (j == 0)
|
||||
threaded_multiway_branch = true;
|
||||
else
|
||||
multiway_branch_in_path = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
|
||||
{
|
||||
gimple *stmt = gsi_stmt (gsi);
|
||||
/* Do not count empty statements and labels. */
|
||||
if (gimple_code (stmt) != GIMPLE_NOP
|
||||
&& gimple_code (stmt) != GIMPLE_LABEL
|
||||
&& !is_gimple_debug (stmt))
|
||||
++n_insns;
|
||||
}
|
||||
/* Note if we thread through the latch, we will want to include
|
||||
the last entry in the array when determining if we thread
|
||||
through the loop latch. */
|
||||
if (loop->latch == bb)
|
||||
threaded_through_latch = true;
|
||||
}
|
||||
|
||||
gimple *stmt = get_gimple_control_stmt ((*path)[0]);
|
||||
gcc_assert (stmt);
|
||||
/* We have found a constant value for ARG. For GIMPLE_SWITCH
|
||||
and GIMPLE_GOTO, we use it as-is. However, for a GIMPLE_COND
|
||||
we need to substitute, fold and simplify so we can determine
|
||||
the edge taken out of the last block. */
|
||||
if (gimple_code (stmt) == GIMPLE_COND)
|
||||
{
|
||||
enum tree_code cond_code = gimple_cond_code (stmt);
|
||||
|
||||
/* We know the underyling format of the condition. */
|
||||
arg = fold_binary (cond_code, boolean_type_node,
|
||||
arg, gimple_cond_rhs (stmt));
|
||||
}
|
||||
|
||||
/* If this path threaded through the loop latch back into the
|
||||
same loop and the destination does not dominate the loop
|
||||
latch, then this thread would create an irreducible loop.
|
||||
|
||||
We have to know the outgoing edge to figure this out. */
|
||||
edge taken_edge = find_taken_edge ((*path)[0], arg);
|
||||
bool creates_irreducible_loop = false;
|
||||
if (threaded_through_latch
|
||||
&& loop == taken_edge->dest->loop_father
|
||||
&& (determine_bb_domination_status (loop, taken_edge->dest)
|
||||
== DOMST_NONDOMINATING))
|
||||
creates_irreducible_loop = true;
|
||||
|
||||
if (path_crosses_loops)
|
||||
{
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
|
@ -296,6 +379,33 @@ fsm_find_control_statement_thread_paths (tree name,
|
|||
continue;
|
||||
}
|
||||
|
||||
/* We avoid creating irreducible loops unless we thread through
|
||||
a multiway branch, in which case we have deemed it worth losing other
|
||||
loop optimizations later. */
|
||||
if (!threaded_multiway_branch && creates_irreducible_loop)
|
||||
{
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
fprintf (dump_file,
|
||||
"FSM would create irreducible loop without threading "
|
||||
"multiway branch.\n");
|
||||
path->pop ();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* When there is a multi-way branch on the path, then threading can
|
||||
explode the CFG due to duplicating the edges for that multi-way
|
||||
branch. So like above, only allow a multi-way branch on the path
|
||||
if we actually thread a multi-way branch. */
|
||||
if (!threaded_multiway_branch && multiway_branch_in_path)
|
||||
{
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
fprintf (dump_file,
|
||||
"FSM Thread through multiway branch without threading "
|
||||
"a multiway branch.\n");
|
||||
path->pop ();
|
||||
continue;
|
||||
}
|
||||
|
||||
vec<jump_thread_edge *> *jump_thread_path
|
||||
= new vec<jump_thread_edge *> ();
|
||||
|
||||
|
@ -309,22 +419,7 @@ fsm_find_control_statement_thread_paths (tree name,
|
|||
jump_thread_path->safe_push (x);
|
||||
}
|
||||
|
||||
gimple *stmt = get_gimple_control_stmt ((*path)[0]);
|
||||
gcc_assert (stmt);
|
||||
/* We have found a constant value for ARG. For GIMPLE_SWITCH
|
||||
and GIMPLE_GOTO, we use it as-is. However, for a GIMPLE_COND
|
||||
we need to substitute, fold and simplify. */
|
||||
if (gimple_code (stmt) == GIMPLE_COND)
|
||||
{
|
||||
enum tree_code cond_code = gimple_cond_code (stmt);
|
||||
|
||||
/* We know the underyling format of the condition. */
|
||||
arg = fold_binary (cond_code, boolean_type_node,
|
||||
arg, gimple_cond_rhs (stmt));
|
||||
}
|
||||
|
||||
/* Add the edge taken when the control variable has value ARG. */
|
||||
edge taken_edge = find_taken_edge ((*path)[0], arg);
|
||||
jump_thread_edge *x
|
||||
= new jump_thread_edge (taken_edge, EDGE_NO_COPY_SRC_BLOCK);
|
||||
jump_thread_path->safe_push (x);
|
||||
|
|
|
@ -1663,16 +1663,6 @@ dbds_continue_enumeration_p (const_basic_block bb, const void *stop)
|
|||
returns the state. */
|
||||
|
||||
enum bb_dom_status
|
||||
{
|
||||
/* BB does not dominate latch of the LOOP. */
|
||||
DOMST_NONDOMINATING,
|
||||
/* The LOOP is broken (there is no path from the header to its latch. */
|
||||
DOMST_LOOP_BROKEN,
|
||||
/* BB dominates the latch of the LOOP. */
|
||||
DOMST_DOMINATING
|
||||
};
|
||||
|
||||
static enum bb_dom_status
|
||||
determine_bb_domination_status (struct loop *loop, basic_block bb)
|
||||
{
|
||||
basic_block *bblocks;
|
||||
|
@ -2389,73 +2379,14 @@ static bool
|
|||
valid_jump_thread_path (vec<jump_thread_edge *> *path)
|
||||
{
|
||||
unsigned len = path->length ();
|
||||
bool threaded_multiway_branch = false;
|
||||
bool multiway_branch_in_path = false;
|
||||
bool threaded_through_latch = false;
|
||||
|
||||
/* Check that the path is connected and see if there's a multi-way
|
||||
branch on the path and whether or not a multi-way branch
|
||||
is threaded. */
|
||||
/* Check that the path is connected. */
|
||||
for (unsigned int j = 0; j < len - 1; j++)
|
||||
{
|
||||
edge e = (*path)[j]->e;
|
||||
struct loop *loop = e->dest->loop_father;
|
||||
|
||||
if (e->dest != (*path)[j+1]->e->src)
|
||||
return false;
|
||||
|
||||
/* If we're threading through the loop latch back into the
|
||||
same loop and the destination does not dominate the loop
|
||||
latch, then this thread would create an irreducible loop. */
|
||||
if (loop->latch
|
||||
&& loop_latch_edge (loop) == e
|
||||
&& loop == path->last()->e->dest->loop_father
|
||||
&& (determine_bb_domination_status (loop, path->last ()->e->dest)
|
||||
== DOMST_NONDOMINATING))
|
||||
threaded_through_latch = true;
|
||||
|
||||
gimple *last = last_stmt (e->dest);
|
||||
if (j == len - 2)
|
||||
threaded_multiway_branch
|
||||
|= (last && gimple_code (last) == GIMPLE_SWITCH);
|
||||
else
|
||||
multiway_branch_in_path
|
||||
|= (last && gimple_code (last) == GIMPLE_SWITCH);
|
||||
}
|
||||
|
||||
/* If we are trying to thread through the loop latch to a block in the
|
||||
loop that does not dominate the loop latch, then that will create an
|
||||
irreducible loop. We avoid that unless the jump thread has a multi-way
|
||||
branch, in which case we have deemed it worth losing other
|
||||
loop optimizations later if we can eliminate the multi-way branch. */
|
||||
if (!threaded_multiway_branch && threaded_through_latch)
|
||||
{
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
{
|
||||
fprintf (dump_file,
|
||||
"Thread through latch without threading a multiway "
|
||||
"branch.\n");
|
||||
dump_jump_thread_path (dump_file, *path, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* When there is a multi-way branch on the path, then threading can
|
||||
explode the CFG due to duplicating the edges for that multi-way
|
||||
branch. So like above, only allow a multi-way branch on the path
|
||||
if we actually thread a multi-way branch. */
|
||||
if (!threaded_multiway_branch && multiway_branch_in_path)
|
||||
{
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
{
|
||||
fprintf (dump_file,
|
||||
"Thread through multiway branch without threading "
|
||||
"a multiway branch.\n");
|
||||
dump_jump_thread_path (dump_file, *path, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,4 +47,17 @@ extern void remove_jump_threads_including (edge);
|
|||
extern void delete_jump_thread_path (vec <class jump_thread_edge *> *);
|
||||
extern void remove_ctrl_stmt_and_useless_edges (basic_block, basic_block);
|
||||
extern void free_dom_edge_info (edge);
|
||||
|
||||
enum bb_dom_status
|
||||
{
|
||||
/* BB does not dominate latch of the LOOP. */
|
||||
DOMST_NONDOMINATING,
|
||||
/* The LOOP is broken (there is no path from the header to its latch. */
|
||||
DOMST_LOOP_BROKEN,
|
||||
/* BB dominates the latch of the LOOP. */
|
||||
DOMST_DOMINATING
|
||||
};
|
||||
|
||||
enum bb_dom_status determine_bb_domination_status (struct loop *, basic_block);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue