re PR tree-optimization/71437 (Performance regression after r235817)
PR tree-optimization/71437 * tree-ssa-dom.c (dom_opt_dom_walker): Remove thread_across_edge member function. Implementation moved into after_dom_children member function and into the threader's thread_outgoing_edges function. (dom_opt_dom_walker::after_dom_children): Simplify by moving some code into new thread_outgoing_edges. * tree-ssa-threadedge.c (thread_across_edge): Make static and simplify definition. Simplify marker handling (do it here). Assume we always have the available expression and the const/copies tables. (thread_outgoing_edges): New function extracted from tree-ssa-dom.c and tree-vrp.c * tree-ssa-threadedge.h (thread_outgoing_edges): Declare. * tree-vrp.c (equiv_stack): No longer file scoped. (vrp_dom_walker): New class. (vrp_dom_walker::before_dom_children): New member function. (vrp_dom_walker::after_dom_children): Likewise. (identify_jump_threads): Setup domwalker. Use it rather than walking edges in a random order by hand. Simplify setup/finalization. (finalize_jump_threads): Remove. (vrp_finalize): Do not call identify_jump_threads here. (execute_vrp): Do it here instead and call thread_through_all_blocks here too. From-SVN: r246208
This commit is contained in:
parent
8d7437be47
commit
c87550223a
|
@ -1,5 +1,29 @@
|
||||||
2017-03-16 Jeff Law <law@redhat.com>
|
2017-03-16 Jeff Law <law@redhat.com>
|
||||||
|
|
||||||
|
PR tree-optimization/71437
|
||||||
|
* tree-ssa-dom.c (dom_opt_dom_walker): Remove thread_across_edge
|
||||||
|
member function. Implementation moved into after_dom_children
|
||||||
|
member function and into the threader's thread_outgoing_edges
|
||||||
|
function.
|
||||||
|
(dom_opt_dom_walker::after_dom_children): Simplify by moving
|
||||||
|
some code into new thread_outgoing_edges.
|
||||||
|
* tree-ssa-threadedge.c (thread_across_edge): Make static and simplify
|
||||||
|
definition. Simplify marker handling (do it here). Assume we always
|
||||||
|
have the available expression and the const/copies tables.
|
||||||
|
(thread_outgoing_edges): New function extracted from tree-ssa-dom.c
|
||||||
|
and tree-vrp.c
|
||||||
|
* tree-ssa-threadedge.h (thread_outgoing_edges): Declare.
|
||||||
|
* tree-vrp.c (equiv_stack): No longer file scoped.
|
||||||
|
(vrp_dom_walker): New class.
|
||||||
|
(vrp_dom_walker::before_dom_children): New member function.
|
||||||
|
(vrp_dom_walker::after_dom_children): Likewise.
|
||||||
|
(identify_jump_threads): Setup domwalker. Use it rather than
|
||||||
|
walking edges in a random order by hand. Simplify setup/finalization.
|
||||||
|
(finalize_jump_threads): Remove.
|
||||||
|
(vrp_finalize): Do not call identify_jump_threads here.
|
||||||
|
(execute_vrp): Do it here instead and call thread_through_all_blocks
|
||||||
|
here too.
|
||||||
|
|
||||||
PR tree-optimization/71437
|
PR tree-optimization/71437
|
||||||
* tree-ssa-dom.c (pfn_simplify): Add basic_block argument. All
|
* tree-ssa-dom.c (pfn_simplify): Add basic_block argument. All
|
||||||
callers changed.
|
callers changed.
|
||||||
|
|
|
@ -350,7 +350,6 @@ public:
|
||||||
virtual void after_dom_children (basic_block);
|
virtual void after_dom_children (basic_block);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void thread_across_edge (edge);
|
|
||||||
|
|
||||||
/* Unwindable equivalences, both const/copy and expression varieties. */
|
/* Unwindable equivalences, both const/copy and expression varieties. */
|
||||||
class const_and_copies *m_const_and_copies;
|
class const_and_copies *m_const_and_copies;
|
||||||
|
@ -816,39 +815,6 @@ record_temporary_equivalences (edge e,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wrapper for common code to attempt to thread an edge. For example,
|
|
||||||
it handles lazily building the dummy condition and the bookkeeping
|
|
||||||
when jump threading is successful. */
|
|
||||||
|
|
||||||
void
|
|
||||||
dom_opt_dom_walker::thread_across_edge (edge e)
|
|
||||||
{
|
|
||||||
if (! m_dummy_cond)
|
|
||||||
m_dummy_cond =
|
|
||||||
gimple_build_cond (NE_EXPR,
|
|
||||||
integer_zero_node, integer_zero_node,
|
|
||||||
NULL, NULL);
|
|
||||||
|
|
||||||
/* Push a marker on both stacks so we can unwind the tables back to their
|
|
||||||
current state. */
|
|
||||||
m_avail_exprs_stack->push_marker ();
|
|
||||||
m_const_and_copies->push_marker ();
|
|
||||||
|
|
||||||
/* With all the edge equivalences in the tables, go ahead and attempt
|
|
||||||
to thread through E->dest. */
|
|
||||||
::thread_across_edge (m_dummy_cond, e,
|
|
||||||
m_const_and_copies, m_avail_exprs_stack,
|
|
||||||
simplify_stmt_for_jump_threading);
|
|
||||||
|
|
||||||
/* And restore the various tables to their state before
|
|
||||||
we threaded this edge.
|
|
||||||
|
|
||||||
XXX The code in tree-ssa-threadedge.c will restore the state of
|
|
||||||
the const_and_copies table. We we just have to restore the expression
|
|
||||||
table. */
|
|
||||||
m_avail_exprs_stack->pop_to_marker ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PHI nodes can create equivalences too.
|
/* PHI nodes can create equivalences too.
|
||||||
|
|
||||||
Ignoring any alternatives which are the same as the result, if
|
Ignoring any alternatives which are the same as the result, if
|
||||||
|
@ -1224,38 +1190,13 @@ dom_opt_dom_walker::before_dom_children (basic_block bb)
|
||||||
void
|
void
|
||||||
dom_opt_dom_walker::after_dom_children (basic_block bb)
|
dom_opt_dom_walker::after_dom_children (basic_block bb)
|
||||||
{
|
{
|
||||||
gimple *last;
|
if (! m_dummy_cond)
|
||||||
|
m_dummy_cond = gimple_build_cond (NE_EXPR, integer_zero_node,
|
||||||
|
integer_zero_node, NULL, NULL);
|
||||||
|
|
||||||
/* If we have an outgoing edge to a block with multiple incoming and
|
thread_outgoing_edges (bb, m_dummy_cond, m_const_and_copies,
|
||||||
outgoing edges, then we may be able to thread the edge, i.e., we
|
m_avail_exprs_stack,
|
||||||
may be able to statically determine which of the outgoing edges
|
simplify_stmt_for_jump_threading);
|
||||||
will be traversed when the incoming edge from BB is traversed. */
|
|
||||||
if (single_succ_p (bb)
|
|
||||||
&& (single_succ_edge (bb)->flags & EDGE_ABNORMAL) == 0
|
|
||||||
&& potentially_threadable_block (single_succ (bb)))
|
|
||||||
{
|
|
||||||
thread_across_edge (single_succ_edge (bb));
|
|
||||||
}
|
|
||||||
else if ((last = last_stmt (bb))
|
|
||||||
&& gimple_code (last) == GIMPLE_COND
|
|
||||||
&& EDGE_COUNT (bb->succs) == 2
|
|
||||||
&& (EDGE_SUCC (bb, 0)->flags & EDGE_ABNORMAL) == 0
|
|
||||||
&& (EDGE_SUCC (bb, 1)->flags & EDGE_ABNORMAL) == 0)
|
|
||||||
{
|
|
||||||
edge true_edge, false_edge;
|
|
||||||
|
|
||||||
extract_true_false_edges_from_block (bb, &true_edge, &false_edge);
|
|
||||||
|
|
||||||
/* Only try to thread the edge if it reaches a target block with
|
|
||||||
more than one predecessor and more than one successor. */
|
|
||||||
if (potentially_threadable_block (true_edge->dest))
|
|
||||||
thread_across_edge (true_edge);
|
|
||||||
|
|
||||||
/* Similarly for the ELSE arm. */
|
|
||||||
if (potentially_threadable_block (false_edge->dest))
|
|
||||||
thread_across_edge (false_edge);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* These remove expressions local to BB from the tables. */
|
/* These remove expressions local to BB from the tables. */
|
||||||
m_avail_exprs_stack->pop_to_marker ();
|
m_avail_exprs_stack->pop_to_marker ();
|
||||||
|
|
|
@ -1100,16 +1100,18 @@ thread_through_normal_block (edge e,
|
||||||
|
|
||||||
SIMPLIFY is a pass-specific function used to simplify statements. */
|
SIMPLIFY is a pass-specific function used to simplify statements. */
|
||||||
|
|
||||||
void
|
static void
|
||||||
thread_across_edge (gcond *dummy_cond,
|
thread_across_edge (gcond *dummy_cond,
|
||||||
edge e,
|
edge e,
|
||||||
class const_and_copies *const_and_copies,
|
class const_and_copies *const_and_copies,
|
||||||
class avail_exprs_stack *avail_exprs_stack,
|
class avail_exprs_stack *avail_exprs_stack,
|
||||||
tree (*simplify) (gimple *, gimple *,
|
pfn_simplify simplify)
|
||||||
class avail_exprs_stack *, basic_block))
|
|
||||||
{
|
{
|
||||||
bitmap visited = BITMAP_ALLOC (NULL);
|
bitmap visited = BITMAP_ALLOC (NULL);
|
||||||
|
|
||||||
|
const_and_copies->push_marker ();
|
||||||
|
avail_exprs_stack->push_marker ();
|
||||||
|
|
||||||
stmt_count = 0;
|
stmt_count = 0;
|
||||||
|
|
||||||
vec<jump_thread_edge *> *path = new vec<jump_thread_edge *> ();
|
vec<jump_thread_edge *> *path = new vec<jump_thread_edge *> ();
|
||||||
|
@ -1132,6 +1134,7 @@ thread_across_edge (gcond *dummy_cond,
|
||||||
propagate_threaded_block_debug_into (path->last ()->e->dest,
|
propagate_threaded_block_debug_into (path->last ()->e->dest,
|
||||||
e->dest);
|
e->dest);
|
||||||
const_and_copies->pop_to_marker ();
|
const_and_copies->pop_to_marker ();
|
||||||
|
avail_exprs_stack->pop_to_marker ();
|
||||||
BITMAP_FREE (visited);
|
BITMAP_FREE (visited);
|
||||||
register_jump_thread (path);
|
register_jump_thread (path);
|
||||||
return;
|
return;
|
||||||
|
@ -1156,6 +1159,7 @@ thread_across_edge (gcond *dummy_cond,
|
||||||
{
|
{
|
||||||
BITMAP_FREE (visited);
|
BITMAP_FREE (visited);
|
||||||
const_and_copies->pop_to_marker ();
|
const_and_copies->pop_to_marker ();
|
||||||
|
avail_exprs_stack->pop_to_marker ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1182,6 +1186,7 @@ thread_across_edge (gcond *dummy_cond,
|
||||||
if (taken_edge->flags & EDGE_ABNORMAL)
|
if (taken_edge->flags & EDGE_ABNORMAL)
|
||||||
{
|
{
|
||||||
const_and_copies->pop_to_marker ();
|
const_and_copies->pop_to_marker ();
|
||||||
|
avail_exprs_stack->pop_to_marker ();
|
||||||
BITMAP_FREE (visited);
|
BITMAP_FREE (visited);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1196,8 +1201,7 @@ thread_across_edge (gcond *dummy_cond,
|
||||||
/* Push a fresh marker so we can unwind the equivalences created
|
/* Push a fresh marker so we can unwind the equivalences created
|
||||||
for each of E->dest's successors. */
|
for each of E->dest's successors. */
|
||||||
const_and_copies->push_marker ();
|
const_and_copies->push_marker ();
|
||||||
if (avail_exprs_stack)
|
avail_exprs_stack->push_marker ();
|
||||||
avail_exprs_stack->push_marker ();
|
|
||||||
|
|
||||||
/* Avoid threading to any block we have already visited. */
|
/* Avoid threading to any block we have already visited. */
|
||||||
bitmap_clear (visited);
|
bitmap_clear (visited);
|
||||||
|
@ -1240,12 +1244,71 @@ thread_across_edge (gcond *dummy_cond,
|
||||||
delete_jump_thread_path (path);
|
delete_jump_thread_path (path);
|
||||||
|
|
||||||
/* And unwind the equivalence table. */
|
/* And unwind the equivalence table. */
|
||||||
if (avail_exprs_stack)
|
avail_exprs_stack->pop_to_marker ();
|
||||||
avail_exprs_stack->pop_to_marker ();
|
|
||||||
const_and_copies->pop_to_marker ();
|
const_and_copies->pop_to_marker ();
|
||||||
}
|
}
|
||||||
BITMAP_FREE (visited);
|
BITMAP_FREE (visited);
|
||||||
}
|
}
|
||||||
|
|
||||||
const_and_copies->pop_to_marker ();
|
const_and_copies->pop_to_marker ();
|
||||||
|
avail_exprs_stack->pop_to_marker ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Examine the outgoing edges from BB and conditionally
|
||||||
|
try to thread them.
|
||||||
|
|
||||||
|
DUMMY_COND is a shared cond_expr used by condition simplification as scratch,
|
||||||
|
to avoid allocating memory.
|
||||||
|
|
||||||
|
CONST_AND_COPIES is used to undo temporary equivalences created during the
|
||||||
|
walk of E->dest.
|
||||||
|
|
||||||
|
The available expression table is referenced vai AVAIL_EXPRS_STACK.
|
||||||
|
|
||||||
|
SIMPLIFY is a pass-specific function used to simplify statements. */
|
||||||
|
|
||||||
|
void
|
||||||
|
thread_outgoing_edges (basic_block bb, gcond *dummy_cond,
|
||||||
|
class const_and_copies *const_and_copies,
|
||||||
|
class avail_exprs_stack *avail_exprs_stack,
|
||||||
|
tree (*simplify) (gimple *, gimple *,
|
||||||
|
class avail_exprs_stack *,
|
||||||
|
basic_block))
|
||||||
|
{
|
||||||
|
int flags = (EDGE_IGNORE | EDGE_COMPLEX | EDGE_ABNORMAL);
|
||||||
|
gimple *last;
|
||||||
|
|
||||||
|
/* If we have an outgoing edge to a block with multiple incoming and
|
||||||
|
outgoing edges, then we may be able to thread the edge, i.e., we
|
||||||
|
may be able to statically determine which of the outgoing edges
|
||||||
|
will be traversed when the incoming edge from BB is traversed. */
|
||||||
|
if (single_succ_p (bb)
|
||||||
|
&& (single_succ_edge (bb)->flags & flags) == 0
|
||||||
|
&& potentially_threadable_block (single_succ (bb)))
|
||||||
|
{
|
||||||
|
thread_across_edge (dummy_cond, single_succ_edge (bb),
|
||||||
|
const_and_copies, avail_exprs_stack,
|
||||||
|
simplify);
|
||||||
|
}
|
||||||
|
else if ((last = last_stmt (bb))
|
||||||
|
&& gimple_code (last) == GIMPLE_COND
|
||||||
|
&& EDGE_COUNT (bb->succs) == 2
|
||||||
|
&& (EDGE_SUCC (bb, 0)->flags & flags) == 0
|
||||||
|
&& (EDGE_SUCC (bb, 1)->flags & flags) == 0)
|
||||||
|
{
|
||||||
|
edge true_edge, false_edge;
|
||||||
|
|
||||||
|
extract_true_false_edges_from_block (bb, &true_edge, &false_edge);
|
||||||
|
|
||||||
|
/* Only try to thread the edge if it reaches a target block with
|
||||||
|
more than one predecessor and more than one successor. */
|
||||||
|
if (potentially_threadable_block (true_edge->dest))
|
||||||
|
thread_across_edge (dummy_cond, true_edge,
|
||||||
|
const_and_copies, avail_exprs_stack, simplify);
|
||||||
|
|
||||||
|
/* Similarly for the ELSE arm. */
|
||||||
|
if (potentially_threadable_block (false_edge->dest))
|
||||||
|
thread_across_edge (dummy_cond, false_edge,
|
||||||
|
const_and_copies, avail_exprs_stack, simplify);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,10 @@ extern void threadedge_initialize_values (void);
|
||||||
extern void threadedge_finalize_values (void);
|
extern void threadedge_finalize_values (void);
|
||||||
extern bool potentially_threadable_block (basic_block);
|
extern bool potentially_threadable_block (basic_block);
|
||||||
extern void propagate_threaded_block_debug_into (basic_block, basic_block);
|
extern void propagate_threaded_block_debug_into (basic_block, basic_block);
|
||||||
extern void thread_across_edge (gcond *, edge,
|
extern void thread_outgoing_edges (basic_block, gcond *,
|
||||||
const_and_copies *,
|
const_and_copies *,
|
||||||
avail_exprs_stack *,
|
avail_exprs_stack *,
|
||||||
tree (*) (gimple *, gimple *,
|
tree (*) (gimple *, gimple *,
|
||||||
avail_exprs_stack *, basic_block));
|
avail_exprs_stack *, basic_block));
|
||||||
|
|
||||||
#endif /* GCC_TREE_SSA_THREADEDGE_H */
|
#endif /* GCC_TREE_SSA_THREADEDGE_H */
|
||||||
|
|
171
gcc/tree-vrp.c
171
gcc/tree-vrp.c
|
@ -10746,9 +10746,6 @@ vrp_fold_stmt (gimple_stmt_iterator *si)
|
||||||
return simplify_stmt_using_ranges (si);
|
return simplify_stmt_using_ranges (si);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unwindable const/copy equivalences. */
|
|
||||||
const_and_copies *equiv_stack;
|
|
||||||
|
|
||||||
/* Return the LHS of any ASSERT_EXPR where OP appears as the first
|
/* Return the LHS of any ASSERT_EXPR where OP appears as the first
|
||||||
argument to the ASSERT_EXPR and in which the ASSERT_EXPR dominates
|
argument to the ASSERT_EXPR and in which the ASSERT_EXPR dominates
|
||||||
BB. If no such ASSERT_EXPR is found, return OP. */
|
BB. If no such ASSERT_EXPR is found, return OP. */
|
||||||
|
@ -10879,6 +10876,74 @@ simplify_stmt_for_jump_threading (gimple *stmt, gimple *within_stmt,
|
||||||
return NULL_TREE;
|
return NULL_TREE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class vrp_dom_walker : public dom_walker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
vrp_dom_walker (cdi_direction direction,
|
||||||
|
class const_and_copies *const_and_copies,
|
||||||
|
class avail_exprs_stack *avail_exprs_stack)
|
||||||
|
: dom_walker (direction, true),
|
||||||
|
m_const_and_copies (const_and_copies),
|
||||||
|
m_avail_exprs_stack (avail_exprs_stack),
|
||||||
|
m_dummy_cond (NULL) {}
|
||||||
|
|
||||||
|
virtual edge before_dom_children (basic_block);
|
||||||
|
virtual void after_dom_children (basic_block);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class const_and_copies *m_const_and_copies;
|
||||||
|
class avail_exprs_stack *m_avail_exprs_stack;
|
||||||
|
|
||||||
|
gcond *m_dummy_cond;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Called before processing dominator children of BB. We want to look
|
||||||
|
at ASSERT_EXPRs and record information from them in the appropriate
|
||||||
|
tables.
|
||||||
|
|
||||||
|
We could look at other statements here. It's not seen as likely
|
||||||
|
to significantly increase the jump threads we discover. */
|
||||||
|
|
||||||
|
edge
|
||||||
|
vrp_dom_walker::before_dom_children (basic_block bb)
|
||||||
|
{
|
||||||
|
gimple_stmt_iterator gsi;
|
||||||
|
|
||||||
|
for (gsi = gsi_start_nondebug_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
|
||||||
|
{
|
||||||
|
gimple *stmt = gsi_stmt (gsi);
|
||||||
|
if (gimple_assign_single_p (stmt)
|
||||||
|
&& TREE_CODE (gimple_assign_rhs1 (stmt)) == ASSERT_EXPR)
|
||||||
|
{
|
||||||
|
tree lhs = gimple_assign_lhs (stmt);
|
||||||
|
tree rhs1 = gimple_assign_rhs1 (stmt);
|
||||||
|
m_const_and_copies->record_const_or_copy (lhs,
|
||||||
|
TREE_OPERAND (rhs1, 0));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called after processing dominator children of BB. This is where we
|
||||||
|
actually call into the threader. */
|
||||||
|
void
|
||||||
|
vrp_dom_walker::after_dom_children (basic_block bb)
|
||||||
|
{
|
||||||
|
if (!m_dummy_cond)
|
||||||
|
m_dummy_cond = gimple_build_cond (NE_EXPR,
|
||||||
|
integer_zero_node, integer_zero_node,
|
||||||
|
NULL, NULL);
|
||||||
|
|
||||||
|
thread_outgoing_edges (bb, m_dummy_cond, m_const_and_copies,
|
||||||
|
m_avail_exprs_stack,
|
||||||
|
simplify_stmt_for_jump_threading);
|
||||||
|
|
||||||
|
m_avail_exprs_stack->pop_to_marker ();
|
||||||
|
m_const_and_copies->pop_to_marker ();
|
||||||
|
}
|
||||||
|
|
||||||
/* Blocks which have more than one predecessor and more than
|
/* Blocks which have more than one predecessor and more than
|
||||||
one successor present jump threading opportunities, i.e.,
|
one successor present jump threading opportunities, i.e.,
|
||||||
when the block is reached from a specific predecessor, we
|
when the block is reached from a specific predecessor, we
|
||||||
|
@ -10902,8 +10967,6 @@ simplify_stmt_for_jump_threading (gimple *stmt, gimple *within_stmt,
|
||||||
static void
|
static void
|
||||||
identify_jump_threads (void)
|
identify_jump_threads (void)
|
||||||
{
|
{
|
||||||
basic_block bb;
|
|
||||||
gcond *dummy;
|
|
||||||
int i;
|
int i;
|
||||||
edge e;
|
edge e;
|
||||||
|
|
||||||
|
@ -10926,69 +10989,15 @@ identify_jump_threads (void)
|
||||||
|
|
||||||
/* Allocate our unwinder stack to unwind any temporary equivalences
|
/* Allocate our unwinder stack to unwind any temporary equivalences
|
||||||
that might be recorded. */
|
that might be recorded. */
|
||||||
equiv_stack = new const_and_copies ();
|
const_and_copies *equiv_stack = new const_and_copies ();
|
||||||
|
|
||||||
/* To avoid lots of silly node creation, we create a single
|
hash_table<expr_elt_hasher> *avail_exprs
|
||||||
conditional and just modify it in-place when attempting to
|
= new hash_table<expr_elt_hasher> (1024);
|
||||||
thread jumps. */
|
avail_exprs_stack *avail_exprs_stack
|
||||||
dummy = gimple_build_cond (EQ_EXPR,
|
= new class avail_exprs_stack (avail_exprs);
|
||||||
integer_zero_node, integer_zero_node,
|
|
||||||
NULL, NULL);
|
|
||||||
|
|
||||||
/* Walk through all the blocks finding those which present a
|
vrp_dom_walker walker (CDI_DOMINATORS, equiv_stack, avail_exprs_stack);
|
||||||
potential jump threading opportunity. We could set this up
|
walker.walk (cfun->cfg->x_entry_block_ptr);
|
||||||
as a dominator walker and record data during the walk, but
|
|
||||||
I doubt it's worth the effort for the classes of jump
|
|
||||||
threading opportunities we are trying to identify at this
|
|
||||||
point in compilation. */
|
|
||||||
FOR_EACH_BB_FN (bb, cfun)
|
|
||||||
{
|
|
||||||
gimple *last;
|
|
||||||
|
|
||||||
/* If the generic jump threading code does not find this block
|
|
||||||
interesting, then there is nothing to do. */
|
|
||||||
if (! potentially_threadable_block (bb))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
last = last_stmt (bb);
|
|
||||||
|
|
||||||
/* We're basically looking for a switch or any kind of conditional with
|
|
||||||
integral or pointer type arguments. Note the type of the second
|
|
||||||
argument will be the same as the first argument, so no need to
|
|
||||||
check it explicitly.
|
|
||||||
|
|
||||||
We also handle the case where there are no statements in the
|
|
||||||
block. This come up with forwarder blocks that are not
|
|
||||||
optimized away because they lead to a loop header. But we do
|
|
||||||
want to thread through them as we can sometimes thread to the
|
|
||||||
loop exit which is obviously profitable. */
|
|
||||||
if (!last
|
|
||||||
|| gimple_code (last) == GIMPLE_SWITCH
|
|
||||||
|| (gimple_code (last) == GIMPLE_COND
|
|
||||||
&& TREE_CODE (gimple_cond_lhs (last)) == SSA_NAME
|
|
||||||
&& (INTEGRAL_TYPE_P (TREE_TYPE (gimple_cond_lhs (last)))
|
|
||||||
|| POINTER_TYPE_P (TREE_TYPE (gimple_cond_lhs (last))))
|
|
||||||
&& (TREE_CODE (gimple_cond_rhs (last)) == SSA_NAME
|
|
||||||
|| is_gimple_min_invariant (gimple_cond_rhs (last)))))
|
|
||||||
{
|
|
||||||
edge_iterator ei;
|
|
||||||
|
|
||||||
/* We've got a block with multiple predecessors and multiple
|
|
||||||
successors which also ends in a suitable conditional or
|
|
||||||
switch statement. For each predecessor, see if we can thread
|
|
||||||
it to a specific successor. */
|
|
||||||
FOR_EACH_EDGE (e, ei, bb->preds)
|
|
||||||
{
|
|
||||||
/* Do not thread across edges marked to ignoreor abnormal
|
|
||||||
edges in the CFG. */
|
|
||||||
if (e->flags & (EDGE_IGNORE | EDGE_COMPLEX))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
thread_across_edge (dummy, e, equiv_stack, NULL,
|
|
||||||
simplify_stmt_for_jump_threading);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear EDGE_IGNORE. */
|
/* Clear EDGE_IGNORE. */
|
||||||
FOR_EACH_VEC_ELT (to_remove_edges, i, e)
|
FOR_EACH_VEC_ELT (to_remove_edges, i, e)
|
||||||
|
@ -10997,19 +11006,8 @@ identify_jump_threads (void)
|
||||||
/* We do not actually update the CFG or SSA graphs at this point as
|
/* We do not actually update the CFG or SSA graphs at this point as
|
||||||
ASSERT_EXPRs are still in the IL and cfg cleanup code does not yet
|
ASSERT_EXPRs are still in the IL and cfg cleanup code does not yet
|
||||||
handle ASSERT_EXPRs gracefully. */
|
handle ASSERT_EXPRs gracefully. */
|
||||||
}
|
|
||||||
|
|
||||||
/* We identified all the jump threading opportunities earlier, but could
|
|
||||||
not transform the CFG at that time. This routine transforms the
|
|
||||||
CFG and arranges for the dominator tree to be rebuilt if necessary.
|
|
||||||
|
|
||||||
Note the SSA graph update will occur during the normal TODO
|
|
||||||
processing by the pass manager. */
|
|
||||||
static void
|
|
||||||
finalize_jump_threads (void)
|
|
||||||
{
|
|
||||||
thread_through_all_blocks (false);
|
|
||||||
delete equiv_stack;
|
delete equiv_stack;
|
||||||
|
delete avail_exprs_stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free VRP lattice. */
|
/* Free VRP lattice. */
|
||||||
|
@ -11075,10 +11073,6 @@ vrp_finalize (bool warn_array_bounds_p)
|
||||||
|
|
||||||
if (warn_array_bounds && warn_array_bounds_p)
|
if (warn_array_bounds && warn_array_bounds_p)
|
||||||
check_all_array_refs ();
|
check_all_array_refs ();
|
||||||
|
|
||||||
/* We must identify jump threading opportunities before we release
|
|
||||||
the datastructures built by VRP. */
|
|
||||||
identify_jump_threads ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* evrp_dom_walker visits the basic blocks in the dominance order and set
|
/* evrp_dom_walker visits the basic blocks in the dominance order and set
|
||||||
|
@ -11670,6 +11664,11 @@ execute_vrp (bool warn_array_bounds_p)
|
||||||
vrp_initialize ();
|
vrp_initialize ();
|
||||||
ssa_propagate (vrp_visit_stmt, vrp_visit_phi_node);
|
ssa_propagate (vrp_visit_stmt, vrp_visit_phi_node);
|
||||||
vrp_finalize (warn_array_bounds_p);
|
vrp_finalize (warn_array_bounds_p);
|
||||||
|
|
||||||
|
/* We must identify jump threading opportunities before we release
|
||||||
|
the datastructures built by VRP. */
|
||||||
|
identify_jump_threads ();
|
||||||
|
|
||||||
vrp_free_lattice ();
|
vrp_free_lattice ();
|
||||||
|
|
||||||
free_numbers_of_iterations_estimates (cfun);
|
free_numbers_of_iterations_estimates (cfun);
|
||||||
|
@ -11686,7 +11685,13 @@ execute_vrp (bool warn_array_bounds_p)
|
||||||
duplication and CFG manipulation. */
|
duplication and CFG manipulation. */
|
||||||
update_ssa (TODO_update_ssa);
|
update_ssa (TODO_update_ssa);
|
||||||
|
|
||||||
finalize_jump_threads ();
|
/* We identified all the jump threading opportunities earlier, but could
|
||||||
|
not transform the CFG at that time. This routine transforms the
|
||||||
|
CFG and arranges for the dominator tree to be rebuilt if necessary.
|
||||||
|
|
||||||
|
Note the SSA graph update will occur during the normal TODO
|
||||||
|
processing by the pass manager. */
|
||||||
|
thread_through_all_blocks (false);
|
||||||
|
|
||||||
/* Remove dead edges from SWITCH_EXPR optimization. This leaves the
|
/* Remove dead edges from SWITCH_EXPR optimization. This leaves the
|
||||||
CFG in a broken state and requires a cfg_cleanup run. */
|
CFG in a broken state and requires a cfg_cleanup run. */
|
||||||
|
|
Loading…
Reference in New Issue