Commit 6b02a499 by Jan Hubicka Committed by Jan Hubicka

cgraph.c (cgraph_node): Maintain master clones.


	* 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.

From-SVN: r100507
parent 04b0eed0
2005-06-02 Jan Hubicka <jh@suse.cz> 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 * cgraphunit.c (cgraph_function_and_variable_visibility): Fix typo in
previous patch. previous patch.
......
...@@ -192,7 +192,12 @@ cgraph_node (tree decl) ...@@ -192,7 +192,12 @@ cgraph_node (tree decl)
slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, INSERT); slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, INSERT);
if (*slot) if (*slot)
return *slot; {
node = *slot;
if (!node->master_clone)
node->master_clone = node;
return node;
}
node = cgraph_create_node (); node = cgraph_create_node ();
node->decl = decl; node->decl = decl;
...@@ -202,6 +207,7 @@ cgraph_node (tree decl) ...@@ -202,6 +207,7 @@ cgraph_node (tree decl)
node->origin = cgraph_node (DECL_CONTEXT (decl)); node->origin = cgraph_node (DECL_CONTEXT (decl));
node->next_nested = node->origin->nested; node->next_nested = node->origin->nested;
node->origin->nested = node; node->origin->nested = node;
node->master_clone = node;
} }
return node; return node;
} }
...@@ -436,7 +442,14 @@ cgraph_remove_node (struct cgraph_node *node) ...@@ -436,7 +442,14 @@ cgraph_remove_node (struct cgraph_node *node)
{ {
if (node->next_clone) 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; node->next_clone->prev_clone = NULL;
} }
else else
...@@ -553,6 +566,10 @@ cgraph_varpool_node_name (struct cgraph_varpool_node *node) ...@@ -553,6 +566,10 @@ cgraph_varpool_node_name (struct cgraph_varpool_node *node)
return lang_hooks.decl_printable_name (node->decl, 2); 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. */ /* Dump given cgraph node. */
void void
dump_cgraph_node (FILE *f, struct cgraph_node *node) dump_cgraph_node (FILE *f, struct cgraph_node *node)
...@@ -563,6 +580,11 @@ 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)", fprintf (f, " (inline copy in %s/%i)",
cgraph_node_name (node->global.inlined_to), cgraph_node_name (node->global.inlined_to),
node->global.inlined_to->uid); 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) if (node->count)
fprintf (f, " executed "HOST_WIDEST_INT_PRINT_DEC"x", fprintf (f, " executed "HOST_WIDEST_INT_PRINT_DEC"x",
(HOST_WIDEST_INT)node->count); (HOST_WIDEST_INT)node->count);
...@@ -614,6 +636,11 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node) ...@@ -614,6 +636,11 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
edge->callee->uid); edge->callee->uid);
if (!edge->inline_failed) if (!edge->inline_failed)
fprintf(f, "(inlined) "); 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"); fprintf (f, "\n");
} }
...@@ -635,6 +662,7 @@ void ...@@ -635,6 +662,7 @@ void
dump_cgraph_varpool_node (FILE *f, struct cgraph_varpool_node *node) dump_cgraph_varpool_node (FILE *f, struct cgraph_varpool_node *node)
{ {
fprintf (f, "%s:", cgraph_varpool_node_name (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)) if (DECL_INITIAL (node->decl))
fprintf (f, " initialized"); fprintf (f, " initialized");
if (node->needed) if (node->needed)
...@@ -886,6 +914,7 @@ cgraph_clone_node (struct cgraph_node *n, gcov_type count, int loop_nest) ...@@ -886,6 +914,7 @@ cgraph_clone_node (struct cgraph_node *n, gcov_type count, int loop_nest)
new->local = n->local; new->local = n->local;
new->global = n->global; new->global = n->global;
new->rtl = n->rtl; new->rtl = n->rtl;
new->master_clone = n->master_clone;
new->count = count; new->count = count;
if (n->count) if (n->count)
count_scale = new->count * REG_BR_PROB_BASE / 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) ...@@ -905,6 +934,28 @@ cgraph_clone_node (struct cgraph_node *n, gcov_type count, int loop_nest)
return new; 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. */ /* NODE is no longer nested function; update cgraph accordingly. */
void void
cgraph_unnest_node (struct cgraph_node *node) cgraph_unnest_node (struct cgraph_node *node)
...@@ -917,4 +968,60 @@ cgraph_unnest_node (struct cgraph_node *node) ...@@ -917,4 +968,60 @@ cgraph_unnest_node (struct cgraph_node *node)
*node2 = node->next_nested; *node2 = node->next_nested;
node->origin = NULL; 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" #include "gt-cgraph.h"
...@@ -24,6 +24,28 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA ...@@ -24,6 +24,28 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "tree.h" #include "tree.h"
#include "basic-block.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. /* Information about the function collected locally.
Available after function is analyzed. */ Available after function is analyzed. */
...@@ -110,6 +132,10 @@ struct cgraph_node GTY((chain_next ("%h.next"), chain_prev ("%h.previous"))) ...@@ -110,6 +132,10 @@ struct cgraph_node GTY((chain_next ("%h.next"), chain_prev ("%h.previous")))
/* Pointer to the next clone. */ /* Pointer to the next clone. */
struct cgraph_node *next_clone; struct cgraph_node *next_clone;
struct cgraph_node *prev_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; PTR GTY ((skip)) aux;
struct cgraph_local_info local; struct cgraph_local_info local;
...@@ -178,7 +204,7 @@ struct cgraph_varpool_node GTY(()) ...@@ -178,7 +204,7 @@ struct cgraph_varpool_node GTY(())
bool analyzed; bool analyzed;
/* Set once it has been finalized so we consider it to be output. */ /* Set once it has been finalized so we consider it to be output. */
bool finalized; bool finalized;
/* Set when function is scheduled to be assembled. */ /* Set when variable is scheduled to be assembled. */
bool output; bool output;
/* Set when function is visible by other units. */ /* Set when function is visible by other units. */
bool externally_visible; bool externally_visible;
...@@ -229,6 +255,11 @@ void cgraph_varpool_enqueue_needed_node (struct cgraph_varpool_node *); ...@@ -229,6 +255,11 @@ void cgraph_varpool_enqueue_needed_node (struct cgraph_varpool_node *);
void cgraph_varpool_reset_queue (void); void cgraph_varpool_reset_queue (void);
bool decide_is_variable_needed (struct cgraph_varpool_node *, tree); 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 */ /* In cgraphunit.c */
bool cgraph_assemble_pending_functions (void); bool cgraph_assemble_pending_functions (void);
bool cgraph_varpool_assemble_pending_decls (void); bool cgraph_varpool_assemble_pending_decls (void);
......
...@@ -967,6 +967,8 @@ cgraph_expand_function (struct cgraph_node *node) ...@@ -967,6 +967,8 @@ cgraph_expand_function (struct cgraph_node *node)
points to the dead function body. */ points to the dead function body. */
cgraph_node_remove_callees (node); cgraph_node_remove_callees (node);
} }
cgraph_function_flags_ready = true;
} }
/* Return true when CALLER_DECL should be inlined into CALLEE_DECL. */ /* Return true when CALLER_DECL should be inlined into CALLEE_DECL. */
...@@ -1128,6 +1130,9 @@ cgraph_optimize (void) ...@@ -1128,6 +1130,9 @@ cgraph_optimize (void)
dump_cgraph (cgraph_dump_file); dump_cgraph (cgraph_dump_file);
} }
ipa_passes (); 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; cgraph_global_info_ready = true;
if (cgraph_dump_file) if (cgraph_dump_file)
{ {
......
...@@ -857,12 +857,6 @@ cgraph_decide_inlining (void) ...@@ -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) if (dump_file)
fprintf (dump_file, fprintf (dump_file,
"\nInlined %i calls, eliminated %i functions, " "\nInlined %i calls, eliminated %i functions, "
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment