summaryrefslogtreecommitdiff
path: root/gcc/cgraph.c
diff options
context:
space:
mode:
authorhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2009-05-08 19:19:51 +0000
committerhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2009-05-08 19:19:51 +0000
commitccf4ab6ba79565bf35426a5aad3140e6354e0121 (patch)
treec97ec571988fc2c7380db58cdf7ad61782d357bf /gcc/cgraph.c
parent45c4e79882e6f8943a5fd2dafe9450a46d54247a (diff)
downloadgcc-ccf4ab6ba79565bf35426a5aad3140e6354e0121.tar.gz
* cgraphbuild.c (compute_call_stmt_bb_frequency): Accept function argument;
handle correctly when profile is absent. (build_cgraph_edges): Update. (rebuild_cgraph_edges): Update. * cgraph.c: Do not include varrau.h (cgraph_set_call_stmt_including_clones, cgraph_create_edge_including_clones): New function (cgraph_update_edges_for_call_stmt_node): New stati cfunction. (cgraph_update_edges_for_call_stmt): Handle clones. (cgraph_remove_node): Handle clone tree. (cgraph_remove_node_and_inline_clones): New function. (dump_cgraph_node): Dump clone tree. (cgraph_clone_node): Handle clone tree. (clone_function_name): Bring here from tree-inline.c (cgraph_create_virtual_clone): New function. * cgraph.h (ipa_replace_map): Move ehre from ipa.h (cgraph_clone_info): New function (strut cgraph_node): Add clone_info and new clone tree pointers. (cgraph_remove_node_and_inline_clones, cgraph_set_call_stmt_including_clones, cgraph_create_edge_including_clones, cgraph_create_virtual_clone): Declare. (cgraph_function_versioning): Use VEC argument. (compute_call_stmt_bb_frequency): Update prototype. (cgraph_materialize_all_clones): New function. * ipa-cp.c (ipcp_update_cloned_node): Remove. (ipcp_create_replace_map): Update to VECtors. (ipcp_update_callgraph): Use virtual clones. (ipcp_update_bb_counts, ipcp_update_edges_counts): Remove. (ipcp_update_profiling): Do not update local profiling. (ipcp_insert_stage): Use VECtors and virtual clones. * cgraphunit.c (verify_cgraph_node): Verify clone tree. (clone_of_p): New function. (cgraph_preserve_function_body_p): Use clone tree. (cgraph_optimize): Materialize clones. (cgraph_function_versioning): Update for VECtors. (save_inline_function_body): Use clone tree. (cgraph_materialize_clone, cgraph_materialize_all_clones): New functions. * ipa-inline.c (cgraph_default_inline_p): Use analyzed flags. * ipa.c: Include gimple.h. (cgraph_remove_unreachable_nodes): Use clone tree. * ipa-prop.c (ipa_note_param_call): Update call of compute_call_stmt_bb_frequency. * ipa-prop.h (ipa_replace_map): Move to cgraph.h. * tree-inline.c: Do not include varray.h; do not include gt-tree-inline.h (copy_bb): Handle updating of clone tree; add new edge when new call appears. (expand_call_inline): Be strict about every call having edge. (clone_fn_id_num, clone_function_name): Move to cgraph.c. (delete_unreachable_blocks_update_callgraph): New function. (tree_function_versioning): Use VECtors; always remove unreachable blocks and fold conditionals. * tree-inline.h: Do not include varray.h (tree_function_versioning): Remove. * Makefile.in (GTFILES): Remove tree-inline.c * passes.c (do_per_function): Do only functions having body. * ipa-struct-reorg.c (do_reorg_1, collect_data_accesses): Handle cone tree. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@147294 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/cgraph.c')
-rw-r--r--gcc/cgraph.c337
1 files changed, 317 insertions, 20 deletions
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index ce696e211b6..2f68d94e3dd 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -78,7 +78,6 @@ The callgraph:
#include "target.h"
#include "basic-block.h"
#include "cgraph.h"
-#include "varray.h"
#include "output.h"
#include "intl.h"
#include "gimple.h"
@@ -628,7 +627,7 @@ cgraph_edge (struct cgraph_node *node, gimple call_stmt)
}
-/* Change field call_smt of edge E to NEW_STMT. */
+/* Change field call_stmt of edge E to NEW_STMT. */
void
cgraph_set_call_stmt (struct cgraph_edge *e, gimple new_stmt)
@@ -655,6 +654,79 @@ cgraph_set_call_stmt (struct cgraph_edge *e, gimple new_stmt)
}
}
+/* Like cgraph_set_call_stmt but walk the clone tree and update all clones sharing
+ same function body. */
+
+void
+cgraph_set_call_stmt_including_clones (struct cgraph_node *orig,
+ gimple old_stmt, gimple new_stmt)
+{
+ struct cgraph_node *node;
+ struct cgraph_edge *edge = cgraph_edge (orig, old_stmt);
+
+ if (edge)
+ cgraph_set_call_stmt (edge, new_stmt);
+ if (orig->clones)
+ for (node = orig->clones; node != orig;)
+ {
+ struct cgraph_edge *edge = cgraph_edge (node, old_stmt);
+ if (edge)
+ cgraph_set_call_stmt (edge, new_stmt);
+ if (node->clones)
+ node = node->clones;
+ else if (node->next_sibling_clone)
+ node = node->next_sibling_clone;
+ else
+ {
+ while (node != orig && !node->next_sibling_clone)
+ node = node->clone_of;
+ if (node != orig)
+ node = node->next_sibling_clone;
+ }
+ }
+}
+
+/* Like cgraph_create_edge walk the clone tree and update all clones sharing
+ same function body.
+
+ TODO: COUNT and LOOP_DEPTH should be properly distributed based on relative
+ frequencies of the clones.
+ */
+
+void
+cgraph_create_edge_including_clones (struct cgraph_node *orig, struct cgraph_node *callee,
+ gimple stmt, gcov_type count, int freq,
+ int loop_depth,
+ cgraph_inline_failed_t reason)
+{
+ struct cgraph_node *node;
+
+ cgraph_create_edge (orig, callee, stmt, count, freq, loop_depth)->inline_failed =
+ reason;
+
+ if (orig->clones)
+ for (node = orig->clones; node != orig;)
+ {
+ /* It is possible that we already constant propagated into the clone
+ and turned indirect call into dirrect call. */
+ if (!cgraph_edge (node, stmt))
+ cgraph_create_edge (node, callee, stmt, count, freq,
+ loop_depth)->inline_failed = reason;
+
+ if (node->clones)
+ node = node->clones;
+ else if (node->next_sibling_clone)
+ node = node->next_sibling_clone;
+ else
+ {
+ while (node != orig && !node->next_sibling_clone)
+ node = node->clone_of;
+ if (node != orig)
+ node = node->next_sibling_clone;
+ }
+ }
+}
+
/* Give initial reasons why inlining would fail on EDGE. This gets either
nullified or usually overwritten by more precise reasons later. */
@@ -828,12 +900,12 @@ cgraph_redirect_edge_callee (struct cgraph_edge *e, struct cgraph_node *n)
/* Update or remove the corresponding cgraph edge if a GIMPLE_CALL
OLD_STMT changed into NEW_STMT. */
-void
-cgraph_update_edges_for_call_stmt (gimple old_stmt, gimple new_stmt)
+static void
+cgraph_update_edges_for_call_stmt_node (struct cgraph_node *node,
+ gimple old_stmt, gimple new_stmt)
{
tree new_call = (is_gimple_call (new_stmt)) ? gimple_call_fn (new_stmt) : 0;
tree old_call = (is_gimple_call (old_stmt)) ? gimple_call_fn (old_stmt) : 0;
- struct cgraph_node *node = cgraph_node (cfun->decl);
if (old_call != new_call)
{
@@ -870,6 +942,34 @@ cgraph_update_edges_for_call_stmt (gimple old_stmt, gimple new_stmt)
}
}
+/* Update or remove the corresponding cgraph edge if a GIMPLE_CALL
+ OLD_STMT changed into NEW_STMT. */
+
+void
+cgraph_update_edges_for_call_stmt (gimple old_stmt, gimple new_stmt)
+{
+ struct cgraph_node *orig = cgraph_node (cfun->decl);
+ struct cgraph_node *node;
+
+ cgraph_update_edges_for_call_stmt_node (orig, old_stmt, new_stmt);
+ if (orig->clones)
+ for (node = orig->clones; node != orig;)
+ {
+ cgraph_update_edges_for_call_stmt_node (node, old_stmt, new_stmt);
+ if (node->clones)
+ node = node->clones;
+ else if (node->next_sibling_clone)
+ node = node->next_sibling_clone;
+ else
+ {
+ while (node != orig && !node->next_sibling_clone)
+ node = node->clone_of;
+ if (node != orig)
+ node = node->next_sibling_clone;
+ }
+ }
+}
+
/* Remove all callees from the node. */
@@ -998,24 +1098,100 @@ cgraph_remove_node (struct cgraph_node *node)
slot = htab_find_slot (cgraph_hash, node, NO_INSERT);
if (*slot == node)
{
- if (node->next_clone)
- {
- struct cgraph_node *new_node = node->next_clone;
+ struct cgraph_node *next_inline_clone;
- *slot = new_node;
- node->next_clone->prev_clone = NULL;
- }
+ for (next_inline_clone = node->clones;
+ next_inline_clone && next_inline_clone->decl != node->decl;
+ next_inline_clone = next_inline_clone->next_sibling_clone)
+ ;
+
+ /* If there is inline clone of the node being removed, we need
+ to put it into the position of removed node and reorganize all
+ other clones to be based on it. */
+ if (next_inline_clone)
+ {
+ struct cgraph_node *n;
+ struct cgraph_node *new_clones;
+
+ *slot = next_inline_clone;
+
+ /* Unlink inline clone from the list of clones of removed node. */
+ if (next_inline_clone->next_sibling_clone)
+ next_inline_clone->next_sibling_clone->prev_sibling_clone
+ = next_inline_clone->prev_sibling_clone;
+ if (next_inline_clone->prev_sibling_clone)
+ {
+ next_inline_clone->prev_sibling_clone->next_sibling_clone
+ = next_inline_clone->next_sibling_clone;
+ }
+ else
+ node->clones = next_inline_clone->next_sibling_clone;
+
+ new_clones = node->clones;
+ node->clones = NULL;
+
+ /* Copy clone info. */
+ next_inline_clone->clone = node->clone;
+
+ /* Now place it into clone tree at same level at NODE. */
+ next_inline_clone->clone_of = node->clone_of;
+ next_inline_clone->prev_sibling_clone = NULL;
+ next_inline_clone->next_sibling_clone = NULL;
+ if (node->clone_of)
+ {
+ next_inline_clone->next_sibling_clone = node->clone_of->clones;
+ node->clone_of->clones = next_inline_clone;
+ }
+
+ /* Merge the clone list. */
+ if (new_clones)
+ {
+ if (!next_inline_clone->clones)
+ next_inline_clone->clones = new_clones;
+ else
+ {
+ n = next_inline_clone->clones;
+ while (n->next_sibling_clone)
+ n = n->next_sibling_clone;
+ n->next_sibling_clone = new_clones;
+ new_clones->prev_sibling_clone = n;
+ }
+ }
+
+ /* Update clone_of pointers. */
+ n = new_clones;
+ while (n)
+ {
+ n->clone_of = next_inline_clone;
+ n = n->next_sibling_clone;
+ }
+ }
else
{
htab_clear_slot (cgraph_hash, slot);
kill_body = true;
}
+
}
else
+ gcc_assert (node->clone_of);
+ if (node->prev_sibling_clone)
+ node->prev_sibling_clone->next_sibling_clone = node->next_sibling_clone;
+ else if (node->clone_of)
+ node->clone_of->clones = node->next_sibling_clone;
+ if (node->next_sibling_clone)
+ node->next_sibling_clone->prev_sibling_clone = node->prev_sibling_clone;
+ if (node->clones)
{
- node->prev_clone->next_clone = node->next_clone;
- if (node->next_clone)
- node->next_clone->prev_clone = node->prev_clone;
+ struct cgraph_node *n;
+
+ for (n = node->clones; n->next_sibling_clone; n = n->next_sibling_clone)
+ n->clone_of = node->clone_of;
+ n->clone_of = node->clone_of;
+ n->next_sibling_clone = node->clone_of->clones;
+ if (node->clone_of->clones)
+ node->clone_of->clones->prev_sibling_clone = n;
+ node->clone_of->clones = node->clones;
}
/* While all the clones are removed after being proceeded, the function
@@ -1025,7 +1201,7 @@ cgraph_remove_node (struct cgraph_node *node)
if (!kill_body && *slot)
{
struct cgraph_node *n = (struct cgraph_node *) *slot;
- if (!n->next_clone && !n->global.inlined_to
+ if (!n->clones && !n->clone_of && !n->global.inlined_to
&& (cgraph_global_info_ready
&& (TREE_ASM_WRITTEN (n->decl) || DECL_EXTERNAL (n->decl))))
kill_body = true;
@@ -1059,6 +1235,21 @@ cgraph_remove_node (struct cgraph_node *node)
free_nodes = node;
}
+/* Remove the node from cgraph. */
+
+void
+cgraph_remove_node_and_inline_clones (struct cgraph_node *node)
+{
+ struct cgraph_edge *e, *next;
+ for (e = node->callees; e; e = next)
+ {
+ next = e->next_callee;
+ if (!e->inline_failed)
+ cgraph_remove_node_and_inline_clones (e->callee);
+ }
+ cgraph_remove_node (node);
+}
+
/* Notify finalize_compilation_unit that given node is reachable. */
void
@@ -1166,6 +1357,10 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
fprintf (f, " (inline copy in %s/%i)",
cgraph_node_name (node->global.inlined_to),
node->global.inlined_to->uid);
+ if (node->clone_of)
+ fprintf (f, " (clone of %s/%i)",
+ cgraph_node_name (node->clone_of),
+ node->clone_of->uid);
if (cgraph_function_flags_ready)
fprintf (f, " availability:%s",
cgraph_availability_names [cgraph_function_body_availability (node)]);
@@ -1382,6 +1577,7 @@ cgraph_clone_node (struct cgraph_node *n, gcov_type count, int freq,
new_node->global = n->global;
new_node->rtl = n->rtl;
new_node->count = count;
+ new_node->clone = n->clone;
if (n->count)
{
if (new_node->count > n->count)
@@ -1402,16 +1598,117 @@ cgraph_clone_node (struct cgraph_node *n, gcov_type count, int freq,
cgraph_clone_edge (e, new_node, e->call_stmt, count_scale, freq, loop_nest,
update_original);
- new_node->next_clone = n->next_clone;
- new_node->prev_clone = n;
- n->next_clone = new_node;
- if (new_node->next_clone)
- new_node->next_clone->prev_clone = new_node;
+ new_node->next_sibling_clone = n->clones;
+ if (n->clones)
+ n->clones->prev_sibling_clone = new_node;
+ n->clones = new_node;
+ new_node->clone_of = n;
cgraph_call_node_duplication_hooks (n, new_node);
return new_node;
}
+/* Create a new name for omp child function. Returns an identifier. */
+
+static GTY(()) unsigned int clone_fn_id_num;
+
+static tree
+clone_function_name (tree decl)
+{
+ tree name = DECL_ASSEMBLER_NAME (decl);
+ size_t len = IDENTIFIER_LENGTH (name);
+ char *tmp_name, *prefix;
+
+ prefix = XALLOCAVEC (char, len + strlen ("_clone") + 1);
+ memcpy (prefix, IDENTIFIER_POINTER (name), len);
+ strcpy (prefix + len, "_clone");
+#ifndef NO_DOT_IN_LABEL
+ prefix[len] = '.';
+#elif !defined NO_DOLLAR_IN_LABEL
+ prefix[len] = '$';
+#endif
+ ASM_FORMAT_PRIVATE_NAME (tmp_name, prefix, clone_fn_id_num++);
+ return get_identifier (tmp_name);
+}
+
+/* Create callgraph node clone with new declaration. The actual body will
+ be copied later at compilation stage.
+
+ TODO: after merging in ipa-sra use function call notes instead of args_to_skip
+ bitmap interface.
+ */
+struct cgraph_node *
+cgraph_create_virtual_clone (struct cgraph_node *old_node,
+ VEC(cgraph_edge_p,heap) *redirect_callers,
+ VEC(ipa_replace_map_p,gc) *tree_map,
+ bitmap args_to_skip)
+{
+ tree old_decl = old_node->decl;
+ struct cgraph_node *new_node = NULL;
+ tree new_decl;
+ struct cgraph_node key, **slot;
+ unsigned i;
+ struct cgraph_edge *e;
+
+ gcc_assert (tree_versionable_function_p (old_decl));
+
+ /* Make a new FUNCTION_DECL tree node */
+ if (!args_to_skip)
+ new_decl = copy_node (old_decl);
+ else
+ new_decl = build_function_decl_skip_args (old_decl, args_to_skip);
+ DECL_STRUCT_FUNCTION (new_decl) = NULL;
+
+ /* Generate a new name for the new version. */
+ DECL_NAME (new_decl) = clone_function_name (old_decl);
+ SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
+ SET_DECL_RTL (new_decl, NULL);
+
+ new_node = cgraph_clone_node (old_node, old_node->count,
+ CGRAPH_FREQ_BASE, 0, false);
+ new_node->decl = new_decl;
+ /* Update the properties.
+ Make clone visible only within this translation unit. Make sure
+ that is not weak also.
+ ??? We cannot use COMDAT linkage because there is no
+ ABI support for this. */
+ DECL_EXTERNAL (new_node->decl) = 0;
+ DECL_ONE_ONLY (new_node->decl) = 0;
+ TREE_PUBLIC (new_node->decl) = 0;
+ DECL_COMDAT (new_node->decl) = 0;
+ DECL_WEAK (new_node->decl) = 0;
+ new_node->clone.tree_map = tree_map;
+ new_node->clone.args_to_skip = args_to_skip;
+ new_node->local.externally_visible = 0;
+ new_node->local.local = 1;
+ new_node->lowered = true;
+ new_node->reachable = true;
+
+ key.decl = new_decl;
+ slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, INSERT);
+ gcc_assert (!*slot);
+ *slot = new_node;
+ if (assembler_name_hash)
+ {
+ void **aslot;
+ tree name = DECL_ASSEMBLER_NAME (new_decl);
+
+ aslot = htab_find_slot_with_hash (assembler_name_hash, name,
+ decl_assembler_name_hash (name),
+ INSERT);
+ gcc_assert (!*aslot);
+ *aslot = new_node;
+ }
+ for (i = 0; VEC_iterate (cgraph_edge_p, redirect_callers, i, e); i++)
+ {
+ /* Redirect calls to the old version node to point to its new
+ version. */
+ cgraph_redirect_edge_callee (e, new_node);
+ }
+
+ return new_node;
+}
+
/* NODE is no longer nested function; update cgraph accordingly. */
void
cgraph_unnest_node (struct cgraph_node *node)