From 6f932b06df8837c89ebb97e12c497f86d08b7f60 Mon Sep 17 00:00:00 2001 From: hubicka Date: Wed, 12 May 2010 13:49:34 +0000 Subject: [PATCH] * cgraph.h (struct varpool_node): Add aux. * varasm.c (find_decl_and_mark_needed): Force output of varpool nodes. * varpool.c (varpool_remove_node): Do not remove initializer. (varpool_reset_queue): Export. (varpool_finalize_decl): Volatile vars are forced to be output. * lto-symtab.c (lto_varpool_replace_node): Clear out initializer of replaced decl. * ipa.c (enqueue_cgraph_node, enqueue_varpool_node, process_references, varpool_can_remove_if_no_refs): New functions. (cgraph_remove_unreachable_nodes): Handle variables too. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@159321 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 13 ++++ gcc/cgraph.c | 2 +- gcc/cgraph.h | 2 + gcc/ipa.c | 212 ++++++++++++++++++++++++++++++++++++++++-------------- gcc/lto-symtab.c | 3 + gcc/tree-inline.c | 2 +- gcc/tree-inline.h | 2 - gcc/varasm.c | 1 + gcc/varpool.c | 6 +- 9 files changed, 181 insertions(+), 62 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 09043fd2ccf..c5d8e6d966b 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2010-05-12 Jan Hubicka + + * cgraph.h (struct varpool_node): Add aux. + * varasm.c (find_decl_and_mark_needed): Force output of varpool nodes. + * varpool.c (varpool_remove_node): Do not remove initializer. + (varpool_reset_queue): Export. + (varpool_finalize_decl): Volatile vars are forced to be output. + * lto-symtab.c (lto_varpool_replace_node): Clear out initializer of + replaced decl. + * ipa.c (enqueue_cgraph_node, enqueue_varpool_node, + process_references, varpool_can_remove_if_no_refs): New functions. + (cgraph_remove_unreachable_nodes): Handle variables too. + 2010-05-12 H.J. Lu PR target/44088 diff --git a/gcc/cgraph.c b/gcc/cgraph.c index a6aed42517a..60effb0f5ce 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -2387,7 +2387,7 @@ cgraph_add_new_function (tree fndecl, bool lowered) bool cgraph_node_can_be_local_p (struct cgraph_node *node) { - return (!node->needed + return (!node->needed && !node->address_taken && ((DECL_COMDAT (node->decl) && !node->same_comdat_group) || !node->local.externally_visible)); } diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 97df56bc418..eaf9bfd5cd3 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -439,6 +439,7 @@ struct GTY((chain_next ("%h.next"), chain_prev ("%h.prev"))) varpool_node { nodes a pointer to the normal node. */ struct varpool_node *extra_name; struct ipa_ref_list ref_list; + PTR GTY ((skip)) aux; /* Ordering of all cgraph nodes. */ int order; @@ -673,6 +674,7 @@ void varpool_remove_unreferenced_decls (void); void varpool_empty_needed_queue (void); bool varpool_extra_name_alias (tree, tree); const char * varpool_node_name (struct varpool_node *node); +void varpool_reset_queue (void); /* Walk all reachable static variables. */ #define FOR_EACH_STATIC_VARIABLE(node) \ diff --git a/gcc/ipa.c b/gcc/ipa.c index 6519b26cec1..1fc3fdd41a6 100644 --- a/gcc/ipa.c +++ b/gcc/ipa.c @@ -118,6 +118,69 @@ update_inlined_to_pointer (struct cgraph_node *node, struct cgraph_node *inlined } } +/* Add cgraph NODE to queue starting at FIRST. */ + +static void +enqueue_cgraph_node (struct cgraph_node *node, struct cgraph_node **first) +{ + node->aux = *first; + *first = node; +} + +/* Add varpool NODE to queue starting at FIRST. */ + +static void +enqueue_varpool_node (struct varpool_node *node, struct varpool_node **first) +{ + node->aux = *first; + *first = node; +} + +/* Process references. */ + +static void +process_references (struct ipa_ref_list *list, + struct cgraph_node **first, + struct varpool_node **first_varpool, + bool before_inlining_p) +{ + int i; + struct ipa_ref *ref; + for (i = 0; ipa_ref_list_reference_iterate (list, i, ref); i++) + { + if (ref->refered_type == IPA_REF_CGRAPH) + { + struct cgraph_node *node = ipa_ref_node (ref); + if (!node->reachable + && (!DECL_EXTERNAL (node->decl) + || before_inlining_p)) + { + node->reachable = true; + enqueue_cgraph_node (node, first); + } + } + else + { + struct varpool_node *node = ipa_ref_varpool_node (ref); + if (!node->needed) + { + varpool_mark_needed_node (node); + enqueue_varpool_node (node, first_varpool); + } + } + } +} + +/* Return true when function NODE can be removed from callgraph + if all direct calls are eliminated. */ + +static inline bool +varpool_can_remove_if_no_refs (struct varpool_node *node) +{ + return (!node->force_output && !node->used_from_other_partition + && (DECL_COMDAT (node->decl) || !node->externally_visible)); +} + /* Perform reachability analysis and reclaim all unreachable nodes. If BEFORE_INLINING_P is true this function is called before inlining decisions has been made. If BEFORE_INLINING_P is false this function also @@ -127,8 +190,10 @@ bool cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file) { struct cgraph_node *first = (struct cgraph_node *) (void *) 1; + struct varpool_node *first_varpool = (struct varpool_node *) (void *) 1; struct cgraph_node *processed = (struct cgraph_node *) (void *) 2; struct cgraph_node *node, *next; + struct varpool_node *vnode, *vnext; bool changed = false; #ifdef ENABLE_CHECKING @@ -140,15 +205,14 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file) for (node = cgraph_nodes; node; node = node->next) gcc_assert (!node->aux); #endif + varpool_reset_queue (); for (node = cgraph_nodes; node; node = node->next) if (!cgraph_can_remove_if_no_direct_calls_p (node) && ((!DECL_EXTERNAL (node->decl)) - || !node->analyzed || before_inlining_p)) { gcc_assert (!node->global.inlined_to); - node->aux = first; - first = node; + enqueue_cgraph_node (node, &first); node->reachable = true; } else @@ -156,68 +220,92 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file) gcc_assert (!node->aux); node->reachable = false; } + for (vnode = varpool_nodes; vnode; vnode = vnode->next) + { + vnode->next_needed = NULL; + vnode->prev_needed = NULL; + if (!varpool_can_remove_if_no_refs (vnode)) + { + vnode->needed = false; + varpool_mark_needed_node (vnode); + enqueue_varpool_node (vnode, &first_varpool); + } + else + vnode->needed = false; + } /* Perform reachability analysis. As a special case do not consider extern inline functions not inlined as live because we won't output them at all. */ - while (first != (void *) 1) + while (first != (struct cgraph_node *) (void *) 1 + || first_varpool != (struct varpool_node *) (void *) 1) { - struct cgraph_edge *e; - node = first; - first = (struct cgraph_node *) first->aux; - node->aux = processed; - - if (node->reachable) - for (e = node->callees; e; e = e->next_callee) - if (!e->callee->reachable - && node->analyzed - && (!e->inline_failed || !e->callee->analyzed - || (!DECL_EXTERNAL (e->callee->decl)) - || before_inlining_p)) + if (first != (struct cgraph_node *) (void *) 1) + { + struct cgraph_edge *e; + node = first; + first = (struct cgraph_node *) first->aux; + node->aux = processed; + + if (node->reachable) + for (e = node->callees; e; e = e->next_callee) + if (!e->callee->reachable + && node->analyzed + && (!e->inline_failed || !e->callee->analyzed + || (!DECL_EXTERNAL (e->callee->decl)) + || before_inlining_p)) + { + bool prev_reachable = e->callee->reachable; + e->callee->reachable |= node->reachable; + if (!e->callee->aux + || (e->callee->aux == processed + && prev_reachable != e->callee->reachable)) + { + e->callee->aux = first; + first = e->callee; + } + } + + /* If any function in a comdat group is reachable, force + all other functions in the same comdat group to be + also reachable. */ + if (node->same_comdat_group + && node->reachable + && !node->global.inlined_to) { - bool prev_reachable = e->callee->reachable; - e->callee->reachable |= node->reachable; - if (!e->callee->aux - || (e->callee->aux == processed - && prev_reachable != e->callee->reachable)) - { - e->callee->aux = first; - first = e->callee; - } + for (next = node->same_comdat_group; + next != node; + next = next->same_comdat_group) + if (!next->reachable) + { + next->aux = first; + first = next; + next->reachable = true; + } } - /* If any function in a comdat group is reachable, force - all other functions in the same comdat group to be - also reachable. */ - if (node->same_comdat_group - && node->reachable - && !node->global.inlined_to) - { - for (next = node->same_comdat_group; - next != node; - next = next->same_comdat_group) - if (!next->reachable) - { - next->aux = first; - first = next; - next->reachable = true; - } - } - - /* We can freely remove inline clones even if they are cloned, however if - function is clone of real clone, we must keep it around in order to - make materialize_clones produce function body with the changes - applied. */ - while (node->clone_of && !node->clone_of->aux && !gimple_has_body_p (node->decl)) - { - bool noninline = node->clone_of->decl != node->decl; - node = node->clone_of; - if (noninline) + /* We can freely remove inline clones even if they are cloned, however if + function is clone of real clone, we must keep it around in order to + make materialize_clones produce function body with the changes + applied. */ + while (node->clone_of && !node->clone_of->aux && !gimple_has_body_p (node->decl)) { - node->aux = first; - first = node; - break; + bool noninline = node->clone_of->decl != node->decl; + node = node->clone_of; + if (noninline) + { + enqueue_cgraph_node (node, &first); + break; + } } + process_references (&node->ref_list, &first, &first_varpool, before_inlining_p); + } + if (first_varpool != (struct varpool_node *) (void *) 1) + { + vnode = first_varpool; + first_varpool = (struct varpool_node *)first_varpool->aux; + vnode->aux = NULL; + process_references (&vnode->ref_list, &first, &first_varpool, before_inlining_p); } } @@ -274,6 +362,7 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file) else gcc_assert (!clone->in_other_partition); cgraph_node_remove_callees (node); + ipa_remove_all_references (&node->ref_list); if (node->prev_sibling_clone) node->prev_sibling_clone->next_sibling_clone = node->next_sibling_clone; else if (node->clone_of) @@ -304,6 +393,19 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file) } node->aux = NULL; } + if (file) + fprintf (file, "\nReclaiming variables:"); + for (vnode = varpool_nodes; vnode; vnode = vnext) + { + vnext = vnode->next; + if (!vnode->needed) + { + if (file) + fprintf (file, " %s", varpool_node_name (vnode)); + varpool_remove_node (vnode); + } + } + #ifdef ENABLE_CHECKING verify_cgraph (); #endif diff --git a/gcc/lto-symtab.c b/gcc/lto-symtab.c index f8349cf763f..f8a244cbeec 100644 --- a/gcc/lto-symtab.c +++ b/gcc/lto-symtab.c @@ -291,6 +291,9 @@ lto_varpool_replace_node (struct varpool_node *vnode, prevailing_node = prevailing_node->extra_name; ipa_clone_refering (NULL, prevailing_node, &vnode->ref_list); + /* Be sure we can garbage collect the initializer. */ + if (DECL_INITIAL (vnode->decl)) + DECL_INITIAL (vnode->decl) = error_mark_node; /* Finally remove the replaced node. */ varpool_remove_node (vnode); } diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index ee7a45712df..302d36b9f34 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -688,7 +688,7 @@ copy_bind_expr (tree *tp, int *walk_subtrees, copy_body_data *id) /* Create a new gimple_seq by remapping all the statements in BODY using the inlining information in ID. */ -gimple_seq +static gimple_seq remap_gimple_seq (gimple_seq body, copy_body_data *id) { gimple_stmt_iterator si; diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h index 29932e84e38..dba5eb2d881 100644 --- a/gcc/tree-inline.h +++ b/gcc/tree-inline.h @@ -174,7 +174,6 @@ tree maybe_inline_call_in_expr (tree); bool tree_inlinable_function_p (tree); tree copy_tree_r (tree *, int *, void *); tree copy_decl_no_change (tree decl, copy_body_data *id); -void save_body (tree, tree *, tree *); int estimate_move_cost (tree type); int estimate_num_insns (gimple, eni_weights *); int estimate_num_insns_fn (tree, eni_weights *); @@ -182,7 +181,6 @@ int count_insns_seq (gimple_seq, eni_weights *); bool tree_versionable_function_p (tree); bool tree_can_inline_p (struct cgraph_edge *e); -extern gimple_seq remap_gimple_seq (gimple_seq, copy_body_data *); extern tree remap_decl (tree decl, copy_body_data *id); extern tree remap_type (tree type, copy_body_data *id); extern gimple_seq copy_gimple_seq_and_replace_locals (gimple_seq seq); diff --git a/gcc/varasm.c b/gcc/varasm.c index 9f2f0a6f957..15ff1b3c212 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -5650,6 +5650,7 @@ find_decl_and_mark_needed (tree decl, tree target) else if (vnode) { varpool_mark_needed_node (vnode); + vnode->force_output = 1; return vnode->decl; } else diff --git a/gcc/varpool.c b/gcc/varpool.c index a540d035a7e..39a6565842a 100644 --- a/gcc/varpool.c +++ b/gcc/varpool.c @@ -196,8 +196,6 @@ varpool_remove_node (struct varpool_node *node) } ipa_remove_all_references (&node->ref_list); ipa_remove_all_refering (&node->ref_list); - if (DECL_INITIAL (node->decl)) - DECL_INITIAL (node->decl) = error_mark_node; ggc_free (node); } @@ -302,7 +300,7 @@ varpool_mark_needed_node (struct varpool_node *node) } /* Reset the queue of needed nodes. */ -static void +void varpool_reset_queue (void) { varpool_last_needed_node = NULL; @@ -383,6 +381,8 @@ varpool_finalize_decl (tree decl) if (node->needed) varpool_enqueue_needed_node (node); node->finalized = true; + if (TREE_THIS_VOLATILE (decl)) + node->force_output = true; if (decide_is_variable_needed (node, decl)) varpool_mark_needed_node (node); -- 2.11.0