re PR testsuite/68580 (FAIL: c-c++-common/tsan/pr65400-1.c -O0 execution test)

PR tree-optimization/68580
	* params.def (FSM_MAXIMUM_PHI_ARGUMENTS): New param.
	* tree-ssa-threadbackward.c
	(fsm_find_control_statement_thread_paths): Do not try to walk
	through large PHI nodes.

From-SVN: r233053
This commit is contained in:
Jeff Law 2016-02-01 15:03:57 -07:00 committed by Jeff Law
parent 800cb72aae
commit da9a8da809
3 changed files with 239 additions and 218 deletions

View File

@ -1,3 +1,11 @@
2016-02-01 Jeff Law <law@redhat.com>
PR tree-optimization/68580
* params.def (FSM_MAXIMUM_PHI_ARGUMENTS): New param.
* tree-ssa-threadbackward.c
(fsm_find_control_statement_thread_paths): Do not try to walk
through large PHI nodes.
2016-02-01 Jakub Jelinek <jakub@redhat.com>
* ifcvt.c (bb_ok_for_noce_convert_multiple_sets): Return false

View File

@ -1150,6 +1150,11 @@ DEFPARAM (PARAM_FSM_SCALE_PATH_STMTS,
"Scale factor to apply to the number of statements in a threading path when comparing to the number of (scaled) blocks.",
2, 1, 10)
DEFPARAM (PARAM_FSM_MAXIMUM_PHI_ARGUMENTS,
"fsm-maximum-phi-arguments",
"Maximum number of arguments a PHI may have before the FSM threader will not try to thread through its block.",
100, 1, 999999)
DEFPARAM (PARAM_FSM_SCALE_PATH_BLOCKS,
"fsm-scale-path-blocks",
"Scale factor to apply to the number of blocks in a threading path when comparing to the number of (scaled) statements.",

View File

@ -114,7 +114,9 @@ fsm_find_control_statement_thread_paths (tree name,
eventually one of the phi arguments will be an integer constant. In the
future, this could be extended to also handle simple assignments of
arithmetic operations. */
if (gimple_code (def_stmt) != GIMPLE_PHI)
if (gimple_code (def_stmt) != GIMPLE_PHI
|| (gimple_phi_num_args (def_stmt)
>= (unsigned) PARAM_VALUE (PARAM_FSM_MAXIMUM_PHI_ARGUMENTS)))
return;
/* Avoid infinite recursion. */
@ -200,247 +202,253 @@ fsm_find_control_statement_thread_paths (tree name,
/* Iterate over the arguments of PHI. */
unsigned int i;
for (i = 0; i < gimple_phi_num_args (phi); i++)
if (gimple_phi_num_args (phi)
< (unsigned) PARAM_VALUE (PARAM_FSM_MAXIMUM_PHI_ARGUMENTS))
{
tree arg = gimple_phi_arg_def (phi, i);
basic_block bbi = gimple_phi_arg_edge (phi, i)->src;
/* Skip edges pointing outside the current loop. */
if (!arg || var_bb->loop_father != bbi->loop_father)
continue;
if (TREE_CODE (arg) == SSA_NAME)
for (i = 0; i < gimple_phi_num_args (phi); i++)
{
vec_safe_push (path, bbi);
/* Recursively follow SSA_NAMEs looking for a constant definition. */
fsm_find_control_statement_thread_paths (arg, visited_bbs, path,
seen_loop_phi);
tree arg = gimple_phi_arg_def (phi, i);
basic_block bbi = gimple_phi_arg_edge (phi, i)->src;
path->pop ();
continue;
}
/* Skip edges pointing outside the current loop. */
if (!arg || var_bb->loop_father != bbi->loop_father)
continue;
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 + 1 > PARAM_VALUE (PARAM_MAX_FSM_THREAD_LENGTH))
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "FSM jump-thread path not considered: "
"the number of basic blocks on the path "
"exceeds PARAM_MAX_FSM_THREAD_LENGTH.\n");
continue;
}
if (max_threaded_paths <= 0)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "FSM jump-thread path not considered: "
"the number of previously recorded FSM paths to thread "
"exceeds PARAM_MAX_FSM_THREAD_PATHS.\n");
continue;
}
/* Add BBI to the path. */
vec_safe_push (path, bbi);
++path_length;
int n_insns = 0;
gimple_stmt_iterator gsi;
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 = 0; j < path_length; j++)
{
basic_block bb = (*path)[j];
/* Remember, blocks in the path are stored in opposite order
in the PATH array. The last entry in the array represents
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)
if (TREE_CODE (arg) == SSA_NAME)
{
if (bb->loop_father != loop)
{
path_crosses_loops = true;
break;
}
vec_safe_push (path, bbi);
/* Recursively follow SSA_NAMEs looking for a constant
definition. */
fsm_find_control_statement_thread_paths (arg, visited_bbs, path,
seen_loop_phi);
for (gsi = gsi_after_labels (bb);
!gsi_end_p (gsi);
gsi_next_nondebug (&gsi))
{
gimple *stmt = gsi_stmt (gsi);
/* Do not count empty statements and labels. */
if (gimple_code (stmt) != GIMPLE_NOP
&& !(gimple_code (stmt) == GIMPLE_ASSIGN
&& gimple_assign_rhs_code (stmt) == ASSERT_EXPR)
&& !is_gimple_debug (stmt))
++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;
}
path->pop ();
continue;
}
/* 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;
}
if (TREE_CODE (arg) != INTEGER_CST)
continue;
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);
/* 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 + 1 > PARAM_VALUE (PARAM_MAX_FSM_THREAD_LENGTH))
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "FSM jump-thread path not considered: "
"the number of basic blocks on the path "
"exceeds PARAM_MAX_FSM_THREAD_LENGTH.\n");
continue;
}
/* We know the underyling format of the condition. */
arg = fold_binary (cond_code, boolean_type_node,
arg, gimple_cond_rhs (stmt));
}
if (max_threaded_paths <= 0)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "FSM jump-thread path not considered: "
"the number of previously recorded FSM paths to "
"thread exceeds PARAM_MAX_FSM_THREAD_PATHS.\n");
continue;
}
/* 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.
/* Add BBI to the path. */
vec_safe_push (path, bbi);
++path_length;
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;
int n_insns = 0;
gimple_stmt_iterator gsi;
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;
/* PHIs in the final target and only the final target will need
to be duplicated. So only count those against the number
of statements. */
gphi_iterator gsip;
for (gsip = gsi_start_phis (taken_edge->dest);
!gsi_end_p (gsip);
gsi_next (&gsip))
{
gphi *phi = gsip.phi ();
tree dst = gimple_phi_result (phi);
/* 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 = 0; j < path_length; j++)
{
basic_block bb = (*path)[j];
/* 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;
}
/* Remember, blocks in the path are stored in opposite order
in the PATH array. The last entry in the array represents
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)
{
if (bb->loop_father != loop)
{
path_crosses_loops = true;
break;
}
if (path_crosses_loops)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "FSM jump-thread path not considered: "
"the path crosses loops.\n");
path->pop ();
continue;
}
for (gsi = gsi_after_labels (bb);
!gsi_end_p (gsi);
gsi_next_nondebug (&gsi))
{
gimple *stmt = gsi_stmt (gsi);
/* Do not count empty statements and labels. */
if (gimple_code (stmt) != GIMPLE_NOP
&& !(gimple_code (stmt) == GIMPLE_ASSIGN
&& gimple_assign_rhs_code (stmt) == ASSERT_EXPR)
&& !is_gimple_debug (stmt))
++n_insns;
}
if (n_insns >= PARAM_VALUE (PARAM_MAX_FSM_THREAD_PATH_INSNS))
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "FSM jump-thread path not considered: "
"the number of instructions on the path "
"exceeds PARAM_MAX_FSM_THREAD_PATH_INSNS.\n");
path->pop ();
continue;
}
/* 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.
/* We avoid creating irreducible inner loops unless we thread through
a multiway branch, in which case we have deemed it worth losing other
loop optimizations later.
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;
}
}
We also consider it worth creating an irreducible inner loop if
the number of copied statement is low relative to the length of
the path -- in that case there's little the traditional loop optimizer
would have done anyway, so an irreducible loop is not so bad. */
if (!threaded_multiway_branch && creates_irreducible_loop
&& (n_insns * PARAM_VALUE (PARAM_FSM_SCALE_PATH_STMTS)
> path_length * PARAM_VALUE (PARAM_FSM_SCALE_PATH_BLOCKS)))
/* 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;
}
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file,
"FSM would create irreducible loop without threading "
"multiway branch.\n");
path->pop ();
continue;
}
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);
/* 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;
}
/* We know the underyling format of the condition. */
arg = fold_binary (cond_code, boolean_type_node,
arg, gimple_cond_rhs (stmt));
}
vec<jump_thread_edge *> *jump_thread_path
= new vec<jump_thread_edge *> ();
/* 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.
/* Record the edges between the blocks in PATH. */
for (j = 0; j < path_length - 1; j++)
{
edge e = find_edge ((*path)[path_length - j - 1],
(*path)[path_length - j - 2]);
gcc_assert (e);
jump_thread_edge *x = new jump_thread_edge (e, EDGE_FSM_THREAD);
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;
/* PHIs in the final target and only the final target will need
to be duplicated. So only count those against the number
of statements. */
gphi_iterator gsip;
for (gsip = gsi_start_phis (taken_edge->dest);
!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;
}
if (path_crosses_loops)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "FSM jump-thread path not considered: "
"the path crosses loops.\n");
path->pop ();
continue;
}
if (n_insns >= PARAM_VALUE (PARAM_MAX_FSM_THREAD_PATH_INSNS))
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "FSM jump-thread path not considered: "
"the number of instructions on the path "
"exceeds PARAM_MAX_FSM_THREAD_PATH_INSNS.\n");
path->pop ();
continue;
}
/* We avoid creating irreducible inner loops unless we thread through
a multiway branch, in which case we have deemed it worth losing
other loop optimizations later.
We also consider it worth creating an irreducible inner loop if
the number of copied statement is low relative to the length of
the path -- in that case there's little the traditional loop
optimizer would have done anyway, so an irreducible loop is not
so bad. */
if (!threaded_multiway_branch && creates_irreducible_loop
&& (n_insns * PARAM_VALUE (PARAM_FSM_SCALE_PATH_STMTS)
> path_length * PARAM_VALUE (PARAM_FSM_SCALE_PATH_BLOCKS)))
{
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 *> ();
/* Record the edges between the blocks in PATH. */
for (j = 0; j < path_length - 1; j++)
{
edge e = find_edge ((*path)[path_length - j - 1],
(*path)[path_length - j - 2]);
gcc_assert (e);
jump_thread_edge *x = new jump_thread_edge (e, EDGE_FSM_THREAD);
jump_thread_path->safe_push (x);
}
/* Add the edge taken when the control variable has value ARG. */
jump_thread_edge *x
= new jump_thread_edge (taken_edge, EDGE_NO_COPY_SRC_BLOCK);
jump_thread_path->safe_push (x);
register_jump_thread (jump_thread_path);
--max_threaded_paths;
/* Remove BBI from the path. */
path->pop ();
}
/* Add the edge taken when the control variable has value ARG. */
jump_thread_edge *x
= new jump_thread_edge (taken_edge, EDGE_NO_COPY_SRC_BLOCK);
jump_thread_path->safe_push (x);
register_jump_thread (jump_thread_path);
--max_threaded_paths;
/* Remove BBI from the path. */
path->pop ();
}
/* Remove all the nodes that we added from NEXT_PATH. */