@@ -40,6 +40,8 @@ along with GCC; see the file COPYING3. If not see
#include "builtins.h"
#include "tree-ssa.h"
#include "tree-inline.h"
+#include "tree-phinodes.h"
+#include "cfgexpand.h"
/* Actual prefixes of different newly synthetized parameters. Keep in sync
@@ -979,7 +981,8 @@ phi_arg_will_live_p (gphi *phi, bitmap blocks_to_copy, tree arg)
any replacement or splitting. */
void
-ipa_param_body_adjustments::mark_dead_statements (tree dead_param)
+ipa_param_body_adjustments::mark_dead_statements (tree dead_param,
+ vec<tree> *debugstack)
{
if (!is_gimple_reg (dead_param))
return;
@@ -988,6 +991,7 @@ ipa_param_body_adjustments::mark_dead_statements (tree dead_param)
return;
auto_vec<tree, 4> stack;
+ hash_set<tree> used_in_debug;
m_dead_ssas.add (parm_ddef);
stack.safe_push (parm_ddef);
while (!stack.is_empty ())
@@ -1010,6 +1014,11 @@ ipa_param_body_adjustments::mark_dead_statements (tree dead_param)
{
m_dead_stmts.add (stmt);
gcc_assert (gimple_debug_bind_p (stmt));
+ if (!used_in_debug.contains (t))
+ {
+ used_in_debug.add (t);
+ debugstack->safe_push (t);
+ }
}
else if (gimple_code (stmt) == GIMPLE_PHI)
{
@@ -1044,6 +1053,155 @@ ipa_param_body_adjustments::mark_dead_statements (tree dead_param)
gcc_unreachable ();
}
}
+
+ if (!MAY_HAVE_DEBUG_STMTS)
+ {
+ gcc_assert (debugstack->is_empty ());
+ return;
+ }
+
+ tree dp_ddecl = make_node (DEBUG_EXPR_DECL);
+ DECL_ARTIFICIAL (dp_ddecl) = 1;
+ TREE_TYPE (dp_ddecl) = TREE_TYPE (dead_param);
+ SET_DECL_MODE (dp_ddecl, DECL_MODE (dead_param));
+ m_dead_ssa_debug_equiv.put (parm_ddef, dp_ddecl);
+}
+
+/* Callback to walk_tree. If REMAP is an SSA_NAME that is present in hash_map
+ passed in DATA, replace it with unshared version of what it was mapped
+ to. */
+
+static tree
+replace_with_mapped_expr (tree *remap, int *walk_subtrees, void *data)
+{
+ if (TYPE_P (*remap))
+ {
+ *walk_subtrees = 0;
+ return 0;
+ }
+ if (TREE_CODE (*remap) != SSA_NAME)
+ return 0;
+
+ *walk_subtrees = 0;
+
+ hash_map<tree, tree> *equivs = (hash_map<tree, tree> *) data;
+ if (tree *p = equivs->get (*remap))
+ *remap = unshare_expr (*p);
+ return 0;
+}
+
+/* Replace all occurances of SSAs in m_dead_ssa_debug_equiv in t with what they
+ are mapped to. */
+
+void
+ipa_param_body_adjustments::remap_with_debug_expressions (tree *t)
+{
+ /* If *t is an SSA_NAME which should have its debug statements reset, it is
+ mapped to NULL in the hash_map. We need to handle that case separately or
+ otherwise the walker would segfault. No expression that is more
+ complicated than that can have its operands mapped to NULL. */
+ if (TREE_CODE (*t) == SSA_NAME)
+ {
+ if (tree *p = m_dead_ssa_debug_equiv.get (*t))
+ *t = *p;
+ }
+ else
+ walk_tree (t, replace_with_mapped_expr, &m_dead_ssa_debug_equiv, NULL);
+}
+
+/* For an SSA_NAME DEAD_SSA which is about to be DCEd because it is based on a
+ useless parameter, prepare an expression that should represent it in
+ debug_binds in the cloned function and add a mapping from DEAD_SSA to
+ m_dead_ssa_debug_equiv. That mapping is to NULL when the associated
+ debug_statement has to be reset instead. In such case return false,
+ ottherwise return true. If DEAD_SSA comes from a basic block which is not
+ about to be copied, ignore it and return true. */
+
+bool
+ipa_param_body_adjustments::prepare_debug_expressions (tree dead_ssa)
+{
+ gcc_checking_assert (m_dead_ssas.contains (dead_ssa));
+ if (tree *d = m_dead_ssa_debug_equiv.get (dead_ssa))
+ return (*d != NULL_TREE);
+
+ gcc_assert (!SSA_NAME_IS_DEFAULT_DEF (dead_ssa));
+ gimple *def = SSA_NAME_DEF_STMT (dead_ssa);
+ if (m_id->blocks_to_copy
+ && !bitmap_bit_p (m_id->blocks_to_copy, gimple_bb (def)->index))
+ return true;
+
+ if (gimple_code (def) == GIMPLE_PHI)
+ {
+ /* In theory, we could ignore all SSAs coming from BBs not in
+ m_id->blocks_to_copy but at the time of the writing this code that
+ should never really be the case because only fnsplit uses that bitmap,
+ so don't bother. */
+ tree value = degenerate_phi_result (as_a <gphi *> (def));
+ if (!value
+ || (m_dead_ssas.contains (value)
+ && !prepare_debug_expressions (value)))
+ {
+ m_dead_ssa_debug_equiv.put (dead_ssa, NULL_TREE);
+ return false;
+ }
+
+ /* PHI operand can be either an invariant or an SSA_NAME, but we are
+ looking at a degenarete phi node having value from a removed
+ parameter, so it has to be the latter. */
+ gcc_assert (TREE_CODE (value) == SSA_NAME);
+
+ tree *d = m_dead_ssa_debug_equiv.get (value);
+ m_dead_ssa_debug_equiv.put (dead_ssa, *d);
+ return true;
+ }
+
+ bool lost = false;
+ use_operand_p use_p;
+ ssa_op_iter oi;
+ FOR_EACH_PHI_OR_STMT_USE (use_p, def, oi, SSA_OP_USE)
+ {
+ tree use = USE_FROM_PTR (use_p);
+ if (m_dead_ssas.contains (use)
+ && !prepare_debug_expressions (use))
+ {
+ lost = true;
+ break;
+ }
+ }
+
+ if (lost)
+ {
+ m_dead_ssa_debug_equiv.put (dead_ssa, NULL_TREE);
+ return false;
+ }
+
+ if (is_gimple_assign (def))
+ {
+ gcc_checking_assert (!gimple_clobber_p (def)
+ && gimple_assign_lhs (def) == dead_ssa);
+
+ if (gimple_assign_copy_p (def)
+ && TREE_CODE (gimple_assign_rhs1 (def)) == SSA_NAME)
+ {
+ tree *d = m_dead_ssa_debug_equiv.get (gimple_assign_rhs1 (def));
+ m_dead_ssa_debug_equiv.put (dead_ssa, *d);
+ return (*d != NULL_TREE);
+ }
+
+ tree val = gimple_assign_rhs_to_tree (def);
+ SET_EXPR_LOCATION (val, UNKNOWN_LOCATION);
+ remap_with_debug_expressions (&val);
+
+ tree vexpr = make_node (DEBUG_EXPR_DECL);
+ DECL_ARTIFICIAL (vexpr) = 1;
+ TREE_TYPE (vexpr) = TREE_TYPE (val);
+ SET_DECL_MODE (vexpr, TYPE_MODE (TREE_TYPE (val)));
+ m_dead_stmt_debug_equiv.put (def, val);
+ m_dead_ssa_debug_equiv.put (dead_ssa, vexpr);
+ return true;
+ }
+ else
+ gcc_unreachable ();
}
/* Common initialization performed by all ipa_param_body_adjustments
@@ -1161,6 +1319,30 @@ ipa_param_body_adjustments::common_initialization (tree old_fndecl,
gcc_unreachable ();
}
+ if (tree_map)
+ {
+ /* Do not treat parameters which were replaced with a constant as
+ completely vanished. */
+ auto_vec <int, 16> index_mapping;
+ bool need_remap = false;
+
+ if (m_id && m_id->src_node->clone.param_adjustments)
+ {
+ ipa_param_adjustments *prev_adjustments
+ = m_id->src_node->clone.param_adjustments;
+ prev_adjustments->get_updated_indices (&index_mapping);
+ need_remap = true;
+ }
+
+ for (unsigned i = 0; i < tree_map->length (); i++)
+ {
+ int parm_num = (*tree_map)[i]->parm_num;
+ gcc_assert (parm_num >= 0);
+ if (need_remap)
+ parm_num = index_mapping[parm_num];
+ kept[parm_num] = true;
+ }
+ }
/* As part of body modifications, we will also have to replace remaining uses
of remaining uses of removed PARM_DECLs (which do not however use the
@@ -1173,70 +1355,43 @@ ipa_param_body_adjustments::common_initialization (tree old_fndecl,
replace_removed_params_ssa_names or perform_cfun_body_modifications when
you construct with ID not equal to NULL. */
+ auto_vec<tree, 8> ssas_to_process_debug;
unsigned op_len = m_oparms.length ();
for (unsigned i = 0; i < op_len; i++)
if (!kept[i])
{
if (m_id)
{
- if (!m_id->decl_map->get (m_oparms[i]))
- {
- /* TODO: Perhaps at least aggregate-type params could re-use
- their isra_dummy_decl here? */
- tree var = copy_decl_to_var (m_oparms[i], m_id);
- insert_decl_map (m_id, m_oparms[i], var);
- /* Declare this new variable. */
- DECL_CHAIN (var) = *vars;
- *vars = var;
-
- /* If this is not a split but a real removal, init hash sets
- that will guide what not to copy to the new body. */
- if (!isra_dummy_decls[i])
- mark_dead_statements (m_oparms[i]);
- }
+ gcc_assert (!m_id->decl_map->get (m_oparms[i]));
+ /* TODO: Perhaps at least aggregate-type params could re-use
+ their isra_dummy_decl here? */
+ tree var = copy_decl_to_var (m_oparms[i], m_id);
+ insert_decl_map (m_id, m_oparms[i], var);
+ /* Declare this new variable. */
+ DECL_CHAIN (var) = *vars;
+ *vars = var;
+
+ /* If this is not a split but a real removal, init hash sets
+ that will guide what not to copy to the new body. */
+ if (!isra_dummy_decls[i])
+ mark_dead_statements (m_oparms[i], &ssas_to_process_debug);
+ if (MAY_HAVE_DEBUG_STMTS
+ && is_gimple_reg (m_oparms[i]))
+ m_reset_debug_decls.safe_push (m_oparms[i]);
}
else
{
m_removed_decls.safe_push (m_oparms[i]);
m_removed_map.put (m_oparms[i], m_removed_decls.length () - 1);
+ if (MAY_HAVE_DEBUG_STMTS
+ && !kept[i]
+ && is_gimple_reg (m_oparms[i]))
+ m_reset_debug_decls.safe_push (m_oparms[i]);
}
}
- if (!MAY_HAVE_DEBUG_STMTS)
- return;
-
- /* Finally, when generating debug info, we fill vector m_reset_debug_decls
- with removed parameters declarations. We do this in order to re-map their
- debug bind statements and create debug decls for them. */
-
- if (tree_map)
- {
- /* Do not output debuginfo for parameter declarations as if they vanished
- when they were in fact replaced by a constant. */
- auto_vec <int, 16> index_mapping;
- bool need_remap = false;
-
- if (m_id && m_id->src_node->clone.param_adjustments)
- {
- ipa_param_adjustments *prev_adjustments
- = m_id->src_node->clone.param_adjustments;
- prev_adjustments->get_updated_indices (&index_mapping);
- need_remap = true;
- }
-
- for (unsigned i = 0; i < tree_map->length (); i++)
- {
- int parm_num = (*tree_map)[i]->parm_num;
- gcc_assert (parm_num >= 0);
- if (need_remap)
- parm_num = index_mapping[parm_num];
- kept[parm_num] = true;
- }
- }
-
- for (unsigned i = 0; i < op_len; i++)
- if (!kept[i] && is_gimple_reg (m_oparms[i]))
- m_reset_debug_decls.safe_push (m_oparms[i]);
+ while (!ssas_to_process_debug.is_empty ())
+ prepare_debug_expressions (ssas_to_process_debug.pop ());
}
/* Constructor of ipa_param_body_adjustments from a simple list of
@@ -1250,9 +1405,9 @@ ipa_param_body_adjustments
tree fndecl)
: m_adj_params (adj_params), m_adjustments (NULL), m_reset_debug_decls (),
m_split_modifications_p (false), m_dead_stmts (), m_dead_ssas (),
- m_fndecl (fndecl), m_id (NULL), m_oparms (), m_new_decls (),
- m_new_types (), m_replacements (), m_removed_decls (), m_removed_map (),
- m_method2func (false)
+ m_dead_ssa_debug_equiv (), m_dead_stmt_debug_equiv (), m_fndecl (fndecl),
+ m_id (NULL), m_oparms (), m_new_decls (), m_new_types (), m_replacements (),
+ m_removed_decls (), m_removed_map (), m_method2func (false)
{
common_initialization (fndecl, NULL, NULL);
}
@@ -1267,7 +1422,8 @@ ipa_param_body_adjustments
tree fndecl)
: m_adj_params (adjustments->m_adj_params), m_adjustments (adjustments),
m_reset_debug_decls (), m_split_modifications_p (false), m_dead_stmts (),
- m_dead_ssas (), m_fndecl (fndecl), m_id (NULL), m_oparms (), m_new_decls (),
+ m_dead_ssas (), m_dead_ssa_debug_equiv (), m_dead_stmt_debug_equiv (),
+ m_fndecl (fndecl), m_id (NULL), m_oparms (), m_new_decls (),
m_new_types (), m_replacements (), m_removed_decls (), m_removed_map (),
m_method2func (false)
{
@@ -1290,8 +1446,9 @@ ipa_param_body_adjustments
vec<ipa_replace_map *, va_gc> *tree_map)
: m_adj_params (adjustments->m_adj_params), m_adjustments (adjustments),
m_reset_debug_decls (), m_split_modifications_p (false), m_dead_stmts (),
- m_dead_ssas (),m_fndecl (fndecl), m_id (id), m_oparms (), m_new_decls (),
- m_new_types (), m_replacements (), m_removed_decls (), m_removed_map (),
+ m_dead_ssas (), m_dead_ssa_debug_equiv (), m_dead_stmt_debug_equiv (),
+ m_fndecl (fndecl), m_id (id), m_oparms (), m_new_decls (), m_new_types (),
+ m_replacements (), m_removed_decls (), m_removed_map (),
m_method2func (false)
{
common_initialization (old_fndecl, vars, tree_map);
@@ -356,6 +356,9 @@ public:
bool modify_gimple_stmt (gimple **stmt, gimple_seq *extra_stmts);
/* Return the new chain of parameters. */
tree get_new_param_chain ();
+ /* Replace all occurances of SSAs in m_dead_ssa_debug_equiv in t with what
+ they are mapped to. */
+ void remap_with_debug_expressions (tree *t);
/* Pointers to data structures defining how the function should be
modified. */
@@ -376,6 +379,12 @@ public:
hash_set<gimple *> m_dead_stmts;
hash_set<tree> m_dead_ssas;
+ /* Mapping from DCEd SSAs to what their potential debug_binds should be. */
+ hash_map<tree, tree> m_dead_ssa_debug_equiv;
+ /* Mapping from DCEd statements to debug expressions that will be placed on
+ the RHS of debug statement that will replace this one. */
+ hash_map<gimple *, tree> m_dead_stmt_debug_equiv;
+
private:
void common_initialization (tree old_fndecl, tree *vars,
vec<ipa_replace_map *, va_gc> *tree_map);
@@ -389,7 +398,8 @@ private:
bool modify_call_stmt (gcall **stmt_p);
bool modify_cfun_body ();
void reset_debug_stmts ();
- void mark_dead_statements (tree dead_param);
+ void mark_dead_statements (tree dead_param, vec<tree> *debugstack);
+ bool prepare_debug_expressions (tree dead_ssa);
tree get_removed_call_arg_placeholder (tree arg);
/* Declaration of the function that is being transformed. */
new file mode 100644
@@ -0,0 +1,45 @@
+/* { dg-do run } */
+/* { dg-options "-g -fno-ipa-icf" } */
+
+
+void __attribute__((noipa))
+use (int x)
+{
+ asm volatile ("" : : "r" (x) : "memory");
+}
+
+static int __attribute__((noinline))
+bar (int i, int k)
+{
+ asm ("" : "+r" (i));
+ use (i); /* { dg-final { gdb-test . "k" "3" { xfail *-*-* } } } */
+ return 6;
+}
+
+volatile int v;
+
+static int __attribute__((noinline))
+foo (int i, int k)
+{
+ int r;
+ v = 9;
+ k = (k + 14)/k;
+ r = bar (i, k); /* { dg-final { gdb-test . "k" "3" } } */
+ return r;
+}
+
+volatile int v;
+
+int __attribute__((noipa))
+get_val1 (void) {return 20;}
+int __attribute__((noipa))
+get_val2 (void) {return 7;}
+
+int
+main (void)
+{
+ int k = get_val2 ();
+ int r = foo (get_val1 (), k);
+ v = r + k; /* k has to live accross the call or all is probably lost */
+ return 0;
+}
@@ -1527,7 +1527,21 @@ remap_gimple_stmt (gimple *stmt, copy_body_data *id)
if (!is_gimple_debug (stmt)
&& id->param_body_adjs
&& id->param_body_adjs->m_dead_stmts.contains (stmt))
- return NULL;
+ {
+ tree *dval = id->param_body_adjs->m_dead_stmt_debug_equiv.get (stmt);
+ if (!dval)
+ return NULL;
+
+ gcc_assert (is_gimple_assign (stmt));
+ tree lhs = gimple_assign_lhs (stmt);
+ tree *dvar = id->param_body_adjs->m_dead_ssa_debug_equiv.get (lhs);
+ gdebug *bind = gimple_build_debug_bind (*dvar, *dval, stmt);
+ if (id->reset_location)
+ gimple_set_location (bind, input_location);
+ id->debug_stmts.safe_push (bind);
+ gimple_seq_add_stmt (&stmts, bind);
+ return stmts;
+ }
/* Begin by recognizing trees that we'll completely rewrite for the
inlining context. Our output for these trees is completely
@@ -1793,15 +1807,13 @@ remap_gimple_stmt (gimple *stmt, copy_body_data *id)
if (gimple_debug_bind_p (stmt))
{
- tree value;
+ tree var = gimple_debug_bind_get_var (stmt);
+ tree value = gimple_debug_bind_get_value (stmt);
if (id->param_body_adjs
&& id->param_body_adjs->m_dead_stmts.contains (stmt))
- value = NULL_TREE;
- else
- value = gimple_debug_bind_get_value (stmt);
- gdebug *copy
- = gimple_build_debug_bind (gimple_debug_bind_get_var (stmt),
- value, stmt);
+ id->param_body_adjs->remap_with_debug_expressions (&value);
+
+ gdebug *copy = gimple_build_debug_bind (var, value, stmt);
if (id->reset_location)
gimple_set_location (copy, input_location);
id->debug_stmts.safe_push (copy);
@@ -6468,7 +6480,6 @@ tree_function_versioning (tree old_decl, tree new_decl,
in the debug info that var (whole DECL_ORIGIN is the parm
PARM_DECL) is optimized away, but could be looked up at the
call site as value of D#X there. */
- tree vexpr;
gimple_stmt_iterator cgsi
= gsi_after_labels (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
gimple *def_temp;
@@ -6476,17 +6487,25 @@ tree_function_versioning (tree old_decl, tree new_decl,
i = vec_safe_length (*debug_args);
do
{
+ tree vexpr = NULL_TREE;
i -= 2;
while (var != NULL_TREE
&& DECL_ABSTRACT_ORIGIN (var) != (**debug_args)[i])
var = TREE_CHAIN (var);
if (var == NULL_TREE)
break;
- vexpr = make_node (DEBUG_EXPR_DECL);
tree parm = (**debug_args)[i];
- DECL_ARTIFICIAL (vexpr) = 1;
- TREE_TYPE (vexpr) = TREE_TYPE (parm);
- SET_DECL_MODE (vexpr, DECL_MODE (parm));
+ if (tree parm_ddef = ssa_default_def (id.src_cfun, parm))
+ if (tree *d
+ = param_body_adjs->m_dead_ssa_debug_equiv.get (parm_ddef))
+ vexpr = *d;
+ if (!vexpr)
+ {
+ vexpr = make_node (DEBUG_EXPR_DECL);
+ DECL_ARTIFICIAL (vexpr) = 1;
+ TREE_TYPE (vexpr) = TREE_TYPE (parm);
+ SET_DECL_MODE (vexpr, DECL_MODE (parm));
+ }
def_temp = gimple_build_debug_bind (var, vexpr, NULL);
gsi_insert_before (&cgsi, def_temp, GSI_NEW_STMT);
def_temp = gimple_build_debug_source_bind (vexpr, parm, NULL);