public:
dom_jump_threader_simplifier (vr_values *v,
avail_exprs_stack *avails)
- : jump_threader_simplifier (v, avails) {}
+ : jump_threader_simplifier (v), m_avail_exprs_stack (avails) { }
private:
- tree simplify (gimple *, gimple *, basic_block);
+ tree simplify (gimple *, gimple *, basic_block, jt_state *) override;
+ avail_exprs_stack *m_avail_exprs_stack;
};
tree
dom_jump_threader_simplifier::simplify (gimple *stmt,
gimple *within_stmt,
- basic_block bb)
+ basic_block bb,
+ jt_state *state)
{
/* First see if the conditional is in the hash table. */
tree cached_lhs = m_avail_exprs_stack->lookup_avail_expr (stmt,
if (cached_lhs)
return cached_lhs;
- return jump_threader_simplifier::simplify (stmt, within_stmt, bb);
+ return jump_threader_simplifier::simplify (stmt, within_stmt, bb, state);
}
class dom_opt_dom_walker : public dom_walker
public:
dom_opt_dom_walker (cdi_direction direction,
jump_threader *threader,
+ jt_state *state,
evrp_range_analyzer *analyzer,
const_and_copies *const_and_copies,
avail_exprs_stack *avail_exprs_stack)
: dom_walker (direction, REACHABLE_BLOCKS)
{
m_evrp_range_analyzer = analyzer;
+ m_state = state;
m_dummy_cond = gimple_build_cond (NE_EXPR, integer_zero_node,
integer_zero_node, NULL, NULL);
m_const_and_copies = const_and_copies;
jump_threader *m_threader;
evrp_range_analyzer *m_evrp_range_analyzer;
+ jt_state *m_state;
};
/* Jump threading, redundancy elimination and const/copy propagation.
/* Recursively walk the dominator tree optimizing statements. */
evrp_range_analyzer analyzer (true);
dom_jump_threader_simplifier simplifier (&analyzer, avail_exprs_stack);
- jump_threader threader (const_and_copies, avail_exprs_stack,
- &simplifier, &analyzer);
+ jt_state state (const_and_copies, avail_exprs_stack, &analyzer);
+ jump_threader threader (&simplifier, &state);
dom_opt_dom_walker walker (CDI_DOMINATORS,
&threader,
+ &state,
&analyzer,
const_and_copies,
avail_exprs_stack);
continue;
}
- /* Compute range information and optimize the stmt. */
- m_evrp_range_analyzer->record_ranges_from_stmt (gsi_stmt (gsi), false);
+ m_state->record_ranges_from_stmt (gsi_stmt (gsi), false);
bool removed_p = false;
taken_edge = this->optimize_stmt (bb, &gsi, &removed_p);
if (!removed_p)
ssa_name_values[SSA_NAME_VERSION (name)] = value;
}
-jump_threader::jump_threader (const_and_copies *copies,
- avail_exprs_stack *avails,
- jump_threader_simplifier *simplifier,
- evrp_range_analyzer *analyzer)
+jump_threader::jump_threader (jump_threader_simplifier *simplifier,
+ jt_state *state)
{
/* Initialize the per SSA_NAME value-handles array. */
gcc_assert (!ssa_name_values.exists ());
dummy_cond = gimple_build_cond (NE_EXPR, integer_zero_node,
integer_zero_node, NULL, NULL);
- m_const_and_copies = copies;
- m_avail_exprs_stack = avails;
m_registry = new jump_thread_path_registry ();
m_simplifier = simplifier;
- m_evrp_range_analyzer = analyzer;
+ m_state = state;
}
jump_threader::~jump_threader (void)
if (!virtual_operand_p (dst))
stmt_count++;
- m_const_and_copies->record_const_or_copy (dst, src);
-
- /* Also update the value range associated with DST, using
- the range from SRC.
-
- Note that even if SRC is a constant we need to set a suitable
- output range so that VR_UNDEFINED ranges do not leak through. */
- if (m_evrp_range_analyzer)
- {
- /* Get an empty new VR we can pass to update_value_range and save
- away in the VR stack. */
- value_range_equiv *new_vr
- = m_evrp_range_analyzer->allocate_value_range_equiv ();
- new (new_vr) value_range_equiv ();
-
- /* There are three cases to consider:
-
- First if SRC is an SSA_NAME, then we can copy the value
- range from SRC into NEW_VR.
-
- Second if SRC is an INTEGER_CST, then we can just wet
- NEW_VR to a singleton range.
-
- Otherwise set NEW_VR to varying. This may be overly
- conservative. */
- if (TREE_CODE (src) == SSA_NAME)
- new_vr->deep_copy (m_evrp_range_analyzer->get_value_range (src));
- else if (TREE_CODE (src) == INTEGER_CST)
- new_vr->set (src);
- else
- new_vr->set_varying (TREE_TYPE (src));
-
- /* This is a temporary range for DST, so push it. */
- m_evrp_range_analyzer->push_value_range (dst, new_vr);
- }
+ m_state->register_equiv (dst, src, /*update_range=*/true);
}
return true;
}
return NULL;
}
- /* These are temporary ranges, do nto reflect them back into
- the global range data. */
- if (m_evrp_range_analyzer)
- m_evrp_range_analyzer->record_ranges_from_stmt (stmt, true);
+ m_state->record_ranges_from_stmt (stmt, true);
/* If this is not a statement that sets an SSA_NAME to a new
value, then do not try to simplify this statement as it will
SET_USE (use_p, tmp);
}
- cached_lhs = m_simplifier->simplify (stmt, stmt, e->src);
+ cached_lhs = m_simplifier->simplify (stmt, stmt, e->src, m_state);
/* Restore the statement's original uses/defs. */
i = 0;
if (cached_lhs
&& (TREE_CODE (cached_lhs) == SSA_NAME
|| is_gimple_min_invariant (cached_lhs)))
- m_const_and_copies->record_const_or_copy (gimple_get_lhs (stmt),
- cached_lhs);
+ m_state->register_equiv (gimple_get_lhs (stmt), cached_lhs);
}
return stmt;
}
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 = m_simplifier->simplify (dummy_switch, stmt, e->src);
+ cached_lhs = m_simplifier->simplify (dummy_switch, stmt, e->src,
+ m_state);
ggc_free (dummy_switch);
}
else
- cached_lhs = m_simplifier->simplify (stmt, stmt, e->src);
+ cached_lhs = m_simplifier->simplify (stmt, stmt, e->src, m_state);
}
/* We couldn't find an invariant. But, callers of this
then use the pass specific callback to simplify the condition. */
if (!res
|| !is_gimple_min_invariant (res))
- res = m_simplifier->simplify (dummy_cond, stmt, e->src);
+ res = m_simplifier->simplify (dummy_cond, stmt, e->src, m_state);
return res;
}
If it is threadable, add it to PATH and VISITED and recurse, ultimately
returning TRUE from the toplevel call. Otherwise do nothing and
- return false.
-
- The available expression table is referenced via AVAIL_EXPRS_STACK. */
+ return false. */
bool
jump_threader::thread_around_empty_blocks (vec<jump_thread_edge *> *path,
limited in that case to avoid short-circuiting the loop
incorrectly.
- STACK is used to undo temporary equivalences created during the walk of
- E->dest.
-
- Our caller is responsible for restoring the state of the expression
- and const_and_copies stacks.
-
Positive return value is success. Zero return value is failure, but
the block can still be duplicated as a joiner in a jump thread path,
negative indicates the block should not be duplicated and thus is not
jump_threader::thread_through_normal_block (vec<jump_thread_edge *> *path,
edge e, bitmap visited)
{
- /* We want to record any equivalences created by traversing E. */
- record_temporary_equivalences (e, m_const_and_copies, m_avail_exprs_stack);
+ m_state->register_equivs_on_edge (e);
/* PHIs create temporary equivalences.
Note that if we found a PHI that made the block non-threadable, then
{
bitmap visited = BITMAP_ALLOC (NULL);
- m_const_and_copies->push_marker ();
- m_avail_exprs_stack->push_marker ();
- if (m_evrp_range_analyzer)
- m_evrp_range_analyzer->push_marker ();
+ m_state->push (e);
stmt_count = 0;
{
propagate_threaded_block_debug_into (path->last ()->e->dest,
e->dest);
- m_const_and_copies->pop_to_marker ();
- m_avail_exprs_stack->pop_to_marker ();
- if (m_evrp_range_analyzer)
- m_evrp_range_analyzer->pop_to_marker ();
+ m_state->pop ();
BITMAP_FREE (visited);
m_registry->register_jump_thread (path);
return;
if (threaded < 0)
{
BITMAP_FREE (visited);
- m_const_and_copies->pop_to_marker ();
- m_avail_exprs_stack->pop_to_marker ();
- if (m_evrp_range_analyzer)
- m_evrp_range_analyzer->pop_to_marker ();
+ m_state->pop ();
return;
}
}
FOR_EACH_EDGE (taken_edge, ei, e->dest->succs)
if (taken_edge->flags & EDGE_COMPLEX)
{
- m_const_and_copies->pop_to_marker ();
- m_avail_exprs_stack->pop_to_marker ();
- if (m_evrp_range_analyzer)
- m_evrp_range_analyzer->pop_to_marker ();
+ m_state->pop ();
BITMAP_FREE (visited);
return;
}
|| (taken_edge->flags & EDGE_DFS_BACK) != 0)
continue;
- /* Push a fresh marker so we can unwind the equivalences created
- for each of E->dest's successors. */
- m_const_and_copies->push_marker ();
- m_avail_exprs_stack->push_marker ();
- if (m_evrp_range_analyzer)
- m_evrp_range_analyzer->push_marker ();
+ m_state->push (taken_edge);
/* Avoid threading to any block we have already visited. */
bitmap_clear (visited);
else
path->release ();
- /* And unwind the equivalence table. */
- if (m_evrp_range_analyzer)
- m_evrp_range_analyzer->pop_to_marker ();
- m_avail_exprs_stack->pop_to_marker ();
- m_const_and_copies->pop_to_marker ();
+ m_state->pop ();
}
BITMAP_FREE (visited);
}
- if (m_evrp_range_analyzer)
- m_evrp_range_analyzer->pop_to_marker ();
- m_const_and_copies->pop_to_marker ();
- m_avail_exprs_stack->pop_to_marker ();
+ m_state->pop ();
}
/* Examine the outgoing edges from BB and conditionally
}
}
+jt_state::jt_state (const_and_copies *copies,
+ avail_exprs_stack *exprs,
+ evrp_range_analyzer *evrp)
+{
+ m_copies = copies;
+ m_exprs = exprs;
+ m_evrp = evrp;
+}
+
+// Record that E is being crossed.
+
+void
+jt_state::push (edge)
+{
+ m_copies->push_marker ();
+ m_exprs->push_marker ();
+ if (m_evrp)
+ m_evrp->push_marker ();
+}
+
+// Pop to the last pushed state.
+
+void
+jt_state::pop ()
+{
+ m_copies->pop_to_marker ();
+ m_exprs->pop_to_marker ();
+ if (m_evrp)
+ m_evrp->pop_to_marker ();
+}
+
+// Record an equivalence from DST to SRC. If UPDATE_RANGE is TRUE,
+// update the value range associated with DST.
+
+void
+jt_state::register_equiv (tree dst, tree src, bool update_range)
+{
+ m_copies->record_const_or_copy (dst, src);
+
+ /* If requested, update the value range associated with DST, using
+ the range from SRC. */
+ if (m_evrp && update_range)
+ {
+ /* Get new VR we can pass to push_value_range. */
+ value_range_equiv *new_vr = m_evrp->allocate_value_range_equiv ();
+ new (new_vr) value_range_equiv ();
+
+ /* There are three cases to consider:
+
+ First if SRC is an SSA_NAME, then we can copy the value range
+ from SRC into NEW_VR.
+
+ Second if SRC is an INTEGER_CST, then we can just set NEW_VR
+ to a singleton range. Note that even if SRC is a constant we
+ need to set a suitable output range so that VR_UNDEFINED
+ ranges do not leak through.
+
+ Otherwise set NEW_VR to varying. This may be overly
+ conservative. */
+ if (TREE_CODE (src) == SSA_NAME)
+ new_vr->deep_copy (m_evrp->get_value_range (src));
+ else if (TREE_CODE (src) == INTEGER_CST)
+ new_vr->set (src);
+ else
+ new_vr->set_varying (TREE_TYPE (src));
+
+ /* This is a temporary range for DST, so push it. */
+ m_evrp->push_value_range (dst, new_vr);
+ }
+}
+
+// Record any ranges calculated in STMT. If TEMPORARY is TRUE, then
+// this is a temporary equivalence and should be recorded into the
+// unwind table, instead of the global table.
+
+void
+jt_state::record_ranges_from_stmt (gimple *stmt, bool temporary)
+{
+ if (m_evrp)
+ m_evrp->record_ranges_from_stmt (stmt, temporary);
+}
+
+// Record any equivalences created by traversing E.
+
+void
+jt_state::register_equivs_on_edge (edge e)
+{
+ record_temporary_equivalences (e, m_copies, m_exprs);
+}
+
+jump_threader_simplifier::jump_threader_simplifier (vr_values *v)
+{
+ m_vr_values = v;
+}
+
+// Return the singleton that resolves STMT, if it is possible to
+// simplify it.
+//
+// STMT may be a dummy statement, not necessarily in the CFG, in which
+// case WITHIN_STMT can be used to determine the position in the CFG
+// where STMT should be evaluated as being in.
+
tree
jump_threader_simplifier::simplify (gimple *stmt,
gimple *within_stmt,
- basic_block)
+ basic_block,
+ jt_state *)
{
if (gcond *cond_stmt = dyn_cast <gcond *> (stmt))
{
#ifndef GCC_TREE_SSA_THREADEDGE_H
#define GCC_TREE_SSA_THREADEDGE_H
+// Class used to maintain path state in the jump threader and pass it
+// to the jump threader simplifier.
+
+class jt_state
+{
+public:
+ jt_state (class const_and_copies *,
+ class avail_exprs_stack *,
+ class evrp_range_analyzer *);
+ void push (edge);
+ void pop ();
+ void register_equiv (tree dest, tree src, bool update_range = false);
+ void register_equivs_on_edge (edge e);
+ void record_ranges_from_stmt (gimple *stmt, bool temporary);
+
+private:
+ const_and_copies *m_copies;
+ avail_exprs_stack *m_exprs;
+ evrp_range_analyzer *m_evrp;
+};
+
// This is the high level threader. The entry point is
// thread_outgoing_edges(), which calculates and registers paths to be
// threaded. When all candidates have been registered,
class jump_threader
{
public:
- jump_threader (class const_and_copies *,
- avail_exprs_stack *,
- class jump_threader_simplifier *,
- class evrp_range_analyzer * = NULL);
+ jump_threader (class jump_threader_simplifier *, class jt_state *);
~jump_threader ();
void thread_outgoing_edges (basic_block);
void remove_jump_threads_including (edge_def *);
// Dummy condition to avoid creating lots of throw away statements.
gcond *dummy_cond;
- const_and_copies *m_const_and_copies;
- avail_exprs_stack *m_avail_exprs_stack;
class jump_thread_path_registry *m_registry;
jump_threader_simplifier *m_simplifier;
- evrp_range_analyzer *m_evrp_range_analyzer;
+ jt_state *m_state;
};
// Statement simplifier callback for the jump threader.
class jump_threader_simplifier
{
public:
- jump_threader_simplifier (class vr_values *v,
- avail_exprs_stack *avails)
- : m_vr_values (v),
- m_avail_exprs_stack (avails)
- { }
+ jump_threader_simplifier (class vr_values *v);
virtual ~jump_threader_simplifier () { }
- virtual tree simplify (gimple *, gimple *, basic_block);
+ virtual tree simplify (gimple *, gimple *, basic_block, jt_state *);
protected:
vr_values *m_vr_values;
- avail_exprs_stack *m_avail_exprs_stack;
};
extern void propagate_threaded_block_debug_into (basic_block, basic_block);