mirror of git://gcc.gnu.org/git/gcc.git
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
This commit is contained in:
parent
18abb35edf
commit
e33c6cd6af
|
|
@ -1,3 +1,82 @@
|
||||||
|
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>
|
2010-04-28 Richard Guenther <rguenther@suse.de>
|
||||||
|
|
||||||
PR tree-optimization/43879
|
PR tree-optimization/43879
|
||||||
|
|
|
||||||
281
gcc/cgraph.c
281
gcc/cgraph.c
|
|
@ -34,10 +34,16 @@ The callgraph:
|
||||||
based on DECL_UID. The call-graph nodes are created lazily using
|
based on DECL_UID. The call-graph nodes are created lazily using
|
||||||
cgraph_node function when called for unknown declaration.
|
cgraph_node function when called for unknown declaration.
|
||||||
|
|
||||||
The callgraph at the moment does not represent indirect calls or calls
|
The callgraph at the moment does not represent all indirect calls or calls
|
||||||
from other compilation unit. Flag NEEDED is set for each node that may
|
from other compilation units. Flag NEEDED is set for each node that may be
|
||||||
be accessed in such an invisible way and it shall be considered an
|
accessed in such an invisible way and it shall be considered an entry point
|
||||||
entry point to the callgraph.
|
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:
|
Interprocedural information:
|
||||||
|
|
||||||
|
|
@ -48,6 +54,9 @@ The callgraph:
|
||||||
rtl_info used by RTL backend to propagate data from already compiled
|
rtl_info used by RTL backend to propagate data from already compiled
|
||||||
functions to their callers.
|
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:
|
Inlining plans:
|
||||||
|
|
||||||
The function inlining information is decided in advance and maintained
|
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;
|
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
|
/* Return the callgraph edge representing the GIMPLE_CALL statement
|
||||||
CALL_STMT. */
|
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
|
solution. It is not good idea to add pointer into CALL_EXPR itself
|
||||||
because we want to make possible having multiple cgraph nodes representing
|
because we want to make possible having multiple cgraph nodes representing
|
||||||
different clones of the same body before the body is actually cloned. */
|
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)
|
if (e->call_stmt == call_stmt)
|
||||||
break;
|
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);
|
node->call_site_hash = htab_create_ggc (120, edge_hash, edge_eq, NULL);
|
||||||
for (e2 = node->callees; e2; e2 = e2->next_callee)
|
for (e2 = node->callees; e2; e2 = e2->next_callee)
|
||||||
{
|
cgraph_add_edge_to_call_site_hash (e2);
|
||||||
void **slot;
|
for (e2 = node->indirect_calls; e2; e2 = e2->next_callee)
|
||||||
slot = htab_find_slot_with_hash (node->call_site_hash,
|
cgraph_add_edge_to_call_site_hash (e2);
|
||||||
e2->call_stmt,
|
|
||||||
htab_hash_pointer (e2->call_stmt),
|
|
||||||
INSERT);
|
|
||||||
gcc_assert (!*slot);
|
|
||||||
*slot = e2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
|
|
@ -774,26 +798,31 @@ cgraph_edge (struct cgraph_node *node, gimple call_stmt)
|
||||||
void
|
void
|
||||||
cgraph_set_call_stmt (struct cgraph_edge *e, gimple new_stmt)
|
cgraph_set_call_stmt (struct cgraph_edge *e, gimple new_stmt)
|
||||||
{
|
{
|
||||||
|
tree decl;
|
||||||
|
|
||||||
if (e->caller->call_site_hash)
|
if (e->caller->call_site_hash)
|
||||||
{
|
{
|
||||||
htab_remove_elt_with_hash (e->caller->call_site_hash,
|
htab_remove_elt_with_hash (e->caller->call_site_hash,
|
||||||
e->call_stmt,
|
e->call_stmt,
|
||||||
htab_hash_pointer (e->call_stmt));
|
htab_hash_pointer (e->call_stmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
e->call_stmt = new_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));
|
push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
|
||||||
e->can_throw_external = stmt_can_throw_external (new_stmt);
|
e->can_throw_external = stmt_can_throw_external (new_stmt);
|
||||||
pop_cfun ();
|
pop_cfun ();
|
||||||
if (e->caller->call_site_hash)
|
if (e->caller->call_site_hash)
|
||||||
{
|
cgraph_add_edge_to_call_site_hash (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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Like cgraph_set_call_stmt but walk the clone tree and update all
|
/* 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;
|
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;
|
e->inline_failed = CIF_BODY_NOT_AVAILABLE;
|
||||||
else if (callee->local.redefined_extern_inline)
|
else if (callee->local.redefined_extern_inline)
|
||||||
e->inline_failed = CIF_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;
|
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 *
|
static struct cgraph_edge *
|
||||||
cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
|
cgraph_create_edge_1 (struct cgraph_node *caller, struct cgraph_node *callee,
|
||||||
gimple call_stmt, gcov_type count, int freq, int nest)
|
gimple call_stmt, gcov_type count, int freq, int nest)
|
||||||
{
|
{
|
||||||
struct cgraph_edge *edge;
|
struct cgraph_edge *edge;
|
||||||
|
|
||||||
|
|
||||||
/* LTO does not actually have access to the call_stmt since these
|
/* LTO does not actually have access to the call_stmt since these
|
||||||
have not been loaded yet. */
|
have not been loaded yet. */
|
||||||
if (call_stmt)
|
if (call_stmt)
|
||||||
|
|
@ -941,47 +973,83 @@ cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
|
||||||
}
|
}
|
||||||
|
|
||||||
edge->aux = NULL;
|
edge->aux = NULL;
|
||||||
|
|
||||||
edge->caller = caller;
|
edge->caller = caller;
|
||||||
edge->callee = callee;
|
edge->callee = callee;
|
||||||
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->prev_caller = NULL;
|
||||||
edge->next_caller = callee->callers;
|
edge->next_caller = NULL;
|
||||||
if (callee->callers)
|
|
||||||
callee->callers->prev_caller = edge;
|
|
||||||
edge->prev_callee = NULL;
|
edge->prev_callee = NULL;
|
||||||
edge->next_callee = caller->callees;
|
edge->next_callee = NULL;
|
||||||
if (caller->callees)
|
|
||||||
caller->callees->prev_callee = edge;
|
|
||||||
caller->callees = edge;
|
|
||||||
callee->callers = edge;
|
|
||||||
edge->count = count;
|
edge->count = count;
|
||||||
gcc_assert (count >= 0);
|
gcc_assert (count >= 0);
|
||||||
edge->frequency = freq;
|
edge->frequency = freq;
|
||||||
gcc_assert (freq >= 0);
|
gcc_assert (freq >= 0);
|
||||||
gcc_assert (freq <= CGRAPH_FREQ_MAX);
|
gcc_assert (freq <= CGRAPH_FREQ_MAX);
|
||||||
edge->loop_nest = nest;
|
edge->loop_nest = nest;
|
||||||
edge->indirect_call = 0;
|
|
||||||
|
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->call_stmt_cannot_inline_p =
|
edge->call_stmt_cannot_inline_p =
|
||||||
(call_stmt ? gimple_call_cannot_inline_p (call_stmt) : false);
|
(call_stmt ? gimple_call_cannot_inline_p (call_stmt) : false);
|
||||||
if (call_stmt && caller->call_site_hash)
|
if (call_stmt && caller->call_site_hash)
|
||||||
{
|
cgraph_add_edge_to_call_site_hash (edge);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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);
|
initialize_inline_failed (edge);
|
||||||
|
|
||||||
|
edge->next_caller = callee->callers;
|
||||||
|
if (callee->callers)
|
||||||
|
callee->callers->prev_caller = edge;
|
||||||
|
edge->next_callee = caller->callees;
|
||||||
|
if (caller->callees)
|
||||||
|
caller->callees->prev_callee = edge;
|
||||||
|
caller->callees = edge;
|
||||||
|
callee->callers = 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;
|
return edge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -990,6 +1058,7 @@ cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
|
||||||
static inline void
|
static inline void
|
||||||
cgraph_edge_remove_callee (struct cgraph_edge *e)
|
cgraph_edge_remove_callee (struct cgraph_edge *e)
|
||||||
{
|
{
|
||||||
|
gcc_assert (!e->indirect_unknown_callee);
|
||||||
if (e->prev_caller)
|
if (e->prev_caller)
|
||||||
e->prev_caller->next_caller = e->next_caller;
|
e->prev_caller->next_caller = e->next_caller;
|
||||||
if (e->next_caller)
|
if (e->next_caller)
|
||||||
|
|
@ -1008,7 +1077,12 @@ cgraph_edge_remove_caller (struct cgraph_edge *e)
|
||||||
if (e->next_callee)
|
if (e->next_callee)
|
||||||
e->next_callee->prev_callee = e->prev_callee;
|
e->next_callee->prev_callee = e->prev_callee;
|
||||||
if (!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;
|
e->caller->callees = e->next_callee;
|
||||||
|
}
|
||||||
if (e->caller->call_site_hash)
|
if (e->caller->call_site_hash)
|
||||||
htab_remove_elt_with_hash (e->caller->call_site_hash,
|
htab_remove_elt_with_hash (e->caller->call_site_hash,
|
||||||
e->call_stmt,
|
e->call_stmt,
|
||||||
|
|
@ -1037,6 +1111,7 @@ cgraph_remove_edge (struct cgraph_edge *e)
|
||||||
/* Call all edge removal hooks. */
|
/* Call all edge removal hooks. */
|
||||||
cgraph_call_edge_removal_hooks (e);
|
cgraph_call_edge_removal_hooks (e);
|
||||||
|
|
||||||
|
if (!e->indirect_unknown_callee)
|
||||||
/* Remove from callers list of the callee. */
|
/* Remove from callers list of the callee. */
|
||||||
cgraph_edge_remove_callee (e);
|
cgraph_edge_remove_callee (e);
|
||||||
|
|
||||||
|
|
@ -1047,6 +1122,20 @@ cgraph_remove_edge (struct cgraph_edge *e)
|
||||||
cgraph_free_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
|
/* Redirect callee of E to N. The function does not update underlying
|
||||||
call expression. */
|
call expression. */
|
||||||
|
|
||||||
|
|
@ -1057,12 +1146,37 @@ cgraph_redirect_edge_callee (struct cgraph_edge *e, struct cgraph_node *n)
|
||||||
cgraph_edge_remove_callee (e);
|
cgraph_edge_remove_callee (e);
|
||||||
|
|
||||||
/* Insert to callers list of the new callee. */
|
/* Insert to callers list of the new callee. */
|
||||||
e->prev_caller = NULL;
|
cgraph_set_edge_callee (e, n);
|
||||||
if (n->callers)
|
}
|
||||||
n->callers->prev_caller = e;
|
|
||||||
e->next_caller = n->callers;
|
/* Make an indirect EDGE with an unknown callee an ordinary edge leading to
|
||||||
n->callers = e;
|
CALLEE. */
|
||||||
e->callee = n;
|
|
||||||
|
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)
|
if (e)
|
||||||
{
|
{
|
||||||
/* See if the call is already there. It might be because of indirect
|
/* See if the edge is already there and has the correct callee. It
|
||||||
inlining already found it. */
|
might be so because of indirect inlining has already updated
|
||||||
if (new_call && e->callee->decl == new_call)
|
it. */
|
||||||
|
if (new_call && e->callee && e->callee->decl == new_call)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Otherwise remove edge and create new one; we can't simply redirect
|
/* 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;
|
f = e->next_callee;
|
||||||
cgraph_call_edge_removal_hooks (e);
|
cgraph_call_edge_removal_hooks (e);
|
||||||
|
if (!e->indirect_unknown_callee)
|
||||||
cgraph_edge_remove_callee (e);
|
cgraph_edge_remove_callee (e);
|
||||||
cgraph_free_edge (e);
|
cgraph_free_edge (e);
|
||||||
}
|
}
|
||||||
|
|
@ -1627,6 +1743,8 @@ void
|
||||||
dump_cgraph_node (FILE *f, struct cgraph_node *node)
|
dump_cgraph_node (FILE *f, struct cgraph_node *node)
|
||||||
{
|
{
|
||||||
struct cgraph_edge *edge;
|
struct cgraph_edge *edge;
|
||||||
|
int indirect_calls_count = 0;
|
||||||
|
|
||||||
fprintf (f, "%s/%i(%i)", cgraph_node_name (node), node->uid,
|
fprintf (f, "%s/%i(%i)", cgraph_node_name (node), node->uid,
|
||||||
node->pid);
|
node->pid);
|
||||||
dump_addr (f, " @", (void *)node);
|
dump_addr (f, " @", (void *)node);
|
||||||
|
|
@ -1708,8 +1826,8 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
|
||||||
edge->frequency / (double)CGRAPH_FREQ_BASE);
|
edge->frequency / (double)CGRAPH_FREQ_BASE);
|
||||||
if (!edge->inline_failed)
|
if (!edge->inline_failed)
|
||||||
fprintf(f, "(inlined) ");
|
fprintf(f, "(inlined) ");
|
||||||
if (edge->indirect_call)
|
if (edge->indirect_inlining_edge)
|
||||||
fprintf(f, "(indirect) ");
|
fprintf(f, "(indirect_inlining) ");
|
||||||
if (edge->can_throw_external)
|
if (edge->can_throw_external)
|
||||||
fprintf(f, "(can throw external) ");
|
fprintf(f, "(can throw external) ");
|
||||||
}
|
}
|
||||||
|
|
@ -1721,8 +1839,8 @@ 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->indirect_call)
|
if (edge->indirect_inlining_edge)
|
||||||
fprintf(f, "(indirect) ");
|
fprintf(f, "(indirect_inlining) ");
|
||||||
if (edge->count)
|
if (edge->count)
|
||||||
fprintf (f, "("HOST_WIDEST_INT_PRINT_DEC"x) ",
|
fprintf (f, "("HOST_WIDEST_INT_PRINT_DEC"x) ",
|
||||||
(HOST_WIDEST_INT)edge->count);
|
(HOST_WIDEST_INT)edge->count);
|
||||||
|
|
@ -1736,6 +1854,12 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
|
||||||
}
|
}
|
||||||
fprintf (f, "\n");
|
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)
|
if (node->same_body)
|
||||||
{
|
{
|
||||||
struct cgraph_node *n;
|
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;
|
freq = e->frequency * (gcov_type) freq_scale / CGRAPH_FREQ_BASE;
|
||||||
if (freq > CGRAPH_FREQ_MAX)
|
if (freq > CGRAPH_FREQ_MAX)
|
||||||
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,
|
new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq,
|
||||||
e->loop_nest + loop_nest);
|
e->loop_nest + loop_nest);
|
||||||
|
|
||||||
new_edge->inline_failed = e->inline_failed;
|
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;
|
new_edge->lto_stmt_uid = stmt_uid;
|
||||||
if (update_original)
|
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,
|
cgraph_clone_edge (e, new_node, e->call_stmt, e->lto_stmt_uid,
|
||||||
count_scale, freq, loop_nest, update_original);
|
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;
|
new_node->next_sibling_clone = n->clones;
|
||||||
if (n->clones)
|
if (n->clones)
|
||||||
n->clones->prev_sibling_clone = new_node;
|
n->clones->prev_sibling_clone = new_node;
|
||||||
|
|
|
||||||
27
gcc/cgraph.h
27
gcc/cgraph.h
|
|
@ -199,6 +199,9 @@ struct GTY((chain_next ("%h.next"), chain_prev ("%h.previous"))) cgraph_node {
|
||||||
struct cgraph_edge *callers;
|
struct cgraph_edge *callers;
|
||||||
struct cgraph_node *next;
|
struct cgraph_node *next;
|
||||||
struct cgraph_node *previous;
|
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. */
|
/* For nested functions points to function the node is nested in. */
|
||||||
struct cgraph_node *origin;
|
struct cgraph_node *origin;
|
||||||
/* Points to first nested function, if any. */
|
/* Points to first nested function, if any. */
|
||||||
|
|
@ -333,6 +336,14 @@ typedef enum {
|
||||||
CIF_N_REASONS
|
CIF_N_REASONS
|
||||||
} cgraph_inline_failed_t;
|
} 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 {
|
struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) cgraph_edge {
|
||||||
/* Expected number of executions: calculated in profile.c. */
|
/* Expected number of executions: calculated in profile.c. */
|
||||||
gcov_type count;
|
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 *prev_callee;
|
||||||
struct cgraph_edge *next_callee;
|
struct cgraph_edge *next_callee;
|
||||||
gimple call_stmt;
|
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;
|
PTR GTY ((skip (""))) aux;
|
||||||
/* When equal to CIF_OK, inline this call. Otherwise, points to the
|
/* When equal to CIF_OK, inline this call. Otherwise, points to the
|
||||||
explanation why function was not inlined. */
|
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;
|
int uid;
|
||||||
/* Depth of loop nest, 1 means no loop nest. */
|
/* Depth of loop nest, 1 means no loop nest. */
|
||||||
unsigned short int loop_nest;
|
unsigned short int loop_nest;
|
||||||
/* Whether this edge describes a call that was originally indirect. */
|
/* Whether this edge was made direct by indirect inlining. */
|
||||||
unsigned int indirect_call : 1;
|
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. */
|
/* True if the corresponding CALL stmt cannot be inlined. */
|
||||||
unsigned int call_stmt_cannot_inline_p : 1;
|
unsigned int call_stmt_cannot_inline_p : 1;
|
||||||
/* Can this call throw externally? */
|
/* 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_edge *cgraph_create_edge (struct cgraph_node *,
|
||||||
struct cgraph_node *,
|
struct cgraph_node *,
|
||||||
gimple, gcov_type, int, int);
|
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_get_node (tree);
|
||||||
struct cgraph_node *cgraph_node (tree);
|
struct cgraph_node *cgraph_node (tree);
|
||||||
bool cgraph_same_body_alias (tree, 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) *);
|
int, bool, VEC(cgraph_edge_p,heap) *);
|
||||||
|
|
||||||
void cgraph_redirect_edge_callee (struct cgraph_edge *, struct cgraph_node *);
|
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);
|
struct cgraph_asm_node *cgraph_add_asm_node (tree);
|
||||||
|
|
||||||
|
|
@ -657,6 +677,7 @@ enum LTO_cgraph_tags
|
||||||
LTO_cgraph_overwritable_node,
|
LTO_cgraph_overwritable_node,
|
||||||
LTO_cgraph_unavail_node,
|
LTO_cgraph_unavail_node,
|
||||||
LTO_cgraph_edge,
|
LTO_cgraph_edge,
|
||||||
|
LTO_cgraph_indirect_edge,
|
||||||
LTO_cgraph_last_tag
|
LTO_cgraph_last_tag
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -607,6 +607,24 @@ verify_cgraph_node (struct cgraph_node *node)
|
||||||
error ("Inline clone is needed");
|
error ("Inline clone is needed");
|
||||||
error_found = true;
|
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)
|
for (e = node->callers; e; e = e->next_caller)
|
||||||
{
|
{
|
||||||
if (e->count < 0)
|
if (e->count < 0)
|
||||||
|
|
@ -759,10 +777,10 @@ verify_cgraph_node (struct cgraph_node *node)
|
||||||
gsi_next (&gsi))
|
gsi_next (&gsi))
|
||||||
{
|
{
|
||||||
gimple stmt = gsi_stmt (gsi);
|
gimple stmt = gsi_stmt (gsi);
|
||||||
tree decl;
|
if (is_gimple_call (stmt))
|
||||||
if (is_gimple_call (stmt) && (decl = gimple_call_fndecl (stmt)))
|
|
||||||
{
|
{
|
||||||
struct cgraph_edge *e = cgraph_edge (node, stmt);
|
struct cgraph_edge *e = cgraph_edge (node, stmt);
|
||||||
|
tree decl = gimple_call_fndecl (stmt);
|
||||||
if (e)
|
if (e)
|
||||||
{
|
{
|
||||||
if (e->aux)
|
if (e->aux)
|
||||||
|
|
@ -771,6 +789,8 @@ verify_cgraph_node (struct cgraph_node *node)
|
||||||
debug_gimple_stmt (stmt);
|
debug_gimple_stmt (stmt);
|
||||||
error_found = true;
|
error_found = true;
|
||||||
}
|
}
|
||||||
|
if (!e->indirect_unknown_callee)
|
||||||
|
{
|
||||||
if (e->callee->same_body_alias)
|
if (e->callee->same_body_alias)
|
||||||
{
|
{
|
||||||
error ("edge points to 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
|
else if (!node->global.inlined_to
|
||||||
&& !e->callee->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:");
|
error ("edge points to wrong declaration:");
|
||||||
debug_tree (e->callee->decl);
|
debug_tree (e->callee->decl);
|
||||||
|
|
@ -787,9 +809,18 @@ verify_cgraph_node (struct cgraph_node *node)
|
||||||
debug_tree (decl);
|
debug_tree (decl);
|
||||||
error_found = true;
|
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;
|
e->aux = (void *)1;
|
||||||
}
|
}
|
||||||
else
|
else if (decl)
|
||||||
{
|
{
|
||||||
error ("missing callgraph edge for call stmt:");
|
error ("missing callgraph edge for call stmt:");
|
||||||
debug_gimple_stmt (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)
|
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",
|
error ("edge %s->%s has no corresponding call_stmt",
|
||||||
identifier_to_locale (cgraph_node_name (e->caller)),
|
identifier_to_locale (cgraph_node_name (e->caller)),
|
||||||
|
|
@ -815,6 +846,17 @@ verify_cgraph_node (struct cgraph_node *node)
|
||||||
}
|
}
|
||||||
e->aux = 0;
|
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)
|
if (error_found)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -84,3 +84,7 @@ DEFCIFCODE(MISMATCHED_ARGUMENTS, N_("mismatched arguments"))
|
||||||
/* Call was originally indirect. */
|
/* Call was originally indirect. */
|
||||||
DEFCIFCODE(ORIGINALLY_INDIRECT_CALL,
|
DEFCIFCODE(ORIGINALLY_INDIRECT_CALL,
|
||||||
N_("originally indirect function call not considered for inlining"))
|
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);
|
ipcp_print_profile_data (dump_file);
|
||||||
}
|
}
|
||||||
/* Free all IPCP structures. */
|
/* Free all IPCP structures. */
|
||||||
free_all_ipa_structures_after_ipa_cp ();
|
ipa_free_all_structures_after_ipa_cp ();
|
||||||
if (dump_file)
|
if (dump_file)
|
||||||
fprintf (dump_file, "\nIPA constant propagation end\n");
|
fprintf (dump_file, "\nIPA constant propagation end\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1346,7 +1346,7 @@ struct ipa_opt_pass_d pass_ipa_cp =
|
||||||
ipcp_read_summary, /* read_summary */
|
ipcp_read_summary, /* read_summary */
|
||||||
NULL, /* write_optimization_summary */
|
NULL, /* write_optimization_summary */
|
||||||
NULL, /* read_optimization_summary */
|
NULL, /* read_optimization_summary */
|
||||||
lto_ipa_fixup_call_notes, /* stmt_fixup */
|
NULL, /* stmt_fixup */
|
||||||
0, /* TODOs */
|
0, /* TODOs */
|
||||||
NULL, /* function_transform */
|
NULL, /* function_transform */
|
||||||
NULL, /* variable_transform */
|
NULL, /* variable_transform */
|
||||||
|
|
|
||||||
|
|
@ -1322,6 +1322,8 @@ cgraph_decide_inlining (void)
|
||||||
cgraph_remove_function_insertion_hook (function_insertion_hook_holder);
|
cgraph_remove_function_insertion_hook (function_insertion_hook_holder);
|
||||||
if (in_lto_p && flag_indirect_inlining)
|
if (in_lto_p && flag_indirect_inlining)
|
||||||
ipa_update_after_lto_read ();
|
ipa_update_after_lto_read ();
|
||||||
|
if (flag_indirect_inlining)
|
||||||
|
ipa_create_all_structures_for_iinln ();
|
||||||
|
|
||||||
max_count = 0;
|
max_count = 0;
|
||||||
max_benefit = 0;
|
max_benefit = 0;
|
||||||
|
|
@ -1442,7 +1444,7 @@ cgraph_decide_inlining (void)
|
||||||
|
|
||||||
/* Free ipa-prop structures if they are no longer needed. */
|
/* Free ipa-prop structures if they are no longer needed. */
|
||||||
if (flag_indirect_inlining)
|
if (flag_indirect_inlining)
|
||||||
free_all_ipa_structures_after_iinln ();
|
ipa_free_all_structures_after_iinln ();
|
||||||
|
|
||||||
if (dump_file)
|
if (dump_file)
|
||||||
fprintf (dump_file,
|
fprintf (dump_file,
|
||||||
|
|
@ -2138,7 +2140,7 @@ struct ipa_opt_pass_d pass_ipa_inline =
|
||||||
inline_read_summary, /* read_summary */
|
inline_read_summary, /* read_summary */
|
||||||
NULL, /* write_optimization_summary */
|
NULL, /* write_optimization_summary */
|
||||||
NULL, /* read_optimization_summary */
|
NULL, /* read_optimization_summary */
|
||||||
lto_ipa_fixup_call_notes, /* stmt_fixup */
|
NULL, /* stmt_fixup */
|
||||||
0, /* TODOs */
|
0, /* TODOs */
|
||||||
inline_transform, /* function_transform */
|
inline_transform, /* function_transform */
|
||||||
NULL, /* variable_transform */
|
NULL, /* variable_transform */
|
||||||
|
|
|
||||||
239
gcc/ipa-prop.c
239
gcc/ipa-prop.c
|
|
@ -41,6 +41,10 @@ VEC (ipa_node_params_t, heap) *ipa_node_params_vector;
|
||||||
/* Vector where the parameter infos are actually stored. */
|
/* Vector where the parameter infos are actually stored. */
|
||||||
VEC (ipa_edge_args_t, gc) *ipa_edge_args_vector;
|
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: */
|
/* Holders of ipa cgraph hooks: */
|
||||||
static struct cgraph_edge_hook_list *edge_removal_hook_holder;
|
static struct cgraph_edge_hook_list *edge_removal_hook_holder;
|
||||||
static struct cgraph_node_hook_list *node_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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Creates a new note describing a call to a parameter number FORMAL_ID and
|
/* Create a new indirect call graph edge describing a call to a parameter
|
||||||
attaches it to the linked list of INFO. It also sets the called flag of the
|
number FORMAL_ID and and set the called flag of the parameter. NODE is the
|
||||||
parameter. STMT is the corresponding call statement. */
|
caller. STMT is the corresponding call statement. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ipa_note_param_call (struct ipa_node_params *info, int formal_id,
|
ipa_note_param_call (struct cgraph_node *node, int formal_id, gimple stmt)
|
||||||
gimple stmt)
|
|
||||||
{
|
{
|
||||||
struct ipa_param_call_note *note;
|
struct cgraph_edge *cs;
|
||||||
basic_block bb = gimple_bb (stmt);
|
basic_block bb = gimple_bb (stmt);
|
||||||
|
int freq;
|
||||||
|
|
||||||
note = XCNEW (struct ipa_param_call_note);
|
freq = compute_call_stmt_bb_frequency (current_function_decl, bb);
|
||||||
note->formal_id = formal_id;
|
cs = cgraph_create_indirect_edge (node, stmt, bb->count, freq,
|
||||||
note->stmt = stmt;
|
bb->loop_depth);
|
||||||
note->lto_stmt_uid = gimple_uid (stmt);
|
cs->indirect_info->param_index = formal_id;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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
|
(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
|
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
|
called flag and an indirect call graph edge describing the call is created.
|
||||||
for ordinary pointers represented in SSA but not-so-nice when it comes to
|
This is very simple for ordinary pointers represented in SSA but not-so-nice
|
||||||
member pointers. The ugly part of this function does nothing more than
|
when it comes to member pointers. The ugly part of this function does
|
||||||
tries to match the pattern of such a call. An example of such a pattern is
|
nothing more than trying to match the pattern of such a call. An example of
|
||||||
the gimple dump below, the call is on the last line:
|
such a pattern is the gimple dump below, the call is on the last line:
|
||||||
|
|
||||||
<bb 2>:
|
<bb 2>:
|
||||||
f$__delta_5 = f.__delta;
|
f$__delta_5 = f.__delta;
|
||||||
|
|
@ -817,7 +813,8 @@ ipa_note_param_call (struct ipa_node_params *info, int formal_id,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void
|
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);
|
tree target = gimple_call_fn (call);
|
||||||
gimple def;
|
gimple def;
|
||||||
|
|
@ -838,7 +835,7 @@ ipa_analyze_call_uses (struct ipa_node_params *info, gimple call)
|
||||||
/* assuming TREE_CODE (var) == PARM_DECL */
|
/* assuming TREE_CODE (var) == PARM_DECL */
|
||||||
index = ipa_get_param_decl_index (info, var);
|
index = ipa_get_param_decl_index (info, var);
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
ipa_note_param_call (info, index, call);
|
ipa_note_param_call (node, index, call);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -935,20 +932,21 @@ ipa_analyze_call_uses (struct ipa_node_params *info, gimple call)
|
||||||
|
|
||||||
index = ipa_get_param_decl_index (info, rec);
|
index = ipa_get_param_decl_index (info, rec);
|
||||||
if (index >= 0 && !ipa_is_param_modified (info, index))
|
if (index >= 0 && !ipa_is_param_modified (info, index))
|
||||||
ipa_note_param_call (info, index, call);
|
ipa_note_param_call (node, index, call);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Analyze the statement STMT with respect to formal parameters (described in
|
/* Analyze the call statement STMT with respect to formal parameters (described
|
||||||
INFO) and their uses. Currently it only checks whether formal parameters
|
in INFO) of caller given by NODE. Currently it only checks whether formal
|
||||||
are called. */
|
parameters are called. */
|
||||||
|
|
||||||
static void
|
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))
|
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.
|
/* 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))
|
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
|
||||||
{
|
{
|
||||||
gimple stmt = gsi_stmt (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. */
|
by JFUNC. NODE is the node where the call is. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_edge_addition_message (FILE *f, struct ipa_param_call_note *nt,
|
print_edge_addition_message (FILE *f, struct cgraph_edge *e,
|
||||||
struct ipa_jump_func *jfunc,
|
struct ipa_jump_func *jfunc)
|
||||||
struct cgraph_node *node)
|
|
||||||
{
|
{
|
||||||
fprintf (f, "ipa-prop: Discovered an indirect call to a known target (");
|
fprintf (f, "ipa-prop: Discovered an indirect call to a known target (");
|
||||||
if (jfunc->type == IPA_JF_CONST_MEMBER_PTR)
|
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
|
else
|
||||||
print_node_brief(f, "", jfunc->value.constant, 0);
|
print_node_brief(f, "", jfunc->value.constant, 0);
|
||||||
|
|
||||||
fprintf (f, ") in %s: ", cgraph_node_name (node));
|
fprintf (f, ") in %s: ", cgraph_node_name (e->caller));
|
||||||
print_gimple_stmt (f, nt->stmt, 2, TDF_SLIM);
|
print_gimple_stmt (f, e->call_stmt, 2, TDF_SLIM);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update the param called notes associated with NODE when CS is being inlined,
|
/* 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. */
|
unless NEW_EDGES is NULL. Return true iff a new edge(s) were created. */
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
update_call_notes_after_inlining (struct cgraph_edge *cs,
|
update_indirect_edges_after_inlining (struct cgraph_edge *cs,
|
||||||
struct cgraph_node *node,
|
struct cgraph_node *node,
|
||||||
VEC (cgraph_edge_p, heap) **new_edges)
|
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_edge_args *top = IPA_EDGE_REF (cs);
|
||||||
struct ipa_param_call_note *nt;
|
struct cgraph_edge *ie, *next_ie;
|
||||||
bool res = false;
|
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;
|
struct ipa_jump_func *jfunc;
|
||||||
|
|
||||||
if (nt->processed)
|
next_ie = ie->next_callee;
|
||||||
|
if (bitmap_bit_p (iinlining_processed_edges, ie->uid))
|
||||||
continue;
|
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: */
|
/* 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;
|
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
|
if (jfunc->type == IPA_JF_PASS_THROUGH
|
||||||
&& jfunc->value.pass_through.operation == NOP_EXPR)
|
&& 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
|
else if (jfunc->type == IPA_JF_CONST
|
||||||
|| jfunc->type == IPA_JF_CONST_MEMBER_PTR)
|
|| jfunc->type == IPA_JF_CONST_MEMBER_PTR)
|
||||||
{
|
{
|
||||||
struct cgraph_node *callee;
|
struct cgraph_node *callee;
|
||||||
struct cgraph_edge *new_indirect_edge;
|
|
||||||
tree decl;
|
tree decl;
|
||||||
|
|
||||||
nt->processed = true;
|
bitmap_set_bit (iinlining_processed_edges, ie->uid);
|
||||||
if (jfunc->type == IPA_JF_CONST_MEMBER_PTR)
|
if (jfunc->type == IPA_JF_CONST_MEMBER_PTR)
|
||||||
decl = jfunc->value.member_cst.pfn;
|
decl = jfunc->value.member_cst.pfn;
|
||||||
else
|
else
|
||||||
|
|
@ -1105,32 +1108,29 @@ update_call_notes_after_inlining (struct cgraph_edge *cs,
|
||||||
|
|
||||||
res = true;
|
res = true;
|
||||||
if (dump_file)
|
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,
|
cgraph_make_edge_direct (ie, callee);
|
||||||
nt->count, nt->frequency,
|
ie->indirect_inlining_edge = 1;
|
||||||
nt->loop_nest);
|
|
||||||
new_indirect_edge->lto_stmt_uid = nt->lto_stmt_uid;
|
|
||||||
new_indirect_edge->indirect_call = 1;
|
|
||||||
ipa_check_create_edge_args ();
|
|
||||||
if (new_edges)
|
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);
|
top = IPA_EDGE_REF (cs);
|
||||||
}
|
}
|
||||||
else
|
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. */
|
not be used on parameters that then get called. */
|
||||||
gcc_assert (jfunc->type == IPA_JF_UNKNOWN);
|
gcc_assert (jfunc->type == IPA_JF_UNKNOWN);
|
||||||
nt->processed = true;
|
bitmap_set_bit (iinlining_processed_edges, ie->uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Recursively traverse subtree of NODE (including node) made of inlined
|
/* Recursively traverse subtree of NODE (including node) made of inlined
|
||||||
cgraph_edges when CS has been inlined and invoke
|
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
|
update_jump_functions_after_inlining on all non-inlined edges that lead out
|
||||||
of this subtree. Newly discovered indirect edges will be added to
|
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
|
*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;
|
struct cgraph_edge *e;
|
||||||
bool res;
|
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)
|
for (e = node->callees; e; e = e->next_callee)
|
||||||
if (!e->inline_failed)
|
if (!e->inline_failed)
|
||||||
|
|
@ -1216,13 +1216,6 @@ ipa_free_node_params_substructures (struct ipa_node_params *info)
|
||||||
if (info->params)
|
if (info->params)
|
||||||
free (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));
|
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 *)
|
new_args->jump_functions = (struct ipa_jump_func *)
|
||||||
duplicate_ggc_array (old_args->jump_functions,
|
duplicate_ggc_array (old_args->jump_functions,
|
||||||
sizeof (struct ipa_jump_func) * arg_count);
|
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. */
|
/* 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)
|
__attribute__((unused)) void *data)
|
||||||
{
|
{
|
||||||
struct ipa_node_params *old_info, *new_info;
|
struct ipa_node_params *old_info, *new_info;
|
||||||
struct ipa_param_call_note *note;
|
|
||||||
int param_count;
|
int param_count;
|
||||||
|
|
||||||
ipa_check_create_node_params ();
|
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);
|
sizeof (struct ipa_param_descriptor) * param_count);
|
||||||
new_info->ipcp_orig_node = old_info->ipcp_orig_node;
|
new_info->ipcp_orig_node = old_info->ipcp_orig_node;
|
||||||
new_info->count_scale = old_info->count_scale;
|
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. */
|
/* Register our cgraph hooks if they are not already there. */
|
||||||
|
|
@ -1387,11 +1372,19 @@ ipa_unregister_cgraph_hooks (void)
|
||||||
node_duplication_hook_holder = NULL;
|
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
|
/* Free all ipa_node_params and all ipa_edge_args structures if they are no
|
||||||
longer needed after ipa-cp. */
|
longer needed after ipa-cp. */
|
||||||
|
|
||||||
void
|
void
|
||||||
free_all_ipa_structures_after_ipa_cp (void)
|
ipa_free_all_structures_after_ipa_cp (void)
|
||||||
{
|
{
|
||||||
if (!flag_indirect_inlining)
|
if (!flag_indirect_inlining)
|
||||||
{
|
{
|
||||||
|
|
@ -1405,8 +1398,10 @@ free_all_ipa_structures_after_ipa_cp (void)
|
||||||
longer needed after indirect inlining. */
|
longer needed after indirect inlining. */
|
||||||
|
|
||||||
void
|
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_edge_args ();
|
||||||
ipa_free_all_node_params ();
|
ipa_free_all_node_params ();
|
||||||
ipa_unregister_cgraph_hooks ();
|
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
|
static void
|
||||||
ipa_write_param_call_note (struct output_block *ob,
|
ipa_write_indirect_edge_info (struct output_block *ob,
|
||||||
struct ipa_param_call_note *note)
|
struct cgraph_edge *cs)
|
||||||
{
|
{
|
||||||
gcc_assert (!note->processed);
|
struct cgraph_indirect_call_info *ii = cs->indirect_info;
|
||||||
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, ii->param_index);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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
|
static void
|
||||||
ipa_read_param_call_note (struct lto_input_block *ib,
|
ipa_read_indirect_edge_info (struct lto_input_block *ib,
|
||||||
struct ipa_node_params *info)
|
struct data_in *data_in ATTRIBUTE_UNUSED,
|
||||||
|
struct cgraph_edge *cs)
|
||||||
{
|
{
|
||||||
struct ipa_param_call_note *note = XCNEW (struct ipa_param_call_note);
|
struct cgraph_indirect_call_info *ii = cs->indirect_info;
|
||||||
|
|
||||||
note->lto_stmt_uid = (unsigned int) lto_input_uleb128 (ib);
|
ii->param_index = (int) lto_input_sleb128 (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);
|
|
||||||
|
|
||||||
note->next = info->param_calls;
|
|
||||||
info->param_calls = note;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Stream out NODE info to OB. */
|
/* Stream out NODE info to OB. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -2019,8 +2005,6 @@ ipa_write_node_info (struct output_block *ob, struct cgraph_node *node)
|
||||||
int j;
|
int j;
|
||||||
struct cgraph_edge *e;
|
struct cgraph_edge *e;
|
||||||
struct bitpack_d *bp;
|
struct bitpack_d *bp;
|
||||||
int note_count = 0;
|
|
||||||
struct ipa_param_call_note *note;
|
|
||||||
|
|
||||||
encoder = ob->decl_state->cgraph_node_encoder;
|
encoder = ob->decl_state->cgraph_node_encoder;
|
||||||
node_ref = lto_cgraph_encoder_encode (encoder, node);
|
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++)
|
for (j = 0; j < ipa_get_cs_argument_count (args); j++)
|
||||||
ipa_write_jump_function (ob, ipa_get_ith_jump_func (args, j));
|
ipa_write_jump_function (ob, ipa_get_ith_jump_func (args, j));
|
||||||
}
|
}
|
||||||
|
for (e = node->indirect_calls; e; e = e->next_callee)
|
||||||
for (note = info->param_calls; note; note = note->next)
|
ipa_write_indirect_edge_info (ob, e);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Srtream in NODE info from IB. */
|
/* 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;
|
int k;
|
||||||
struct cgraph_edge *e;
|
struct cgraph_edge *e;
|
||||||
struct bitpack_d *bp;
|
struct bitpack_d *bp;
|
||||||
int i, note_count;
|
|
||||||
|
|
||||||
ipa_initialize_node_params (node);
|
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++)
|
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);
|
ipa_read_jump_function (ib, ipa_get_ith_jump_func (args, k), data_in);
|
||||||
}
|
}
|
||||||
|
for (e = node->indirect_calls; e; e = e->next_callee)
|
||||||
note_count = lto_input_uleb128 (ib);
|
ipa_read_indirect_edge_info (ib, data_in, e);
|
||||||
for (i = 0; i < note_count; i++)
|
|
||||||
ipa_read_param_call_note (ib, info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write jump functions for nodes in SET. */
|
/* 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));
|
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;
|
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. */
|
/* Structure describing a single formal parameter. */
|
||||||
struct ipa_param_descriptor
|
struct ipa_param_descriptor
|
||||||
{
|
{
|
||||||
|
|
@ -193,8 +167,6 @@ struct ipa_node_params
|
||||||
/* Pointer to an array of structures describing individual formal
|
/* Pointer to an array of structures describing individual formal
|
||||||
parameters. */
|
parameters. */
|
||||||
struct ipa_param_descriptor *params;
|
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,
|
/* Only for versioned nodes this field would not be NULL,
|
||||||
it points to the node that IPA cp cloned from. */
|
it points to the node that IPA cp cloned from. */
|
||||||
struct cgraph_node *ipcp_orig_node;
|
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_node_params_substructures (struct ipa_node_params *);
|
||||||
void ipa_free_all_node_params (void);
|
void ipa_free_all_node_params (void);
|
||||||
void ipa_free_all_edge_args (void);
|
void ipa_free_all_edge_args (void);
|
||||||
void free_all_ipa_structures_after_ipa_cp (void);
|
void ipa_create_all_structures_for_iinln (void);
|
||||||
void free_all_ipa_structures_after_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);
|
void ipa_register_cgraph_hooks (void);
|
||||||
|
|
||||||
/* This function ensures the array of node param infos is big enough to
|
/* 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;
|
intptr_t ref;
|
||||||
struct bitpack_d *bp;
|
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);
|
lto_output_uleb128_stream (ob->main_stream, LTO_cgraph_edge);
|
||||||
|
|
||||||
ref = lto_cgraph_encoder_lookup (encoder, edge->caller);
|
ref = lto_cgraph_encoder_lookup (encoder, edge->caller);
|
||||||
gcc_assert (ref != LCC_NOT_FOUND);
|
gcc_assert (ref != LCC_NOT_FOUND);
|
||||||
lto_output_sleb128_stream (ob->main_stream, ref);
|
lto_output_sleb128_stream (ob->main_stream, ref);
|
||||||
|
|
||||||
|
if (!edge->indirect_unknown_callee)
|
||||||
|
{
|
||||||
ref = lto_cgraph_encoder_lookup (encoder, edge->callee);
|
ref = lto_cgraph_encoder_lookup (encoder, edge->callee);
|
||||||
gcc_assert (ref != LCC_NOT_FOUND);
|
gcc_assert (ref != LCC_NOT_FOUND);
|
||||||
lto_output_sleb128_stream (ob->main_stream, ref);
|
lto_output_sleb128_stream (ob->main_stream, ref);
|
||||||
|
}
|
||||||
|
|
||||||
lto_output_sleb128_stream (ob->main_stream, edge->count);
|
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->inline_failed, HOST_BITS_PER_INT);
|
||||||
bp_pack_value (bp, edge->frequency, 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->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->call_stmt_cannot_inline_p, 1);
|
||||||
bp_pack_value (bp, edge->can_throw_external, 1);
|
bp_pack_value (bp, edge->can_throw_external, 1);
|
||||||
lto_output_bitpack (ob->main_stream, bp);
|
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);
|
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. */
|
/* Output the part of the cgraph in SET. */
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -468,16 +493,8 @@ output_cgraph (cgraph_node_set set)
|
||||||
for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
|
for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
|
||||||
{
|
{
|
||||||
node = csi_node (csi);
|
node = csi_node (csi);
|
||||||
if (node->callees)
|
output_outgoing_cgraph_edges (node->callees, ob, encoder);
|
||||||
{
|
output_outgoing_cgraph_edges (node->indirect_calls, ob, encoder);
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lto_output_uleb128_stream (ob->main_stream, 0);
|
lto_output_uleb128_stream (ob->main_stream, 0);
|
||||||
|
|
@ -497,7 +514,6 @@ output_cgraph (cgraph_node_set set)
|
||||||
lto_destroy_simple_output_block (ob);
|
lto_destroy_simple_output_block (ob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Overwrite the information in NODE based on FILE_DATA, TAG, FLAGS,
|
/* Overwrite the information in NODE based on FILE_DATA, TAG, FLAGS,
|
||||||
STACK_SIZE, SELF_TIME and SELF_SIZE. This is called either to initialize
|
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
|
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
|
/* Read an edge from IB. NODES points to a vector of previously read nodes for
|
||||||
nodes for decoding caller and callee of the edge to be read. */
|
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
|
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_node *caller, *callee;
|
||||||
struct cgraph_edge *edge;
|
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)
|
if (caller == NULL || caller->decl == NULL_TREE)
|
||||||
internal_error ("bytecode stream: no caller found while reading edge");
|
internal_error ("bytecode stream: no caller found while reading edge");
|
||||||
|
|
||||||
|
if (!indirect)
|
||||||
|
{
|
||||||
callee = VEC_index (cgraph_node_ptr, nodes, lto_input_sleb128 (ib));
|
callee = VEC_index (cgraph_node_ptr, nodes, lto_input_sleb128 (ib));
|
||||||
if (callee == NULL || callee->decl == NULL_TREE)
|
if (callee == NULL || callee->decl == NULL_TREE)
|
||||||
internal_error ("bytecode stream: no callee found while reading edge");
|
internal_error ("bytecode stream: no callee found while reading edge");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
callee = NULL;
|
||||||
|
|
||||||
count = (gcov_type) lto_input_sleb128 (ib);
|
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)
|
|| caller_resolution == LDPR_PREEMPTED_IR)
|
||||||
return;
|
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 = 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->lto_stmt_uid = stmt_id;
|
||||||
edge->inline_failed = inline_failed;
|
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->call_stmt_cannot_inline_p = bp_unpack_value (bp, 1);
|
||||||
edge->can_throw_external = bp_unpack_value (bp, 1);
|
edge->can_throw_external = bp_unpack_value (bp, 1);
|
||||||
bitpack_delete (bp);
|
bitpack_delete (bp);
|
||||||
|
|
@ -734,7 +762,9 @@ input_cgraph_1 (struct lto_file_decl_data *file_data,
|
||||||
while (tag)
|
while (tag)
|
||||||
{
|
{
|
||||||
if (tag == LTO_cgraph_edge)
|
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
|
else
|
||||||
{
|
{
|
||||||
node = input_node (file_data, ib, tag);
|
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;
|
struct cgraph_edge *cedge;
|
||||||
for (cedge = node->callees; cedge; cedge = cedge->next_callee)
|
for (cedge = node->callees; cedge; cedge = cedge->next_callee)
|
||||||
cedge->call_stmt = stmts[cedge->lto_stmt_uid];
|
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. */
|
/* Fixup call_stmt pointers in NODE and all clones. */
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
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>
|
2010-04-28 Richard Guenther <rguenther@suse.de>
|
||||||
|
|
||||||
PR tree-optimization/43879
|
PR tree-optimization/43879
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
/* 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
|
/* Constant propagation on argument done during inlining
|
||||||
may create new direct call. Produce an edge for it. */
|
may create new direct call. Produce an edge for it. */
|
||||||
if ((!edge
|
if ((!edge
|
||||||
|| (edge->indirect_call
|
|| (edge->indirect_inlining_edge
|
||||||
&& id->transform_call_graph_edges == CB_CGE_MOVE_CLONES))
|
&& id->transform_call_graph_edges == CB_CGE_MOVE_CLONES))
|
||||||
&& is_gimple_call (stmt)
|
|
||||||
&& (fn = gimple_call_fndecl (stmt)) != NULL)
|
&& (fn = gimple_call_fndecl (stmt)) != NULL)
|
||||||
{
|
{
|
||||||
struct cgraph_node *dest = cgraph_node (fn);
|
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;
|
struct cgraph_edge *cs;
|
||||||
|
|
||||||
cs = cgraph_edge (node, stmt);
|
cs = cgraph_edge (node, stmt);
|
||||||
if (cs)
|
if (cs && !cs->indirect_unknown_callee)
|
||||||
return cs->callee->decl;
|
return cs->callee->decl;
|
||||||
|
|
||||||
return NULL_TREE;
|
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
|
/* If this call was originally indirect, we do not want to emit any
|
||||||
inlining related warnings or sorry messages because there are no
|
inlining related warnings or sorry messages because there are no
|
||||||
guarantees regarding those. */
|
guarantees regarding those. */
|
||||||
if (cg_edge->indirect_call)
|
if (cg_edge->indirect_inlining_edge)
|
||||||
goto egress;
|
goto egress;
|
||||||
|
|
||||||
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn))
|
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue