diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 18 | ||||
-rw-r--r-- | gcc/cgraph.c | 111 | ||||
-rw-r--r-- | gcc/cgraph.h | 33 | ||||
-rw-r--r-- | gcc/cgraphunit.c | 5 | ||||
-rw-r--r-- | gcc/ipa-inline.c | 6 |
5 files changed, 164 insertions, 9 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1ee5d8ea9fc..407911ec2f7 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,23 @@ 2005-06-02 Jan Hubicka <jh@suse.cz> + * cgraph.c (cgraph_node): Maintain master clones. + (cgraph_remove_node): Likewise. + (availability_names): New static variable. + (dump_cgraph_node): Dump availability. + (dump_cgraph_varpool_node): Likewise. + (cgraph_is_master_clone, cgraph_master_clone, + cgraph_function_body_availability, + cgraph_variable_initializer_availability): New functions. + * cgraph.h (availability): New enum. + (struct cgraph_node): Add master_clone. + (cgraph_is_master_clone, cgraph_master_clone, + cgraph_function_body_availability, + cgraph_variable_initializer_availability): Declare. + * cgraphunit.c (cgraph_expand_function): Setcgraph_function_flags_ready. + (cgraph_remove_unreachable_nodes): Remove unreachable nodes. + * ipa-inline.c (cgraph_decide_inlining): Do not call + cgraph_remove_unreachable_nodes. + * cgraphunit.c (cgraph_function_and_variable_visibility): Fix typo in previous patch. diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 04711910fe0..6af75e12dcf 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -192,7 +192,12 @@ cgraph_node (tree decl) slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, INSERT); if (*slot) - return *slot; + { + node = *slot; + if (!node->master_clone) + node->master_clone = node; + return node; + } node = cgraph_create_node (); node->decl = decl; @@ -202,6 +207,7 @@ cgraph_node (tree decl) node->origin = cgraph_node (DECL_CONTEXT (decl)); node->next_nested = node->origin->nested; node->origin->nested = node; + node->master_clone = node; } return node; } @@ -436,7 +442,14 @@ cgraph_remove_node (struct cgraph_node *node) { if (node->next_clone) { - *slot = node->next_clone; + struct cgraph_node *new_node = node->next_clone; + struct cgraph_node *n; + + /* Make the next clone be the master clone */ + for (n = new_node; n; n = n->next_clone) + n->master_clone = new_node; + + *slot = new_node; node->next_clone->prev_clone = NULL; } else @@ -553,6 +566,10 @@ cgraph_varpool_node_name (struct cgraph_varpool_node *node) return lang_hooks.decl_printable_name (node->decl, 2); } +/* Names used to print out the availability enum. */ +static const char * const availability_names[] = + {"unset", "not_available", "overwrittable", "available", "local"}; + /* Dump given cgraph node. */ void dump_cgraph_node (FILE *f, struct cgraph_node *node) @@ -563,6 +580,11 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node) fprintf (f, " (inline copy in %s/%i)", cgraph_node_name (node->global.inlined_to), node->global.inlined_to->uid); + if (cgraph_function_flags_ready) + fprintf (f, " availability:%s", + availability_names [cgraph_function_body_availability (node)]); + if (node->master_clone && node->master_clone->uid != node->uid) + fprintf (f, "(%i)", node->master_clone->uid); if (node->count) fprintf (f, " executed "HOST_WIDEST_INT_PRINT_DEC"x", (HOST_WIDEST_INT)node->count); @@ -614,6 +636,11 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node) edge->callee->uid); if (!edge->inline_failed) fprintf(f, "(inlined) "); + if (edge->count) + fprintf (f, "("HOST_WIDEST_INT_PRINT_DEC"x) ", + (HOST_WIDEST_INT)edge->count); + if (edge->loop_nest) + fprintf (f, "(nested in %i loops) ", edge->loop_nest); } fprintf (f, "\n"); } @@ -635,6 +662,7 @@ void dump_cgraph_varpool_node (FILE *f, struct cgraph_varpool_node *node) { fprintf (f, "%s:", cgraph_varpool_node_name (node)); + fprintf (f, " availability:%s", availability_names [cgraph_variable_initializer_availability (node)]); if (DECL_INITIAL (node->decl)) fprintf (f, " initialized"); if (node->needed) @@ -886,6 +914,7 @@ cgraph_clone_node (struct cgraph_node *n, gcov_type count, int loop_nest) new->local = n->local; new->global = n->global; new->rtl = n->rtl; + new->master_clone = n->master_clone; new->count = count; if (n->count) count_scale = new->count * REG_BR_PROB_BASE / n->count; @@ -905,6 +934,28 @@ cgraph_clone_node (struct cgraph_node *n, gcov_type count, int loop_nest) return new; } +/* Return true if N is an master_clone, (see cgraph_master_clone). */ + +bool +cgraph_is_master_clone (struct cgraph_node *n) +{ + return (n == cgraph_master_clone (n)); +} + +struct cgraph_node * +cgraph_master_clone (struct cgraph_node *n) +{ + enum availability avail = cgraph_function_body_availability (n); + + if (avail == AVAIL_NOT_AVAILABLE || avail == AVAIL_OVERWRITABLE) + return NULL; + + if (!n->master_clone) + n->master_clone = cgraph_node (n->decl); + + return n->master_clone; +} + /* NODE is no longer nested function; update cgraph accordingly. */ void cgraph_unnest_node (struct cgraph_node *node) @@ -917,4 +968,60 @@ cgraph_unnest_node (struct cgraph_node *node) *node2 = node->next_nested; node->origin = NULL; } + +/* Return function availability. See cgraph.h for description of individual + return values. */ +enum availability +cgraph_function_body_availability (struct cgraph_node *node) +{ + enum availability avail; + gcc_assert (cgraph_function_flags_ready); + if (!node->local.finalized) + avail = AVAIL_NOT_AVAILABLE; + else if (node->local.local) + avail = AVAIL_LOCAL; + else if (node->local.externally_visible) + avail = AVAIL_AVAILABLE; + + /* If the function can be overwritten, return OVERWRITABLE. Take + care at least of two notable extensions - the COMDAT functions + used to share template instantiations in C++ (this is symmetric + to code cp_cannot_inline_tree_fn and probably shall be shared and + the inlinability hooks completelly elliminated). + + ??? Does the C++ one definition rule allow us to always return + AVAIL_AVAILABLE here? That would be good reason to preserve this + hook Similarly deal with extern inline functions - this is again + neccesary to get C++ shared functions having keyed templates + right and in the C extension documentation we probably should + document the requirement of both versions of function (extern + inline and offline) having same side effect characteristics as + good optimization is what this optimization is about. */ + + else if (!(*targetm.binds_local_p) (node->decl) + && !DECL_COMDAT (node->decl) && !DECL_EXTERNAL (node->decl)) + avail = AVAIL_OVERWRITABLE; + else avail = AVAIL_AVAILABLE; + + return avail; +} + +/* Return variable availability. See cgraph.h for description of individual + return values. */ +enum availability +cgraph_variable_initializer_availability (struct cgraph_varpool_node *node) +{ + gcc_assert (cgraph_function_flags_ready); + if (!node->finalized) + return AVAIL_NOT_AVAILABLE; + if (!TREE_PUBLIC (node->decl)) + return AVAIL_AVAILABLE; + /* If the variable can be overwritted, return OVERWRITABLE. Takes + care of at least two notable extensions - the COMDAT variables + used to share template instantiations in C++. */ + if (!(*targetm.binds_local_p) (node->decl) && !DECL_COMDAT (node->decl)) + return AVAIL_OVERWRITABLE; + return AVAIL_AVAILABLE; +} + #include "gt-cgraph.h" diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 96e382a4a43..9406e090f33 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -24,6 +24,28 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "tree.h" #include "basic-block.h" +enum availability +{ + /* Not yet set by cgraph_function_body_availability. */ + AVAIL_UNSET, + /* Function body/variable initializer is unknown. */ + AVAIL_NOT_AVAILABLE, + /* Function body/variable initializer is known but might be replaced + by a different one from other compilation unit and thus needs to + be dealt with a care. Like AVAIL_NOT_AVAILABLE it can have + arbitrary side effects on escaping variables and functions, while + like AVAILABLE it might access static variables. */ + AVAIL_OVERWRITABLE, + /* Function body/variable initializer is known and will be used in final + program. */ + AVAIL_AVAILABLE, + /* Function body/variable initializer is known and all it's uses are explicitly + visible within current unit (ie it's address is never taken and it is not + exported to other units). + Currently used only for functions. */ + AVAIL_LOCAL +}; + /* Information about the function collected locally. Available after function is analyzed. */ @@ -110,6 +132,10 @@ struct cgraph_node GTY((chain_next ("%h.next"), chain_prev ("%h.previous"))) /* Pointer to the next clone. */ struct cgraph_node *next_clone; struct cgraph_node *prev_clone; + /* Pointer to a single unique cgraph node for this function. If the + function is to be output, this is the copy that will survive. */ + struct cgraph_node *master_clone; + PTR GTY ((skip)) aux; struct cgraph_local_info local; @@ -178,7 +204,7 @@ struct cgraph_varpool_node GTY(()) bool analyzed; /* Set once it has been finalized so we consider it to be output. */ bool finalized; - /* Set when function is scheduled to be assembled. */ + /* Set when variable is scheduled to be assembled. */ bool output; /* Set when function is visible by other units. */ bool externally_visible; @@ -229,6 +255,11 @@ void cgraph_varpool_enqueue_needed_node (struct cgraph_varpool_node *); void cgraph_varpool_reset_queue (void); bool decide_is_variable_needed (struct cgraph_varpool_node *, tree); +enum availability cgraph_function_body_availability (struct cgraph_node *); +enum availability cgraph_variable_initializer_availability (struct cgraph_varpool_node *); +bool cgraph_is_master_clone (struct cgraph_node *); +struct cgraph_node *cgraph_master_clone (struct cgraph_node *); + /* In cgraphunit.c */ bool cgraph_assemble_pending_functions (void); bool cgraph_varpool_assemble_pending_decls (void); diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index 7f8a96d36f6..ee859f7167b 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -967,6 +967,8 @@ cgraph_expand_function (struct cgraph_node *node) points to the dead function body. */ cgraph_node_remove_callees (node); } + + cgraph_function_flags_ready = true; } /* Return true when CALLER_DECL should be inlined into CALLEE_DECL. */ @@ -1128,6 +1130,9 @@ cgraph_optimize (void) dump_cgraph (cgraph_dump_file); } ipa_passes (); + /* This pass remove bodies of extern inline functions we never inlined. + Do this later so other IPA passes see what is really going on. */ + cgraph_remove_unreachable_nodes (false, dump_file); cgraph_global_info_ready = true; if (cgraph_dump_file) { diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c index e58ac0f0cfa..5a336ff1b1b 100644 --- a/gcc/ipa-inline.c +++ b/gcc/ipa-inline.c @@ -857,12 +857,6 @@ cgraph_decide_inlining (void) } } - /* We will never output extern functions we didn't inline. - ??? Perhaps we can prevent accounting of growth of external - inline functions. */ - - cgraph_remove_unreachable_nodes (false, dump_file); - if (dump_file) fprintf (dump_file, "\nInlined %i calls, eliminated %i functions, " |