diff options
author | hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4> | 2010-10-16 23:47:08 +0000 |
---|---|---|
committer | hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4> | 2010-10-16 23:47:08 +0000 |
commit | 7fb046a4d65311926f93d6dcf202a5dc6c10478d (patch) | |
tree | ffa391cf9b24280a9b420916c1900577ff6fd326 /gcc/ipa.c | |
parent | e6ce85826bd7dd6f2a98d76f480a9df2db256cef (diff) | |
download | gcc-7fb046a4d65311926f93d6dcf202a5dc6c10478d.tar.gz |
PR middle-end/44206
* g++.dg/torture/pr44206.C: New.
* ipa.c (cgraph_remove_unreachable_nodes): Cleanup; check
existence of refrences before removing the function.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@165569 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/ipa.c')
-rw-r--r-- | gcc/ipa.c | 153 |
1 files changed, 112 insertions, 41 deletions
diff --git a/gcc/ipa.c b/gcc/ipa.c index dcf1e9ac072..af71330765f 100644 --- a/gcc/ipa.c +++ b/gcc/ipa.c @@ -384,22 +384,31 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file) } if (!node->aux) { + struct cgraph_edge *e; + bool found = false; + int i; + struct ipa_ref *ref; + node->global.inlined_to = NULL; if (file) fprintf (file, " %s", cgraph_node_name (node)); - if (!node->analyzed || !DECL_EXTERNAL (node->decl) || before_inlining_p) - cgraph_remove_node (node); - else + /* See if there is reachable caller. */ + for (e = node->callers; e && !found; e = e->next_caller) + if (e->caller->reachable) + found = true; + for (i = 0; (ipa_ref_list_refering_iterate (&node->ref_list, i, ref) + && !found); i++) + if (ref->refering_type == IPA_REF_CGRAPH + && ipa_ref_refering_node (ref)->reachable) + found = true; + else if (ref->refering_type == IPA_REF_VARPOOL + && ipa_ref_refering_varpool_node (ref)->needed) + found = true; + + /* If so, we need to keep node in the callgraph. */ + if (found || node->needed) { - struct cgraph_edge *e; - - /* See if there is reachable caller. */ - for (e = node->callers; e; e = e->next_caller) - if (e->caller->reachable) - break; - - /* If so, we need to keep node in the callgraph. */ - if (e || node->needed) + if (node->analyzed) { struct cgraph_node *clone; @@ -419,10 +428,10 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file) node->clone_of->clones = node->next_sibling_clone; if (node->next_sibling_clone) node->next_sibling_clone->prev_sibling_clone = node->prev_sibling_clone; -#ifdef ENABLE_CHECKING + #ifdef ENABLE_CHECKING if (node->clone_of) node->former_clone_of = node->clone_of->decl; -#endif + #endif node->clone_of = NULL; node->next_sibling_clone = NULL; node->prev_sibling_clone = NULL; @@ -430,13 +439,16 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file) else gcc_assert (!clone->in_other_partition); node->analyzed = false; + changed = true; cgraph_node_remove_callees (node); ipa_remove_all_references (&node->ref_list); } - else - cgraph_remove_node (node); } - changed = true; + else + { + cgraph_remove_node (node); + changed = true; + } } } for (node = cgraph_nodes; node; node = node->next) @@ -581,6 +593,7 @@ ipa_discover_readonly_nonaddressable_vars (void) static bool cgraph_externally_visible_p (struct cgraph_node *node, bool whole_program, bool aliased) { + struct cgraph_node *alias; if (!node->local.finalized) return false; if (!DECL_COMDAT (node->decl) @@ -595,6 +608,23 @@ cgraph_externally_visible_p (struct cgraph_node *node, bool whole_program, bool /* If linker counts on us, we must preserve the function. */ if (cgraph_used_from_object_file_p (node)) return true; + if (DECL_PRESERVE_P (node->decl)) + return true; + if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (node->decl))) + return true; + + /* See if we have linker information about symbol not being used or + if we need to make guess based on the declaration. + + Even if the linker clams the symbol is unused, never bring internal + symbols that are declared by user as used or externally visible. + This is needed for i.e. references from asm statements. */ + for (alias = node->same_body; alias; alias = alias->next) + if (alias->resolution != LDPR_PREVAILING_DEF_IRONLY) + break; + if (!alias && node->resolution == LDPR_PREVAILING_DEF_IRONLY) + return false; + /* When doing link time optimizations, hidden symbols become local. */ if (in_lto_p && (DECL_VISIBILITY (node->decl) == VISIBILITY_HIDDEN @@ -626,11 +656,70 @@ cgraph_externally_visible_p (struct cgraph_node *node, bool whole_program, bool return true; } } - if (DECL_PRESERVE_P (node->decl)) - return true; + if (MAIN_NAME_P (DECL_NAME (node->decl))) return true; - if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (node->decl))) + + return false; +} + +/* Return true when variable VNODE should be considered externally visible. */ + +static bool +varpool_externally_visible_p (struct varpool_node *vnode, bool aliased) +{ + struct varpool_node *alias; + if (!DECL_COMDAT (vnode->decl) && !TREE_PUBLIC (vnode->decl)) + return false; + + /* Do not even try to be smart about aliased nodes. Until we properly + represent everything by same body alias, these are just evil. */ + if (aliased) + return true; + + /* If linker counts on us, we must preserve the function. */ + if (varpool_used_from_object_file_p (vnode)) + return true; + + if (DECL_PRESERVE_P (vnode->decl)) + return true; + if (lookup_attribute ("externally_visible", + DECL_ATTRIBUTES (vnode->decl))) + return true; + + /* See if we have linker information about symbol not being used or + if we need to make guess based on the declaration. + + Even if the linker clams the symbol is unused, never bring internal + symbols that are declared by user as used or externally visible. + This is needed for i.e. references from asm statements. */ + if (varpool_used_from_object_file_p (vnode)) + return true; + for (alias = vnode->extra_name; alias; alias = alias->next) + if (alias->resolution != LDPR_PREVAILING_DEF_IRONLY) + break; + if (!alias && vnode->resolution == LDPR_PREVAILING_DEF_IRONLY) + return false; + + /* When doing link time optimizations, hidden symbols become local. */ + if (in_lto_p + && (DECL_VISIBILITY (vnode->decl) == VISIBILITY_HIDDEN + || DECL_VISIBILITY (vnode->decl) == VISIBILITY_INTERNAL) + /* Be sure that node is defined in IR file, not in other object + file. In that case we don't set used_from_other_object_file. */ + && vnode->finalized) + ; + else if (!flag_whole_program) + return true; + + /* Do not attempt to privatize COMDATS by default. + This would break linking with C++ libraries sharing + inline definitions. + + FIXME: We can do so for readonly vars with no address taken and + possibly also for vtables since no direct pointer comparsion is done. + It might be interesting to do so to reduce linking overhead. */ + if (DECL_COMDAT (vnode->decl) || DECL_WEAK (vnode->decl)) return true; return false; } @@ -786,27 +875,9 @@ function_and_variable_visibility (bool whole_program) if (!vnode->finalized) continue; if (vnode->needed - && (DECL_COMDAT (vnode->decl) || TREE_PUBLIC (vnode->decl)) - && (((!whole_program - /* We can privatize comdat readonly variables whose address is - not taken, but doing so is not going to bring us - optimization oppurtunities until we start reordering - datastructures. */ - || DECL_COMDAT (vnode->decl) - || DECL_WEAK (vnode->decl)) - /* When doing linktime optimizations, all hidden symbols will - become local. */ - && (!in_lto_p - || (DECL_VISIBILITY (vnode->decl) != VISIBILITY_HIDDEN - && DECL_VISIBILITY (vnode->decl) != VISIBILITY_INTERNAL) - /* We can get prevailing decision in other object file. - In this case we do not sed used_from_object_file. */ - || !vnode->finalized)) - || DECL_PRESERVE_P (vnode->decl) - || varpool_used_from_object_file_p (vnode) - || pointer_set_contains (aliased_vnodes, vnode) - || lookup_attribute ("externally_visible", - DECL_ATTRIBUTES (vnode->decl)))) + && varpool_externally_visible_p + (vnode, + pointer_set_contains (aliased_vnodes, vnode))) vnode->externally_visible = true; else vnode->externally_visible = false; |