aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-ssa.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-ssa.c')
-rw-r--r--gcc/tree-ssa.c240
1 files changed, 143 insertions, 97 deletions
diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c
index 9015d19c189..9858b109d03 100644
--- a/gcc/tree-ssa.c
+++ b/gcc/tree-ssa.c
@@ -295,152 +295,198 @@ find_released_ssa_name (tree *tp, int *walk_subtrees, void *data_)
return NULL_TREE;
}
-/* Given a VAR whose definition STMT is to be moved to the iterator
- position TOGSIP in the TOBB basic block, verify whether we're
- moving it across any of the debug statements that use it, and
- adjust them as needed. If TOBB is NULL, then the definition is
- understood as being removed, and TOGSIP is unused. */
+/* Insert a DEBUG BIND stmt before the DEF of VAR if VAR is referenced
+ by other DEBUG stmts, and replace uses of the DEF with the
+ newly-created debug temp. */
+
void
-propagate_var_def_into_debug_stmts (tree var,
- basic_block tobb,
- const gimple_stmt_iterator *togsip)
+insert_debug_temp_for_var_def (gimple_stmt_iterator *gsi, tree var)
{
imm_use_iterator imm_iter;
- gimple stmt;
use_operand_p use_p;
+ gimple stmt;
+ gimple def_stmt = NULL;
+ int usecount = 0;
tree value = NULL;
- bool no_value = false;
if (!MAY_HAVE_DEBUG_STMTS)
return;
- FOR_EACH_IMM_USE_STMT (stmt, imm_iter, var)
+ /* First of all, check whether there are debug stmts that reference
+ this variable and, if there are, decide whether we should use a
+ debug temp. */
+ FOR_EACH_IMM_USE_FAST (use_p, imm_iter, var)
{
- basic_block bb;
- gimple_stmt_iterator si;
+ stmt = USE_STMT (use_p);
- if (!is_gimple_debug (stmt))
+ if (!gimple_debug_bind_p (stmt))
continue;
- if (tobb)
+ if (usecount++)
+ break;
+
+ if (gimple_debug_bind_get_value (stmt) != var)
{
- bb = gimple_bb (stmt);
+ /* Count this as an additional use, so as to make sure we
+ use a temp unless VAR's definition has a SINGLE_RHS that
+ can be shared. */
+ usecount++;
+ break;
+ }
+ }
- if (bb != tobb)
- {
- gcc_assert (dom_info_available_p (CDI_DOMINATORS));
- if (dominated_by_p (CDI_DOMINATORS, bb, tobb))
- continue;
- }
- else
- {
- si = *togsip;
+ if (!usecount)
+ return;
- if (gsi_end_p (si))
- continue;
+ if (gsi)
+ def_stmt = gsi_stmt (*gsi);
+ else
+ def_stmt = SSA_NAME_DEF_STMT (var);
- do
- {
- gsi_prev (&si);
- if (gsi_end_p (si))
- break;
- }
- while (gsi_stmt (si) != stmt);
+ /* If we didn't get an insertion point, and the stmt has already
+ been removed, we won't be able to insert the debug bind stmt, so
+ we'll have to drop debug information. */
+ if (is_gimple_assign (def_stmt))
+ {
+ bool no_value = false;
- if (gsi_end_p (si))
- continue;
- }
+ if (!dom_info_available_p (CDI_DOMINATORS))
+ {
+ struct walk_stmt_info wi;
+
+ memset (&wi, 0, sizeof (wi));
+
+ /* When removing blocks without following reverse dominance
+ order, we may sometimes encounter SSA_NAMEs that have
+ already been released, referenced in other SSA_DEFs that
+ we're about to release. Consider:
+
+ <bb X>:
+ v_1 = foo;
+
+ <bb Y>:
+ w_2 = v_1 + bar;
+ # DEBUG w => w_2
+
+ If we deleted BB X first, propagating the value of w_2
+ won't do us any good. It's too late to recover their
+ original definition of v_1: when it was deleted, it was
+ only referenced in other DEFs, it couldn't possibly know
+ it should have been retained, and propagating every
+ single DEF just in case it might have to be propagated
+ into a DEBUG STMT would probably be too wasteful.
+
+ When dominator information is not readily available, we
+ check for and accept some loss of debug information. But
+ if it is available, there's no excuse for us to remove
+ blocks in the wrong order, so we don't even check for
+ dead SSA NAMEs. SSA verification shall catch any
+ errors. */
+ if ((!gsi && !gimple_bb (def_stmt))
+ || !walk_gimple_op (def_stmt, find_released_ssa_name,
+ &wi))
+ no_value = true;
}
- /* Here we compute (lazily) the value assigned to VAR, but we
- remember if we tried before and failed, so that we don't try
- again. */
- if (!value && !no_value)
+ if (!no_value)
+ value = gimple_assign_rhs_to_tree (def_stmt);
+ }
+
+ if (value)
+ {
+ /* If there's a single use of VAR, and VAR is the entire debug
+ expression (usecount would have been incremented again
+ otherwise), and the definition involves only constants and
+ SSA names, then we can propagate VALUE into this single use,
+ avoiding the temp.
+
+ We can also avoid using a temp if VALUE can be shared and
+ propagated into all uses, without generating expressions that
+ wouldn't be valid gimple RHSs.
+
+ Other cases that would require unsharing or non-gimple RHSs
+ are deferred to a debug temp, although we could avoid temps
+ at the expense of duplication of expressions. */
+
+ if (CONSTANT_CLASS_P (value)
+ || (usecount == 1
+ && (!gimple_assign_single_p (def_stmt)
+ || is_gimple_min_invariant (value)))
+ || is_gimple_reg (value))
+ value = unshare_expr (value);
+ else
{
- gimple def_stmt = SSA_NAME_DEF_STMT (var);
+ gimple def_temp;
+ tree vexpr = make_node (DEBUG_EXPR_DECL);
- if (is_gimple_assign (def_stmt))
- {
- if (!dom_info_available_p (CDI_DOMINATORS))
- {
- struct walk_stmt_info wi;
-
- memset (&wi, 0, sizeof (wi));
-
- /* When removing blocks without following reverse
- dominance order, we may sometimes encounter SSA_NAMEs
- that have already been released, referenced in other
- SSA_DEFs that we're about to release. Consider:
-
- <bb X>:
- v_1 = foo;
-
- <bb Y>:
- w_2 = v_1 + bar;
- # DEBUG w => w_2
-
- If we deleted BB X first, propagating the value of
- w_2 won't do us any good. It's too late to recover
- their original definition of v_1: when it was
- deleted, it was only referenced in other DEFs, it
- couldn't possibly know it should have been retained,
- and propagating every single DEF just in case it
- might have to be propagated into a DEBUG STMT would
- probably be too wasteful.
-
- When dominator information is not readily
- available, we check for and accept some loss of
- debug information. But if it is available,
- there's no excuse for us to remove blocks in the
- wrong order, so we don't even check for dead SSA
- NAMEs. SSA verification shall catch any
- errors. */
- if (!walk_gimple_op (def_stmt, find_released_ssa_name, &wi))
- no_value = true;
- }
+ def_temp = gimple_build_debug_bind (vexpr,
+ unshare_expr (value),
+ def_stmt);
+
+ DECL_ARTIFICIAL (vexpr) = 1;
+ TREE_TYPE (vexpr) = TREE_TYPE (value);
+ if (DECL_P (value))
+ DECL_MODE (vexpr) = DECL_MODE (value);
+ else
+ DECL_MODE (vexpr) = TYPE_MODE (TREE_TYPE (value));
- if (!no_value)
- value = gimple_assign_rhs_to_tree (def_stmt);
+ if (gsi)
+ gsi_insert_before (gsi, def_temp, GSI_SAME_STMT);
+ else
+ {
+ gimple_stmt_iterator ngsi = gsi_for_stmt (def_stmt);
+ gsi_insert_before (&ngsi, def_temp, GSI_SAME_STMT);
}
- if (!value)
- no_value = true;
+ value = vexpr;
}
+ }
- if (no_value)
- gimple_debug_bind_reset_value (stmt);
- else
+ FOR_EACH_IMM_USE_STMT (stmt, imm_iter, var)
+ {
+ if (!gimple_debug_bind_p (stmt))
+ continue;
+
+ if (value)
FOR_EACH_IMM_USE_ON_STMT (use_p, imm_iter)
- SET_USE (use_p, unshare_expr (value));
+ /* unshare_expr is not needed here. vexpr is either a
+ SINGLE_RHS, that can be safely shared, some other RHS
+ that was unshared when we found it had a single debug
+ use, or a DEBUG_EXPR_DECL, that can be safely
+ shared. */
+ SET_USE (use_p, value);
+ else
+ gimple_debug_bind_reset_value (stmt);
update_stmt (stmt);
}
}
-/* Given a STMT to be moved to the iterator position TOBSIP in the
- TOBB basic block, verify whether we're moving it across any of the
- debug statements that use it. If TOBB is NULL, then the definition
- is understood as being removed, and TOBSIP is unused. */
+/* Insert a DEBUG BIND stmt before STMT for each DEF referenced by
+ other DEBUG stmts, and replace uses of the DEF with the
+ newly-created debug temp. */
void
-propagate_defs_into_debug_stmts (gimple def, basic_block tobb,
- const gimple_stmt_iterator *togsip)
+insert_debug_temps_for_defs (gimple_stmt_iterator *gsi)
{
+ gimple stmt;
ssa_op_iter op_iter;
def_operand_p def_p;
if (!MAY_HAVE_DEBUG_STMTS)
return;
- FOR_EACH_SSA_DEF_OPERAND (def_p, def, op_iter, SSA_OP_DEF)
+ stmt = gsi_stmt (*gsi);
+
+ FOR_EACH_SSA_DEF_OPERAND (def_p, stmt, op_iter, SSA_OP_DEF)
{
tree var = DEF_FROM_PTR (def_p);
if (TREE_CODE (var) != SSA_NAME)
continue;
- propagate_var_def_into_debug_stmts (var, tobb, togsip);
+ insert_debug_temp_for_var_def (gsi, var);
}
}