/* Callgraph clones
- Copyright (C) 2003-2014 Free Software Foundation, Inc.
+ Copyright (C) 2003-2015 Free Software Foundation, Inc.
Contributed by Jan Hubicka
This file is part of GCC.
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
+#include "alias.h"
+#include "symtab.h"
#include "tree.h"
+#include "fold-const.h"
#include "stringpool.h"
+#include "hard-reg-set.h"
#include "function.h"
#include "emit-rtl.h"
+#include "predict.h"
#include "basic-block.h"
#include "tree-ssa-alias.h"
#include "internal-fn.h"
#include "tree-eh.h"
#include "gimple-expr.h"
-#include "is-a.h"
#include "gimple.h"
#include "bitmap.h"
#include "tree-cfg.h"
#include "diagnostic.h"
#include "params.h"
#include "intl.h"
-#include "function.h"
+#include "cgraph.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
#include "ipa-prop.h"
#include "tree-iterator.h"
#include "tree-dump.h"
#include "lto-streamer.h"
#include "except.h"
-/* Create clone of E in the node N represented by CALL_EXPR the callgraph. */
-struct cgraph_edge *
-cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n,
- gimple call_stmt, unsigned stmt_uid, gcov_type count_scale,
- int freq_scale, bool update_original)
+/* Create clone of edge in the node N represented by CALL_EXPR
+ the callgraph. */
+
+cgraph_edge *
+cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
+ gcov_type count_scale, int freq_scale, bool update_original)
{
- struct cgraph_edge *new_edge;
- gcov_type count = apply_probability (e->count, count_scale);
+ cgraph_edge *new_edge;
+ gcov_type gcov_count = apply_probability (count, count_scale);
gcov_type freq;
/* We do not want to ignore loop nest after frequency drops to 0. */
if (!freq_scale)
freq_scale = 1;
- freq = e->frequency * (gcov_type) freq_scale / CGRAPH_FREQ_BASE;
+ freq = frequency * (gcov_type) freq_scale / CGRAPH_FREQ_BASE;
if (freq > CGRAPH_FREQ_MAX)
freq = CGRAPH_FREQ_MAX;
- if (e->indirect_unknown_callee)
+ if (indirect_unknown_callee)
{
tree decl;
if (call_stmt && (decl = gimple_call_fndecl (call_stmt))
/* When the call is speculative, we need to resolve it
via cgraph_resolve_speculation and not here. */
- && !e->speculative)
+ && !speculative)
{
- struct cgraph_node *callee = cgraph_get_node (decl);
+ cgraph_node *callee = cgraph_node::get (decl);
gcc_checking_assert (callee);
- new_edge = cgraph_create_edge (n, callee, call_stmt, count, freq);
+ new_edge = n->create_edge (callee, call_stmt, gcov_count, freq);
}
else
{
- new_edge = cgraph_create_indirect_edge (n, call_stmt,
- e->indirect_info->ecf_flags,
- count, freq);
- *new_edge->indirect_info = *e->indirect_info;
+ new_edge = n->create_indirect_edge (call_stmt,
+ indirect_info->ecf_flags,
+ count, freq, false);
+ *new_edge->indirect_info = *indirect_info;
}
}
else
{
- new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq);
- if (e->indirect_info)
+ new_edge = n->create_edge (callee, call_stmt, gcov_count, freq);
+ if (indirect_info)
{
new_edge->indirect_info
- = ggc_alloc_cleared_cgraph_indirect_call_info ();
- *new_edge->indirect_info = *e->indirect_info;
+ = ggc_cleared_alloc<cgraph_indirect_call_info> ();
+ *new_edge->indirect_info = *indirect_info;
}
}
- new_edge->inline_failed = e->inline_failed;
- new_edge->indirect_inlining_edge = e->indirect_inlining_edge;
+ new_edge->inline_failed = inline_failed;
+ new_edge->indirect_inlining_edge = indirect_inlining_edge;
new_edge->lto_stmt_uid = stmt_uid;
/* Clone flags that depend on call_stmt availability manually. */
- new_edge->can_throw_external = e->can_throw_external;
- new_edge->call_stmt_cannot_inline_p = e->call_stmt_cannot_inline_p;
- new_edge->speculative = e->speculative;
+ new_edge->can_throw_external = can_throw_external;
+ new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
+ new_edge->speculative = speculative;
+ new_edge->in_polymorphic_cdtor = in_polymorphic_cdtor;
if (update_original)
{
- e->count -= new_edge->count;
- if (e->count < 0)
- e->count = 0;
+ count -= new_edge->count;
+ if (count < 0)
+ count = 0;
}
- cgraph_call_edge_duplication_hooks (e, new_edge);
+ symtab->call_edge_duplication_hooks (this, new_edge);
return new_edge;
}
-
-/* Create node representing clone of N executed COUNT times. Decrease
- the execution counts from original node too.
- The new clone will have decl set to DECL that may or may not be the same
- as decl of N.
-
- When UPDATE_ORIGINAL is true, the counts are subtracted from the original
- function's profile to reflect the fact that part of execution is handled
- by node.
- When CALL_DUPLICATOIN_HOOK is true, the ipa passes are acknowledged about
- the new clone. Otherwise the caller is responsible for doing so later.
-
- If the new node is being inlined into another one, NEW_INLINED_TO should be
- the outline function the new one is (even indirectly) inlined to. All hooks
- will see this in node's global.inlined_to, when invoked. Can be NULL if the
- node is not inlined. */
-
-struct cgraph_node *
-cgraph_clone_node (struct cgraph_node *n, tree decl, gcov_type count, int freq,
- bool update_original,
- vec<cgraph_edge_p> redirect_callers,
- bool call_duplication_hook,
- struct cgraph_node *new_inlined_to)
-{
- struct cgraph_node *new_node = cgraph_create_empty_node ();
- struct cgraph_edge *e;
- gcov_type count_scale;
- unsigned i;
-
- new_node->decl = decl;
- symtab_register_node (new_node);
- new_node->origin = n->origin;
- new_node->lto_file_data = n->lto_file_data;
- if (new_node->origin)
- {
- new_node->next_nested = new_node->origin->nested;
- new_node->origin->nested = new_node;
- }
- new_node->analyzed = n->analyzed;
- new_node->definition = n->definition;
- new_node->local = n->local;
- new_node->externally_visible = false;
- new_node->local.local = true;
- new_node->global = n->global;
- new_node->global.inlined_to = new_inlined_to;
- new_node->rtl = n->rtl;
- new_node->count = count;
- new_node->frequency = n->frequency;
- new_node->clone = n->clone;
- new_node->clone.tree_map = NULL;
- new_node->tp_first_run = n->tp_first_run;
- if (n->count)
- {
- if (new_node->count > n->count)
- count_scale = REG_BR_PROB_BASE;
- else
- count_scale = GCOV_COMPUTE_SCALE (new_node->count, n->count);
- }
- else
- count_scale = 0;
- if (update_original)
- {
- n->count -= count;
- if (n->count < 0)
- n->count = 0;
- }
-
- FOR_EACH_VEC_ELT (redirect_callers, i, e)
- {
- /* Redirect calls to the old version node to point to its new
- version. The only exception is when the edge was proved to
- be unreachable during the clonning procedure. */
- if (!e->callee
- || DECL_BUILT_IN_CLASS (e->callee->decl) != BUILT_IN_NORMAL
- || DECL_FUNCTION_CODE (e->callee->decl) != BUILT_IN_UNREACHABLE)
- cgraph_redirect_edge_callee (e, new_node);
- }
-
-
- for (e = n->callees;e; e=e->next_callee)
- cgraph_clone_edge (e, new_node, e->call_stmt, e->lto_stmt_uid,
- count_scale, freq, 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, update_original);
- ipa_clone_references (new_node, &n->ref_list);
-
- 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;
-
- if (call_duplication_hook)
- cgraph_call_node_duplication_hooks (n, new_node);
- return new_node;
-}
-
-/* Return a new assembler name for a clone of DECL with SUFFIX. */
-
-static GTY(()) unsigned int clone_fn_id_num;
-
-tree
-clone_function_name (tree decl, const char *suffix)
-{
- tree name = DECL_ASSEMBLER_NAME (decl);
- size_t len = IDENTIFIER_LENGTH (name);
- char *tmp_name, *prefix;
-
- prefix = XALLOCAVEC (char, len + strlen (suffix) + 2);
- memcpy (prefix, IDENTIFIER_POINTER (name), len);
- strcpy (prefix + len + 1, suffix);
-#ifndef NO_DOT_IN_LABEL
- prefix[len] = '.';
-#elif !defined NO_DOLLAR_IN_LABEL
- prefix[len] = '$';
-#else
- prefix[len] = '_';
-#endif
- ASM_FORMAT_PRIVATE_NAME (tmp_name, prefix, clone_fn_id_num++);
- return get_identifier (tmp_name);
-}
-
/* Build variant of function type ORIG_TYPE skipping ARGS_TO_SKIP and the
return value if SKIP_RETURN is true. */
bool skip_return)
{
tree new_type = NULL;
- tree args, new_args = NULL, t;
+ tree args, new_args = NULL;
tree new_reversed;
int i = 0;
if (skip_return)
TREE_TYPE (new_type) = void_type_node;
- /* This is a new type, not a copy of an old type. Need to reassociate
- variants. We can handle everything except the main variant lazily. */
- t = TYPE_MAIN_VARIANT (orig_type);
- if (t != orig_type)
- {
- t = build_function_type_skip_args (t, args_to_skip, skip_return);
- TYPE_MAIN_VARIANT (new_type) = t;
- TYPE_NEXT_VARIANT (new_type) = TYPE_NEXT_VARIANT (t);
- TYPE_NEXT_VARIANT (t) = new_type;
- }
- else
- {
- TYPE_MAIN_VARIANT (new_type) = new_type;
- TYPE_NEXT_VARIANT (new_type) = NULL;
- }
-
return new_type;
}
return new_decl;
}
+/* Set flags of NEW_NODE and its decl. NEW_NODE is a newly created private
+ clone or its thunk. */
+
+static void
+set_new_clone_decl_and_node_flags (cgraph_node *new_node)
+{
+ DECL_EXTERNAL (new_node->decl) = 0;
+ TREE_PUBLIC (new_node->decl) = 0;
+ DECL_COMDAT (new_node->decl) = 0;
+ DECL_WEAK (new_node->decl) = 0;
+ DECL_VIRTUAL_P (new_node->decl) = 0;
+ DECL_STATIC_CONSTRUCTOR (new_node->decl) = 0;
+ DECL_STATIC_DESTRUCTOR (new_node->decl) = 0;
+
+ new_node->externally_visible = 0;
+ new_node->local.local = 1;
+ new_node->lowered = true;
+}
+
+/* Duplicate thunk THUNK if necessary but make it to refer to NODE.
+ ARGS_TO_SKIP, if non-NULL, determines which parameters should be omitted.
+ Function can return NODE if no thunk is necessary, which can happen when
+ thunk is this_adjusting but we are removing this parameter. */
+
+static cgraph_node *
+duplicate_thunk_for_node (cgraph_node *thunk, cgraph_node *node)
+{
+ cgraph_node *new_thunk, *thunk_of;
+ thunk_of = thunk->callees->callee->ultimate_alias_target ();
+
+ if (thunk_of->thunk.thunk_p)
+ node = duplicate_thunk_for_node (thunk_of, node);
+
+ if (!DECL_ARGUMENTS (thunk->decl))
+ thunk->get_untransformed_body ();
+
+ cgraph_edge *cs;
+ for (cs = node->callers; cs; cs = cs->next_caller)
+ if (cs->caller->thunk.thunk_p
+ && cs->caller->thunk.this_adjusting == thunk->thunk.this_adjusting
+ && cs->caller->thunk.fixed_offset == thunk->thunk.fixed_offset
+ && cs->caller->thunk.virtual_offset_p == thunk->thunk.virtual_offset_p
+ && cs->caller->thunk.virtual_value == thunk->thunk.virtual_value)
+ return cs->caller;
+
+ tree new_decl;
+ if (!node->clone.args_to_skip)
+ new_decl = copy_node (thunk->decl);
+ else
+ {
+ /* We do not need to duplicate this_adjusting thunks if we have removed
+ this. */
+ if (thunk->thunk.this_adjusting
+ && bitmap_bit_p (node->clone.args_to_skip, 0))
+ return node;
+
+ new_decl = build_function_decl_skip_args (thunk->decl,
+ node->clone.args_to_skip,
+ false);
+ }
+
+ tree *link = &DECL_ARGUMENTS (new_decl);
+ int i = 0;
+ for (tree pd = DECL_ARGUMENTS (thunk->decl); pd; pd = DECL_CHAIN (pd), i++)
+ {
+ if (!node->clone.args_to_skip
+ || !bitmap_bit_p (node->clone.args_to_skip, i))
+ {
+ tree nd = copy_node (pd);
+ DECL_CONTEXT (nd) = new_decl;
+ *link = nd;
+ link = &DECL_CHAIN (nd);
+ }
+ }
+ *link = NULL_TREE;
+
+ gcc_checking_assert (!DECL_STRUCT_FUNCTION (new_decl));
+ gcc_checking_assert (!DECL_INITIAL (new_decl));
+ gcc_checking_assert (!DECL_RESULT (new_decl));
+ gcc_checking_assert (!DECL_RTL_SET_P (new_decl));
+
+ DECL_NAME (new_decl) = clone_function_name (thunk->decl, "artificial_thunk");
+ SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
+
+ new_thunk = cgraph_node::create (new_decl);
+ set_new_clone_decl_and_node_flags (new_thunk);
+ new_thunk->definition = true;
+ new_thunk->thunk = thunk->thunk;
+ new_thunk->unique_name = in_lto_p;
+ new_thunk->former_clone_of = thunk->decl;
+ new_thunk->clone.args_to_skip = node->clone.args_to_skip;
+ new_thunk->clone.combined_args_to_skip = node->clone.combined_args_to_skip;
+
+ cgraph_edge *e = new_thunk->create_edge (node, NULL, 0,
+ CGRAPH_FREQ_BASE);
+ e->call_stmt_cannot_inline_p = true;
+ symtab->call_edge_duplication_hooks (thunk->callees, e);
+ symtab->call_cgraph_duplication_hooks (thunk, new_thunk);
+ return new_thunk;
+}
+
+/* If E does not lead to a thunk, simply redirect it to N. Otherwise create
+ one or more equivalent thunks for N and redirect E to the first in the
+ chain. Note that it is then necessary to call
+ n->expand_all_artificial_thunks once all callers are redirected. */
+
+void
+cgraph_edge::redirect_callee_duplicating_thunks (cgraph_node *n)
+{
+ cgraph_node *orig_to = callee->ultimate_alias_target ();
+ if (orig_to->thunk.thunk_p)
+ n = duplicate_thunk_for_node (orig_to, n);
+
+ redirect_callee (n);
+}
+
+/* Call expand_thunk on all callers that are thunks and if analyze those nodes
+ that were expanded. */
+
+void
+cgraph_node::expand_all_artificial_thunks ()
+{
+ cgraph_edge *e;
+ for (e = callers; e;)
+ if (e->caller->thunk.thunk_p)
+ {
+ cgraph_node *thunk = e->caller;
+
+ e = e->next_caller;
+ if (thunk->expand_thunk (false, false))
+ {
+ thunk->thunk.thunk_p = false;
+ thunk->analyze ();
+ }
+ thunk->expand_all_artificial_thunks ();
+ }
+ else
+ e = e->next_caller;
+}
+
+/* Create node representing clone of N executed COUNT times. Decrease
+ the execution counts from original node too.
+ The new clone will have decl set to DECL that may or may not be the same
+ as decl of N.
+
+ When UPDATE_ORIGINAL is true, the counts are subtracted from the original
+ function's profile to reflect the fact that part of execution is handled
+ by node.
+ When CALL_DUPLICATOIN_HOOK is true, the ipa passes are acknowledged about
+ the new clone. Otherwise the caller is responsible for doing so later.
+
+ If the new node is being inlined into another one, NEW_INLINED_TO should be
+ the outline function the new one is (even indirectly) inlined to. All hooks
+ will see this in node's global.inlined_to, when invoked. Can be NULL if the
+ node is not inlined. */
+
+cgraph_node *
+cgraph_node::create_clone (tree new_decl, gcov_type gcov_count, int freq,
+ bool update_original,
+ vec<cgraph_edge *> redirect_callers,
+ bool call_duplication_hook,
+ cgraph_node *new_inlined_to,
+ bitmap args_to_skip)
+{
+ cgraph_node *new_node = symtab->create_empty ();
+ cgraph_edge *e;
+ gcov_type count_scale;
+ unsigned i;
+
+ new_node->decl = new_decl;
+ new_node->register_symbol ();
+ new_node->origin = origin;
+ new_node->lto_file_data = lto_file_data;
+ if (new_node->origin)
+ {
+ new_node->next_nested = new_node->origin->nested;
+ new_node->origin->nested = new_node;
+ }
+ new_node->analyzed = analyzed;
+ new_node->definition = definition;
+ new_node->local = local;
+ new_node->externally_visible = false;
+ new_node->no_reorder = no_reorder;
+ new_node->local.local = true;
+ new_node->global = global;
+ new_node->global.inlined_to = new_inlined_to;
+ new_node->rtl = rtl;
+ new_node->count = count;
+ new_node->frequency = frequency;
+ new_node->tp_first_run = tp_first_run;
+ new_node->tm_clone = tm_clone;
+ new_node->icf_merged = icf_merged;
+ new_node->merged = merged;
+
+ new_node->clone.tree_map = NULL;
+ new_node->clone.args_to_skip = args_to_skip;
+ new_node->split_part = split_part;
+ if (!args_to_skip)
+ new_node->clone.combined_args_to_skip = clone.combined_args_to_skip;
+ else if (clone.combined_args_to_skip)
+ {
+ new_node->clone.combined_args_to_skip = BITMAP_GGC_ALLOC ();
+ bitmap_ior (new_node->clone.combined_args_to_skip,
+ clone.combined_args_to_skip, args_to_skip);
+ }
+ else
+ new_node->clone.combined_args_to_skip = args_to_skip;
+
+ if (count)
+ {
+ if (new_node->count > count)
+ count_scale = REG_BR_PROB_BASE;
+ else
+ count_scale = GCOV_COMPUTE_SCALE (new_node->count, count);
+ }
+ else
+ count_scale = 0;
+ if (update_original)
+ {
+ count -= gcov_count;
+ if (count < 0)
+ count = 0;
+ }
+
+ FOR_EACH_VEC_ELT (redirect_callers, i, e)
+ {
+ /* Redirect calls to the old version node to point to its new
+ version. The only exception is when the edge was proved to
+ be unreachable during the clonning procedure. */
+ if (!e->callee
+ || DECL_BUILT_IN_CLASS (e->callee->decl) != BUILT_IN_NORMAL
+ || DECL_FUNCTION_CODE (e->callee->decl) != BUILT_IN_UNREACHABLE)
+ e->redirect_callee_duplicating_thunks (new_node);
+ }
+ new_node->expand_all_artificial_thunks ();
+
+ for (e = callees;e; e=e->next_callee)
+ e->clone (new_node, e->call_stmt, e->lto_stmt_uid, count_scale,
+ freq, update_original);
+
+ for (e = indirect_calls; e; e = e->next_callee)
+ e->clone (new_node, e->call_stmt, e->lto_stmt_uid,
+ count_scale, freq, update_original);
+ new_node->clone_references (this);
+
+ new_node->next_sibling_clone = clones;
+ if (clones)
+ clones->prev_sibling_clone = new_node;
+ clones = new_node;
+ new_node->clone_of = this;
+
+ if (call_duplication_hook)
+ symtab->call_cgraph_duplication_hooks (this, new_node);
+ return new_node;
+}
+
+static GTY(()) unsigned int clone_fn_id_num;
+
+/* Return a new assembler name for a clone with SUFFIX of a decl named
+ NAME. */
+
+tree
+clone_function_name_1 (const char *name, const char *suffix)
+{
+ size_t len = strlen (name);
+ char *tmp_name, *prefix;
+
+ prefix = XALLOCAVEC (char, len + strlen (suffix) + 2);
+ memcpy (prefix, name, len);
+ strcpy (prefix + len + 1, suffix);
+#ifndef NO_DOT_IN_LABEL
+ prefix[len] = '.';
+#elif !defined NO_DOLLAR_IN_LABEL
+ prefix[len] = '$';
+#else
+ prefix[len] = '_';
+#endif
+ ASM_FORMAT_PRIVATE_NAME (tmp_name, prefix, clone_fn_id_num++);
+ return get_identifier (tmp_name);
+}
+
+/* Return a new assembler name for a clone of DECL with SUFFIX. */
+
+tree
+clone_function_name (tree decl, const char *suffix)
+{
+ tree name = DECL_ASSEMBLER_NAME (decl);
+ return clone_function_name_1 (IDENTIFIER_POINTER (name), suffix);
+}
+
+
/* 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> redirect_callers,
- vec<ipa_replace_map_p, va_gc> *tree_map,
- bitmap args_to_skip,
- const char * suffix)
+cgraph_node *
+cgraph_node::create_virtual_clone (vec<cgraph_edge *> redirect_callers,
+ vec<ipa_replace_map *, va_gc> *tree_map,
+ bitmap args_to_skip, const char * suffix)
{
- tree old_decl = old_node->decl;
- struct cgraph_node *new_node = NULL;
+ tree old_decl = decl;
+ cgraph_node *new_node = NULL;
tree new_decl;
size_t len, i;
- struct ipa_replace_map *map;
+ ipa_replace_map *map;
char *name;
if (!in_lto_p)
- gcc_checking_assert (tree_versionable_function_p (old_decl));
+ gcc_checking_assert (tree_versionable_function_p (old_decl));
- gcc_assert (old_node->local.can_change_signature || !args_to_skip);
+ gcc_assert (local.can_change_signature || !args_to_skip);
/* Make a new FUNCTION_DECL tree node */
if (!args_to_skip)
SET_DECL_ASSEMBLER_NAME (new_decl, clone_function_name (old_decl, suffix));
SET_DECL_RTL (new_decl, NULL);
- new_node = cgraph_clone_node (old_node, new_decl, old_node->count,
- CGRAPH_FREQ_BASE, false,
- redirect_callers, false, NULL);
+ new_node = create_clone (new_decl, count, CGRAPH_FREQ_BASE, false,
+ redirect_callers, false, NULL, args_to_skip);
+
/* 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;
- if (DECL_ONE_ONLY (old_decl))
- DECL_SECTION_NAME (new_node->decl) = NULL;
- DECL_COMDAT_GROUP (new_node->decl) = 0;
- TREE_PUBLIC (new_node->decl) = 0;
- DECL_COMDAT (new_node->decl) = 0;
- DECL_WEAK (new_node->decl) = 0;
- DECL_VIRTUAL_P (new_node->decl) = 0;
- DECL_STATIC_CONSTRUCTOR (new_node->decl) = 0;
- DECL_STATIC_DESTRUCTOR (new_node->decl) = 0;
+ set_new_clone_decl_and_node_flags (new_node);
new_node->clone.tree_map = tree_map;
- new_node->clone.args_to_skip = args_to_skip;
+ if (!implicit_section)
+ new_node->set_section (get_section ());
/* Clones of global symbols or symbols with unique names are unique. */
if ((TREE_PUBLIC (old_decl)
|| in_lto_p)
new_node->unique_name = true;
FOR_EACH_VEC_SAFE_ELT (tree_map, i, map)
- ipa_maybe_record_reference (new_node, map->new_tree,
- IPA_REF_ADDR, NULL);
- if (!args_to_skip)
- new_node->clone.combined_args_to_skip = old_node->clone.combined_args_to_skip;
- else if (old_node->clone.combined_args_to_skip)
- {
- int newi = 0, oldi = 0;
- tree arg;
- bitmap new_args_to_skip = BITMAP_GGC_ALLOC ();
- struct cgraph_node *orig_node;
- for (orig_node = old_node; orig_node->clone_of; orig_node = orig_node->clone_of)
- ;
- for (arg = DECL_ARGUMENTS (orig_node->decl);
- arg; arg = DECL_CHAIN (arg), oldi++)
- {
- if (bitmap_bit_p (old_node->clone.combined_args_to_skip, oldi))
- {
- bitmap_set_bit (new_args_to_skip, oldi);
- continue;
- }
- if (bitmap_bit_p (args_to_skip, newi))
- bitmap_set_bit (new_args_to_skip, oldi);
- newi++;
- }
- new_node->clone.combined_args_to_skip = new_args_to_skip;
- }
- else
- new_node->clone.combined_args_to_skip = args_to_skip;
- new_node->externally_visible = 0;
- new_node->local.local = 1;
- new_node->lowered = true;
+ new_node->maybe_create_reference (map->new_tree, IPA_REF_ADDR, NULL);
- cgraph_call_node_duplication_hooks (old_node, new_node);
+ if (ipa_transforms_to_apply.exists ())
+ new_node->ipa_transforms_to_apply
+ = ipa_transforms_to_apply.copy ();
+ symtab->call_cgraph_duplication_hooks (this, new_node);
return new_node;
}
-/* NODE is being removed from symbol table; see if its entry can be replaced by
- other inline clone. */
-struct cgraph_node *
-cgraph_find_replacement_node (struct cgraph_node *node)
+/* callgraph node being removed from symbol table; see if its entry can be
+ replaced by other inline clone. */
+cgraph_node *
+cgraph_node::find_replacement (void)
{
- struct cgraph_node *next_inline_clone, *replacement;
+ cgraph_node *next_inline_clone, *replacement;
- for (next_inline_clone = node->clones;
+ for (next_inline_clone = clones;
next_inline_clone
- && next_inline_clone->decl != node->decl;
+ && next_inline_clone->decl != decl;
next_inline_clone = next_inline_clone->next_sibling_clone)
;
other clones to be based on it. */
if (next_inline_clone)
{
- struct cgraph_node *n;
- struct cgraph_node *new_clones;
+ cgraph_node *n;
+ cgraph_node *new_clones;
replacement = next_inline_clone;
= next_inline_clone->prev_sibling_clone;
if (next_inline_clone->prev_sibling_clone)
{
- gcc_assert (node->clones != next_inline_clone);
+ gcc_assert (clones != next_inline_clone);
next_inline_clone->prev_sibling_clone->next_sibling_clone
= next_inline_clone->next_sibling_clone;
}
else
{
- gcc_assert (node->clones == next_inline_clone);
- node->clones = next_inline_clone->next_sibling_clone;
+ gcc_assert (clones == next_inline_clone);
+ clones = next_inline_clone->next_sibling_clone;
}
- new_clones = node->clones;
- node->clones = NULL;
+ new_clones = clones;
+ clones = NULL;
/* Copy clone info. */
- next_inline_clone->clone = node->clone;
+ next_inline_clone->clone = clone;
/* Now place it into clone tree at same level at NODE. */
- next_inline_clone->clone_of = node->clone_of;
+ next_inline_clone->clone_of = clone_of;
next_inline_clone->prev_sibling_clone = NULL;
next_inline_clone->next_sibling_clone = NULL;
- if (node->clone_of)
+ if (clone_of)
{
- if (node->clone_of->clones)
- node->clone_of->clones->prev_sibling_clone = next_inline_clone;
- next_inline_clone->next_sibling_clone = node->clone_of->clones;
- node->clone_of->clones = next_inline_clone;
+ if (clone_of->clones)
+ clone_of->clones->prev_sibling_clone = next_inline_clone;
+ next_inline_clone->next_sibling_clone = clone_of->clones;
+ clone_of->clones = next_inline_clone;
}
/* Merge the clone list. */
{
n = next_inline_clone->clones;
while (n->next_sibling_clone)
- n = n->next_sibling_clone;
+ n = n->next_sibling_clone;
n->next_sibling_clone = new_clones;
new_clones->prev_sibling_clone = n;
}
call. */
void
-cgraph_set_call_stmt_including_clones (struct cgraph_node *orig,
- gimple old_stmt, gimple new_stmt,
- bool update_speculative)
+cgraph_node::set_call_stmt_including_clones (gimple old_stmt,
+ gcall *new_stmt,
+ bool update_speculative)
{
- struct cgraph_node *node;
- struct cgraph_edge *edge = cgraph_edge (orig, old_stmt);
+ cgraph_node *node;
+ cgraph_edge *edge = get_edge (old_stmt);
if (edge)
- cgraph_set_call_stmt (edge, new_stmt, update_speculative);
+ edge->set_call_stmt (new_stmt, update_speculative);
- node = orig->clones;
+ node = clones;
if (node)
- while (node != orig)
+ while (node != this)
{
- struct cgraph_edge *edge = cgraph_edge (node, old_stmt);
+ cgraph_edge *edge = node->get_edge (old_stmt);
if (edge)
{
- cgraph_set_call_stmt (edge, new_stmt, update_speculative);
+ edge->set_call_stmt (new_stmt, update_speculative);
/* If UPDATE_SPECULATIVE is false, it means that we are turning
speculative call into a real code sequence. Update the
callgraph edges. */
if (edge->speculative && !update_speculative)
{
- struct cgraph_edge *direct, *indirect;
- struct ipa_ref *ref;
+ cgraph_edge *direct, *indirect;
+ ipa_ref *ref;
gcc_assert (!edge->indirect_unknown_callee);
- cgraph_speculative_call_info (edge, direct, indirect, ref);
+ edge->speculative_call_info (direct, indirect, ref);
direct->speculative = false;
indirect->speculative = false;
ref->speculative = false;
node = node->next_sibling_clone;
else
{
- while (node != orig && !node->next_sibling_clone)
+ while (node != this && !node->next_sibling_clone)
node = node->clone_of;
- if (node != orig)
+ if (node != this)
node = node->next_sibling_clone;
}
}
frequencies of the clones. */
void
-cgraph_create_edge_including_clones (struct cgraph_node *orig,
- struct cgraph_node *callee,
- gimple old_stmt,
- gimple stmt, gcov_type count,
- int freq,
- cgraph_inline_failed_t reason)
+cgraph_node::create_edge_including_clones (cgraph_node *callee,
+ gimple old_stmt, gcall *stmt,
+ gcov_type count,
+ int freq,
+ cgraph_inline_failed_t reason)
{
- struct cgraph_node *node;
- struct cgraph_edge *edge;
+ cgraph_node *node;
+ cgraph_edge *edge;
- if (!cgraph_edge (orig, stmt))
+ if (!get_edge (stmt))
{
- edge = cgraph_create_edge (orig, callee, stmt, count, freq);
+ edge = create_edge (callee, stmt, count, freq);
edge->inline_failed = reason;
}
- node = orig->clones;
+ node = clones;
if (node)
- while (node != orig)
+ while (node != this)
{
- struct cgraph_edge *edge = cgraph_edge (node, old_stmt);
+ cgraph_edge *edge = node->get_edge (old_stmt);
/* It is possible that clones already contain the edge while
master didn't. Either we promoted indirect call into direct
call in the clone or we are processing clones of unreachable
master where edges has been removed. */
if (edge)
- cgraph_set_call_stmt (edge, stmt);
- else if (!cgraph_edge (node, stmt))
+ edge->set_call_stmt (stmt);
+ else if (! node->get_edge (stmt))
{
- edge = cgraph_create_edge (node, callee, stmt, count,
- freq);
+ edge = node->create_edge (callee, stmt, count, freq);
edge->inline_failed = reason;
}
node = node->next_sibling_clone;
else
{
- while (node != orig && !node->next_sibling_clone)
+ while (node != this && !node->next_sibling_clone)
node = node->clone_of;
- if (node != orig)
+ if (node != this)
node = node->next_sibling_clone;
}
}
tree. */
bool
-cgraph_remove_node_and_inline_clones (struct cgraph_node *node, struct cgraph_node *forbidden_node)
+cgraph_node::remove_symbol_and_inline_clones (cgraph_node *forbidden_node)
{
- struct cgraph_edge *e, *next;
+ cgraph_edge *e, *next;
bool found = false;
- if (node == forbidden_node)
+ if (this == forbidden_node)
{
- cgraph_remove_edge (node->callers);
+ callers->remove ();
return true;
}
- for (e = node->callees; e; e = next)
+ for (e = callees; e; e = next)
{
next = e->next_callee;
if (!e->inline_failed)
- found |= cgraph_remove_node_and_inline_clones (e->callee, forbidden_node);
+ found |= e->callee->remove_symbol_and_inline_clones (forbidden_node);
}
- cgraph_remove_node (node);
+ remove ();
return found;
}
respective tree code should be updated to call the NEW_VERSION. */
static void
-update_call_expr (struct cgraph_node *new_version)
+update_call_expr (cgraph_node *new_version)
{
- struct cgraph_edge *e;
+ cgraph_edge *e;
gcc_assert (new_version);
/* Update the call expr on the edges to call the new version. */
for (e = new_version->callers; e; e = e->next_caller)
{
- struct function *inner_function = DECL_STRUCT_FUNCTION (e->caller->decl);
+ function *inner_function = DECL_STRUCT_FUNCTION (e->caller->decl);
gimple_call_set_fndecl (e->call_stmt, new_version->decl);
maybe_clean_eh_stmt_fn (inner_function, e->call_stmt);
}
/* Create a new cgraph node which is the new version of
- OLD_VERSION node. REDIRECT_CALLERS holds the callers
+ callgraph node. REDIRECT_CALLERS holds the callers
edges which should be redirected to point to
- NEW_VERSION. ALL the callees edges of OLD_VERSION
+ NEW_VERSION. ALL the callees edges of the node
are cloned to the new version node. Return the new
version node.
was copied to prevent duplications of calls that are dead
in the clone. */
-struct cgraph_node *
-cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
- tree new_decl,
- vec<cgraph_edge_p> redirect_callers,
- bitmap bbs_to_copy)
+cgraph_node *
+cgraph_node::create_version_clone (tree new_decl,
+ vec<cgraph_edge *> redirect_callers,
+ bitmap bbs_to_copy)
{
- struct cgraph_node *new_version;
- struct cgraph_edge *e;
+ cgraph_node *new_version;
+ cgraph_edge *e;
unsigned i;
- gcc_assert (old_version);
-
- new_version = cgraph_create_node (new_decl);
+ new_version = cgraph_node::create (new_decl);
- new_version->analyzed = old_version->analyzed;
- new_version->definition = old_version->definition;
- new_version->local = old_version->local;
+ new_version->analyzed = analyzed;
+ new_version->definition = definition;
+ new_version->local = local;
new_version->externally_visible = false;
+ new_version->no_reorder = no_reorder;
new_version->local.local = new_version->definition;
- new_version->global = old_version->global;
- new_version->rtl = old_version->rtl;
- new_version->count = old_version->count;
+ new_version->global = global;
+ new_version->rtl = rtl;
+ new_version->count = count;
- for (e = old_version->callees; e; e=e->next_callee)
+ for (e = callees; e; e=e->next_callee)
if (!bbs_to_copy
|| bitmap_bit_p (bbs_to_copy, gimple_bb (e->call_stmt)->index))
- cgraph_clone_edge (e, new_version, e->call_stmt,
- e->lto_stmt_uid, REG_BR_PROB_BASE,
- CGRAPH_FREQ_BASE,
- true);
- for (e = old_version->indirect_calls; e; e=e->next_callee)
+ e->clone (new_version, e->call_stmt,
+ e->lto_stmt_uid, REG_BR_PROB_BASE,
+ CGRAPH_FREQ_BASE,
+ true);
+ for (e = indirect_calls; e; e=e->next_callee)
if (!bbs_to_copy
|| bitmap_bit_p (bbs_to_copy, gimple_bb (e->call_stmt)->index))
- cgraph_clone_edge (e, new_version, e->call_stmt,
- e->lto_stmt_uid, REG_BR_PROB_BASE,
- CGRAPH_FREQ_BASE,
- true);
+ e->clone (new_version, e->call_stmt,
+ e->lto_stmt_uid, REG_BR_PROB_BASE,
+ CGRAPH_FREQ_BASE,
+ true);
FOR_EACH_VEC_ELT (redirect_callers, i, e)
{
/* Redirect calls to the old version node to point to its new
version. */
- cgraph_redirect_edge_callee (e, new_version);
+ e->redirect_callee (new_version);
}
- cgraph_call_node_duplication_hooks (old_version, new_version);
+ symtab->call_cgraph_duplication_hooks (this, new_version);
return new_version;
}
TREE_MAP is a mapping of tree nodes we want to replace with
new ones (according to results of prior analysis).
- OLD_VERSION_NODE is the node that is versioned.
If non-NULL ARGS_TO_SKIP determine function parameters to remove
from new version.
Return the new version's cgraph node. */
-struct cgraph_node *
-cgraph_function_versioning (struct cgraph_node *old_version_node,
- vec<cgraph_edge_p> redirect_callers,
- vec<ipa_replace_map_p, va_gc> *tree_map,
- bitmap args_to_skip,
- bool skip_return,
- bitmap bbs_to_copy,
- basic_block new_entry_block,
- const char *clone_name)
+cgraph_node *
+cgraph_node::create_version_clone_with_body
+ (vec<cgraph_edge *> redirect_callers,
+ vec<ipa_replace_map *, va_gc> *tree_map, bitmap args_to_skip,
+ bool skip_return, bitmap bbs_to_copy, basic_block new_entry_block,
+ const char *clone_name)
{
- tree old_decl = old_version_node->decl;
- struct cgraph_node *new_version_node = NULL;
+ tree old_decl = decl;
+ cgraph_node *new_version_node = NULL;
tree new_decl;
if (!tree_versionable_function_p (old_decl))
return NULL;
- gcc_assert (old_version_node->local.can_change_signature || !args_to_skip);
+ gcc_assert (local.can_change_signature || !args_to_skip);
/* Make a new FUNCTION_DECL tree node for the new version. */
if (!args_to_skip && !skip_return)
/* Create the new version's call-graph node.
and update the edges of the new node. */
- new_version_node =
- cgraph_copy_node_for_versioning (old_version_node, new_decl,
- redirect_callers, bbs_to_copy);
+ new_version_node = create_version_clone (new_decl, redirect_callers,
+ bbs_to_copy);
+ if (ipa_transforms_to_apply.exists ())
+ new_version_node->ipa_transforms_to_apply
+ = ipa_transforms_to_apply.copy ();
/* Copy the OLD_VERSION_NODE function tree to the new version. */
tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip,
skip_return, bbs_to_copy, new_entry_block);
that is not weak also.
??? We cannot use COMDAT linkage because there is no
ABI support for this. */
- symtab_make_decl_local (new_version_node->decl);
+ new_version_node->make_decl_local ();
DECL_VIRTUAL_P (new_version_node->decl) = 0;
new_version_node->externally_visible = 0;
new_version_node->local.local = 1;
new_version_node->lowered = true;
+ if (!implicit_section)
+ new_version_node->set_section (get_section ());
/* Clones of global symbols or symbols with unique names are unique. */
if ((TREE_PUBLIC (old_decl)
&& !DECL_EXTERNAL (old_decl)
/* Update the call_expr on the edges to call the new version node. */
update_call_expr (new_version_node);
- cgraph_call_function_insertion_hooks (new_version_node);
+ symtab->call_cgraph_insertion_hooks (this);
return new_version_node;
}
/* Given virtual clone, turn it into actual clone. */
static void
-cgraph_materialize_clone (struct cgraph_node *node)
+cgraph_materialize_clone (cgraph_node *node)
{
bitmap_obstack_initialize (NULL);
node->former_clone_of = node->clone_of->decl;
node->clone.tree_map, true,
node->clone.args_to_skip, false,
NULL, NULL);
- if (cgraph_dump_file)
+ if (symtab->dump_file)
{
- dump_function_to_file (node->clone_of->decl, cgraph_dump_file, dump_flags);
- dump_function_to_file (node->decl, cgraph_dump_file, dump_flags);
+ dump_function_to_file (node->clone_of->decl, symtab->dump_file,
+ dump_flags);
+ dump_function_to_file (node->decl, symtab->dump_file, dump_flags);
}
/* Function is no longer clone. */
node->prev_sibling_clone = NULL;
if (!node->clone_of->analyzed && !node->clone_of->clones)
{
- cgraph_release_function_body (node->clone_of);
- cgraph_node_remove_callees (node->clone_of);
- ipa_remove_all_references (&node->clone_of->ref_list);
+ node->clone_of->release_body ();
+ node->clone_of->remove_callees ();
+ node->clone_of->remove_all_references ();
}
node->clone_of = NULL;
bitmap_obstack_release (NULL);
this order. */
void
-cgraph_materialize_all_clones (void)
+symbol_table::materialize_all_clones (void)
{
- struct cgraph_node *node;
+ cgraph_node *node;
bool stabilized = false;
- if (cgraph_dump_file)
- fprintf (cgraph_dump_file, "Materializing clones\n");
+ if (symtab->dump_file)
+ fprintf (symtab->dump_file, "Materializing clones\n");
#ifdef ENABLE_CHECKING
- verify_cgraph ();
+ cgraph_node::verify_cgraph_nodes ();
#endif
/* We can also do topological order, but number of iterations should be
&& !gimple_has_body_p (node->decl))
{
if (!node->clone_of->clone_of)
- cgraph_get_body (node->clone_of);
+ node->clone_of->get_untransformed_body ();
if (gimple_has_body_p (node->clone_of->decl))
{
- if (cgraph_dump_file)
+ if (symtab->dump_file)
{
- fprintf (cgraph_dump_file, "cloning %s to %s\n",
- xstrdup (node->clone_of->name ()),
- xstrdup (node->name ()));
+ fprintf (symtab->dump_file, "cloning %s to %s\n",
+ xstrdup_for_dump (node->clone_of->name ()),
+ xstrdup_for_dump (node->name ()));
if (node->clone.tree_map)
{
unsigned int i;
- fprintf (cgraph_dump_file, " replace map: ");
+ fprintf (symtab->dump_file, " replace map: ");
for (i = 0;
i < vec_safe_length (node->clone.tree_map);
i++)
{
- struct ipa_replace_map *replace_info;
+ ipa_replace_map *replace_info;
replace_info = (*node->clone.tree_map)[i];
- print_generic_expr (cgraph_dump_file, replace_info->old_tree, 0);
- fprintf (cgraph_dump_file, " -> ");
- print_generic_expr (cgraph_dump_file, replace_info->new_tree, 0);
- fprintf (cgraph_dump_file, "%s%s;",
+ print_generic_expr (symtab->dump_file, replace_info->old_tree, 0);
+ fprintf (symtab->dump_file, " -> ");
+ print_generic_expr (symtab->dump_file, replace_info->new_tree, 0);
+ fprintf (symtab->dump_file, "%s%s;",
replace_info->replace_p ? "(replace)":"",
replace_info->ref_p ? "(ref)":"");
}
- fprintf (cgraph_dump_file, "\n");
+ fprintf (symtab->dump_file, "\n");
}
if (node->clone.args_to_skip)
{
- fprintf (cgraph_dump_file, " args_to_skip: ");
- dump_bitmap (cgraph_dump_file, node->clone.args_to_skip);
+ fprintf (symtab->dump_file, " args_to_skip: ");
+ dump_bitmap (symtab->dump_file,
+ node->clone.args_to_skip);
}
if (node->clone.args_to_skip)
{
- fprintf (cgraph_dump_file, " combined_args_to_skip:");
- dump_bitmap (cgraph_dump_file, node->clone.combined_args_to_skip);
+ fprintf (symtab->dump_file, " combined_args_to_skip:");
+ dump_bitmap (symtab->dump_file, node->clone.combined_args_to_skip);
}
}
cgraph_materialize_clone (node);
FOR_EACH_FUNCTION (node)
if (!node->analyzed && node->callees)
{
- cgraph_node_remove_callees (node);
- ipa_remove_all_references (&node->ref_list);
+ node->remove_callees ();
+ node->remove_all_references ();
}
else
- ipa_clear_stmts_in_references (node);
- if (cgraph_dump_file)
- fprintf (cgraph_dump_file, "Materialization Call site updates done.\n");
+ node->clear_stmts_in_references ();
+ if (symtab->dump_file)
+ fprintf (symtab->dump_file, "Materialization Call site updates done.\n");
#ifdef ENABLE_CHECKING
- verify_cgraph ();
+ cgraph_node::verify_cgraph_nodes ();
#endif
- symtab_remove_unreachable_nodes (false, cgraph_dump_file);
+ symtab->remove_unreachable_nodes (symtab->dump_file);
}
#include "gt-cgraphclones.h"