speculative indirect call, remove "speculative" of the indirect call and
also redirect stmt to it's final direct target.
+ When called from within tree-inline, KILLED_SSAs has to contain the pointer
+ to killed_new_ssa_names within the copy_body_data structure and SSAs
+ discovered to be useless (if LHS is removed) will be added to it, otherwise
+ it needs to be NULL.
+
It is up to caller to iteratively transform each "speculative"
direct call as appropriate. */
gimple *
-cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e)
+cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e,
+ hash_set <tree> *killed_ssas)
{
tree decl = gimple_call_fndecl (e->call_stmt);
gcall *new_stmt;
remove_stmt_from_eh_lp (e->call_stmt);
tree old_fntype = gimple_call_fntype (e->call_stmt);
- new_stmt = padjs->modify_call (e, false);
+ new_stmt = padjs->modify_call (e, false, killed_ssas);
cgraph_node *origin = e->callee;
while (origin->clone_of)
origin = origin->clone_of;
speculative indirect call, remove "speculative" of the indirect call and
also redirect stmt to it's final direct target.
+ When called from within tree-inline, KILLED_SSAs has to contain the
+ pointer to killed_new_ssa_names within the copy_body_data structure and
+ SSAs discovered to be useless (if LHS is removed) will be added to it,
+ otherwise it needs to be NULL.
+
It is up to caller to iteratively transform each "speculative"
direct call as appropriate. */
- static gimple *redirect_call_stmt_to_callee (cgraph_edge *e);
+ static gimple *redirect_call_stmt_to_callee (cgraph_edge *e,
+ hash_set <tree>
+ *killed_ssas = nullptr);
/* Create clone of edge in the node N represented
by CALL_EXPR the callgraph. */
return true;
}
+/* Remove all statements that use NAME directly or indirectly. KILLED_SSAS
+ contains the SSA_NAMEs that are already being or have been processed and new
+ ones need to be added to it. The function only has to process situations
+ handled by ssa_name_only_returned_p in ipa-sra.cc with the exception that it
+ can assume it must never reach a use in a return statement. */
+
+static void
+purge_all_uses (tree name, hash_set <tree> *killed_ssas)
+{
+ imm_use_iterator imm_iter;
+ gimple *stmt;
+ auto_vec <tree, 4> worklist;
+
+ worklist.safe_push (name);
+ while (!worklist.is_empty ())
+ {
+ tree cur_name = worklist.pop ();
+ FOR_EACH_IMM_USE_STMT (stmt, imm_iter, cur_name)
+ {
+ if (gimple_debug_bind_p (stmt))
+ {
+ /* When runing within tree-inline, we will never end up here but
+ adding the SSAs to killed_ssas will do the trick in this case
+ and the respective debug statements will get reset. */
+ gimple_debug_bind_reset_value (stmt);
+ update_stmt (stmt);
+ continue;
+ }
+
+ tree lhs = NULL_TREE;
+ if (is_gimple_assign (stmt))
+ lhs = gimple_assign_lhs (stmt);
+ else if (gimple_code (stmt) == GIMPLE_PHI)
+ lhs = gimple_phi_result (stmt);
+ gcc_assert (lhs
+ && (TREE_CODE (lhs) == SSA_NAME)
+ && !gimple_vdef (stmt));
+ if (!killed_ssas->add (lhs))
+ {
+ worklist.safe_push (lhs);
+ gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+ gsi_remove (&gsi, true);
+ }
+ }
+ }
+}
+
/* Modify actual arguments of a function call in statement currently belonging
to CS, and make it call CS->callee->decl. Return the new statement that
replaced the old one. When invoked, cfun and current_function_decl have to
- be set to the caller. */
+ be set to the caller. When called from within tree-inline, KILLED_SSAs has
+ to contain the pointer to killed_new_ssa_names within the copy_body_data
+ structure and SSAs discovered to be useless (if LHS is removed) will be
+ added to it, otherwise it needs to be NULL. */
gcall *
ipa_param_adjustments::modify_call (cgraph_edge *cs,
- bool update_references)
+ bool update_references,
+ hash_set <tree> *killed_ssas)
{
gcall *stmt = cs->call_stmt;
tree callee_decl = cs->callee->decl;
gcall *new_stmt = gimple_build_call_vec (callee_decl, vargs);
- tree ssa_to_remove = NULL;
+ hash_set <tree> *ssas_to_remove = NULL;
if (tree lhs = gimple_call_lhs (stmt))
{
if (!m_skip_return)
gimple_call_set_lhs (new_stmt, lhs);
else if (TREE_CODE (lhs) == SSA_NAME)
{
- /* LHS should now by a default-def SSA. Unfortunately default-def
- SSA_NAMEs need a backing variable (or at least some code examining
- SSAs assumes it is non-NULL). So we either have to re-use the
- decl we have at hand or introdice a new one. */
- tree repl = create_tmp_var (TREE_TYPE (lhs), "removed_return");
- repl = get_or_create_ssa_default_def (cfun, repl);
- SSA_NAME_IS_DEFAULT_DEF (repl) = true;
- imm_use_iterator ui;
- use_operand_p use_p;
- gimple *using_stmt;
- FOR_EACH_IMM_USE_STMT (using_stmt, ui, lhs)
+ if (!killed_ssas)
{
- FOR_EACH_IMM_USE_ON_STMT (use_p, ui)
- {
- SET_USE (use_p, repl);
- }
- update_stmt (using_stmt);
+ ssas_to_remove = new hash_set<tree> (8);
+ killed_ssas = ssas_to_remove;
}
- ssa_to_remove = lhs;
+ killed_ssas->add (lhs);
+ purge_all_uses (lhs, killed_ssas);
}
}
fprintf (dump_file, "\n");
}
gsi_replace (&gsi, new_stmt, true);
- if (ssa_to_remove)
- release_ssa_name (ssa_to_remove);
+ if (ssas_to_remove)
+ {
+ ipa_release_ssas_in_hash (ssas_to_remove);
+ delete ssas_to_remove;
+ }
if (update_references)
do
{
ipa_edge_modifications = NULL;
}
+/* Helper used to sort a vector of SSA_NAMES. */
+static int
+compare_ssa_versions (const void *va, const void *vb)
+{
+ const_tree const a = *(const_tree const*)va;
+ const_tree const b = *(const_tree const*)vb;
+
+ if (SSA_NAME_VERSION (a) < SSA_NAME_VERSION (b))
+ return -1;
+ if (SSA_NAME_VERSION (a) > SSA_NAME_VERSION (b))
+ return 1;
+ return 0;
+}
+
+/* Call release_ssa_name on all elements in KILLED_SSAS in a defined order. */
+
+void
+ipa_release_ssas_in_hash (hash_set <tree> *killed_ssas)
+{
+ auto_vec<tree, 16> ssas_to_release;
+ for (tree sn : *killed_ssas)
+ ssas_to_release.safe_push (sn);
+ ssas_to_release.qsort (compare_ssa_versions);
+ for (tree sn : ssas_to_release)
+ release_ssa_name (sn);
+}
/* Modify a call statement arguments (and possibly remove the return value)
as described in the data fields of this class. */
- gcall *modify_call (cgraph_edge *cs, bool update_references);
+ gcall *modify_call (cgraph_edge *cs, bool update_references,
+ hash_set <tree> *killed_ssas);
/* Return if the first parameter is left intact. */
bool first_param_intact_p ();
/* Build a function type corresponding to the modified call. */
void push_function_arg_types (vec<tree> *types, tree fntype);
void ipa_verify_edge_has_no_modifications (cgraph_edge *cs);
void ipa_edge_modifications_finalize ();
-
+void ipa_release_ssas_in_hash (hash_set <tree> *killed_ssas);
#endif /* IPA_PARAM_MANIPULATION_H */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-Os -fno-dce -fno-tree-dce -g" } */
+
+/* This tests that when IPA-SRA removes a LHS of a call statement which, in the
+ original source, is fed into a useless operation which however can trap when
+ given nonsensical input, that we remove it even when the user has turned off
+ normal DCE. */
+
+int a, b, d, e, f = 10000000, h;
+short c, g;
+static int *i() {
+ g = f;
+ L:
+ h = e = ~g;
+ g = ~f % g & e;
+ if (!g)
+ goto L;
+ c++;
+ while (g < 1)
+ ;
+ return &a;
+}
+static void k() {
+ int *l, m = 2;
+ l = i();
+ for (; d < 1; d++)
+ m |= *l >= b;
+}
+int main() {
+ k();
+ return 0;
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+unsigned a;
+int b, d, e, f = 2, g, h = 1, *i = &b;
+volatile int c = 1;
+static int *o() {
+ long m = ~a;
+ int j = f / b, k = f - 1, n = m << -1 / ~g / k;
+ if (j && n)
+ c;
+ return &e;
+}
+static long p() {
+ int *q = 0, **r = &q;
+ if (c) {
+ *i = h;
+ *r = o();
+ }
+ return *q;
+}
+int main() {
+ p();
+ int *l = 0;
+ if (d)
+ c = *l;
+ return 0;
+}
struct cgraph_edge *edge = id->dst_node->get_edge (stmt);
if (edge)
{
+ if (!id->killed_new_ssa_names)
+ id->killed_new_ssa_names = new hash_set<tree> (16);
gimple *new_stmt
- = cgraph_edge::redirect_call_stmt_to_callee (edge);
- /* If IPA-SRA transformation, run as part of edge redirection,
- removed the LHS because it is unused, save it to
- killed_new_ssa_names so that we can prune it from debug
- statements. */
+ = cgraph_edge::redirect_call_stmt_to_callee (edge,
+ id->killed_new_ssa_names);
if (old_lhs
&& TREE_CODE (old_lhs) == SSA_NAME
&& !gimple_call_lhs (new_stmt))
- {
- if (!id->killed_new_ssa_names)
- id->killed_new_ssa_names = new hash_set<tree> (16);
- id->killed_new_ssa_names->add (old_lhs);
- }
+ /* In case of IPA-SRA removing the LHS, the name should have
+ been already added to the hash. But in case of redirecting
+ to builtin_unreachable it was not and the name still should
+ be pruned from debug statements. */
+ id->killed_new_ssa_names->add (old_lhs);
if (stmt == last && id->call_stmt && maybe_clean_eh_stmt (stmt))
gimple_purge_dead_eh_edges (bb);
body = copy_cfg_body (id, entry_block_map, exit_block_map,
new_entry);
copy_debug_stmts (id);
- delete id->killed_new_ssa_names;
- id->killed_new_ssa_names = NULL;
+ if (id->killed_new_ssa_names)
+ {
+ ipa_release_ssas_in_hash (id->killed_new_ssa_names);
+ delete id->killed_new_ssa_names;
+ id->killed_new_ssa_names = NULL;
+ }
return body;
}