Commit e33c6cd6 by Martin Jambor Committed by Martin Jambor

cgraph.h (struct cgraph_node): New field indirect_calls.

2010-04-28  Martin Jambor  <mjambor@suse.cz>

	* cgraph.h (struct cgraph_node): New field indirect_calls.
	(struct cgraph_indirect_call_info): New type.
	(struct cgraph_edge): Removed field indirect_call. New fields
	indirect_info, indirect_inlining_edge and indirect_unknown_callee.
	(cgraph_create_indirect_edge): Declare.
	(cgraph_make_edge_direct): Likewise.
	(enum LTO_cgraph_tags): New item LTO_cgraph_indirect_edge.
	* ipa-prop.h (struct ipa_param_call_note): Removed.
	(struct ipa_node_params): Removed field param_calls.
	(ipa_create_all_structures_for_iinln): Declare.
	* cgraph.c: Described indirect edges and uids in initial comment.
	(cgraph_add_edge_to_call_site_hash): New function.
	(cgraph_edge): Search also among the indirect edges, use
	cgraph_add_edge_to_call_site_hash to add edges to the call site hash.
	(cgraph_set_call_stmt): Possibly turn an indirect edge into a direct
	one, use cgraph_add_edge_to_call_site_hash to add edges to the call
	site hash.
	(initialize_inline_failed): Assign a reason to indirect edges.
	(cgraph_create_edge_1): New function.
	(cgraph_create_edge): Moved some functionality to
	cgraph_create_edge_1.
	(cgraph_create_indirect_edge): New function.
	(cgraph_edge_remove_callee): Add an assert checking for
	non-indirectness.
	(cgraph_edge_remove_caller): Special-case indirect edges.
	(cgraph_remove_edge): Likewise.
	(cgraph_set_edge_callee): New function.
	(cgraph_redirect_edge_callee): Use cgraph_set_edge_callee.
	(cgraph_make_edge_direct): New function.
	(cgraph_update_edges_for_call_stmt_node): Do nothing only when also
	the declaration of the call statement matches.
	(cgraph_node_remove_callees): Special-case indirect edges.
	(cgraph_clone_edge): Likewise.
	(cgraph_clone_node): Clone also the indirect edges.
	(dump_cgraph_node): Dump indirect_inlining_edge flag instead of
	indirect_call, dump count of indirect_calls edges.
	* ipa-prop.c (iinlining_processed_edges): New variable.
	(ipa_note_param_call): Create indirect edges instead of
	creating notes.  New parameter node.
	(ipa_analyze_call_uses): New parameter node, pass it on to
	ipa_note_param_call.
	(ipa_analyze_stmt_uses): Likewise.
	(ipa_analyze_params_uses): Pass node to ipa_analyze_stmt_uses.
	(print_edge_addition_message): Work on edges rather than on notes.
	(update_call_notes_after_inlining): Likewise, renamed to
	update_indirect_edges_after_inlining.
	(ipa_create_all_structures_for_iinln): New function.
	(ipa_free_node_params_substructures): Do not free notes.
	(ipa_edge_duplication_hook): Propagate bits within
	iinlining_processed_edges bitmap.
	(ipa_node_duplication_hook): Do not duplicate notes.
	(free_all_ipa_structures_after_ipa_cp): Renamed to
	ipa_free_all_structures_after_ipa_cp.
	(free_all_ipa_structures_after_iinln): Renamed to
	ipa_free_all_structures_after_iinln.g
	(ipa_write_param_call_note): Removed.
	(ipa_read_param_call_note): Removed.
	(ipa_write_indirect_edge_info): New function.
	(ipa_read_indirect_edge_info): Likewise.
	(ipa_write_node_info): Do not stream notes, do stream information
	in indirect edges.
	(ipa_read_node_info): Likewise.
	(lto_ipa_fixup_call_notes): Removed.
	* ipa-cp.c (pass_ipa_cp): Set stmt_fixup to NULL.
	* ipa-inline.c (pass_ipa_inline): Likewise.
	* cgraphunit.c (verify_cgraph_node): Check also indirect edges.
	* cif-code.def (INDIRECT_UNKNOWN_CALL): New reason.
	* tree-inline.c (copy_bb): Removed an unnecessary double check for
	is_gimple_call.
	* tree-inline.c (get_indirect_callee_fndecl): Do not consider indirect
	edges.
	* lto-cgraph.c (output_outgoing_cgraph_edges): New function.
	(output_cgraph): Stream also indirect edges.
	(lto_output_edge): Added capability to stream indirect edges.
	(input_edge): Likewise.
	(input_cgraph_1): Likewise.

	* testsuite/gcc.dg/lto/20091209-1_0.c: New testcase.

From-SVN: r158827
parent 18abb35e
2010-04-28 Martin Jambor <mjambor@suse.cz>
* cgraph.h (struct cgraph_node): New field indirect_calls.
(struct cgraph_indirect_call_info): New type.
(struct cgraph_edge): Removed field indirect_call. New fields
indirect_info, indirect_inlining_edge and indirect_unknown_callee.
(cgraph_create_indirect_edge): Declare.
(cgraph_make_edge_direct): Likewise.
(enum LTO_cgraph_tags): New item LTO_cgraph_indirect_edge.
* ipa-prop.h (struct ipa_param_call_note): Removed.
(struct ipa_node_params): Removed field param_calls.
(ipa_create_all_structures_for_iinln): Declare.
* cgraph.c: Described indirect edges and uids in initial comment.
(cgraph_add_edge_to_call_site_hash): New function.
(cgraph_edge): Search also among the indirect edges, use
cgraph_add_edge_to_call_site_hash to add edges to the call site hash.
(cgraph_set_call_stmt): Possibly turn an indirect edge into a direct
one, use cgraph_add_edge_to_call_site_hash to add edges to the call
site hash.
(initialize_inline_failed): Assign a reason to indirect edges.
(cgraph_create_edge_1): New function.
(cgraph_create_edge): Moved some functionality to
cgraph_create_edge_1.
(cgraph_create_indirect_edge): New function.
(cgraph_edge_remove_callee): Add an assert checking for
non-indirectness.
(cgraph_edge_remove_caller): Special-case indirect edges.
(cgraph_remove_edge): Likewise.
(cgraph_set_edge_callee): New function.
(cgraph_redirect_edge_callee): Use cgraph_set_edge_callee.
(cgraph_make_edge_direct): New function.
(cgraph_update_edges_for_call_stmt_node): Do nothing only when also
the declaration of the call statement matches.
(cgraph_node_remove_callees): Special-case indirect edges.
(cgraph_clone_edge): Likewise.
(cgraph_clone_node): Clone also the indirect edges.
(dump_cgraph_node): Dump indirect_inlining_edge flag instead of
indirect_call, dump count of indirect_calls edges.
* ipa-prop.c (iinlining_processed_edges): New variable.
(ipa_note_param_call): Create indirect edges instead of
creating notes. New parameter node.
(ipa_analyze_call_uses): New parameter node, pass it on to
ipa_note_param_call.
(ipa_analyze_stmt_uses): Likewise.
(ipa_analyze_params_uses): Pass node to ipa_analyze_stmt_uses.
(print_edge_addition_message): Work on edges rather than on notes.
(update_call_notes_after_inlining): Likewise, renamed to
update_indirect_edges_after_inlining.
(ipa_create_all_structures_for_iinln): New function.
(ipa_free_node_params_substructures): Do not free notes.
(ipa_edge_duplication_hook): Propagate bits within
iinlining_processed_edges bitmap.
(ipa_node_duplication_hook): Do not duplicate notes.
(free_all_ipa_structures_after_ipa_cp): Renamed to
ipa_free_all_structures_after_ipa_cp.
(free_all_ipa_structures_after_iinln): Renamed to
ipa_free_all_structures_after_iinln.g
(ipa_write_param_call_note): Removed.
(ipa_read_param_call_note): Removed.
(ipa_write_indirect_edge_info): New function.
(ipa_read_indirect_edge_info): Likewise.
(ipa_write_node_info): Do not stream notes, do stream information
in indirect edges.
(ipa_read_node_info): Likewise.
(lto_ipa_fixup_call_notes): Removed.
* ipa-cp.c (pass_ipa_cp): Set stmt_fixup to NULL.
* ipa-inline.c (pass_ipa_inline): Likewise.
* cgraphunit.c (verify_cgraph_node): Check also indirect edges.
* cif-code.def (INDIRECT_UNKNOWN_CALL): New reason.
* tree-inline.c (copy_bb): Removed an unnecessary double check for
is_gimple_call.
* tree-inline.c (get_indirect_callee_fndecl): Do not consider indirect
edges.
* lto-cgraph.c (output_outgoing_cgraph_edges): New function.
(output_cgraph): Stream also indirect edges.
(lto_output_edge): Added capability to stream indirect edges.
(input_edge): Likewise.
(input_cgraph_1): Likewise.
2010-04-28 Richard Guenther <rguenther@suse.de>
PR tree-optimization/43879
......
......@@ -34,10 +34,16 @@ The callgraph:
based on DECL_UID. The call-graph nodes are created lazily using
cgraph_node function when called for unknown declaration.
The callgraph at the moment does not represent indirect calls or calls
from other compilation unit. Flag NEEDED is set for each node that may
be accessed in such an invisible way and it shall be considered an
entry point to the callgraph.
The callgraph at the moment does not represent all indirect calls or calls
from other compilation units. Flag NEEDED is set for each node that may be
accessed in such an invisible way and it shall be considered an entry point
to the callgraph.
On the other hand, the callgraph currently does contain some edges for
indirect calls with unknown callees which can be accessed through
indirect_calls field of a node. It should be noted however that at the
moment only calls which are potential candidates for indirect inlining are
added there.
Interprocedural information:
......@@ -48,6 +54,9 @@ The callgraph:
rtl_info used by RTL backend to propagate data from already compiled
functions to their callers.
Moreover, each node has a uid which can be used to keep information in
on-the-side arrays. UIDs are reused and therefore reasonably dense.
Inlining plans:
The function inlining information is decided in advance and maintained
......@@ -723,6 +732,19 @@ edge_eq (const void *x, const void *y)
return ((const struct cgraph_edge *) x)->call_stmt == y;
}
/* Add call graph edge E to call site hash of its caller. */
static inline void
cgraph_add_edge_to_call_site_hash (struct cgraph_edge *e)
{
void **slot;
slot = htab_find_slot_with_hash (e->caller->call_site_hash,
e->call_stmt,
htab_hash_pointer (e->call_stmt),
INSERT);
gcc_assert (!*slot);
*slot = e;
}
/* Return the callgraph edge representing the GIMPLE_CALL statement
CALL_STMT. */
......@@ -743,7 +765,15 @@ cgraph_edge (struct cgraph_node *node, gimple call_stmt)
solution. It is not good idea to add pointer into CALL_EXPR itself
because we want to make possible having multiple cgraph nodes representing
different clones of the same body before the body is actually cloned. */
for (e = node->callees; e; e= e->next_callee)
for (e = node->callees; e; e = e->next_callee)
{
if (e->call_stmt == call_stmt)
break;
n++;
}
if (!e)
for (e = node->indirect_calls; e; e = e->next_callee)
{
if (e->call_stmt == call_stmt)
break;
......@@ -754,15 +784,9 @@ cgraph_edge (struct cgraph_node *node, gimple call_stmt)
{
node->call_site_hash = htab_create_ggc (120, edge_hash, edge_eq, NULL);
for (e2 = node->callees; e2; e2 = e2->next_callee)
{
void **slot;
slot = htab_find_slot_with_hash (node->call_site_hash,
e2->call_stmt,
htab_hash_pointer (e2->call_stmt),
INSERT);
gcc_assert (!*slot);
*slot = e2;
}
cgraph_add_edge_to_call_site_hash (e2);
for (e2 = node->indirect_calls; e2; e2 = e2->next_callee)
cgraph_add_edge_to_call_site_hash (e2);
}
return e;
......@@ -774,26 +798,31 @@ cgraph_edge (struct cgraph_node *node, gimple call_stmt)
void
cgraph_set_call_stmt (struct cgraph_edge *e, gimple new_stmt)
{
tree decl;
if (e->caller->call_site_hash)
{
htab_remove_elt_with_hash (e->caller->call_site_hash,
e->call_stmt,
htab_hash_pointer (e->call_stmt));
}
e->call_stmt = new_stmt;
if (e->indirect_unknown_callee
&& (decl = gimple_call_fndecl (new_stmt)))
{
/* Constant propagation (and possibly also inlining?) can turn an
indirect call into a direct one. */
struct cgraph_node *new_callee = cgraph_node (decl);
cgraph_make_edge_direct (e, new_callee);
}
push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
e->can_throw_external = stmt_can_throw_external (new_stmt);
pop_cfun ();
if (e->caller->call_site_hash)
{
void **slot;
slot = htab_find_slot_with_hash (e->caller->call_site_hash,
e->call_stmt,
htab_hash_pointer
(e->call_stmt), INSERT);
gcc_assert (!*slot);
*slot = e;
}
cgraph_add_edge_to_call_site_hash (e);
}
/* Like cgraph_set_call_stmt but walk the clone tree and update all
......@@ -895,7 +924,9 @@ initialize_inline_failed (struct cgraph_edge *e)
{
struct cgraph_node *callee = e->callee;
if (!callee->analyzed)
if (e->indirect_unknown_callee)
e->inline_failed = CIF_INDIRECT_UNKNOWN_CALL;
else if (!callee->analyzed)
e->inline_failed = CIF_BODY_NOT_AVAILABLE;
else if (callee->local.redefined_extern_inline)
e->inline_failed = CIF_REDEFINED_EXTERN_INLINE;
......@@ -907,15 +938,16 @@ initialize_inline_failed (struct cgraph_edge *e)
e->inline_failed = CIF_FUNCTION_NOT_CONSIDERED;
}
/* Create edge from CALLER to CALLEE in the cgraph. */
/* Allocate a cgraph_edge structure and fill it with data according to the
parameters of which only CALLEE can be NULL (when creating an indirect call
edge). */
struct cgraph_edge *
cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
static struct cgraph_edge *
cgraph_create_edge_1 (struct cgraph_node *caller, struct cgraph_node *callee,
gimple call_stmt, gcov_type count, int freq, int nest)
{
struct cgraph_edge *edge;
/* LTO does not actually have access to the call_stmt since these
have not been loaded yet. */
if (call_stmt)
......@@ -941,47 +973,83 @@ cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
}
edge->aux = NULL;
edge->caller = caller;
edge->callee = callee;
edge->prev_caller = NULL;
edge->next_caller = NULL;
edge->prev_callee = NULL;
edge->next_callee = NULL;
edge->count = count;
gcc_assert (count >= 0);
edge->frequency = freq;
gcc_assert (freq >= 0);
gcc_assert (freq <= CGRAPH_FREQ_MAX);
edge->loop_nest = nest;
edge->call_stmt = call_stmt;
push_cfun (DECL_STRUCT_FUNCTION (caller->decl));
edge->can_throw_external
= call_stmt ? stmt_can_throw_external (call_stmt) : false;
pop_cfun ();
edge->prev_caller = NULL;
edge->call_stmt_cannot_inline_p =
(call_stmt ? gimple_call_cannot_inline_p (call_stmt) : false);
if (call_stmt && caller->call_site_hash)
cgraph_add_edge_to_call_site_hash (edge);
edge->indirect_info = NULL;
edge->indirect_inlining_edge = 0;
return edge;
}
/* Create edge from CALLER to CALLEE in the cgraph. */
struct cgraph_edge *
cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
gimple call_stmt, gcov_type count, int freq, int nest)
{
struct cgraph_edge *edge = cgraph_create_edge_1 (caller, callee, call_stmt,
count, freq, nest);
edge->indirect_unknown_callee = 0;
initialize_inline_failed (edge);
edge->next_caller = callee->callers;
if (callee->callers)
callee->callers->prev_caller = edge;
edge->prev_callee = NULL;
edge->next_callee = caller->callees;
if (caller->callees)
caller->callees->prev_callee = edge;
caller->callees = edge;
callee->callers = edge;
edge->count = count;
gcc_assert (count >= 0);
edge->frequency = freq;
gcc_assert (freq >= 0);
gcc_assert (freq <= CGRAPH_FREQ_MAX);
edge->loop_nest = nest;
edge->indirect_call = 0;
edge->call_stmt_cannot_inline_p =
(call_stmt ? gimple_call_cannot_inline_p (call_stmt) : false);
if (call_stmt && caller->call_site_hash)
{
void **slot;
slot = htab_find_slot_with_hash (caller->call_site_hash,
edge->call_stmt,
htab_hash_pointer
(edge->call_stmt),
INSERT);
gcc_assert (!*slot);
*slot = edge;
}
return edge;
}
/* Create an indirect edge with a yet-undetermined callee where the call
statement destination is a formal parameter of the caller with index
PARAM_INDEX. */
struct cgraph_edge *
cgraph_create_indirect_edge (struct cgraph_node *caller, gimple call_stmt,
gcov_type count, int freq, int nest)
{
struct cgraph_edge *edge = cgraph_create_edge_1 (caller, NULL, call_stmt,
count, freq, nest);
edge->indirect_unknown_callee = 1;
initialize_inline_failed (edge);
edge->indirect_info = GGC_NEW (struct cgraph_indirect_call_info);
edge->indirect_info->param_index = -1;
edge->next_callee = caller->indirect_calls;
if (caller->indirect_calls)
caller->indirect_calls->prev_callee = edge;
caller->indirect_calls = edge;
return edge;
}
......@@ -990,6 +1058,7 @@ cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
static inline void
cgraph_edge_remove_callee (struct cgraph_edge *e)
{
gcc_assert (!e->indirect_unknown_callee);
if (e->prev_caller)
e->prev_caller->next_caller = e->next_caller;
if (e->next_caller)
......@@ -1008,7 +1077,12 @@ cgraph_edge_remove_caller (struct cgraph_edge *e)
if (e->next_callee)
e->next_callee->prev_callee = e->prev_callee;
if (!e->prev_callee)
{
if (e->indirect_unknown_callee)
e->caller->indirect_calls = e->next_callee;
else
e->caller->callees = e->next_callee;
}
if (e->caller->call_site_hash)
htab_remove_elt_with_hash (e->caller->call_site_hash,
e->call_stmt,
......@@ -1037,6 +1111,7 @@ cgraph_remove_edge (struct cgraph_edge *e)
/* Call all edge removal hooks. */
cgraph_call_edge_removal_hooks (e);
if (!e->indirect_unknown_callee)
/* Remove from callers list of the callee. */
cgraph_edge_remove_callee (e);
......@@ -1047,6 +1122,20 @@ cgraph_remove_edge (struct cgraph_edge *e)
cgraph_free_edge (e);
}
/* Set callee of call graph edge E and add it to the corresponding set of
callers. */
static void
cgraph_set_edge_callee (struct cgraph_edge *e, struct cgraph_node *n)
{
e->prev_caller = NULL;
if (n->callers)
n->callers->prev_caller = e;
e->next_caller = n->callers;
n->callers = e;
e->callee = n;
}
/* Redirect callee of E to N. The function does not update underlying
call expression. */
......@@ -1057,12 +1146,37 @@ cgraph_redirect_edge_callee (struct cgraph_edge *e, struct cgraph_node *n)
cgraph_edge_remove_callee (e);
/* Insert to callers list of the new callee. */
e->prev_caller = NULL;
if (n->callers)
n->callers->prev_caller = e;
e->next_caller = n->callers;
n->callers = e;
e->callee = n;
cgraph_set_edge_callee (e, n);
}
/* Make an indirect EDGE with an unknown callee an ordinary edge leading to
CALLEE. */
void
cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee)
{
edge->indirect_unknown_callee = 0;
/* Get the edge out of the indirect edge list. */
if (edge->prev_callee)
edge->prev_callee->next_callee = edge->next_callee;
if (edge->next_callee)
edge->next_callee->prev_callee = edge->prev_callee;
if (!edge->prev_callee)
edge->caller->indirect_calls = edge->next_callee;
/* Put it into the normal callee list */
edge->prev_callee = NULL;
edge->next_callee = edge->caller->callees;
if (edge->caller->callees)
edge->caller->callees->prev_callee = edge;
edge->caller->callees = edge;
/* Insert to callers list of the new callee. */
cgraph_set_edge_callee (edge, callee);
/* We need to re-determine the inlining status of the edge. */
initialize_inline_failed (edge);
}
......@@ -1091,9 +1205,10 @@ cgraph_update_edges_for_call_stmt_node (struct cgraph_node *node,
if (e)
{
/* See if the call is already there. It might be because of indirect
inlining already found it. */
if (new_call && e->callee->decl == new_call)
/* See if the edge is already there and has the correct callee. It
might be so because of indirect inlining has already updated
it. */
if (new_call && e->callee && e->callee->decl == new_call)
return;
/* Otherwise remove edge and create new one; we can't simply redirect
......@@ -1171,6 +1286,7 @@ cgraph_node_remove_callees (struct cgraph_node *node)
{
f = e->next_callee;
cgraph_call_edge_removal_hooks (e);
if (!e->indirect_unknown_callee)
cgraph_edge_remove_callee (e);
cgraph_free_edge (e);
}
......@@ -1627,6 +1743,8 @@ void
dump_cgraph_node (FILE *f, struct cgraph_node *node)
{
struct cgraph_edge *edge;
int indirect_calls_count = 0;
fprintf (f, "%s/%i(%i)", cgraph_node_name (node), node->uid,
node->pid);
dump_addr (f, " @", (void *)node);
......@@ -1708,8 +1826,8 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
edge->frequency / (double)CGRAPH_FREQ_BASE);
if (!edge->inline_failed)
fprintf(f, "(inlined) ");
if (edge->indirect_call)
fprintf(f, "(indirect) ");
if (edge->indirect_inlining_edge)
fprintf(f, "(indirect_inlining) ");
if (edge->can_throw_external)
fprintf(f, "(can throw external) ");
}
......@@ -1721,8 +1839,8 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
edge->callee->uid);
if (!edge->inline_failed)
fprintf(f, "(inlined) ");
if (edge->indirect_call)
fprintf(f, "(indirect) ");
if (edge->indirect_inlining_edge)
fprintf(f, "(indirect_inlining) ");
if (edge->count)
fprintf (f, "("HOST_WIDEST_INT_PRINT_DEC"x) ",
(HOST_WIDEST_INT)edge->count);
......@@ -1736,6 +1854,12 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
}
fprintf (f, "\n");
for (edge = node->indirect_calls; edge; edge = edge->next_callee)
indirect_calls_count++;
if (indirect_calls_count)
fprintf (f, " has %i outgoing edges for indirect calls.\n",
indirect_calls_count);
if (node->same_body)
{
struct cgraph_node *n;
......@@ -1855,11 +1979,30 @@ cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n,
freq = e->frequency * (gcov_type) freq_scale / CGRAPH_FREQ_BASE;
if (freq > CGRAPH_FREQ_MAX)
freq = CGRAPH_FREQ_MAX;
if (e->indirect_unknown_callee)
{
tree decl;
if (call_stmt && (decl = gimple_call_fndecl (call_stmt)))
{
struct cgraph_node *callee = cgraph_node (decl);
new_edge = cgraph_create_edge (n, callee, call_stmt, count, freq,
e->loop_nest + loop_nest);
}
else
{
new_edge = cgraph_create_indirect_edge (n, call_stmt, count, freq,
e->loop_nest + loop_nest);
new_edge->indirect_info->param_index = e->indirect_info->param_index;
}
}
else
new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq,
e->loop_nest + loop_nest);
new_edge->inline_failed = e->inline_failed;
new_edge->indirect_call = e->indirect_call;
new_edge->indirect_inlining_edge = e->indirect_inlining_edge;
new_edge->lto_stmt_uid = stmt_uid;
if (update_original)
{
......@@ -1933,6 +2076,10 @@ cgraph_clone_node (struct cgraph_node *n, gcov_type count, int freq,
cgraph_clone_edge (e, new_node, e->call_stmt, e->lto_stmt_uid,
count_scale, freq, loop_nest, update_original);
for (e = n->indirect_calls; e; e = e->next_callee)
cgraph_clone_edge (e, new_node, e->call_stmt, e->lto_stmt_uid,
count_scale, freq, loop_nest, update_original);
new_node->next_sibling_clone = n->clones;
if (n->clones)
n->clones->prev_sibling_clone = new_node;
......
......@@ -199,6 +199,9 @@ struct GTY((chain_next ("%h.next"), chain_prev ("%h.previous"))) cgraph_node {
struct cgraph_edge *callers;
struct cgraph_node *next;
struct cgraph_node *previous;
/* List of edges representing indirect calls with a yet undetermined
callee. */
struct cgraph_edge *indirect_calls;
/* For nested functions points to function the node is nested in. */
struct cgraph_node *origin;
/* Points to first nested function, if any. */
......@@ -333,6 +336,14 @@ typedef enum {
CIF_N_REASONS
} cgraph_inline_failed_t;
/* Structure containing additional information about an indirect call. */
struct GTY(()) cgraph_indirect_call_info
{
/* Index of the parameter that is called. */
int param_index;
};
struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) cgraph_edge {
/* Expected number of executions: calculated in profile.c. */
gcov_type count;
......@@ -343,6 +354,9 @@ struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) cgrap
struct cgraph_edge *prev_callee;
struct cgraph_edge *next_callee;
gimple call_stmt;
/* Additional information about an indirect call. Not cleared when an edge
becomes direct. */
struct cgraph_indirect_call_info *indirect_info;
PTR GTY ((skip (""))) aux;
/* When equal to CIF_OK, inline this call. Otherwise, points to the
explanation why function was not inlined. */
......@@ -358,8 +372,12 @@ struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) cgrap
int uid;
/* Depth of loop nest, 1 means no loop nest. */
unsigned short int loop_nest;
/* Whether this edge describes a call that was originally indirect. */
unsigned int indirect_call : 1;
/* Whether this edge was made direct by indirect inlining. */
unsigned int indirect_inlining_edge : 1;
/* Whether this edge describes an indirect call with an undetermined
callee. */
unsigned int indirect_unknown_callee : 1;
/* Whether this edge is still a dangling */
/* True if the corresponding CALL stmt cannot be inlined. */
unsigned int call_stmt_cannot_inline_p : 1;
/* Can this call throw externally? */
......@@ -461,7 +479,8 @@ void cgraph_node_remove_callees (struct cgraph_node *node);
struct cgraph_edge *cgraph_create_edge (struct cgraph_node *,
struct cgraph_node *,
gimple, gcov_type, int, int);
struct cgraph_edge *cgraph_create_indirect_edge (struct cgraph_node *, gimple,
gcov_type, int, int);
struct cgraph_node * cgraph_get_node (tree);
struct cgraph_node *cgraph_node (tree);
bool cgraph_same_body_alias (tree, tree);
......@@ -487,6 +506,7 @@ struct cgraph_node * cgraph_clone_node (struct cgraph_node *, gcov_type, int,
int, bool, VEC(cgraph_edge_p,heap) *);
void cgraph_redirect_edge_callee (struct cgraph_edge *, struct cgraph_node *);
void cgraph_make_edge_direct (struct cgraph_edge *, struct cgraph_node *);
struct cgraph_asm_node *cgraph_add_asm_node (tree);
......@@ -657,6 +677,7 @@ enum LTO_cgraph_tags
LTO_cgraph_overwritable_node,
LTO_cgraph_unavail_node,
LTO_cgraph_edge,
LTO_cgraph_indirect_edge,
LTO_cgraph_last_tag
};
......
......@@ -607,6 +607,24 @@ verify_cgraph_node (struct cgraph_node *node)
error ("Inline clone is needed");
error_found = true;
}
for (e = node->indirect_calls; e; e = e->next_callee)
{
if (e->aux)
{
error ("aux field set for indirect edge from %s",
identifier_to_locale (cgraph_node_name (e->caller)));
error_found = true;
}
if (!e->indirect_unknown_callee
|| !e->indirect_info)
{
error ("An indirect edge from %s is not marked as indirect or has "
"associated indirect_info, the corresponding statement is: ",
identifier_to_locale (cgraph_node_name (e->caller)));
debug_gimple_stmt (e->call_stmt);
error_found = true;
}
}
for (e = node->callers; e; e = e->next_caller)
{
if (e->count < 0)
......@@ -759,10 +777,10 @@ verify_cgraph_node (struct cgraph_node *node)
gsi_next (&gsi))
{
gimple stmt = gsi_stmt (gsi);
tree decl;
if (is_gimple_call (stmt) && (decl = gimple_call_fndecl (stmt)))
if (is_gimple_call (stmt))
{
struct cgraph_edge *e = cgraph_edge (node, stmt);
tree decl = gimple_call_fndecl (stmt);
if (e)
{
if (e->aux)
......@@ -771,6 +789,8 @@ verify_cgraph_node (struct cgraph_node *node)
debug_gimple_stmt (stmt);
error_found = true;
}
if (!e->indirect_unknown_callee)
{
if (e->callee->same_body_alias)
{
error ("edge points to same body alias:");
......@@ -779,7 +799,9 @@ verify_cgraph_node (struct cgraph_node *node)
}
else if (!node->global.inlined_to
&& !e->callee->global.inlined_to
&& !clone_of_p (cgraph_node (decl), e->callee))
&& decl
&& !clone_of_p (cgraph_node (decl),
e->callee))
{
error ("edge points to wrong declaration:");
debug_tree (e->callee->decl);
......@@ -787,9 +809,18 @@ verify_cgraph_node (struct cgraph_node *node)
debug_tree (decl);
error_found = true;
}
}
else if (decl)
{
error ("an indirect edge with unknown callee "
"corresponding to a call_stmt with "
"a known declaration:");
error_found = true;
debug_gimple_stmt (e->call_stmt);
}
e->aux = (void *)1;
}
else
else if (decl)
{
error ("missing callgraph edge for call stmt:");
debug_gimple_stmt (stmt);
......@@ -805,7 +836,7 @@ verify_cgraph_node (struct cgraph_node *node)
for (e = node->callees; e; e = e->next_callee)
{
if (!e->aux && !e->indirect_call)
if (!e->aux)
{
error ("edge %s->%s has no corresponding call_stmt",
identifier_to_locale (cgraph_node_name (e->caller)),
......@@ -815,6 +846,17 @@ verify_cgraph_node (struct cgraph_node *node)
}
e->aux = 0;
}
for (e = node->indirect_calls; e; e = e->next_callee)
{
if (!e->aux)
{
error ("an indirect edge from %s has no corresponding call_stmt",
identifier_to_locale (cgraph_node_name (e->caller)));
debug_gimple_stmt (e->call_stmt);
error_found = true;
}
e->aux = 0;
}
}
if (error_found)
{
......
......@@ -84,3 +84,7 @@ DEFCIFCODE(MISMATCHED_ARGUMENTS, N_("mismatched arguments"))
/* Call was originally indirect. */
DEFCIFCODE(ORIGINALLY_INDIRECT_CALL,
N_("originally indirect function call not considered for inlining"))
/* Ths edge represents an indirect edge with a yet-undetermined callee . */
DEFCIFCODE(INDIRECT_UNKNOWN_CALL,
N_("indirect function call with a yet undetermined callee"))
......@@ -1282,7 +1282,7 @@ ipcp_driver (void)
ipcp_print_profile_data (dump_file);
}
/* Free all IPCP structures. */
free_all_ipa_structures_after_ipa_cp ();
ipa_free_all_structures_after_ipa_cp ();
if (dump_file)
fprintf (dump_file, "\nIPA constant propagation end\n");
return 0;
......@@ -1346,7 +1346,7 @@ struct ipa_opt_pass_d pass_ipa_cp =
ipcp_read_summary, /* read_summary */
NULL, /* write_optimization_summary */
NULL, /* read_optimization_summary */
lto_ipa_fixup_call_notes, /* stmt_fixup */
NULL, /* stmt_fixup */
0, /* TODOs */
NULL, /* function_transform */
NULL, /* variable_transform */
......
......@@ -1322,6 +1322,8 @@ cgraph_decide_inlining (void)
cgraph_remove_function_insertion_hook (function_insertion_hook_holder);
if (in_lto_p && flag_indirect_inlining)
ipa_update_after_lto_read ();
if (flag_indirect_inlining)
ipa_create_all_structures_for_iinln ();
max_count = 0;
max_benefit = 0;
......@@ -1442,7 +1444,7 @@ cgraph_decide_inlining (void)
/* Free ipa-prop structures if they are no longer needed. */
if (flag_indirect_inlining)
free_all_ipa_structures_after_iinln ();
ipa_free_all_structures_after_iinln ();
if (dump_file)
fprintf (dump_file,
......@@ -2138,7 +2140,7 @@ struct ipa_opt_pass_d pass_ipa_inline =
inline_read_summary, /* read_summary */
NULL, /* write_optimization_summary */
NULL, /* read_optimization_summary */
lto_ipa_fixup_call_notes, /* stmt_fixup */
NULL, /* stmt_fixup */
0, /* TODOs */
inline_transform, /* function_transform */
NULL, /* variable_transform */
......
......@@ -41,6 +41,10 @@ VEC (ipa_node_params_t, heap) *ipa_node_params_vector;
/* Vector where the parameter infos are actually stored. */
VEC (ipa_edge_args_t, gc) *ipa_edge_args_vector;
/* Bitmap with all UIDs of call graph edges that have been already processed
by indirect inlining. */
static bitmap iinlining_processed_edges;
/* Holders of ipa cgraph hooks: */
static struct cgraph_edge_hook_list *edge_removal_hook_holder;
static struct cgraph_node_hook_list *node_removal_hook_holder;
......@@ -745,39 +749,31 @@ ipa_is_ssa_with_stmt_def (tree t)
return false;
}
/* Creates a new note describing a call to a parameter number FORMAL_ID and
attaches it to the linked list of INFO. It also sets the called flag of the
parameter. STMT is the corresponding call statement. */
/* Create a new indirect call graph edge describing a call to a parameter
number FORMAL_ID and and set the called flag of the parameter. NODE is the
caller. STMT is the corresponding call statement. */
static void
ipa_note_param_call (struct ipa_node_params *info, int formal_id,
gimple stmt)
ipa_note_param_call (struct cgraph_node *node, int formal_id, gimple stmt)
{
struct ipa_param_call_note *note;
struct cgraph_edge *cs;
basic_block bb = gimple_bb (stmt);
int freq;
note = XCNEW (struct ipa_param_call_note);
note->formal_id = formal_id;
note->stmt = stmt;
note->lto_stmt_uid = gimple_uid (stmt);
note->count = bb->count;
note->frequency = compute_call_stmt_bb_frequency (current_function_decl, bb);
note->loop_nest = bb->loop_depth;
note->next = info->param_calls;
info->param_calls = note;
return;
freq = compute_call_stmt_bb_frequency (current_function_decl, bb);
cs = cgraph_create_indirect_edge (node, stmt, bb->count, freq,
bb->loop_depth);
cs->indirect_info->param_index = formal_id;
}
/* Analyze the CALL and examine uses of formal parameters of the caller
/* Analyze the CALL and examine uses of formal parameters of the caller NODE
(described by INFO). Currently it checks whether the call calls a pointer
that is a formal parameter and if so, the parameter is marked with the
called flag and a note describing the call is created. This is very simple
for ordinary pointers represented in SSA but not-so-nice when it comes to
member pointers. The ugly part of this function does nothing more than
tries to match the pattern of such a call. An example of such a pattern is
the gimple dump below, the call is on the last line:
called flag and an indirect call graph edge describing the call is created.
This is very simple for ordinary pointers represented in SSA but not-so-nice
when it comes to member pointers. The ugly part of this function does
nothing more than trying to match the pattern of such a call. An example of
such a pattern is the gimple dump below, the call is on the last line:
<bb 2>:
f$__delta_5 = f.__delta;
......@@ -817,7 +813,8 @@ ipa_note_param_call (struct ipa_node_params *info, int formal_id,
*/
static void
ipa_analyze_call_uses (struct ipa_node_params *info, gimple call)
ipa_analyze_call_uses (struct cgraph_node *node, struct ipa_node_params *info,
gimple call)
{
tree target = gimple_call_fn (call);
gimple def;
......@@ -838,7 +835,7 @@ ipa_analyze_call_uses (struct ipa_node_params *info, gimple call)
/* assuming TREE_CODE (var) == PARM_DECL */
index = ipa_get_param_decl_index (info, var);
if (index >= 0)
ipa_note_param_call (info, index, call);
ipa_note_param_call (node, index, call);
return;
}
......@@ -935,20 +932,21 @@ ipa_analyze_call_uses (struct ipa_node_params *info, gimple call)
index = ipa_get_param_decl_index (info, rec);
if (index >= 0 && !ipa_is_param_modified (info, index))
ipa_note_param_call (info, index, call);
ipa_note_param_call (node, index, call);
return;
}
/* Analyze the statement STMT with respect to formal parameters (described in
INFO) and their uses. Currently it only checks whether formal parameters
are called. */
/* Analyze the call statement STMT with respect to formal parameters (described
in INFO) of caller given by NODE. Currently it only checks whether formal
parameters are called. */
static void
ipa_analyze_stmt_uses (struct ipa_node_params *info, gimple stmt)
ipa_analyze_stmt_uses (struct cgraph_node *node, struct ipa_node_params *info,
gimple stmt)
{
if (is_gimple_call (stmt))
ipa_analyze_call_uses (info, stmt);
ipa_analyze_call_uses (node, info, stmt);
}
/* Scan the function body of NODE and inspect the uses of formal parameters.
......@@ -973,7 +971,7 @@ ipa_analyze_params_uses (struct cgraph_node *node)
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
gimple stmt = gsi_stmt (gsi);
ipa_analyze_stmt_uses (info, stmt);
ipa_analyze_stmt_uses (node, info, stmt);
}
}
......@@ -1029,9 +1027,8 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs,
by JFUNC. NODE is the node where the call is. */
static void
print_edge_addition_message (FILE *f, struct ipa_param_call_note *nt,
struct ipa_jump_func *jfunc,
struct cgraph_node *node)
print_edge_addition_message (FILE *f, struct cgraph_edge *e,
struct ipa_jump_func *jfunc)
{
fprintf (f, "ipa-prop: Discovered an indirect call to a known target (");
if (jfunc->type == IPA_JF_CONST_MEMBER_PTR)
......@@ -1042,8 +1039,8 @@ print_edge_addition_message (FILE *f, struct ipa_param_call_note *nt,
else
print_node_brief(f, "", jfunc->value.constant, 0);
fprintf (f, ") in %s: ", cgraph_node_name (node));
print_gimple_stmt (f, nt->stmt, 2, TDF_SLIM);
fprintf (f, ") in %s: ", cgraph_node_name (e->caller));
print_gimple_stmt (f, e->call_stmt, 2, TDF_SLIM);
}
/* Update the param called notes associated with NODE when CS is being inlined,
......@@ -1053,41 +1050,47 @@ print_edge_addition_message (FILE *f, struct ipa_param_call_note *nt,
unless NEW_EDGES is NULL. Return true iff a new edge(s) were created. */
static bool
update_call_notes_after_inlining (struct cgraph_edge *cs,
update_indirect_edges_after_inlining (struct cgraph_edge *cs,
struct cgraph_node *node,
VEC (cgraph_edge_p, heap) **new_edges)
{
struct ipa_node_params *info = IPA_NODE_REF (node);
struct ipa_edge_args *top = IPA_EDGE_REF (cs);
struct ipa_param_call_note *nt;
struct cgraph_edge *ie, *next_ie;
bool res = false;
for (nt = info->param_calls; nt; nt = nt->next)
ipa_check_create_edge_args ();
for (ie = node->indirect_calls; ie; ie = next_ie)
{
struct cgraph_indirect_call_info *ici = ie->indirect_info;
struct ipa_jump_func *jfunc;
if (nt->processed)
next_ie = ie->next_callee;
if (bitmap_bit_p (iinlining_processed_edges, ie->uid))
continue;
/* If we ever use indirect edges for anything other than indirect
inlining, we will need to skip those with negative param_indices. */
gcc_assert (ici->param_index >= 0);
/* We must check range due to calls with variable number of arguments: */
if (nt->formal_id >= ipa_get_cs_argument_count (top))
if (ici->param_index >= ipa_get_cs_argument_count (top))
{
nt->processed = true;
bitmap_set_bit (iinlining_processed_edges, ie->uid);
continue;
}
jfunc = ipa_get_ith_jump_func (top, nt->formal_id);
jfunc = ipa_get_ith_jump_func (top, ici->param_index);
if (jfunc->type == IPA_JF_PASS_THROUGH
&& jfunc->value.pass_through.operation == NOP_EXPR)
nt->formal_id = jfunc->value.pass_through.formal_id;
ici->param_index = jfunc->value.pass_through.formal_id;
else if (jfunc->type == IPA_JF_CONST
|| jfunc->type == IPA_JF_CONST_MEMBER_PTR)
{
struct cgraph_node *callee;
struct cgraph_edge *new_indirect_edge;
tree decl;
nt->processed = true;
bitmap_set_bit (iinlining_processed_edges, ie->uid);
if (jfunc->type == IPA_JF_CONST_MEMBER_PTR)
decl = jfunc->value.member_cst.pfn;
else
......@@ -1105,32 +1108,29 @@ update_call_notes_after_inlining (struct cgraph_edge *cs,
res = true;
if (dump_file)
print_edge_addition_message (dump_file, nt, jfunc, node);
print_edge_addition_message (dump_file, ie, jfunc);
new_indirect_edge = cgraph_create_edge (node, callee, nt->stmt,
nt->count, nt->frequency,
nt->loop_nest);
new_indirect_edge->lto_stmt_uid = nt->lto_stmt_uid;
new_indirect_edge->indirect_call = 1;
ipa_check_create_edge_args ();
cgraph_make_edge_direct (ie, callee);
ie->indirect_inlining_edge = 1;
if (new_edges)
VEC_safe_push (cgraph_edge_p, heap, *new_edges, new_indirect_edge);
VEC_safe_push (cgraph_edge_p, heap, *new_edges, ie);
top = IPA_EDGE_REF (cs);
}
else
{
/* Ancestor jum functions and pass theoughs with operations should
/* Ancestor jump functions and pass theoughs with operations should
not be used on parameters that then get called. */
gcc_assert (jfunc->type == IPA_JF_UNKNOWN);
nt->processed = true;
bitmap_set_bit (iinlining_processed_edges, ie->uid);
}
}
return res;
}
/* Recursively traverse subtree of NODE (including node) made of inlined
cgraph_edges when CS has been inlined and invoke
update_call_notes_after_inlining on all nodes and
update_indirect_edges_after_inlining on all nodes and
update_jump_functions_after_inlining on all non-inlined edges that lead out
of this subtree. Newly discovered indirect edges will be added to
*NEW_EDGES, unless NEW_EDGES is NULL. Return true iff a new edge(s) were
......@@ -1144,7 +1144,7 @@ propagate_info_to_inlined_callees (struct cgraph_edge *cs,
struct cgraph_edge *e;
bool res;
res = update_call_notes_after_inlining (cs, node, new_edges);
res = update_indirect_edges_after_inlining (cs, node, new_edges);
for (e = node->callees; e; e = e->next_callee)
if (!e->inline_failed)
......@@ -1216,13 +1216,6 @@ ipa_free_node_params_substructures (struct ipa_node_params *info)
if (info->params)
free (info->params);
while (info->param_calls)
{
struct ipa_param_call_note *note = info->param_calls;
info->param_calls = note->next;
free (note);
}
memset (info, 0, sizeof (*info));
}
......@@ -1317,6 +1310,10 @@ ipa_edge_duplication_hook (struct cgraph_edge *src, struct cgraph_edge *dst,
new_args->jump_functions = (struct ipa_jump_func *)
duplicate_ggc_array (old_args->jump_functions,
sizeof (struct ipa_jump_func) * arg_count);
if (iinlining_processed_edges
&& bitmap_bit_p (iinlining_processed_edges, src->uid))
bitmap_set_bit (iinlining_processed_edges, dst->uid);
}
/* Hook that is called by cgraph.c when a node is duplicated. */
......@@ -1326,7 +1323,6 @@ ipa_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst,
__attribute__((unused)) void *data)
{
struct ipa_node_params *old_info, *new_info;
struct ipa_param_call_note *note;
int param_count;
ipa_check_create_node_params ();
......@@ -1340,17 +1336,6 @@ ipa_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst,
sizeof (struct ipa_param_descriptor) * param_count);
new_info->ipcp_orig_node = old_info->ipcp_orig_node;
new_info->count_scale = old_info->count_scale;
for (note = old_info->param_calls; note; note = note->next)
{
struct ipa_param_call_note *nn;
nn = (struct ipa_param_call_note *)
xcalloc (1, sizeof (struct ipa_param_call_note));
memcpy (nn, note, sizeof (struct ipa_param_call_note));
nn->next = new_info->param_calls;
new_info->param_calls = nn;
}
}
/* Register our cgraph hooks if they are not already there. */
......@@ -1387,11 +1372,19 @@ ipa_unregister_cgraph_hooks (void)
node_duplication_hook_holder = NULL;
}
/* Allocate all necessary data strucutures necessary for indirect inlining. */
void
ipa_create_all_structures_for_iinln (void)
{
iinlining_processed_edges = BITMAP_ALLOC (NULL);
}
/* Free all ipa_node_params and all ipa_edge_args structures if they are no
longer needed after ipa-cp. */
void
free_all_ipa_structures_after_ipa_cp (void)
ipa_free_all_structures_after_ipa_cp (void)
{
if (!flag_indirect_inlining)
{
......@@ -1405,8 +1398,10 @@ free_all_ipa_structures_after_ipa_cp (void)
longer needed after indirect inlining. */
void
free_all_ipa_structures_after_iinln (void)
ipa_free_all_structures_after_iinln (void)
{
BITMAP_FREE (iinlining_processed_edges);
ipa_free_all_edge_args ();
ipa_free_all_node_params ();
ipa_unregister_cgraph_hooks ();
......@@ -1974,40 +1969,31 @@ ipa_read_jump_function (struct lto_input_block *ib,
}
}
/* Stream out a parameter call note. */
/* Stream out parts of cgraph_indirect_call_info corresponding to CS that are
relevant to indirect inlining to OB. */
static void
ipa_write_param_call_note (struct output_block *ob,
struct ipa_param_call_note *note)
ipa_write_indirect_edge_info (struct output_block *ob,
struct cgraph_edge *cs)
{
gcc_assert (!note->processed);
lto_output_uleb128_stream (ob->main_stream, gimple_uid (note->stmt));
lto_output_sleb128_stream (ob->main_stream, note->formal_id);
lto_output_sleb128_stream (ob->main_stream, note->count);
lto_output_sleb128_stream (ob->main_stream, note->frequency);
lto_output_sleb128_stream (ob->main_stream, note->loop_nest);
struct cgraph_indirect_call_info *ii = cs->indirect_info;
lto_output_sleb128_stream (ob->main_stream, ii->param_index);
}
/* Read in a parameter call note. */
/* Read in parts of cgraph_indirect_call_info corresponding to CS that are
relevant to indirect inlining from IB. */
static void
ipa_read_param_call_note (struct lto_input_block *ib,
struct ipa_node_params *info)
ipa_read_indirect_edge_info (struct lto_input_block *ib,
struct data_in *data_in ATTRIBUTE_UNUSED,
struct cgraph_edge *cs)
{
struct ipa_param_call_note *note = XCNEW (struct ipa_param_call_note);
note->lto_stmt_uid = (unsigned int) lto_input_uleb128 (ib);
note->formal_id = (int) lto_input_sleb128 (ib);
note->count = (gcov_type) lto_input_sleb128 (ib);
note->frequency = (int) lto_input_sleb128 (ib);
note->loop_nest = (int) lto_input_sleb128 (ib);
struct cgraph_indirect_call_info *ii = cs->indirect_info;
note->next = info->param_calls;
info->param_calls = note;
ii->param_index = (int) lto_input_sleb128 (ib);
}
/* Stream out NODE info to OB. */
static void
......@@ -2019,8 +2005,6 @@ ipa_write_node_info (struct output_block *ob, struct cgraph_node *node)
int j;
struct cgraph_edge *e;
struct bitpack_d *bp;
int note_count = 0;
struct ipa_param_call_note *note;
encoder = ob->decl_state->cgraph_node_encoder;
node_ref = lto_cgraph_encoder_encode (encoder, node);
......@@ -2046,12 +2030,8 @@ ipa_write_node_info (struct output_block *ob, struct cgraph_node *node)
for (j = 0; j < ipa_get_cs_argument_count (args); j++)
ipa_write_jump_function (ob, ipa_get_ith_jump_func (args, j));
}
for (note = info->param_calls; note; note = note->next)
note_count++;
lto_output_uleb128_stream (ob->main_stream, note_count);
for (note = info->param_calls; note; note = note->next)
ipa_write_param_call_note (ob, note);
for (e = node->indirect_calls; e; e = e->next_callee)
ipa_write_indirect_edge_info (ob, e);
}
/* Srtream in NODE info from IB. */
......@@ -2064,7 +2044,6 @@ ipa_read_node_info (struct lto_input_block *ib, struct cgraph_node *node,
int k;
struct cgraph_edge *e;
struct bitpack_d *bp;
int i, note_count;
ipa_initialize_node_params (node);
......@@ -2094,10 +2073,8 @@ ipa_read_node_info (struct lto_input_block *ib, struct cgraph_node *node,
for (k = 0; k < ipa_get_cs_argument_count (args); k++)
ipa_read_jump_function (ib, ipa_get_ith_jump_func (args, k), data_in);
}
note_count = lto_input_uleb128 (ib);
for (i = 0; i < note_count; i++)
ipa_read_param_call_note (ib, info);
for (e = node->indirect_calls; e; e = e->next_callee)
ipa_read_indirect_edge_info (ib, data_in, e);
}
/* Write jump functions for nodes in SET. */
......@@ -2222,29 +2199,3 @@ ipa_update_after_lto_read (void)
ipa_set_called_with_variable_arg (IPA_NODE_REF (cs->callee));
}
}
/* Walk param call notes of NODE and set their call statements given the uid
stored in each note and STMTS which is an array of statements indexed by the
uid. */
void
lto_ipa_fixup_call_notes (struct cgraph_node *node, gimple *stmts)
{
struct ipa_node_params *info;
struct ipa_param_call_note *note;
ipa_check_create_node_params ();
info = IPA_NODE_REF (node);
note = info->param_calls;
/* If there are no notes or they have already been fixed up (the same fixup
is called for both inlining and ipa-cp), there's nothing to do. */
if (!note || note->stmt)
return;
do
{
note->stmt = stmts[note->lto_stmt_uid];
note = note->next;
}
while (note);
}
......@@ -135,32 +135,6 @@ struct ipcp_lattice
tree constant;
};
/* Each instance of the following structure describes a statement that calls a
function parameter. Those referring to statements within the same function
are linked in a list. */
struct ipa_param_call_note
{
/* Expected number of executions: calculated in profile.c. */
gcov_type count;
/* Linked list's next */
struct ipa_param_call_note *next;
/* Statement that contains the call to the parameter above. */
gimple stmt;
/* When in LTO, we the above stmt will be NULL and we need an uid. */
unsigned int lto_stmt_uid;
/* Index of the parameter that is called. */
int formal_id;
/* Expected frequency of executions within the function. see cgraph_edge in
cgraph.h for more on this. */
int frequency;
/* Depth of loop nest, 1 means no loop nest. */
unsigned short int loop_nest;
/* Set when we have already found the target to be a compile time constant
and turned this into an edge or when the note was found unusable for some
reason. */
bool processed;
};
/* Structure describing a single formal parameter. */
struct ipa_param_descriptor
{
......@@ -193,8 +167,6 @@ struct ipa_node_params
/* Pointer to an array of structures describing individual formal
parameters. */
struct ipa_param_descriptor *params;
/* List of structures enumerating calls to a formal parameter. */
struct ipa_param_call_note *param_calls;
/* Only for versioned nodes this field would not be NULL,
it points to the node that IPA cp cloned from. */
struct cgraph_node *ipcp_orig_node;
......@@ -337,8 +309,9 @@ void ipa_free_edge_args_substructures (struct ipa_edge_args *);
void ipa_free_node_params_substructures (struct ipa_node_params *);
void ipa_free_all_node_params (void);
void ipa_free_all_edge_args (void);
void free_all_ipa_structures_after_ipa_cp (void);
void free_all_ipa_structures_after_iinln (void);
void ipa_create_all_structures_for_iinln (void);
void ipa_free_all_structures_after_ipa_cp (void);
void ipa_free_all_structures_after_iinln (void);
void ipa_register_cgraph_hooks (void);
/* This function ensures the array of node param infos is big enough to
......
......@@ -139,15 +139,21 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
intptr_t ref;
struct bitpack_d *bp;
if (edge->indirect_unknown_callee)
lto_output_uleb128_stream (ob->main_stream, LTO_cgraph_indirect_edge);
else
lto_output_uleb128_stream (ob->main_stream, LTO_cgraph_edge);
ref = lto_cgraph_encoder_lookup (encoder, edge->caller);
gcc_assert (ref != LCC_NOT_FOUND);
lto_output_sleb128_stream (ob->main_stream, ref);
if (!edge->indirect_unknown_callee)
{
ref = lto_cgraph_encoder_lookup (encoder, edge->callee);
gcc_assert (ref != LCC_NOT_FOUND);
lto_output_sleb128_stream (ob->main_stream, ref);
}
lto_output_sleb128_stream (ob->main_stream, edge->count);
......@@ -157,7 +163,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
bp_pack_value (bp, edge->inline_failed, HOST_BITS_PER_INT);
bp_pack_value (bp, edge->frequency, HOST_BITS_PER_INT);
bp_pack_value (bp, edge->loop_nest, 30);
bp_pack_value (bp, edge->indirect_call, 1);
bp_pack_value (bp, edge->indirect_inlining_edge, 1);
bp_pack_value (bp, edge->call_stmt_cannot_inline_p, 1);
bp_pack_value (bp, edge->can_throw_external, 1);
lto_output_bitpack (ob->main_stream, bp);
......@@ -400,6 +406,25 @@ add_node_to (lto_cgraph_encoder_t encoder, struct cgraph_node *node)
lto_cgraph_encoder_encode (encoder, node);
}
/* Output all callees or indirect outgoing edges. EDGE must be the first such
edge. */
static void
output_outgoing_cgraph_edges (struct cgraph_edge *edge,
struct lto_simple_output_block *ob,
lto_cgraph_encoder_t encoder)
{
if (!edge)
return;
/* Output edges in backward direction, so the reconstructed callgraph match
and it is easy to associate call sites in the IPA pass summaries. */
while (edge->next_callee)
edge = edge->next_callee;
for (; edge; edge = edge->prev_callee)
lto_output_edge (ob, edge, encoder);
}
/* Output the part of the cgraph in SET. */
void
......@@ -468,16 +493,8 @@ output_cgraph (cgraph_node_set set)
for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
{
node = csi_node (csi);
if (node->callees)
{
/* Output edges in backward direction, so the reconstructed callgraph
match and it is easy to associate call sites in the IPA pass summaries. */
edge = node->callees;
while (edge->next_callee)
edge = edge->next_callee;
for (; edge; edge = edge->prev_callee)
lto_output_edge (ob, edge, encoder);
}
output_outgoing_cgraph_edges (node->callees, ob, encoder);
output_outgoing_cgraph_edges (node->indirect_calls, ob, encoder);
}
lto_output_uleb128_stream (ob->main_stream, 0);
......@@ -497,7 +514,6 @@ output_cgraph (cgraph_node_set set)
lto_destroy_simple_output_block (ob);
}
/* Overwrite the information in NODE based on FILE_DATA, TAG, FLAGS,
STACK_SIZE, SELF_TIME and SELF_SIZE. This is called either to initialize
NODE or to replace the values in it, for instance because the first
......@@ -668,11 +684,14 @@ input_node (struct lto_file_decl_data *file_data,
}
/* Read an edge from IB. NODES points to a vector of previously read
nodes for decoding caller and callee of the edge to be read. */
/* Read an edge from IB. NODES points to a vector of previously read nodes for
decoding caller and callee of the edge to be read. If INDIRECT is true, the
edge being read is indirect (in the sense that it has
indirect_unknown_callee set). */
static void
input_edge (struct lto_input_block *ib, VEC(cgraph_node_ptr, heap) *nodes)
input_edge (struct lto_input_block *ib, VEC(cgraph_node_ptr, heap) *nodes,
bool indirect)
{
struct cgraph_node *caller, *callee;
struct cgraph_edge *edge;
......@@ -688,9 +707,14 @@ input_edge (struct lto_input_block *ib, VEC(cgraph_node_ptr, heap) *nodes)
if (caller == NULL || caller->decl == NULL_TREE)
internal_error ("bytecode stream: no caller found while reading edge");
if (!indirect)
{
callee = VEC_index (cgraph_node_ptr, nodes, lto_input_sleb128 (ib));
if (callee == NULL || callee->decl == NULL_TREE)
internal_error ("bytecode stream: no callee found while reading edge");
}
else
callee = NULL;
count = (gcov_type) lto_input_sleb128 (ib);
......@@ -708,10 +732,14 @@ input_edge (struct lto_input_block *ib, VEC(cgraph_node_ptr, heap) *nodes)
|| caller_resolution == LDPR_PREEMPTED_IR)
return;
if (indirect)
edge = cgraph_create_indirect_edge (caller, NULL, count, freq, nest);
else
edge = cgraph_create_edge (caller, callee, NULL, count, freq, nest);
edge->indirect_inlining_edge = bp_unpack_value (bp, 1);
edge->lto_stmt_uid = stmt_id;
edge->inline_failed = inline_failed;
edge->indirect_call = bp_unpack_value (bp, 1);
edge->call_stmt_cannot_inline_p = bp_unpack_value (bp, 1);
edge->can_throw_external = bp_unpack_value (bp, 1);
bitpack_delete (bp);
......@@ -734,7 +762,9 @@ input_cgraph_1 (struct lto_file_decl_data *file_data,
while (tag)
{
if (tag == LTO_cgraph_edge)
input_edge (ib, nodes);
input_edge (ib, nodes, false);
else if (tag == LTO_cgraph_indirect_edge)
input_edge (ib, nodes, true);
else
{
node = input_node (file_data, ib, tag);
......
......@@ -1248,6 +1248,8 @@ fixup_call_stmt_edges_1 (struct cgraph_node *node, gimple *stmts)
struct cgraph_edge *cedge;
for (cedge = node->callees; cedge; cedge = cedge->next_callee)
cedge->call_stmt = stmts[cedge->lto_stmt_uid];
for (cedge = node->indirect_calls; cedge; cedge = cedge->next_callee)
cedge->call_stmt = stmts[cedge->lto_stmt_uid];
}
/* Fixup call_stmt pointers in NODE and all clones. */
......
2010-04-28 Martin Jambor <mjambor@suse.cz>
* gcc.dg/lto/20091209-1_0.c: New testcase.
2010-04-28 Richard Guenther <rguenther@suse.de>
PR tree-optimization/43879
......
/* Stream an indirect edge in and out. */
/* { dg-lto-do link } */
/* { dg-lto-options {{ -O3 -fno-early-inlining -flto }} } */
volatile int something;
static void hooray ()
{
something = 1;
}
static void hiphip (void (*f)())
{
something = 2;
f ();
}
int main (int argc, int *argv[])
{
hiphip (hooray);
return 0;
}
......@@ -1698,9 +1698,8 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale,
/* Constant propagation on argument done during inlining
may create new direct call. Produce an edge for it. */
if ((!edge
|| (edge->indirect_call
|| (edge->indirect_inlining_edge
&& id->transform_call_graph_edges == CB_CGE_MOVE_CLONES))
&& is_gimple_call (stmt)
&& (fn = gimple_call_fndecl (stmt)) != NULL)
{
struct cgraph_node *dest = cgraph_node (fn);
......@@ -3553,7 +3552,7 @@ get_indirect_callee_fndecl (struct cgraph_node *node, gimple stmt)
struct cgraph_edge *cs;
cs = cgraph_edge (node, stmt);
if (cs)
if (cs && !cs->indirect_unknown_callee)
return cs->callee->decl;
return NULL_TREE;
......@@ -3636,7 +3635,7 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id)
/* If this call was originally indirect, we do not want to emit any
inlining related warnings or sorry messages because there are no
guarantees regarding those. */
if (cg_edge->indirect_call)
if (cg_edge->indirect_inlining_edge)
goto egress;
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn))
......
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