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
This commit is contained in:
Jan Hubicka 2005-06-02 21:41:31 +02:00 committed by Jan Hubicka
parent 04b0eed045
commit 6b02a4997b
5 changed files with 164 additions and 9 deletions

View File

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

View File

@ -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"

View File

@ -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);

View File

@ -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)
{

View File

@ -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, "