From: Richard Guenther <richard.guenther@gmail.com>
To: Martin Jambor <mjambor@suse.cz>
Cc: GCC Patches <gcc-patches@gcc.gnu.org>, Jan Hubicka <hubicka@ucw.cz>
Subject: Re: [PATCH 2/6] Indirect call graph edges
Date: Sat, 13 Feb 2010 18:17:00 -0000 [thread overview]
Message-ID: <84fc9c001002131016s3a81d6a9ib25b73d8064473e5@mail.gmail.com> (raw)
In-Reply-To: <20100213180157.129177066@alvy.suse.cz>
On Sat, Feb 13, 2010 at 7:01 PM, Martin Jambor <mjambor@suse.cz> wrote:
> Hi,
>
> this is a re-post of the same patch I already sent here in January.
> There are only minor differences. I have renamed the flag
> indirect_call to indirect_inlining_edge and changed its purpose to be
> exactly that, an edge made direct by indirect inlining (it may go away
> completely, I currently use it only for asserts in ipa-cp
> devirtualization) and I have added a few things to cgraph node
> dumping. Most importantly, I have re-worked streaming so that
> information for indirect inlining associated with nodes is written to
> the jump functions section again in order to allow the cgraph stream
> to remain simple and concise (which means it cannot store trees).
>
> The patch adds a new kind of edge to the call graph to represent
> indirect calls with unknown callees (which we hope we can determine at
> some stage) and uses them to replace ipa_param_call_notes in
> ipa-prop.h. My immediate reason to do this is to move all the
> information there from the notes because I will need them to be in
> memory managed by the garbage collector (and did not want
> ipa_node_params to become garbage collected) but in a broader sense
> this is the right thing to do because it facilitates a more general
> approach to keep information about such calls (ipa-cp jump functions
> and the like) that is also consistent with how we do this for direct
> edges.
>
> The data structure layout (a flag and a pointer in struct cgraph_edge
> to a another special structure) was really chosen by Honza. The
> subsequent patches add some more stuff to cgraph_indirect_call_info so
> it won't look this silly for long.
>
> I have successfully bootstrapped and tested this beast on x86_84-linux
> and would like to commit something along these lines when stage1 opens
> again. Obviously, I welcome any comments.
The cgraph.c file at the top currently contains very brief documentation
which needs to be updated as it says:
The callgraph at the moment does not represent indirect calls or calls
from other compilation unit.
Note that I think we should have some more elaborate overall overview
about the cgraph interface.
Thanks,
Richard.
> Thanks,
>
> Martin
>
>
> 2010-02-10 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.
> * cgraph.c (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 (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.
> (ipa_free_node_params_substructures): Do not free notes.
> (ipa_node_duplication_hook): Do not duplicate notes.
> (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.
>
>
> Index: icln/gcc/cgraph.h
> ===================================================================
> --- icln.orig/gcc/cgraph.h
> +++ icln/gcc/cgraph.h
> @@ -184,6 +184,9 @@ struct GTY((chain_next ("%h.next"), chai
> struct cgraph_edge *callers;
> struct cgraph_node *next;
> struct cgraph_node *previous;
> + /* List of edges representing indirect calls with a yet undetermined
> + caller. */
> + 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. */
> @@ -311,6 +314,17 @@ 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;
> +
> + /* Whether this edge has already been looked at by indirect inlining. */
> + unsigned int inlining_processed : 1;
> +};
> +
> 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;
> @@ -321,6 +335,8 @@ struct GTY((chain_next ("%h.next_caller"
> struct cgraph_edge *prev_callee;
> struct cgraph_edge *next_callee;
> gimple call_stmt;
> + /* Additional information about an indirect call. Can be NULL for */
> + 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. */
> @@ -336,8 +352,12 @@ struct GTY((chain_next ("%h.next_caller"
> 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 created by indirect inlining. */
> + unsigned int indirect_inlining_edge : 1;
> + /* Whether this edge describes an inderect call with an undetermined
> + callee. */
> + unsigned int indirect_unknown_callee : 1;
> + /* Whether this edge is still a danglig */
> /* True if the corresponding CALL stmt cannot be inlined. */
> unsigned int call_stmt_cannot_inline_p : 1;
> /* Can this call throw externally? */
> @@ -435,7 +455,8 @@ void cgraph_node_remove_callees (struct
> 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);
> @@ -461,6 +482,7 @@ struct cgraph_node * cgraph_clone_node (
> 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);
>
> @@ -628,6 +650,7 @@ enum LTO_cgraph_tags
> LTO_cgraph_overwritable_node,
> LTO_cgraph_unavail_node,
> LTO_cgraph_edge,
> + LTO_cgraph_indirect_edge, /* edges with indirect_unknown_callee set */
> LTO_cgraph_last_tag
> };
>
> Index: icln/gcc/ipa-prop.h
> ===================================================================
> --- icln.orig/gcc/ipa-prop.h
> +++ icln/gcc/ipa-prop.h
> @@ -134,32 +134,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
> {
> @@ -185,8 +159,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;
> Index: icln/gcc/cgraph.c
> ===================================================================
> --- icln.orig/gcc/cgraph.c
> +++ icln/gcc/cgraph.c
> @@ -721,6 +721,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. */
> @@ -741,26 +754,28 @@ cgraph_edge (struct cgraph_node *node, g
> 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;
> + n++;
> + }
> +
> if (n > 100)
> {
> 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;
> @@ -772,26 +787,31 @@ cgraph_edge (struct cgraph_node *node, g
> 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
> @@ -893,7 +913,9 @@ initialize_inline_failed (struct cgraph_
> {
> 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;
> @@ -905,15 +927,16 @@ initialize_inline_failed (struct cgraph_
> e->inline_failed = CIF_FUNCTION_NOT_CONSIDERED;
> }
>
> -/* 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)
> +/* Allocate a cgraph_edge structure and fill it with data according to the
> + parameters ow which only CALLEE can be NULL (when creating an indirect call
> + edge). */
> +
> +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)
> @@ -939,47 +962,84 @@ cgraph_create_edge (struct cgraph_node *
> }
>
> 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->indirect_info->inlining_processed = 0;
> +
> + edge->next_callee = caller->indirect_calls;
> + if (caller->indirect_calls)
> + caller->indirect_calls->prev_callee = edge;
> + caller->indirect_calls = edge;
> +
> return edge;
> }
>
> @@ -988,6 +1048,7 @@ cgraph_create_edge (struct cgraph_node *
> 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)
> @@ -1006,7 +1067,12 @@ cgraph_edge_remove_caller (struct cgraph
> if (e->next_callee)
> e->next_callee->prev_callee = e->prev_callee;
> if (!e->prev_callee)
> - e->caller->callees = e->next_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,
> @@ -1035,8 +1101,9 @@ cgraph_remove_edge (struct cgraph_edge *
> /* Call all edge removal hooks. */
> cgraph_call_edge_removal_hooks (e);
>
> - /* Remove from callers list of the callee. */
> - cgraph_edge_remove_callee (e);
> + if (!e->indirect_unknown_callee)
> + /* Remove from callers list of the callee. */
> + cgraph_edge_remove_callee (e);
>
> /* Remove from callees list of the callers. */
> cgraph_edge_remove_caller (e);
> @@ -1045,6 +1112,20 @@ cgraph_remove_edge (struct cgraph_edge *
> 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. */
>
> @@ -1055,12 +1136,37 @@ cgraph_redirect_edge_callee (struct cgra
> 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,7 +1197,7 @@ cgraph_update_edges_for_call_stmt_node (
> {
> /* 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)
> + if (new_call && e->callee && e->callee->decl == new_call)
> return;
>
> /* Otherwise remove edge and create new one; we can't simply redirect
> @@ -1169,7 +1275,8 @@ cgraph_node_remove_callees (struct cgrap
> {
> f = e->next_callee;
> cgraph_call_edge_removal_hooks (e);
> - cgraph_edge_remove_callee (e);
> + if (!e->indirect_unknown_callee)
> + cgraph_edge_remove_callee (e);
> cgraph_free_edge (e);
> }
> node->callees = NULL;
> @@ -1624,6 +1731,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);
> @@ -1697,8 +1806,8 @@ dump_cgraph_node (FILE *f, struct cgraph
> 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) ");
> }
> @@ -1710,8 +1819,8 @@ dump_cgraph_node (FILE *f, struct cgraph
> 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);
> @@ -1725,6 +1834,12 @@ dump_cgraph_node (FILE *f, struct cgraph
> }
> 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;
> @@ -1844,11 +1959,30 @@ cgraph_clone_edge (struct cgraph_edge *e
> freq = e->frequency * (gcov_type) freq_scale / CGRAPH_FREQ_BASE;
> if (freq > CGRAPH_FREQ_MAX)
> freq = CGRAPH_FREQ_MAX;
> - new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq,
> - e->loop_nest + loop_nest);
> +
> + 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)
> {
> @@ -1918,6 +2052,10 @@ cgraph_clone_node (struct cgraph_node *n
> 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;
> Index: icln/gcc/ipa-prop.c
> ===================================================================
> --- icln.orig/gcc/ipa-prop.c
> +++ icln/gcc/ipa-prop.c
> @@ -735,41 +735,36 @@ 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 and is described by INFO. 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, struct ipa_node_params *info,
> + int formal_id, gimple stmt)
> {
> - struct ipa_param_call_note *note;
> + struct cgraph_edge *cs;
> basic_block bb = gimple_bb (stmt);
> + int freq;
>
> info->params[formal_id].called = 1;
>
> - 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 a 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 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:
>
> <bb 2>:
> f$__delta_5 = f.__delta;
> @@ -809,7 +804,8 @@ ipa_note_param_call (struct ipa_node_par
> */
>
> 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;
> @@ -830,7 +826,7 @@ ipa_analyze_call_uses (struct ipa_node_p
> /* 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, info, index, call);
> return;
> }
>
> @@ -927,20 +923,21 @@ ipa_analyze_call_uses (struct ipa_node_p
>
> 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, info, 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.
> @@ -965,7 +962,7 @@ ipa_analyze_params_uses (struct cgraph_n
> 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);
> }
> }
>
> @@ -1021,9 +1018,8 @@ update_jump_functions_after_inlining (st
> 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)
> @@ -1034,8 +1030,8 @@ print_edge_addition_message (FILE *f, st
> 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,
> @@ -1049,37 +1045,43 @@ update_call_notes_after_inlining (struct
> 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 (ici->inlining_processed)
> 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;
> + ici->inlining_processed = true;
> 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;
> + ici->inlining_processed = true;
> if (jfunc->type == IPA_JF_CONST_MEMBER_PTR)
> decl = jfunc->value.member_cst.pfn;
> else
> @@ -1097,16 +1099,12 @@ update_call_notes_after_inlining (struct
>
> 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
> @@ -1114,9 +1112,10 @@ update_call_notes_after_inlining (struct
> /* Ancestor jum 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;
> + ici->inlining_processed = true;
> }
> }
> +
> return res;
> }
>
> @@ -1208,13 +1207,6 @@ ipa_free_node_params_substructures (stru
> 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));
> }
>
> @@ -1314,7 +1306,6 @@ ipa_node_duplication_hook (struct cgraph
> __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 ();
> @@ -1328,17 +1319,6 @@ ipa_node_duplication_hook (struct cgraph
> 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. */
> @@ -1964,40 +1944,40 @@ ipa_read_jump_function (struct lto_input
> }
> }
>
> -/* 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;
> + struct bitpack_d *bp;
> +
> + lto_output_sleb128_stream (ob->main_stream, ii->param_index);
> + bp = bitpack_create ();
> + bp_pack_value (bp, ii->inlining_processed, 1);
> + lto_output_bitpack (ob->main_stream, bp);
> + bitpack_delete (bp);
> }
>
> -/* 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;
> + struct bitpack_d *bp;
>
> - note->next = info->param_calls;
> - info->param_calls = note;
> + ii->param_index = (int) lto_input_sleb128 (ib);
> + bp = lto_input_bitpack (ib);
> + ii->inlining_processed = bp_unpack_value (bp, 1);
> + bitpack_delete (bp);
> }
>
> -
> /* Stream out NODE info to OB. */
>
> static void
> @@ -2009,8 +1989,6 @@ ipa_write_node_info (struct output_block
> 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);
> @@ -2024,10 +2002,7 @@ ipa_write_node_info (struct output_block
> gcc_assert (!info->node_enqueued);
> gcc_assert (!info->ipcp_orig_node);
> for (j = 0; j < ipa_get_param_count (info); j++)
> - {
> - bp_pack_value (bp, info->params[j].modified, 1);
> - bp_pack_value (bp, info->params[j].called, 1);
> - }
> + bp_pack_value (bp, info->params[j].modified, 1);
> lto_output_bitpack (ob->main_stream, bp);
> bitpack_delete (bp);
> for (e = node->callees; e; e = e->next_callee)
> @@ -2039,12 +2014,8 @@ ipa_write_node_info (struct output_block
> 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. */
> @@ -2057,7 +2028,6 @@ ipa_read_node_info (struct lto_input_blo
> int k;
> struct cgraph_edge *e;
> struct bitpack_d *bp;
> - int i, note_count;
>
> ipa_initialize_node_params (node);
>
> @@ -2071,10 +2041,7 @@ ipa_read_node_info (struct lto_input_blo
> }
> info->node_enqueued = false;
> for (k = 0; k < ipa_get_param_count (info); k++)
> - {
> - info->params[k].modified = bp_unpack_value (bp, 1);
> - info->params[k].called = bp_unpack_value (bp, 1);
> - }
> + info->params[k].modified = bp_unpack_value (bp, 1);
> bitpack_delete (bp);
> for (e = node->callees; e; e = e->next_callee)
> {
> @@ -2090,10 +2057,8 @@ ipa_read_node_info (struct lto_input_blo
> 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. */
> @@ -2218,29 +2183,3 @@ ipa_update_after_lto_read (void)
> }
> }
> }
> -
> -/* 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);
> -}
> Index: icln/gcc/ipa-cp.c
> ===================================================================
> --- icln.orig/gcc/ipa-cp.c
> +++ icln/gcc/ipa-cp.c
> @@ -1332,7 +1332,7 @@ struct ipa_opt_pass_d pass_ipa_cp =
> ipcp_write_summary, /* write_summary */
> ipcp_read_summary, /* read_summary */
> NULL, /* function_read_summary */
> - lto_ipa_fixup_call_notes, /* stmt_fixup */
> + NULL, /* stmt_fixup */
> 0, /* TODOs */
> NULL, /* function_transform */
> NULL, /* variable_transform */
> Index: icln/gcc/ipa-inline.c
> ===================================================================
> --- icln.orig/gcc/ipa-inline.c
> +++ icln/gcc/ipa-inline.c
> @@ -2066,7 +2066,7 @@ struct ipa_opt_pass_d pass_ipa_inline =
> inline_write_summary, /* write_summary */
> inline_read_summary, /* read_summary */
> NULL, /* function_read_summary */
> - lto_ipa_fixup_call_notes, /* stmt_fixup */
> + NULL, /* stmt_fixup */
> 0, /* TODOs */
> inline_transform, /* function_transform */
> NULL, /* variable_transform */
> Index: icln/gcc/cgraphunit.c
> ===================================================================
> --- icln.orig/gcc/cgraphunit.c
> +++ icln/gcc/cgraphunit.c
> @@ -607,6 +607,24 @@ verify_cgraph_node (struct cgraph_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)
> @@ -733,10 +751,10 @@ verify_cgraph_node (struct cgraph_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)
> @@ -745,25 +763,38 @@ verify_cgraph_node (struct cgraph_node *
> debug_gimple_stmt (stmt);
> error_found = true;
> }
> - if (e->callee->same_body_alias)
> + if (!e->indirect_unknown_callee)
> {
> - error ("edge points to same body alias:");
> - debug_tree (e->callee->decl);
> - error_found = true;
> + if (e->callee->same_body_alias)
> + {
> + error ("edge points to same body alias:");
> + debug_tree (e->callee->decl);
> + error_found = true;
> + }
> + else if (!node->global.inlined_to
> + && !e->callee->global.inlined_to
> + && decl
> + && !clone_of_p (cgraph_node (decl),
> + e->callee))
> + {
> + error ("edge points to wrong declaration:");
> + debug_tree (e->callee->decl);
> + fprintf (stderr," Instead of:");
> + debug_tree (decl);
> + error_found = true;
> + }
> }
> - else if (!node->global.inlined_to
> - && !e->callee->global.inlined_to
> - && !clone_of_p (cgraph_node (decl), e->callee))
> + else if (decl)
> {
> - error ("edge points to wrong declaration:");
> - debug_tree (e->callee->decl);
> - fprintf (stderr," Instead of:");
> - debug_tree (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);
> @@ -779,7 +810,7 @@ verify_cgraph_node (struct cgraph_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)),
> @@ -787,6 +818,17 @@ verify_cgraph_node (struct cgraph_node *
> debug_gimple_stmt (e->call_stmt);
> error_found = true;
> }
> + 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;
> }
> }
> Index: icln/gcc/cif-code.def
> ===================================================================
> --- icln.orig/gcc/cif-code.def
> +++ icln/gcc/cif-code.def
> @@ -84,3 +84,7 @@ DEFCIFCODE(MISMATCHED_ARGUMENTS, N_("mis
> /* 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"))
> Index: icln/gcc/tree-inline.c
> ===================================================================
> --- icln.orig/gcc/tree-inline.c
> +++ icln/gcc/tree-inline.c
> @@ -1674,9 +1674,8 @@ copy_bb (copy_body_data *id, basic_block
> /* 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);
> @@ -3452,7 +3451,7 @@ get_indirect_callee_fndecl (struct cgrap
> struct cgraph_edge *cs;
>
> cs = cgraph_edge (node, stmt);
> - if (cs)
> + if (cs && !cs->indirect_unknown_callee)
> return cs->callee->decl;
>
> return NULL_TREE;
> @@ -3535,7 +3534,7 @@ expand_call_inline (basic_block bb, gimp
> /* 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))
> Index: icln/gcc/lto-cgraph.c
> ===================================================================
> --- icln.orig/gcc/lto-cgraph.c
> +++ icln/gcc/lto-cgraph.c
> @@ -139,15 +139,21 @@ lto_output_edge (struct lto_simple_outpu
> intptr_t ref;
> struct bitpack_d *bp;
>
> - lto_output_uleb128_stream (ob->main_stream, LTO_cgraph_edge);
> + 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);
>
> - ref = lto_cgraph_encoder_lookup (encoder, edge->callee);
> - 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_outpu
> 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);
> @@ -371,6 +377,24 @@ output_profile_summary (struct lto_simpl
> lto_output_uleb128_stream (ob->main_stream, 0);
> }
>
> +/* 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. */
>
> @@ -460,16 +484,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);
> @@ -657,11 +673,14 @@ input_node (struct lto_file_decl_data *f
> }
>
>
> -/* 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;
> @@ -677,9 +696,14 @@ input_edge (struct lto_input_block *ib,
> if (caller == NULL || caller->decl == NULL_TREE)
> internal_error ("bytecode stream: no caller found while reading edge");
>
> - 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");
> + 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);
>
> @@ -697,10 +721,14 @@ input_edge (struct lto_input_block *ib,
> || caller_resolution == LDPR_PREEMPTED_IR)
> return;
>
> - edge = cgraph_create_edge (caller, callee, NULL, count, freq, nest);
> + 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);
> @@ -723,7 +751,9 @@ input_cgraph_1 (struct lto_file_decl_dat
> 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);
> Index: icln/gcc/lto-streamer-in.c
> ===================================================================
> --- icln.orig/gcc/lto-streamer-in.c
> +++ icln/gcc/lto-streamer-in.c
> @@ -1220,6 +1220,8 @@ fixup_call_stmt_edges_1 (struct cgraph_n
> 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. */
> Index: icln/gcc/testsuite/gcc.dg/lto/20091209-1_0.c
> ===================================================================
> --- /dev/null
> +++ icln/gcc/testsuite/gcc.dg/lto/20091209-1_0.c
> @@ -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;
> +}
>
>
next prev parent reply other threads:[~2010-02-13 18:17 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-02-13 18:03 [PATCH 0/6] Cgraph changes and various devirtualizations Martin Jambor
2010-02-13 18:03 ` [PATCH 1/6] Clarify edge redirection for inline clones Martin Jambor
2010-02-22 14:23 ` Jan Hubicka
2010-02-13 18:04 ` [PATCH 4/6] Remove unused ipa_note_param_call.called flag (approved) Martin Jambor
2010-02-13 18:14 ` Richard Guenther
2010-03-05 16:19 ` Martin Jambor
2010-02-22 15:04 ` Jan Hubicka
2010-02-13 18:04 ` [PATCH 2/6] Indirect call graph edges Martin Jambor
2010-02-13 18:17 ` Richard Guenther [this message]
2010-02-13 18:25 ` Richard Guenther
2010-03-05 17:06 ` Martin Jambor
2010-02-22 15:52 ` Jan Hubicka
2010-02-22 16:05 ` Richard Guenther
2010-02-22 16:06 ` Jan Hubicka
2010-02-13 18:04 ` [PATCH 5/6] Indirect inlining of virtual calls Martin Jambor
2010-02-22 16:49 ` Jan Hubicka
2010-03-10 13:45 ` Martin Jambor
2010-03-10 15:24 ` Jan Hubicka
2010-02-13 18:04 ` [PATCH 3/6] Folding " Martin Jambor
2010-02-13 18:12 ` Richard Guenther
2010-02-13 18:04 ` [PATCH 6/6] Devirtualization in ipa-cp Martin Jambor
2010-02-22 16:37 ` Jan Hubicka
2010-03-11 13:42 ` Martin Jambor
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=84fc9c001002131016s3a81d6a9ib25b73d8064473e5@mail.gmail.com \
--to=richard.guenther@gmail.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=hubicka@ucw.cz \
--cc=mjambor@suse.cz \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).