diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc index d565c005f622fd2da7bdddeeccbe2a7950aaae83..0ac8f73204b2418ffab9d2c3d25d0976a5e53037 100644 --- a/gcc/cgraph.cc +++ b/gcc/cgraph.cc @@ -1403,11 +1403,17 @@ cgraph_edge::redirect_callee (cgraph_node *n) 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; @@ -1527,7 +1533,7 @@ cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e) 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; diff --git a/gcc/cgraph.h b/gcc/cgraph.h index b4f028b3f3034056de1050ea1ab93a682197d0e1..47f35e8078df288ef01d25a72583f1988c09ef3a 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -1837,9 +1837,16 @@ public: 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. */ diff --git a/gcc/ipa-param-manipulation.cc b/gcc/ipa-param-manipulation.cc index 8772476ca40fa9e4d176fb8c367b5a94fbd388be..02f71a42237e1d1ad79be05c6d8d4da743ad1d39 100644 --- a/gcc/ipa-param-manipulation.cc +++ b/gcc/ipa-param-manipulation.cc @@ -593,14 +593,65 @@ isra_get_ref_base_and_offset (tree expr, tree *base_p, unsigned *unit_offset_p) 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; @@ -910,32 +961,20 @@ ipa_param_adjustments::modify_call (cgraph_edge *cs, 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); } } @@ -954,8 +993,11 @@ ipa_param_adjustments::modify_call (cgraph_edge *cs, 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 { @@ -2552,4 +2594,30 @@ ipa_edge_modifications_finalize () 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); +} diff --git a/gcc/ipa-param-manipulation.h b/gcc/ipa-param-manipulation.h index ddfed454c26f61a2a85b97a4908c65518262bd65..8dd5e5bdeaee660cdb0283c4d032d87ae7116141 100644 --- a/gcc/ipa-param-manipulation.h +++ b/gcc/ipa-param-manipulation.h @@ -224,7 +224,8 @@ public: /* 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. */ @@ -442,6 +443,6 @@ void push_function_arg_decls (vec<tree> *args, tree fndecl); 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 */ diff --git a/gcc/testsuite/gcc.dg/ipa/pr108007.c b/gcc/testsuite/gcc.dg/ipa/pr108007.c new file mode 100644 index 0000000000000000000000000000000000000000..77fc95975cfd4080b71052fd5d1c92373f0171d5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/pr108007.c @@ -0,0 +1,32 @@ +/* { 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; +} diff --git a/gcc/testsuite/gcc.dg/ipa/pr112616.c b/gcc/testsuite/gcc.dg/ipa/pr112616.c new file mode 100644 index 0000000000000000000000000000000000000000..5f730da71e711f43e9a60b788905dafdab7741cd --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/pr112616.c @@ -0,0 +1,28 @@ +/* { 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; +} diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc index 17628a34c70226b9ae9a79d2a87ab8c0bcb91ab8..1a2541ad95238ef1b0cee94edbbcc38c92ad6d8b 100644 --- a/gcc/tree-inline.cc +++ b/gcc/tree-inline.cc @@ -2988,20 +2988,19 @@ redirect_all_calls (copy_body_data * id, basic_block bb) 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); @@ -3328,8 +3327,12 @@ copy_body (copy_body_data *id, 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; }