From 17e0fc9202d9edf96a8c70e54f9a105f8d477016 Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Thu, 11 Dec 2014 22:48:48 +0100 Subject: [PATCH] re PR ipa/61324 (ICE: SIGSEGV at ipa-comdats.c:321 with -fno-use-cxa-atexit -fkeep-inline-functions) PR ipa/61324 * testsuite/g++.dg/pr61324.C: New testcase by Trevor Saunders. * testsuite/g++.dg/tm/pr51411-2.C: Update se the extern function is not eliminated early. * testsuite/gcc.target/i386/pr57756.c: Turn extern inline into static inline. * passes.c (execute_todo): Update call of remove_unreachable_nodes. * ipa-chkp.c (chkp_produce_thunks): Use TODO_remove_functions. * cgraphunit.c (symbol_table::process_new_functions): Add IPA_SSA_AFTER_INLINING. (ipa_passes): Update call of remove_unreachable_nodes. (symbol_table::compile): Remove call of remove_unreachable_nodes. * ipa-inline.c (inline_small_functions): Do not ICE with -flto-partition=none (ipa_inline): Update symtab->state; fix formatting update call of remove_unreachable_nodes. * cgraphclones.c (symbol_table::materialize_all_clones): Likewise. * cgraph.h (enum symtab_state): Add IPA_SSA_AFTER_INLINING. (remove_unreachable_nodes): Update. * ipa.c (process_references): Keep external references only when optimizing. (walk_polymorphic_call_targets): Keep possible polymorphic call target only when devirtualizing. (symbol_table::remove_unreachable_nodes): Remove BEFORE_INLINING_P parameter. (ipa_single_use): Update comment. * ipa-pure-const.c (cdtor_p): New function. (propagate_pure_const): Track if some cdtor was turned pure/const. (execute): Return TODO_remove_functions if needed. * ipa-comdats.c (ipa_comdats): Update comment. * lto.c (read_cgraph_and_symbols): Update call of remove_unreachable_nodes. (do_whole_program_analysis): Remove call of symtab->remove_unreachable_nodes From-SVN: r218640 --- gcc/ChangeLog | 29 +++++++++++ gcc/cgraph.h | 9 ++-- gcc/cgraphclones.c | 2 +- gcc/cgraphunit.c | 10 ++-- gcc/ipa-chkp.c | 4 +- gcc/ipa-comdats.c | 9 +++- gcc/ipa-inline.c | 23 +++++---- gcc/ipa-pure-const.c | 24 +++++++-- gcc/ipa.c | 68 ++++++++++++++++++------- gcc/lto/ChangeLog | 8 +++ gcc/lto/lto.c | 3 +- gcc/passes.c | 2 +- gcc/testsuite/ChangeLog | 9 ++++ gcc/testsuite/g++.dg/pr61324.C | 13 +++++ gcc/testsuite/g++.dg/tm/pr51411-2.C | 3 +- gcc/testsuite/gcc.target/i386/pr57756.c | 2 +- 16 files changed, 168 insertions(+), 50 deletions(-) create mode 100644 gcc/testsuite/g++.dg/pr61324.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 7f233e8626e..c0de7443afc 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,32 @@ +2014-12-11 Jan Hubicka + + PR ipa/61324 + * passes.c (execute_todo): Update call of remove_unreachable_nodes. + * ipa-chkp.c (chkp_produce_thunks): Use TODO_remove_functions. + * cgraphunit.c (symbol_table::process_new_functions): Add + IPA_SSA_AFTER_INLINING. + (ipa_passes): Update call of remove_unreachable_nodes. + (symbol_table::compile): Remove call of remove_unreachable_nodes. + * ipa-inline.c (inline_small_functions): Do not ICE with + -flto-partition=none + (ipa_inline): Update symtab->state; fix formatting + update call of remove_unreachable_nodes. + * passes.c (execute_todo): Update call of remove_unreachable_nodes. + * cgraphclones.c (symbol_table::materialize_all_clones): Likewise. + * cgraph.h (enum symtab_state): Add IPA_SSA_AFTER_INLINING. + (remove_unreachable_nodes): Update. + * ipa.c (process_references): Keep external references only + when optimizing. + (walk_polymorphic_call_targets): Keep possible polymorphic call + target only when devirtualizing. + (symbol_table::remove_unreachable_nodes): Remove BEFORE_INLINING_P + parameter. + (ipa_single_use): Update comment. + * ipa-pure-const.c (cdtor_p): New function. + (propagate_pure_const): Track if some cdtor was turned pure/const. + (execute): Return TODO_remove_functions if needed. + * ipa-comdats.c (ipa_comdats): Update comment. + 2014-12-11 Aldy Hernandez * dwarf2out.c (gen_lexical_block_die): Remove unused `depth' diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 891b92500e7..a2fc56300a7 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -1801,12 +1801,15 @@ enum symtab_state PARSING, /* Callgraph is being constructed. It is safe to add new functions. */ CONSTRUCTION, - /* Callgraph is being at LTO time. */ + /* Callgraph is being streamed-in at LTO time. */ LTO_STREAMING, - /* Callgraph is built and IPA passes are being run. */ + /* Callgraph is built and early IPA passes are being run. */ IPA, /* Callgraph is built and all functions are transformed to SSA form. */ IPA_SSA, + /* All inline decisions are done; it is now possible to remove extern inline + functions and virtual call targets. */ + IPA_SSA_AFTER_INLINING, /* Functions are now ordered and being passed to RTL expanders. */ EXPANSION, /* All cgraph expansion is done. */ @@ -1876,7 +1879,7 @@ public: } /* Perform reachability analysis and reclaim all unreachable nodes. */ - bool remove_unreachable_nodes (bool before_inlining_p, FILE *file); + bool remove_unreachable_nodes (FILE *file); /* Optimization of function bodies might've rendered some variables as unnecessary so we want to avoid these from being compiled. Re-do diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c index 13bb0e95b44..43d81a9f0ab 100644 --- a/gcc/cgraphclones.c +++ b/gcc/cgraphclones.c @@ -1146,7 +1146,7 @@ symbol_table::materialize_all_clones (void) #ifdef ENABLE_CHECKING cgraph_node::verify_cgraph_nodes (); #endif - symtab->remove_unreachable_nodes (false, symtab->dump_file); + symtab->remove_unreachable_nodes (symtab->dump_file); } #include "gt-cgraphclones.h" diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index fed1a3e0053..53abd173fa6 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -327,6 +327,7 @@ symbol_table::process_new_functions (void) case IPA: case IPA_SSA: + case IPA_SSA_AFTER_INLINING: /* When IPA optimization already started, do all essential transformations that has been already performed on the whole cgraph but not on this function. */ @@ -335,7 +336,7 @@ symbol_table::process_new_functions (void) if (!node->analyzed) node->analyze (); push_cfun (DECL_STRUCT_FUNCTION (fndecl)); - if (state == IPA_SSA + if ((state == IPA_SSA || state == IPA_SSA_AFTER_INLINING) && !gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl))) g->get_passes ()->execute_early_local_passes (); else if (inline_summary_vec != NULL) @@ -507,6 +508,7 @@ cgraph_node::add_new_function (tree fndecl, bool lowered) case IPA: case IPA_SSA: + case IPA_SSA_AFTER_INLINING: case EXPANSION: /* Bring the function into finalized state and enqueue for later analyzing and compilation. */ @@ -2053,7 +2055,7 @@ ipa_passes (void) /* This extra symtab_remove_unreachable_nodes pass tends to catch some devirtualization and other changes where removal iterate. */ - symtab->remove_unreachable_nodes (true, symtab->dump_file); + symtab->remove_unreachable_nodes (symtab->dump_file); /* If pass_all_early_optimizations was not scheduled, the state of the cgraph will not be properly updated. Update it now. */ @@ -2194,10 +2196,6 @@ symbol_table::compile (void) return; } - /* This pass remove bodies of extern inline functions we never inlined. - Do this later so other IPA passes see what is really going on. - FIXME: This should be run just after inlining by pasmanager. */ - remove_unreachable_nodes (false, dump_file); global_info_ready = true; if (dump_file) { diff --git a/gcc/ipa-chkp.c b/gcc/ipa-chkp.c index 46b2139758a..c6bd15ff61a 100644 --- a/gcc/ipa-chkp.c +++ b/gcc/ipa-chkp.c @@ -647,9 +647,7 @@ chkp_produce_thunks (void) chkp_function_mark_instrumented (node->decl); } - symtab->remove_unreachable_nodes (true, dump_file); - - return 0; + return TODO_remove_functions; } const pass_data pass_data_ipa_chkp_versioning = diff --git a/gcc/ipa-comdats.c b/gcc/ipa-comdats.c index af2aef8efad..306f55f8f86 100644 --- a/gcc/ipa-comdats.c +++ b/gcc/ipa-comdats.c @@ -327,7 +327,14 @@ ipa_comdats (void) && !symbol->alias && symbol->real_symbol_p ()) { - tree group = *map.get (symbol); + tree *val = map.get (symbol); + + /* A NULL here means that SYMBOL is unreachable in the definition + of ipa-comdats. Either ipa-comdats is wrong about this or someone + forgot to cleanup and remove unreachable functions earlier. */ + gcc_assert (val); + + tree group = *val; if (group == error_mark_node) continue; diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c index 9f600b092c2..8954e493533 100644 --- a/gcc/ipa-inline.c +++ b/gcc/ipa-inline.c @@ -1731,9 +1731,9 @@ inline_small_functions (void) " to be inlined into %s/%i in %s:%i\n" " Estimated badness is %"PRId64", frequency %.2f.\n", edge->caller->name (), edge->caller->order, - flag_wpa ? "unknown" + edge->call_stmt ? "unknown" : gimple_filename ((const_gimple) edge->call_stmt), - flag_wpa ? -1 + edge->call_stmt ? -1 : gimple_lineno ((const_gimple) edge->call_stmt), badness.to_int (), edge->frequency / (double)CGRAPH_FREQ_BASE); @@ -2188,9 +2188,12 @@ ipa_inline (void) inline_small_functions (); - /* Do first after-inlining removal. We want to remove all "stale" extern inline - functions and virtual functions so we really know what is called once. */ - symtab->remove_unreachable_nodes (false, dump_file); + gcc_assert (symtab->state == IPA_SSA); + symtab->state = IPA_SSA_AFTER_INLINING; + /* Do first after-inlining removal. We want to remove all "stale" extern + inline functions and virtual functions so we really know what is called + once. */ + symtab->remove_unreachable_nodes (dump_file); free (order); /* Inline functions with a property that after inlining into all callers the @@ -2199,7 +2202,8 @@ ipa_inline (void) are met. */ if (dump_file) fprintf (dump_file, - "\nDeciding on functions to be inlined into all callers and removing useless speculations:\n"); + "\nDeciding on functions to be inlined into all callers and " + "removing useless speculations:\n"); /* Inlining one function called once has good chance of preventing inlining other function into the same callee. Ideally we should @@ -2247,10 +2251,11 @@ ipa_inline (void) int num_calls = 0; node->call_for_symbol_thunks_and_aliases (sum_callers, &num_calls, true); - while (node->call_for_symbol_thunks_and_aliases (inline_to_all_callers, - &num_calls, true)) + while (node->call_for_symbol_thunks_and_aliases + (inline_to_all_callers, &num_calls, true)) ; - remove_functions = true; + if (num_calls) + remove_functions = true; } } } diff --git a/gcc/ipa-pure-const.c b/gcc/ipa-pure-const.c index 13e3a258c9b..94c3f81dfc8 100644 --- a/gcc/ipa-pure-const.c +++ b/gcc/ipa-pure-const.c @@ -1160,10 +1160,21 @@ self_recursive_p (struct cgraph_node *node) return false; } +/* Return true if N is cdtor that is not const or pure. In this case we may + need to remove unreachable function if it is marked const/pure. */ + +static bool +cdtor_p (cgraph_node *n, void *) +{ + if (DECL_STATIC_CONSTRUCTOR (n->decl) || DECL_STATIC_DESTRUCTOR (n->decl)) + return !TREE_READONLY (n->decl) && !DECL_PURE_P (n->decl); + return false; +} + /* Produce transitive closure over the callgraph and compute pure/const attributes. */ -static void +static bool propagate_pure_const (void) { struct cgraph_node *node; @@ -1173,6 +1184,7 @@ propagate_pure_const (void) int order_pos; int i; struct ipa_dfs_info * w_info; + bool remove_p = false; order_pos = ipa_reduced_postorder (order, true, false, NULL); if (dump_file) @@ -1447,6 +1459,8 @@ propagate_pure_const (void) this_looping ? "looping " : "", w->name ()); } + remove_p |= w->call_for_symbol_and_aliases (cdtor_p, + NULL, true); w->set_const_flag (true, this_looping); break; @@ -1459,6 +1473,8 @@ propagate_pure_const (void) this_looping ? "looping " : "", w->name ()); } + remove_p |= w->call_for_symbol_and_aliases (cdtor_p, + NULL, true); w->set_pure_flag (true, this_looping); break; @@ -1472,6 +1488,7 @@ propagate_pure_const (void) ipa_free_postorder_info (); free (order); + return remove_p; } /* Produce transitive closure over the callgraph and compute nothrow @@ -1581,6 +1598,7 @@ pass_ipa_pure_const:: execute (function *) { struct cgraph_node *node; + bool remove_p; symtab->remove_cgraph_insertion_hook (function_insertion_hook_holder); symtab->remove_cgraph_duplication_hook (node_duplication_hook_holder); @@ -1589,14 +1607,14 @@ execute (function *) /* Nothrow makes more function to not lead to return and improve later analysis. */ propagate_nothrow (); - propagate_pure_const (); + remove_p = propagate_pure_const (); /* Cleanup. */ FOR_EACH_FUNCTION (node) if (has_function_state (node)) free (get_function_state (node)); funct_state_vec.release (); - return 0; + return remove_p ? TODO_remove_functions : 0; } static bool diff --git a/gcc/ipa.c b/gcc/ipa.c index 4f87b75946c..bed20e9d664 100644 --- a/gcc/ipa.c +++ b/gcc/ipa.c @@ -123,21 +123,33 @@ process_references (symtab_node *snode, for (i = 0; snode->iterate_reference (i, ref); i++) { symtab_node *node = ref->referred; + symtab_node *body = node->ultimate_alias_target (); if (node->definition && !node->in_other_partition && ((!DECL_EXTERNAL (node->decl) || node->alias) || (((before_inlining_p - && (symtab->state < IPA_SSA - || !lookup_attribute ("always_inline", - DECL_ATTRIBUTES (node->decl))))) - /* We use variable constructors during late complation for + && (TREE_CODE (node->decl) != FUNCTION_DECL + || opt_for_fn (body->decl, optimize) + || (symtab->state < IPA_SSA + && lookup_attribute + ("always_inline", + DECL_ATTRIBUTES (body->decl)))))) + /* We use variable constructors during late compilation for constant folding. Keep references alive so partitioning knows about potential references. */ || (TREE_CODE (node->decl) == VAR_DECL && flag_wpa && ctor_for_folding (node->decl) != error_mark_node)))) - reachable->add (node); + { + /* Be sure that we will not optimize out alias target + body. */ + if (DECL_EXTERNAL (node->decl) + && node->alias + && before_inlining_p) + reachable->add (body); + reachable->add (node); + } enqueue_node (node, first, reachable); } } @@ -178,15 +190,23 @@ walk_polymorphic_call_targets (hash_set *reachable_call_targets, (method_class_type (TREE_TYPE (n->decl)))) continue; + symtab_node *body = n->function_symbol (); + /* Prior inlining, keep alive bodies of possible targets for devirtualization. */ if (n->definition && (before_inlining_p - && (symtab->state < IPA_SSA - || !lookup_attribute ("always_inline", - DECL_ATTRIBUTES (n->decl))))) - reachable->add (n); - + && opt_for_fn (body->decl, optimize) + && opt_for_fn (body->decl, flag_devirtualize))) + { + /* Be sure that we will not optimize out alias target + body. */ + if (DECL_EXTERNAL (n->decl) + && n->alias + && before_inlining_p) + reachable->add (body); + reachable->add (n); + } /* Even after inlining we want to keep the possible targets in the boundary, so late passes can still produce direct call even if the chance for inlining is lost. */ @@ -246,8 +266,6 @@ walk_polymorphic_call_targets (hash_set *reachable_call_targets, After inlining we release their bodies and turn them into unanalyzed nodes even when they are reachable. - BEFORE_INLINING_P specify whether we are before or after inlining. - - virtual functions are kept in callgraph even if they seem unreachable in hope calls to them will be devirtualized. @@ -293,7 +311,7 @@ walk_polymorphic_call_targets (hash_set *reachable_call_targets, we set AUX pointer of processed symbols in the boundary to constant 2. */ bool -symbol_table::remove_unreachable_nodes (bool before_inlining_p, FILE *file) +symbol_table::remove_unreachable_nodes (FILE *file) { symtab_node *first = (symtab_node *) (void *) 1; struct cgraph_node *node, *next; @@ -302,6 +320,8 @@ symbol_table::remove_unreachable_nodes (bool before_inlining_p, FILE *file) hash_set reachable; hash_set body_needed_for_clonning; hash_set reachable_call_targets; + bool before_inlining_p = symtab->state < (!optimize ? IPA_SSA + : IPA_SSA_AFTER_INLINING); timevar_push (TV_IPA_UNREACHABLE); build_type_inheritance_graph (); @@ -414,19 +434,25 @@ symbol_table::remove_unreachable_nodes (bool before_inlining_p, FILE *file) } for (e = cnode->callees; e; e = e->next_callee) { + symtab_node *body = e->callee->function_symbol (); if (e->callee->definition && !e->callee->in_other_partition && (!e->inline_failed || !DECL_EXTERNAL (e->callee->decl) || e->callee->alias - || before_inlining_p)) + || (before_inlining_p + && (opt_for_fn (body->decl, optimize) + || (symtab->state < IPA_SSA + && lookup_attribute + ("always_inline", + DECL_ATTRIBUTES (body->decl))))))) { /* Be sure that we will not optimize out alias target body. */ if (DECL_EXTERNAL (e->callee->decl) && e->callee->alias && before_inlining_p) - reachable.add (e->callee->function_symbol ()); + reachable.add (body); reachable.add (e->callee); } enqueue_node (e->callee, &first, &reachable); @@ -1219,14 +1245,15 @@ propagate_single_user (varpool_node *vnode, cgraph_node *function, function = BOTTOM; } else - function = meet (function, dyn_cast (ref->referring), single_user_map); + function = meet (function, dyn_cast (ref->referring), + single_user_map); } return function; } /* Pass setting used_by_single_function flag. - This flag is set on variable when there is only one function that may possibly - referr to it. */ + This flag is set on variable when there is only one function that may + possibly referr to it. */ static unsigned int ipa_single_use (void) @@ -1304,7 +1331,10 @@ ipa_single_use (void) if (var->aux != BOTTOM) { #ifdef ENABLE_CHECKING - if (!single_user_map.get (var)) + /* Not having the single user known means that the VAR is + unreachable. Either someone forgot to remove unreachable + variables or the reachability here is wrong. */ + gcc_assert (single_user_map.get (var)); #endif if (dump_file) diff --git a/gcc/lto/ChangeLog b/gcc/lto/ChangeLog index 5f509f01b6e..7f1088fa145 100644 --- a/gcc/lto/ChangeLog +++ b/gcc/lto/ChangeLog @@ -1,3 +1,11 @@ +2014-12-11 Jan Hubicka + + PR ipa/61324 + * lto.c (read_cgraph_and_symbols): Update call of + remove_unreachable_nodes. + (do_whole_program_analysis): Remove call of + symtab->remove_unreachable_nodes + 2014-12-08 Trevor Saunders * lto.c (read_cgraph_and_symbols): allocate gimple_canonical_types diff --git a/gcc/lto/lto.c b/gcc/lto/lto.c index 1ff02f86ff8..324538a0cb8 100644 --- a/gcc/lto/lto.c +++ b/gcc/lto/lto.c @@ -3098,7 +3098,7 @@ read_cgraph_and_symbols (unsigned nfiles, const char **fnames) /* Removal of unreachable symbols is needed to make verify_symtab to pass; we are still having duplicated comdat groups containing local statics. We could also just remove them while merging. */ - symtab->remove_unreachable_nodes (true, dump_file); + symtab->remove_unreachable_nodes (dump_file); ggc_collect (); symtab->state = IPA_SSA; @@ -3255,7 +3255,6 @@ do_whole_program_analysis (void) symtab->state = IPA_SSA; execute_ipa_pass_list (g->get_passes ()->all_regular_ipa_passes); - symtab->remove_unreachable_nodes (false, dump_file); if (symtab->dump_file) { diff --git a/gcc/passes.c b/gcc/passes.c index 74b40e5d80b..3f9f7df11ed 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -2003,7 +2003,7 @@ execute_todo (unsigned int flags) if (flags & TODO_remove_functions) { gcc_assert (!cfun); - symtab->remove_unreachable_nodes (true, dump_file); + symtab->remove_unreachable_nodes (dump_file); } if ((flags & TODO_dump_symtab) && dump_file && !current_function_decl) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 4507037087e..e85830cadc5 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2014-12-11 Jan Hubicka + + PR ipa/61324 + * testsuite/g++.dg/pr61324.C: New testcase by Trevor Saunders. + * testsuite/g++.dg/tm/pr51411-2.C: Update se the extern function is + not eliminated early. + * testsuite/gcc.target/i386/pr57756.c: Turn extern inline into static + inline. + 2014-12-11 Richard Biener PR tree-optimization/42108 diff --git a/gcc/testsuite/g++.dg/pr61324.C b/gcc/testsuite/g++.dg/pr61324.C new file mode 100644 index 00000000000..610257436b3 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr61324.C @@ -0,0 +1,13 @@ +// { dg-do compile } +// { dg-options "-O -fkeep-inline-functions -fno-use-cxa-atexit" } +void foo (); + +struct S +{ + ~S () + { + foo (); + } +}; + +S s; diff --git a/gcc/testsuite/g++.dg/tm/pr51411-2.C b/gcc/testsuite/g++.dg/tm/pr51411-2.C index 4105d66ec9d..3147d2f1d87 100644 --- a/gcc/testsuite/g++.dg/tm/pr51411-2.C +++ b/gcc/testsuite/g++.dg/tm/pr51411-2.C @@ -26,6 +26,7 @@ public: bool compare(const basic_string& __str) const { return 0; } + void key (); }; typedef basic_string string; @@ -35,7 +36,7 @@ inline bool operator<(const basic_string<_CharT, _Traits>& __lhs, const basic_st return __lhs.compare(__rhs); } -extern template class basic_string; +template class basic_string; } diff --git a/gcc/testsuite/gcc.target/i386/pr57756.c b/gcc/testsuite/gcc.target/i386/pr57756.c index f3faa4f816e..7edd4b5d697 100644 --- a/gcc/testsuite/gcc.target/i386/pr57756.c +++ b/gcc/testsuite/gcc.target/i386/pr57756.c @@ -9,7 +9,7 @@ __inline int callee () /* { dg-error "inlining failed in call to always_inline" } __attribute__((target("sse"))) -__inline int caller () +static __inline int caller () { return callee(); /* { dg-error "called from here" } */ } -- 2.30.2