Update error message prefix in libgcov profiling.
[gcc.git] / gcc / cgraphclones.c
index 04cb990cc86e635681f7b51538a89a4688d90728..15f7e119d18ea1ad6ad18c827e4d4bd6cfa41364 100644 (file)
@@ -1,5 +1,5 @@
 /* Callgraph clones
-   Copyright (C) 2003-2013 Free Software Foundation, Inc.
+   Copyright (C) 2003-2019 Free Software Foundation, Inc.
    Contributed by Jan Hubicka
 
 This file is part of GCC.
@@ -67,96 +67,340 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tm.h"
-#include "tree.h"
-#include "rtl.h"
-#include "tree-flow.h"
-#include "tree-inline.h"
-#include "langhooks.h"
-#include "pointer-set.h"
-#include "toplev.h"
-#include "flags.h"
-#include "ggc.h"
-#include "debug.h"
+#include "backend.h"
 #include "target.h"
-#include "cgraph.h"
-#include "diagnostic.h"
-#include "params.h"
-#include "intl.h"
-#include "function.h"
-#include "ipa-prop.h"
+#include "rtl.h"
+#include "tree.h"
 #include "gimple.h"
-#include "tree-iterator.h"
-#include "tree-dump.h"
-#include "gimple-pretty-print.h"
-#include "coverage.h"
-#include "ipa-inline.h"
-#include "ipa-utils.h"
+#include "stringpool.h"
+#include "cgraph.h"
 #include "lto-streamer.h"
-#include "except.h"
+#include "tree-eh.h"
+#include "tree-cfg.h"
+#include "tree-inline.h"
+#include "dumpfile.h"
+#include "gimple-pretty-print.h"
+
+/* Create clone of edge in the node N represented by CALL_EXPR
+   the callgraph.  */
 
-/* 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)
+cgraph_edge *
+cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
+                   profile_count num, profile_count den,
+                   bool update_original)
 {
-  struct cgraph_edge *new_edge;
-  gcov_type count = apply_probability (e->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;
-  if (freq > CGRAPH_FREQ_MAX)
-    freq = CGRAPH_FREQ_MAX;
-
-  if (e->indirect_unknown_callee)
+  cgraph_edge *new_edge;
+  profile_count::adjust_for_ipa_scaling (&num, &den);
+  profile_count prof_count = count.apply_scale (num, den);
+
+  if (indirect_unknown_callee)
     {
       tree decl;
 
-      if (call_stmt && (decl = gimple_call_fndecl (call_stmt)))
+      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.  */
+         && !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, prof_count);
        }
       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,
+                                             prof_count, 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, prof_count);
+      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->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;
+
+  /* Update IPA profile.  Local profiles need no updating in original.  */
   if (update_original)
+    count = count.combine_with_ipa_count (count.ipa () 
+                                         - new_edge->count.ipa ());
+  symtab->call_edge_duplication_hooks (this, new_edge);
+  return new_edge;
+}
+
+/* Build variant of function type ORIG_TYPE skipping ARGS_TO_SKIP and the
+   return value if SKIP_RETURN is true.  */
+
+tree
+cgraph_build_function_type_skip_args (tree orig_type, bitmap args_to_skip,
+                                     bool skip_return)
+{
+  tree new_type = NULL;
+  tree args, new_args = NULL;
+  tree new_reversed;
+  int i = 0;
+
+  for (args = TYPE_ARG_TYPES (orig_type); args && args != void_list_node;
+       args = TREE_CHAIN (args), i++)
+    if (!args_to_skip || !bitmap_bit_p (args_to_skip, i))
+      new_args = tree_cons (NULL_TREE, TREE_VALUE (args), new_args);
+
+  new_reversed = nreverse (new_args);
+  if (args)
     {
-      e->count -= new_edge->count;
-      if (e->count < 0)
-       e->count = 0;
+      if (new_reversed)
+        TREE_CHAIN (new_args) = void_list_node;
+      else
+       new_reversed = void_list_node;
     }
-  cgraph_call_edge_duplication_hooks (e, new_edge);
-  return new_edge;
+
+  /* Use copy_node to preserve as much as possible from original type
+     (debug info, attribute lists etc.)
+     Exception is METHOD_TYPEs must have THIS argument.
+     When we are asked to remove it, we need to build new FUNCTION_TYPE
+     instead.  */
+  if (TREE_CODE (orig_type) != METHOD_TYPE
+      || !args_to_skip
+      || !bitmap_bit_p (args_to_skip, 0))
+    {
+      new_type = build_distinct_type_copy (orig_type);
+      TYPE_ARG_TYPES (new_type) = new_reversed;
+    }
+  else
+    {
+      new_type
+        = build_distinct_type_copy (build_function_type (TREE_TYPE (orig_type),
+                                                        new_reversed));
+      TYPE_CONTEXT (new_type) = TYPE_CONTEXT (orig_type);
+    }
+
+  if (skip_return)
+    TREE_TYPE (new_type) = void_type_node;
+
+  return new_type;
+}
+
+/* Build variant of function decl ORIG_DECL skipping ARGS_TO_SKIP and the
+   return value if SKIP_RETURN is true.
+
+   Arguments from DECL_ARGUMENTS list can't be removed now, since they are
+   linked by TREE_CHAIN directly.  The caller is responsible for eliminating
+   them when they are being duplicated (i.e. copy_arguments_for_versioning).  */
+
+static tree
+build_function_decl_skip_args (tree orig_decl, bitmap args_to_skip,
+                              bool skip_return)
+{
+  tree new_decl = copy_node (orig_decl);
+  tree new_type;
+
+  new_type = TREE_TYPE (orig_decl);
+  if (prototype_p (new_type)
+      || (skip_return && !VOID_TYPE_P (TREE_TYPE (new_type))))
+    new_type
+      = cgraph_build_function_type_skip_args (new_type, args_to_skip,
+                                             skip_return);
+  TREE_TYPE (new_decl) = new_type;
+
+  /* For declarations setting DECL_VINDEX (i.e. methods)
+     we expect first argument to be THIS pointer.   */
+  if (args_to_skip && bitmap_bit_p (args_to_skip, 0))
+    DECL_VINDEX (new_decl) = NULL_TREE;
+
+  /* When signature changes, we need to clear builtin info.  */
+  if (fndecl_built_in_p (new_decl)
+      && args_to_skip
+      && !bitmap_empty_p (args_to_skip))
+    {
+      DECL_BUILT_IN_CLASS (new_decl) = NOT_BUILT_IN;
+      DECL_FUNCTION_CODE (new_decl) = (enum built_in_function) 0;
+    }
+  /* The FE might have information and assumptions about the other
+     arguments.  */
+  DECL_LANG_SPECIFIC (new_decl) = NULL;
+  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.fixed_offset == thunk->thunk.fixed_offset
+       && cs->caller->thunk.virtual_value == thunk->thunk.virtual_value
+       && cs->caller->thunk.indirect_offset == thunk->thunk.indirect_offset
+       && cs->caller->thunk.this_adjusting == thunk->thunk.this_adjusting
+       && cs->caller->thunk.virtual_offset_p == thunk->thunk.virtual_offset_p)
+      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_numbered (thunk->decl,
+                                                      "artificial_thunk");
+  SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
+
+  /* We need to force DECL_IGNORED_P because the new thunk is created after
+     early debug was run.  */
+  DECL_IGNORED_P (new_decl) = 1;
+
+  new_thunk = cgraph_node::create (new_decl);
+  set_new_clone_decl_and_node_flags (new_thunk);
+  new_thunk->definition = true;
+  new_thunk->local.can_change_signature = node->local.can_change_signature;
+  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, new_thunk->count);
+  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;
+}
+
+void
+dump_callgraph_transformation (const cgraph_node *original,
+                              const cgraph_node *clone,
+                              const char *suffix)
+{
+  if (symtab->ipa_clones_dump_file)
+    {
+      fprintf (symtab->ipa_clones_dump_file,
+              "Callgraph clone;%s;%d;%s;%d;%d;%s;%d;%s;%d;%d;%s\n",
+              original->asm_name (), original->order,
+              DECL_SOURCE_FILE (original->decl),
+              DECL_SOURCE_LINE (original->decl),
+              DECL_SOURCE_COLUMN (original->decl), clone->asm_name (),
+              clone->order, DECL_SOURCE_FILE (clone->decl),
+              DECL_SOURCE_LINE (clone->decl), DECL_SOURCE_COLUMN (clone->decl),
+              suffix);
+
+      symtab->cloned_nodes.add (original);
+      symtab->cloned_nodes.add (clone);
+    }
+}
 
 /* Create node representing clone of N executed COUNT times.  Decrease
    the execution counts from original node too.
@@ -174,131 +418,222 @@ cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n,
    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)
+cgraph_node *
+cgraph_node::create_clone (tree new_decl, profile_count prof_count,
+                          bool update_original,
+                          vec<cgraph_edge *> redirect_callers,
+                          bool call_duplication_hook,
+                          cgraph_node *new_inlined_to,
+                          bitmap args_to_skip, const char *suffix)
 {
-  struct cgraph_node *new_node = cgraph_create_empty_node ();
-  struct cgraph_edge *e;
-  gcov_type count_scale;
+  cgraph_node *new_node = symtab->create_empty ();
+  cgraph_edge *e;
   unsigned i;
+  profile_count old_count = count;
+
+  if (new_inlined_to)
+    dump_callgraph_transformation (this, new_inlined_to, "inlining to");
+
+  /* When inlining we scale precisely to prof_count, when cloning we can
+     preserve local profile.  */
+  if (!new_inlined_to)
+    prof_count = count.combine_with_ipa_count (prof_count);
+  new_node->count = prof_count;
 
-  new_node->symbol.decl = decl;
-  symtab_register_node ((symtab_node)new_node);
-  new_node->origin = n->origin;
-  new_node->symbol.lto_file_data = n->symbol.lto_file_data;
+  /* Update IPA profile.  Local profiles need no updating in original.  */
+  if (update_original)
+    count = count.combine_with_ipa_count (count.ipa () - prof_count.ipa ());
+  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->symbol.analyzed = n->symbol.analyzed;
-  new_node->symbol.definition = n->symbol.definition;
-  new_node->local = n->local;
-  new_node->symbol.externally_visible = false;
+  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 = n->global;
+  new_node->global = 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->rtl = rtl;
+  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_comdat = merged_comdat;
+  new_node->thunk = thunk;
+
   new_node->clone.tree_map = NULL;
-  if (n->count)
+  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)
     {
-      if (new_node->count > n->count)
-        count_scale = REG_BR_PROB_BASE;
-      else
-        count_scale = GCOV_COMPUTE_SCALE (new_node->count, n->count);
+      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
-    count_scale = 0;
-  if (update_original)
-    {
-      n->count -= count;
-      if (n->count < 0)
-       n->count = 0;
-    }
+    new_node->clone.combined_args_to_skip = args_to_skip;
 
   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_node);
+        version.  The only exception is when the edge was proved to
+        be unreachable during the clonning procedure.  */
+      if (!e->callee
+         || !fndecl_built_in_p (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, new_node->count, old_count,
+             update_original);
 
-  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 ((symtab_node)new_node, &n->symbol.ref_list);
+  for (e = indirect_calls; e; e = e->next_callee)
+    e->clone (new_node, e->call_stmt, e->lto_stmt_uid,
+             new_node->count, old_count, update_original);
+  new_node->clone_references (this);
 
-  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;
+  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)
-    cgraph_call_node_duplication_hooks (n, new_node);
+    symtab->call_cgraph_duplication_hooks (this, new_node);
+
+  if (!new_inlined_to)
+    dump_callgraph_transformation (this, new_node, suffix);
+
   return new_node;
 }
 
-/* Create a new name for clone of DECL, add SUFFIX.  Returns an identifier.  */
+static GTY(()) hash_map<const char *, unsigned> *clone_fn_ids;
 
-static GTY(()) unsigned int clone_fn_id_num;
+/* Return a new assembler name for a clone of decl named NAME.  Apart
+   from the string SUFFIX, the new name will end with a unique (for
+   each NAME) unspecified number.  If clone numbering is not needed
+   then the two argument clone_function_name should be used instead.
+   Should not be called directly except for by
+   lto-partition.c:privatize_symbol_name_1.  */
 
 tree
-clone_function_name (tree decl, const char *suffix)
+clone_function_name_numbered (const char *name, const char *suffix)
+{
+  /* Initialize the function->counter mapping the first time it's
+     needed.  */
+  if (!clone_fn_ids)
+    clone_fn_ids = hash_map<const char *, unsigned int>::create_ggc (64);
+  unsigned int &suffix_counter = clone_fn_ids->get_or_insert (
+                                  IDENTIFIER_POINTER (get_identifier (name)));
+  return clone_function_name (name, suffix, suffix_counter++);
+}
+
+/* Return a new assembler name for a clone of DECL.  Apart from string
+   SUFFIX, the new name will end with a unique (for each DECL
+   assembler name) unspecified number.  If clone numbering is not
+   needed then the two argument clone_function_name should be used
+   instead.  */
+
+tree
+clone_function_name_numbered (tree decl, const char *suffix)
 {
   tree name = DECL_ASSEMBLER_NAME (decl);
-  size_t len = IDENTIFIER_LENGTH (name);
+  return clone_function_name_numbered (IDENTIFIER_POINTER (name),
+                                      suffix);
+}
+
+/* Return a new assembler name for a clone of decl named NAME.  Apart
+   from the string SUFFIX, the new name will end with the specified
+   NUMBER.  If clone numbering is not needed then the two argument
+   clone_function_name should be used instead.  */
+
+tree
+clone_function_name (const char *name, const char *suffix,
+                    unsigned long number)
+{
+  size_t len = strlen (name);
   char *tmp_name, *prefix;
 
   prefix = XALLOCAVEC (char, len + strlen (suffix) + 2);
-  memcpy (prefix, IDENTIFIER_POINTER (name), len);
+  memcpy (prefix, name, len);
   strcpy (prefix + len + 1, suffix);
-#ifndef NO_DOT_IN_LABEL
-  prefix[len] = '.';
-#elif !defined NO_DOLLAR_IN_LABEL
-  prefix[len] = '$';
+  prefix[len] = symbol_table::symbol_suffix_separator ();
+  ASM_FORMAT_PRIVATE_NAME (tmp_name, prefix, number);
+  return get_identifier (tmp_name);
+}
+
+/* Return a new assembler name for a clone of DECL.  Apart from the
+   string SUFFIX, the new name will end with the specified NUMBER.  If
+   clone numbering is not needed then the two argument
+   clone_function_name should be used instead.  */
+
+tree
+clone_function_name (tree decl, const char *suffix,
+                    unsigned long number)
+{
+  return clone_function_name (
+          IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), suffix, number);
+}
+
+/* Return a new assembler name ending with the string SUFFIX for a
+   clone of DECL.  */
+
+tree
+clone_function_name (tree decl, const char *suffix)
+{
+  tree identifier = DECL_ASSEMBLER_NAME (decl);
+  /* For consistency this needs to behave the same way as
+     ASM_FORMAT_PRIVATE_NAME does, but without the final number
+     suffix.  */
+  char *separator = XALLOCAVEC (char, 2);
+  separator[0] = symbol_table::symbol_suffix_separator ();
+  separator[1] = 0;
+#if defined (NO_DOT_IN_LABEL) && defined (NO_DOLLAR_IN_LABEL)
+  const char *prefix = "__";
 #else
-  prefix[len] = '_';
+  const char *prefix = "";
 #endif
-  ASM_FORMAT_PRIVATE_NAME (tmp_name, prefix, clone_fn_id_num++);
-  return get_identifier (tmp_name);
+  char *result = ACONCAT ((prefix,
+                          IDENTIFIER_POINTER (identifier),
+                          separator,
+                          suffix,
+                          (char*)0));
+  return get_identifier (result);
 }
 
-/* Create callgraph node clone with new declaration.  The actual body will
-   be copied later at compilation stage.
+
+/* Create callgraph node clone with new declaration.  The actual body will be
+   copied later at compilation stage.  The name of the new clone will be
+   constructed from the name of the original node, SUFFIX and NUM_SUFFIX.
 
    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,
+                                  unsigned num_suffix)
 {
-  tree old_decl = old_node->symbol.decl;
-  struct cgraph_node *new_node = NULL;
+  tree old_decl = decl;
+  cgraph_node *new_node = NULL;
   tree new_decl;
-  size_t i;
-  struct ipa_replace_map *map;
-
-  if (!flag_wpa)
-    gcc_checking_assert  (tree_versionable_function_p (old_decl));
+  size_t len, i;
+  ipa_replace_map *map;
+  char *name;
 
-  gcc_assert (old_node->local.can_change_signature || !args_to_skip);
+  gcc_checking_assert (local.versionable);
+  gcc_assert (local.can_change_signature || !args_to_skip);
 
   /* Make a new FUNCTION_DECL tree node */
   if (!args_to_skip)
@@ -313,34 +648,32 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node,
   DECL_ARGUMENTS (new_decl) = NULL;
   DECL_INITIAL (new_decl) = NULL;
   DECL_RESULT (new_decl) = NULL; 
-  /* We can not do DECL_RESULT (new_decl) = NULL; here because of LTO partitioning
+  /* We cannot do DECL_RESULT (new_decl) = NULL; here because of LTO partitioning
      sometimes storing only clone decl instead of original.  */
 
   /* Generate a new name for the new version. */
-  DECL_NAME (new_decl) = clone_function_name (old_decl, suffix);
-  SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
+  len = IDENTIFIER_LENGTH (DECL_NAME (old_decl));
+  name = XALLOCAVEC (char, len + strlen (suffix) + 2);
+  memcpy (name, IDENTIFIER_POINTER (DECL_NAME (old_decl)), len);
+  strcpy (name + len + 1, suffix);
+  name[len] = '.';
+  DECL_NAME (new_decl) = get_identifier (name);
+  SET_DECL_ASSEMBLER_NAME (new_decl,
+                          clone_function_name (old_decl, suffix, num_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, false,
+                          redirect_callers, false, NULL, args_to_skip, suffix);
+
   /* 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->symbol.decl) = 0;
-  if (DECL_ONE_ONLY (old_decl))
-    DECL_SECTION_NAME (new_node->symbol.decl) = NULL;
-  DECL_COMDAT_GROUP (new_node->symbol.decl) = 0;
-  TREE_PUBLIC (new_node->symbol.decl) = 0;
-  DECL_COMDAT (new_node->symbol.decl) = 0;
-  DECL_WEAK (new_node->symbol.decl) = 0;
-  DECL_VIRTUAL_P (new_node->symbol.decl) = 0;
-  DECL_STATIC_CONSTRUCTOR (new_node->symbol.decl) = 0;
-  DECL_STATIC_DESTRUCTOR (new_node->symbol.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)
@@ -348,56 +681,29 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node,
        && !DECL_WEAK (old_decl)
        && !DECL_COMDAT (old_decl))
       || in_lto_p)
-    new_node->symbol.unique_name = true;
+    new_node->unique_name = true;
   FOR_EACH_VEC_SAFE_ELT (tree_map, i, map)
-    ipa_maybe_record_reference ((symtab_node) 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->symbol.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->symbol.externally_visible = 0;
-  new_node->local.local = 1;
-  new_node->lowered = true;
+    new_node->maybe_create_reference (map->new_tree, 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->symbol.decl != node->symbol.decl;
+       && next_inline_clone->decl != decl;
        next_inline_clone = next_inline_clone->next_sibling_clone)
     ;
 
@@ -406,8 +712,8 @@ cgraph_find_replacement_node (struct cgraph_node *node)
      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;
 
@@ -417,32 +723,32 @@ cgraph_find_replacement_node (struct cgraph_node *node)
          = 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.  */
@@ -454,7 +760,7 @@ cgraph_find_replacement_node (struct cgraph_node *node)
            {
              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;
            }
@@ -474,34 +780,54 @@ cgraph_find_replacement_node (struct cgraph_node *node)
 }
 
 /* Like cgraph_set_call_stmt but walk the clone tree and update all
-   clones sharing the same function body.  */
+   clones sharing the same function body.  
+   When WHOLE_SPECULATIVE_EDGES is true, all three components of
+   speculative edge gets updated.  Otherwise we update only direct
+   call.  */
 
 void
-cgraph_set_call_stmt_including_clones (struct cgraph_node *orig,
-                                      gimple old_stmt, gimple new_stmt)
+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);
+    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);
+         {
+           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)
+             {
+               cgraph_edge *direct, *indirect;
+               ipa_ref *ref;
+
+               gcc_assert (!edge->indirect_unknown_callee);
+               edge->speculative_call_info (direct, indirect, ref);
+               direct->speculative = false;
+               indirect->speculative = false;
+               ref->speculative = false;
+             }
+         }
        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)
+           while (node != this && !node->next_sibling_clone)
              node = node->clone_of;
-           if (node != orig)
+           if (node != this)
              node = node->next_sibling_clone;
          }
       }
@@ -515,53 +841,52 @@ cgraph_set_call_stmt_including_clones (struct cgraph_node *orig,
    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,
+                                          profile_count count,
+                                          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);
       edge->inline_failed = reason;
     }
 
-  node = orig->clones;
+  node = clones;
   if (node)
-    while (node != orig)
-      {
-       struct cgraph_edge *edge = cgraph_edge (node, 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 = cgraph_create_edge (node, callee, stmt, count,
-                                      freq);
-           edge->inline_failed = reason;
-         }
+    while (node != this)
+      /* Thunk clones do not get updated while copying inline function body.  */
+      if (!node->thunk.thunk_p)
+       {
+         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)
+           edge->set_call_stmt (stmt);
+         else if (! node->get_edge (stmt))
+           {
+             edge = node->create_edge (callee, stmt, count);
+             edge->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;
-         }
-      }
+         if (node->clones)
+           node = node->clones;
+         else if (node->next_sibling_clone)
+           node = node->next_sibling_clone;
+         else
+           {
+             while (node != this && !node->next_sibling_clone)
+               node = node->clone_of;
+             if (node != this)
+               node = node->next_sibling_clone;
+           }
+       }
 }
 
 /* Remove the node from cgraph and all inline clones inlined into it.
@@ -570,23 +895,23 @@ cgraph_create_edge_including_clones (struct cgraph_node *orig,
    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;
 }
 
@@ -595,26 +920,26 @@ cgraph_remove_node_and_inline_clones (struct cgraph_node *node, struct cgraph_no
    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->symbol.decl);
-      gimple_call_set_fndecl (e->call_stmt, new_version->symbol.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. 
 
@@ -622,51 +947,50 @@ update_call_expr (struct cgraph_node *new_version)
    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,
+                                 const char *suffix)
  {
-   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->symbol.analyzed = old_version->symbol.analyzed;
-   new_version->symbol.definition = old_version->symbol.definition;
-   new_version->local = old_version->local;
-   new_version->symbol.externally_visible = false;
-   new_version->local.local = new_version->symbol.definition;
-   new_version->global = old_version->global;
-   new_version->rtl = old_version->rtl;
-   new_version->count = old_version->count;
+   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 = 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, count, count,
+                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, count, count,
+                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);
+
+   dump_callgraph_transformation (this, new_version, suffix);
 
    return new_version;
  }
@@ -681,7 +1005,6 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_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.
@@ -689,26 +1012,28 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
    If non-NULL BLOCK_TO_COPY determine what basic blocks to copy.
    If non_NULL NEW_ENTRY determine new entry BB of the clone.
 
+   If TARGET_ATTRIBUTES is non-null, when creating a new declaration,
+   add the attributes to DECL_ATTRIBUTES.  And call valid_attribute_p
+   that will promote value of the attribute DECL_FUNCTION_SPECIFIC_TARGET
+   of the declaration.
+
    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 *suffix, tree target_attributes)
 {
-  tree old_decl = old_version_node->symbol.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)
@@ -718,20 +1043,37 @@ cgraph_function_versioning (struct cgraph_node *old_version_node,
       = build_function_decl_skip_args (old_decl, args_to_skip, skip_return);
 
   /* Generate a new name for the new version. */
-  DECL_NAME (new_decl) = clone_function_name (old_decl, clone_name);
+  DECL_NAME (new_decl) = clone_function_name_numbered (old_decl, suffix);
   SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
   SET_DECL_RTL (new_decl, NULL);
 
+  DECL_VIRTUAL_P (new_decl) = 0;
+
+  if (target_attributes)
+    {
+      DECL_ATTRIBUTES (new_decl) = target_attributes;
+
+      location_t saved_loc = input_location;
+      tree v = TREE_VALUE (target_attributes);
+      input_location = DECL_SOURCE_LOCATION (new_decl);
+      bool r = targetm.target_option.valid_attribute_p (new_decl, NULL, v, 0);
+      input_location = saved_loc;
+      if (!r)
+       return NULL;
+    }
+
   /* When the old decl was a con-/destructor make sure the clone isn't.  */
-  DECL_STATIC_CONSTRUCTOR(new_decl) = 0;
-  DECL_STATIC_DESTRUCTOR(new_decl) = 0;
+  DECL_STATIC_CONSTRUCTOR (new_decl) = 0;
+  DECL_STATIC_DESTRUCTOR (new_decl) = 0;
 
   /* 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, suffix);
 
+  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);
@@ -741,44 +1083,47 @@ cgraph_function_versioning (struct cgraph_node *old_version_node,
      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->symbol.decl);
-  DECL_VIRTUAL_P (new_version_node->symbol.decl) = 0;
-  new_version_node->symbol.externally_visible = 0;
+  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)
        && !DECL_WEAK (old_decl)
        && !DECL_COMDAT (old_decl))
       || in_lto_p)
-    new_version_node->symbol.unique_name = true;
+    new_version_node->unique_name = true;
 
   /* 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 (new_version_node);
   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->symbol.decl;
+  node->former_clone_of = node->clone_of->decl;
   if (node->clone_of->former_clone_of)
     node->former_clone_of = node->clone_of->former_clone_of;
   /* Copy the OLD_VERSION_NODE function tree to the new version.  */
-  tree_function_versioning (node->clone_of->symbol.decl, node->symbol.decl,
+  tree_function_versioning (node->clone_of->decl, node->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->symbol.decl, cgraph_dump_file, dump_flags);
-      dump_function_to_file (node->symbol.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.  */
@@ -790,11 +1135,11 @@ cgraph_materialize_clone (struct cgraph_node *node)
     node->clone_of->clones = node->next_sibling_clone;
   node->next_sibling_clone = NULL;
   node->prev_sibling_clone = NULL;
-  if (!node->clone_of->symbol.analyzed && !node->clone_of->clones)
+  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->symbol.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);
@@ -803,20 +1148,20 @@ cgraph_materialize_clone (struct cgraph_node *node)
 /* Once all functions from compilation unit are in memory, produce all clones
    and update all calls.  We might also do this on demand if we don't want to
    bring all functions to memory prior compilation, but current WHOPR
-   implementation does that and it is is bit easier to keep everything right in
+   implementation does that and it is a bit easier to keep everything right in
    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");
-#ifdef ENABLE_CHECKING
-  verify_cgraph ();
-#endif
+  if (symtab->dump_file)
+    fprintf (symtab->dump_file, "Materializing clones\n");
+
+  cgraph_node::checking_verify_cgraph_nodes ();
 
   /* We can also do topological order, but number of iterations should be
      bounded by number of IPA passes since single IPA pass is probably not
@@ -826,44 +1171,49 @@ cgraph_materialize_all_clones (void)
       stabilized = true;
       FOR_EACH_FUNCTION (node)
         {
-         if (node->clone_of && node->symbol.decl != node->clone_of->symbol.decl
-             && !gimple_has_body_p (node->symbol.decl))
+         if (node->clone_of && node->decl != node->clone_of->decl
+             && !gimple_has_body_p (node->decl))
            {
-             if (gimple_has_body_p (node->clone_of->symbol.decl))
+             if (!node->clone_of->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 (cgraph_node_name (node->clone_of)),
-                              xstrdup (cgraph_node_name (node)));
+                     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);
+                             fprintf (symtab->dump_file, " -> ");
+                             print_generic_expr (symtab->dump_file,
+                                                 replace_info->new_tree);
+                             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);
@@ -873,14 +1223,19 @@ cgraph_materialize_all_clones (void)
        }
     }
   FOR_EACH_FUNCTION (node)
-    if (!node->symbol.analyzed && node->callees)
-      cgraph_node_remove_callees (node);
-  if (cgraph_dump_file)
-    fprintf (cgraph_dump_file, "Materialization Call site updates done.\n");
-#ifdef ENABLE_CHECKING
-  verify_cgraph ();
-#endif
-  symtab_remove_unreachable_nodes (false, cgraph_dump_file);
+    if (!node->analyzed && node->callees)
+      {
+       node->remove_callees ();
+       node->remove_all_references ();
+      }
+    else
+      node->clear_stmts_in_references ();
+  if (symtab->dump_file)
+    fprintf (symtab->dump_file, "Materialization Call site updates done.\n");
+
+  cgraph_node::checking_verify_cgraph_nodes ();
+
+  symtab->remove_unreachable_nodes (symtab->dump_file);
 }
 
 #include "gt-cgraphclones.h"