]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/cgraph.c
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / cgraph.c
index 802214dc81c4b6d389ff8b35228425b210d034a8..de0786537814690bf7e841500be532e96f62132a 100644 (file)
@@ -1,5 +1,5 @@
 /* Callgraph handling code.
-   Copyright (C) 2003-2018 Free Software Foundation, Inc.
+   Copyright (C) 2003-2021 Free Software Foundation, Inc.
    Contributed by Jan Hubicka
 
 This file is part of GCC.
@@ -57,12 +57,16 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-pretty-print.h"
 #include "tree-dfa.h"
 #include "profile.h"
-#include "params.h"
-#include "tree-chkp.h"
 #include "context.h"
 #include "gimplify.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "selftest.h"
+#include "tree-into-ssa.h"
+#include "ipa-inline.h"
+#include "tree-nested.h"
+#include "symtab-thunks.h"
+#include "symtab-clones.h"
 
 /* FIXME: Only for PROP_loops, but cgraph shouldn't have to know about this.  */
 #include "tree-pass.h"
@@ -121,7 +125,7 @@ static GTY(()) hash_table<function_version_hasher> *cgraph_fnver_htab = NULL;
 hashval_t
 function_version_hasher::hash (cgraph_function_version_info *ptr)
 {
-  int uid = ptr->this_node->uid;
+  int uid = ptr->this_node->get_uid ();
   return (hashval_t)(uid);
 }
 
@@ -130,7 +134,7 @@ bool
 function_version_hasher::equal (cgraph_function_version_info *n1,
                                cgraph_function_version_info *n2)
 {
-  return n1->this_node->uid == n2->this_node->uid;
+  return n1->this_node->get_uid () == n2->this_node->get_uid ();
 }
 
 /* Mark as GC root all allocated nodes.  */
@@ -197,6 +201,9 @@ delete_function_version (cgraph_function_version_info *decl_v)
   if (decl_v == NULL)
     return;
 
+  if (version_info_node == decl_v)
+    version_info_node = NULL;
+
   if (decl_v->prev != NULL)
     decl_v->prev->next = decl_v->next;
 
@@ -280,14 +287,8 @@ symbol_table::initialize (void)
 cgraph_node *
 symbol_table::create_empty (void)
 {
-  cgraph_node *node = allocate_cgraph_symbol ();
-
-  node->type = SYMTAB_FUNCTION;
-  node->frequency = NODE_FREQUENCY_NORMAL;
-  node->count_materialization_scale = REG_BR_PROB_BASE;
   cgraph_count++;
-
-  return node;
+  return new (ggc_alloc<cgraph_node> ()) cgraph_node (cgraph_max_uid++);
 }
 
 /* Register HOOK to be called with DATA on each removed edge.  */
@@ -507,8 +508,6 @@ cgraph_node::create (tree decl)
 
   node->decl = decl;
 
-  node->count = profile_count::uninitialized ();
-
   if ((flag_openacc || flag_openmp)
       && lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
     {
@@ -517,14 +516,12 @@ cgraph_node::create (tree decl)
        g->have_offload = true;
     }
 
+  if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (decl)))
+    node->ifunc_resolver = true;
+
   node->register_symbol ();
+  maybe_record_nested_function (node);
 
-  if (DECL_CONTEXT (decl) && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)
-    {
-      node->origin = cgraph_node::get_create (DECL_CONTEXT (decl));
-      node->next_nested = node->origin->nested;
-      node->origin->nested = node;
-    }
   return node;
 }
 
@@ -536,7 +533,7 @@ cgraph_node::get_create (tree decl)
 {
   cgraph_node *first_clone = cgraph_node::get (decl);
 
-  if (first_clone && !first_clone->global.inlined_to)
+  if (first_clone && !first_clone->inlined_to)
     return first_clone;
 
   cgraph_node *node = cgraph_node::create (decl);
@@ -544,6 +541,7 @@ cgraph_node::get_create (tree decl)
     {
       first_clone->clone_of = node;
       node->clones = first_clone;
+      node->order = first_clone->order;
       symtab->symtab_prevail_in_asm_name_hash (node);
       node->decl->decl_with_vis.symtab_node = node;
       if (dump_file)
@@ -558,7 +556,8 @@ cgraph_node::get_create (tree decl)
 }
 
 /* Mark ALIAS as an alias to DECL.  DECL_NODE is cgraph node representing
-   the function body is associated with (not necessarily cgraph_node (DECL).  */
+   the function body is associated with
+   (not necessarily cgraph_node (DECL)).  */
 
 cgraph_node *
 cgraph_node::create_alias (tree alias, tree target)
@@ -575,6 +574,8 @@ cgraph_node::create_alias (tree alias, tree target)
   alias_node->alias = true;
   if (lookup_attribute ("weakref", DECL_ATTRIBUTES (alias)) != NULL)
     alias_node->transparent_alias = alias_node->weakref = true;
+  if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (alias)))
+    alias_node->ifunc_resolver = true;
   return alias_node;
 }
 
@@ -613,6 +614,7 @@ cgraph_node *
 cgraph_node::create_thunk (tree alias, tree, bool this_adjusting,
                           HOST_WIDE_INT fixed_offset,
                           HOST_WIDE_INT virtual_value,
+                          HOST_WIDE_INT indirect_offset,
                           tree virtual_offset,
                           tree real_alias)
 {
@@ -629,14 +631,24 @@ cgraph_node::create_thunk (tree alias, tree, bool this_adjusting,
                       ? virtual_value == wi::to_wide (virtual_offset)
                       : virtual_value == 0);
 
-  node->thunk.fixed_offset = fixed_offset;
-  node->thunk.virtual_value = virtual_value;
-  node->thunk.alias = real_alias;
-  node->thunk.this_adjusting = this_adjusting;
-  node->thunk.virtual_offset_p = virtual_offset != NULL;
-  node->thunk.thunk_p = true;
+  node->thunk = true;
   node->definition = true;
 
+  thunk_info *i;
+  thunk_info local_info;
+  if (symtab->state < CONSTRUCTION)
+    i = &local_info;
+  else
+    i = thunk_info::get_create (node);
+  i->fixed_offset = fixed_offset;
+  i->virtual_value = virtual_value;
+  i->indirect_offset = indirect_offset;
+  i->alias = real_alias;
+  i->this_adjusting = this_adjusting;
+  i->virtual_offset_p = virtual_offset != NULL;
+  if (symtab->state < CONSTRUCTION)
+    i->register_early (node);
+
   return node;
 }
 
@@ -652,7 +664,7 @@ cgraph_node::get_for_asmname (tree asmname)
        node = node->next_sharing_asm_name)
     {
       cgraph_node *cn = dyn_cast <cgraph_node *> (node);
-      if (cn && !cn->global.inlined_to)
+      if (cn && !cn->inlined_to)
        return cn;
     }
   return NULL;
@@ -710,7 +722,9 @@ cgraph_add_edge_to_call_site_hash (cgraph_edge *e)
   if (*slot)
     {
       gcc_assert (((cgraph_edge *)*slot)->speculative);
-      if (e->callee)
+      if (e->callee && (!e->prev_callee
+                       || !e->prev_callee->speculative
+                       || e->prev_callee->call_stmt != e->call_stmt))
        *slot = e;
       return;
     }
@@ -764,66 +778,99 @@ cgraph_node::get_edge (gimple *call_stmt)
 }
 
 
-/* Change field call_stmt of edge to NEW_STMT.
-   If UPDATE_SPECULATIVE and E is any component of speculative
-   edge, then update all components.  */
+/* Change field call_stmt of edge E to NEW_STMT.  If UPDATE_SPECULATIVE and E
+   is any component of speculative edge, then update all components.
+   Speculations can be resolved in the process and EDGE can be removed and
+   deallocated.  Return the edge that now represents the call.  */
 
-void
-cgraph_edge::set_call_stmt (gcall *new_stmt, bool update_speculative)
+cgraph_edge *
+cgraph_edge::set_call_stmt (cgraph_edge *e, gcall *new_stmt,
+                           bool update_speculative)
 {
   tree decl;
 
-  /* Speculative edges has three component, update all of them
-     when asked to.  */
-  if (update_speculative && speculative)
+  cgraph_node *new_direct_callee = NULL;
+  if ((e->indirect_unknown_callee || e->speculative)
+      && (decl = gimple_call_fndecl (new_stmt)))
     {
-      cgraph_edge *direct, *indirect;
-      ipa_ref *ref;
-
-      speculative_call_info (direct, indirect, ref);
-      direct->set_call_stmt (new_stmt, false);
-      indirect->set_call_stmt (new_stmt, false);
-      ref->stmt = new_stmt;
-      return;
+      /* Constant propagation and especially inlining can turn an indirect call
+        into a direct one.  */
+      new_direct_callee = cgraph_node::get (decl);
+      gcc_checking_assert (new_direct_callee);
     }
 
-  /* Only direct speculative edges go to call_site_hash.  */
-  if (caller->call_site_hash
-      && (!speculative || !indirect_unknown_callee))
+  /* Speculative edges has three component, update all of them
+     when asked to.  */
+  if (update_speculative && e->speculative
+      /* If we are about to resolve the speculation by calling make_direct
+        below, do not bother going over all the speculative edges now.  */
+      && !new_direct_callee)
     {
-      caller->call_site_hash->remove_elt_with_hash
-       (call_stmt, cgraph_edge_hasher::hash (call_stmt));
-    }
+      cgraph_edge *direct, *indirect, *next;
+      ipa_ref *ref;
+      bool e_indirect = e->indirect_unknown_callee;
+      int n = 0;
 
-  cgraph_edge *e = this;
+      direct = e->first_speculative_call_target ();
+      indirect = e->speculative_call_indirect_edge ();
 
-  call_stmt = new_stmt;
-  if (indirect_unknown_callee
-      && (decl = gimple_call_fndecl (new_stmt)))
-    {
-      /* Constant propagation (and possibly also inlining?) can turn an
-        indirect call into a direct one.  */
-      cgraph_node *new_callee = cgraph_node::get (decl);
+      gcall *old_stmt = direct->call_stmt;
+      for (cgraph_edge *d = direct; d; d = next)
+       {
+         next = d->next_speculative_call_target ();
+         cgraph_edge *d2 = set_call_stmt (d, new_stmt, false);
+         gcc_assert (d2 == d);
+         n++;
+       }
+      gcc_checking_assert (indirect->num_speculative_call_targets_p () == n);
+      for (unsigned int i = 0; e->caller->iterate_reference (i, ref); i++)
+       if (ref->speculative && ref->stmt == old_stmt)
+         {
+           ref->stmt = new_stmt;
+           n--;
+         }
 
-      gcc_checking_assert (new_callee);
-      e = make_direct (new_callee);
+      indirect = set_call_stmt (indirect, new_stmt, false);
+      return e_indirect ? indirect : direct;
     }
 
-  push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
-  e->can_throw_external = stmt_can_throw_external (new_stmt);
-  pop_cfun ();
-  if (e->caller->call_site_hash)
+  if (new_direct_callee)
+    e = make_direct (e, new_direct_callee);
+
+  /* Only direct speculative edges go to call_site_hash.  */
+  if (e->caller->call_site_hash
+      && (!e->speculative || !e->indirect_unknown_callee)
+      /* It is possible that edge was previously speculative.  In this case
+        we have different value in call stmt hash which needs preserving.  */
+      && e->caller->get_edge (e->call_stmt) == e)
+    e->caller->call_site_hash->remove_elt_with_hash
+      (e->call_stmt, cgraph_edge_hasher::hash (e->call_stmt));
+
+  e->call_stmt = new_stmt;
+
+  function *fun = DECL_STRUCT_FUNCTION (e->caller->decl);
+  e->can_throw_external = stmt_can_throw_external (fun, new_stmt);
+  /* Update call stite hash.  For speculative calls we only record the first
+     direct edge.  */
+  if (e->caller->call_site_hash
+      && (!e->speculative
+         || (e->callee
+             && (!e->prev_callee || !e->prev_callee->speculative
+                 || e->prev_callee->call_stmt != e->call_stmt))
+         || (e->speculative && !e->callee)))
     cgraph_add_edge_to_call_site_hash (e);
+  return e;
 }
 
 /* Allocate a cgraph_edge structure and fill it with data according to the
    parameters of which only CALLEE can be NULL (when creating an indirect call
-   edge).  */
+   edge).  CLONING_P should be set if properties that are copied from an
+   original edge should not be calculated.  */
 
 cgraph_edge *
 symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
                           gcall *call_stmt, profile_count count,
-                          bool indir_unknown_callee)
+                          bool indir_unknown_callee, bool cloning_p)
 {
   cgraph_edge *edge;
 
@@ -840,19 +887,12 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
       gcc_assert (is_gimple_call (call_stmt));
     }
 
-  if (free_edges)
-    {
-      edge = free_edges;
-      free_edges = NEXT_FREE_EDGE (edge);
-    }
-  else
-    {
-      edge = ggc_alloc<cgraph_edge> ();
-      edge->uid = edges_max_uid++;
-    }
-
+  edge = ggc_alloc<cgraph_edge> ();
+  edge->m_summary_id = -1;
   edges_count++;
 
+  gcc_assert (++edges_max_uid != 0);
+  edge->m_uid = edges_max_uid;
   edge->aux = NULL;
   edge->caller = caller;
   edge->callee = callee;
@@ -861,55 +901,56 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
   edge->prev_callee = NULL;
   edge->next_callee = NULL;
   edge->lto_stmt_uid = 0;
+  edge->speculative_id = 0;
 
   edge->count = count;
-
   edge->call_stmt = call_stmt;
-  push_cfun (DECL_STRUCT_FUNCTION (caller->decl));
-  edge->can_throw_external
-    = call_stmt ? stmt_can_throw_external (call_stmt) : false;
-  pop_cfun ();
-  if (call_stmt
-      && callee && callee->decl
-      && !gimple_check_call_matching_types (call_stmt, callee->decl,
-                                           false))
-    {
-      edge->inline_failed = CIF_MISMATCHED_ARGUMENTS;
-      edge->call_stmt_cannot_inline_p = true;
-    }
-  else
-    {
-      edge->inline_failed = CIF_FUNCTION_NOT_CONSIDERED;
-      edge->call_stmt_cannot_inline_p = false;
-    }
-
   edge->indirect_info = NULL;
   edge->indirect_inlining_edge = 0;
   edge->speculative = false;
   edge->indirect_unknown_callee = indir_unknown_callee;
+  if (call_stmt && caller->call_site_hash)
+    cgraph_add_edge_to_call_site_hash (edge);
+
+  if (cloning_p)
+    return edge;
+
+  edge->can_throw_external
+    = call_stmt ? stmt_can_throw_external (DECL_STRUCT_FUNCTION (caller->decl),
+                                          call_stmt) : false;
+  edge->inline_failed = CIF_FUNCTION_NOT_CONSIDERED;
+  edge->call_stmt_cannot_inline_p = false;
+
   if (opt_for_fn (edge->caller->decl, flag_devirtualize)
       && call_stmt && DECL_STRUCT_FUNCTION (caller->decl))
     edge->in_polymorphic_cdtor
       = decl_maybe_in_construction_p (NULL, NULL, call_stmt,
                                      caller->decl);
   else
-    edge->in_polymorphic_cdtor = caller->thunk.thunk_p;
-  if (call_stmt && caller->call_site_hash)
-    cgraph_add_edge_to_call_site_hash (edge);
+    edge->in_polymorphic_cdtor = caller->thunk;
+  if (callee)
+    caller->calls_declare_variant_alt |= callee->declare_variant_alt;
+
+  if (callee && symtab->state != LTO_STREAMING
+      && edge->callee->comdat_local_p ())
+    edge->caller->calls_comdat_local = true;
 
   return edge;
 }
 
-/* Create edge from a given function to CALLEE in the cgraph.  */
+/* Create edge from a given function to CALLEE in the cgraph.  CLONING_P should
+   be set if properties that are copied from an original edge should not be
+   calculated.  */
 
 cgraph_edge *
 cgraph_node::create_edge (cgraph_node *callee,
-                         gcall *call_stmt, profile_count count)
+                         gcall *call_stmt, profile_count count, bool cloning_p)
 {
   cgraph_edge *edge = symtab->create_edge (this, callee, call_stmt, count,
-                                          false);
+                                          false, cloning_p);
 
-  initialize_inline_failed (edge);
+  if (!cloning_p)
+    initialize_inline_failed (edge);
 
   edge->next_caller = callee->callers;
   if (callee->callers)
@@ -937,25 +978,28 @@ cgraph_allocate_init_indirect_info (void)
 
 /* Create an indirect edge with a yet-undetermined callee where the call
    statement destination is a formal parameter of the caller with index
-   PARAM_INDEX. */
+   PARAM_INDEX. CLONING_P should be set if properties that are copied from an
+   original edge should not be calculated and indirect_info structure should
+   not be calculated.  */
 
 cgraph_edge *
 cgraph_node::create_indirect_edge (gcall *call_stmt, int ecf_flags,
                                   profile_count count,
-                                  bool compute_indirect_info)
+                                  bool cloning_p)
 {
-  cgraph_edge *edge = symtab->create_edge (this, NULL, call_stmt,
-                                                           count, true);
+  cgraph_edge *edge = symtab->create_edge (this, NULL, call_stmt, count, true,
+                                          cloning_p);
   tree target;
 
-  initialize_inline_failed (edge);
+  if (!cloning_p)
+    initialize_inline_failed (edge);
 
   edge->indirect_info = cgraph_allocate_init_indirect_info ();
   edge->indirect_info->ecf_flags = ecf_flags;
   edge->indirect_info->vptr_changed = true;
 
   /* Record polymorphic call info.  */
-  if (compute_indirect_info
+  if (!cloning_p
       && call_stmt
       && (target = gimple_call_fn (call_stmt))
       && virtual_method_call_p (target))
@@ -996,7 +1040,8 @@ cgraph_edge::remove_caller (void)
       else
        caller->callees = next_callee;
     }
-  if (caller->call_site_hash)
+  if (caller->call_site_hash
+      && this == caller->get_edge (call_stmt))
     caller->call_site_hash->remove_elt_with_hash
        (call_stmt, cgraph_edge_hasher::hash (call_stmt));
 }
@@ -1006,36 +1051,32 @@ cgraph_edge::remove_caller (void)
 void
 symbol_table::free_edge (cgraph_edge *e)
 {
-  int uid = e->uid;
+  edges_count--;
+  if (e->m_summary_id != -1)
+    edge_released_summary_ids.safe_push (e->m_summary_id);
 
   if (e->indirect_info)
     ggc_free (e->indirect_info);
-
-  /* Clear out the edge so we do not dangle pointers.  */
-  memset (e, 0, sizeof (*e));
-  e->uid = uid;
-  NEXT_FREE_EDGE (e) = free_edges;
-  free_edges = e;
-  edges_count--;
+  ggc_free (e);
 }
 
 /* Remove the edge in the cgraph.  */
 
 void
-cgraph_edge::remove (void)
+cgraph_edge::remove (cgraph_edge *edge)
 {
   /* Call all edge removal hooks.  */
-  symtab->call_edge_removal_hooks (this);
+  symtab->call_edge_removal_hooks (edge);
 
-  if (!indirect_unknown_callee)
+  if (!edge->indirect_unknown_callee)
     /* Remove from callers list of the callee.  */
-    remove_callee ();
+    edge->remove_callee ();
 
   /* Remove from callees list of the callers.  */
-  remove_caller ();
+  edge->remove_caller ();
 
   /* Put the edge onto the free list.  */
-  symtab->free_edge (this);
+  symtab->free_edge (edge);
 }
 
 /* Turn edge into speculative call calling N2. Update
@@ -1051,13 +1092,17 @@ cgraph_edge::remove (void)
      call call_dest
 
    At this time the function just creates the direct call,
-   the referencd representing the if conditional and attaches
-   them all to the orginal indirect call statement.  
+   the reference representing the if conditional and attaches
+   them all to the original indirect call statement.  
+
+   speculative_id is used to link direct calls with their corresponding
+   IPA_REF_ADDR references when representing speculative calls.
 
    Return direct edge created.  */
 
 cgraph_edge *
-cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
+cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
+                              unsigned int speculative_id)
 {
   cgraph_node *n = caller;
   ipa_ref *ref = NULL;
@@ -1075,90 +1120,102 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
   else
     e2->can_throw_external = can_throw_external;
   e2->lto_stmt_uid = lto_stmt_uid;
+  e2->speculative_id = speculative_id;
   e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
+  indirect_info->num_speculative_call_targets++;
   count -= e2->count;
   symtab->call_edge_duplication_hooks (this, e2);
   ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
   ref->lto_stmt_uid = lto_stmt_uid;
+  ref->speculative_id = speculative_id;
   ref->speculative = speculative;
   n2->mark_address_taken ();
   return e2;
 }
 
-/* Speculative call consist of three components:
-   1) an indirect edge representing the original call
-   2) an direct edge representing the new call
-   3) ADDR_EXPR reference representing the speculative check.
-   All three components are attached to single statement (the indirect
-   call) and if one of them exists, all of them must exist.
+/* Speculative call consists of an indirect edge and one or more
+   direct edge+ref pairs.
 
-   Given speculative call edge, return all three components.
- */
+   Given an edge which is part of speculative call, return the first
  direct call edge in the speculative call sequence.  */
 
-void
-cgraph_edge::speculative_call_info (cgraph_edge *&direct,
-                                   cgraph_edge *&indirect,
-                                   ipa_ref *&reference)
+cgraph_edge *
+cgraph_edge::first_speculative_call_target ()
 {
-  ipa_ref *ref;
-  int i;
-  cgraph_edge *e2;
   cgraph_edge *e = this;
 
-  if (!e->indirect_unknown_callee)
-    for (e2 = e->caller->indirect_calls;
-        e2->call_stmt != e->call_stmt || e2->lto_stmt_uid != e->lto_stmt_uid;
-        e2 = e2->next_callee)
-      ;
-  else
+  gcc_checking_assert (e->speculative);
+  if (e->callee)
     {
-      e2 = e;
-      /* We can take advantage of the call stmt hash.  */
-      if (e2->call_stmt)
+      while (e->prev_callee && e->prev_callee->speculative
+            && e->prev_callee->call_stmt == e->call_stmt
+            && e->prev_callee->lto_stmt_uid == e->lto_stmt_uid)
+       e = e->prev_callee;
+      return e;
+    }
+  /* Call stmt site hash always points to the first target of the
+     speculative call sequence.  */
+  if (e->call_stmt)
+    return e->caller->get_edge (e->call_stmt);
+  for (cgraph_edge *e2 = e->caller->callees; true; e2 = e2->next_callee)
+    if (e2->speculative
+       && e->call_stmt == e2->call_stmt
+       && e->lto_stmt_uid == e2->lto_stmt_uid)
+      return e2;
+}
+
+/* We always maintain first direct edge in the call site hash, if one
+   exists.  E is going to be removed.  See if it is first one and update
+   hash accordingly.  INDIRECT is the indirect edge of speculative call.
+   We assume that INDIRECT->num_speculative_call_targets_p () is already
+   updated for removal of E.  */
+static void
+update_call_stmt_hash_for_removing_direct_edge (cgraph_edge *e,
+                                               cgraph_edge *indirect)
+{
+  if (e->caller->call_site_hash)
+    {
+      if (e->caller->get_edge (e->call_stmt) != e)
+       ;
+      else if (!indirect->num_speculative_call_targets_p ())
+       cgraph_update_edge_in_call_site_hash (indirect);
+      else
        {
-         e = e->caller->get_edge (e2->call_stmt);
-         gcc_assert (e->speculative && !e->indirect_unknown_callee);
+         gcc_checking_assert (e->next_callee && e->next_callee->speculative
+                              && e->next_callee->call_stmt == e->call_stmt);
+         cgraph_update_edge_in_call_site_hash (e->next_callee);
        }
-      else
-       for (e = e->caller->callees; 
-            e2->call_stmt != e->call_stmt
-            || e2->lto_stmt_uid != e->lto_stmt_uid;
-            e = e->next_callee)
-         ;
-    }
-  gcc_assert (e->speculative && e2->speculative);
-  direct = e;
-  indirect = e2;
-
-  reference = NULL;
-  for (i = 0; e->caller->iterate_reference (i, ref); i++)
-    if (ref->speculative
-       && ((ref->stmt && ref->stmt == e->call_stmt)
-           || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
-      {
-       reference = ref;
-       break;
-      }
-
-  /* Speculative edge always consist of all three components - direct edge,
-     indirect and reference.  */
-  
-  gcc_assert (e && e2 && ref);
+    }
 }
 
-/* Speculative call edge turned out to be direct call to CALLE_DECL.
-   Remove the speculative call sequence and return edge representing the call.
-   It is up to caller to redirect the call as appropriate. */
+/* Speculative call EDGE turned out to be direct call to CALLEE_DECL.  Remove
+   the speculative call sequence and return edge representing the call, the
+   original EDGE can be removed and deallocated.  Return the edge that now
+   represents the call.
+
+   For "speculative" indirect call that contains multiple "speculative"
+   targets (i.e. edge->indirect_info->num_speculative_call_targets > 1),
+   decrease the count and only remove current direct edge.
+
+   If no speculative direct call left to the speculative indirect call, remove
+   the speculative of both the indirect call and corresponding direct edge.
+
+   It is up to caller to iteratively resolve each "speculative" direct call and
+   redirect the call as appropriate.  */
 
 cgraph_edge *
-cgraph_edge::resolve_speculation (tree callee_decl)
+cgraph_edge::resolve_speculation (cgraph_edge *edge, tree callee_decl)
 {
-  cgraph_edge *edge = this;
   cgraph_edge *e2;
   ipa_ref *ref;
 
-  gcc_assert (edge->speculative);
-  edge->speculative_call_info (e2, edge, ref);
+  gcc_assert (edge->speculative && (!callee_decl || edge->callee));
+  if (!edge->callee)
+    e2 = edge->first_speculative_call_target ();
+  else
+    e2 = edge;
+  ref = e2->speculative_call_target_ref ();
+  edge = edge->speculative_call_indirect_edge ();
   if (!callee_decl
       || !ref->referred->semantically_equivalent_p
           (symtab_node::get (callee_decl)))
@@ -1186,125 +1243,192 @@ cgraph_edge::resolve_speculation (tree callee_decl)
     {
       cgraph_edge *tmp = edge;
       if (dump_file)
-        fprintf (dump_file, "Speculative call turned into direct call.\n");
+       fprintf (dump_file, "Speculative call turned into direct call.\n");
       edge = e2;
       e2 = tmp;
-      /* FIXME:  If EDGE is inlined, we should scale up the frequencies and counts
-         in the functions inlined through it.  */
+      /* FIXME:  If EDGE is inlined, we should scale up the frequencies
+        and counts in the functions inlined through it.  */
     }
   edge->count += e2->count;
-  edge->speculative = false;
+  if (edge->num_speculative_call_targets_p ())
+    {
+      /* The indirect edge has multiple speculative targets, don't remove
+        speculative until all related direct edges are resolved.  */
+      edge->indirect_info->num_speculative_call_targets--;
+      if (!edge->indirect_info->num_speculative_call_targets)
+       edge->speculative = false;
+    }
+  else
+    edge->speculative = false;
   e2->speculative = false;
+  update_call_stmt_hash_for_removing_direct_edge (e2, edge);
   ref->remove_reference ();
   if (e2->indirect_unknown_callee || e2->inline_failed)
-    e2->remove ();
+    remove (e2);
   else
     e2->callee->remove_symbol_and_inline_clones ();
-  if (edge->caller->call_site_hash)
-    cgraph_update_edge_in_call_site_hash (edge);
   return edge;
 }
 
-/* Make an indirect edge with an unknown callee an ordinary edge leading to
-   CALLEE.  DELTA is an integer constant that is to be added to the this
-   pointer (first parameter) to compensate for skipping a thunk adjustment.  */
+/* Return edge corresponding to speculative call to a given target.
+   NULL if speculative call does not have one.  */
 
 cgraph_edge *
-cgraph_edge::make_direct (cgraph_node *callee)
+cgraph_edge::speculative_call_for_target (cgraph_node *target)
 {
-  cgraph_edge *edge = this;
-  gcc_assert (indirect_unknown_callee);
+  for (cgraph_edge *direct = first_speculative_call_target ();
+       direct;
+       direct = direct->next_speculative_call_target ())
+    if (direct->speculative_call_target_ref ()
+       ->referred->semantically_equivalent_p (target))
+      return direct;
+  return NULL;
+}
+
+/* Make an indirect or speculative EDGE with an unknown callee an ordinary edge
+   leading to CALLEE.  Speculations can be resolved in the process and EDGE can
+   be removed and deallocated.  Return the edge that now represents the
+   call.  */
+
+cgraph_edge *
+cgraph_edge::make_direct (cgraph_edge *edge, cgraph_node *callee)
+{
+  gcc_assert (edge->indirect_unknown_callee || edge->speculative);
 
   /* If we are redirecting speculative call, make it non-speculative.  */
-  if (indirect_unknown_callee && speculative)
+  if (edge->speculative)
     {
-      edge = edge->resolve_speculation (callee->decl);
+      cgraph_edge *found = NULL;
+      cgraph_edge *direct, *next;
+
+      edge = edge->speculative_call_indirect_edge ();
+
+      /* Look all speculative targets and remove all but one corresponding
+        to callee (if it exists).  */
+      for (direct = edge->first_speculative_call_target ();
+          direct;
+          direct = next)
+       {
+         next = direct->next_speculative_call_target ();
+
+         /* Compare ref not direct->callee.  Direct edge is possibly
+            inlined or redirected.  */
+         if (!direct->speculative_call_target_ref ()
+              ->referred->semantically_equivalent_p (callee))
+           edge = direct->resolve_speculation (direct, NULL);
+         else
+           {
+             gcc_checking_assert (!found);
+             found = direct;
+           }
+       }
 
-      /* On successful speculation just return the pre existing direct edge.  */
-      if (!indirect_unknown_callee)
-        return edge;
+      /* On successful speculation just remove the indirect edge and
+        return the pre existing direct edge.
+        It is important to not remove it and redirect because the direct
+        edge may be inlined or redirected.  */
+      if (found)
+       {
+         cgraph_edge *e2 = resolve_speculation (found, callee->decl);
+         gcc_checking_assert (!found->speculative && e2 == found);
+         return found;
+       }
+      gcc_checking_assert (!edge->speculative);
     }
 
-  indirect_unknown_callee = 0;
-  ggc_free (indirect_info);
-  indirect_info = NULL;
+  edge->indirect_unknown_callee = 0;
+  ggc_free (edge->indirect_info);
+  edge->indirect_info = NULL;
 
   /* Get the edge out of the indirect edge list. */
-  if (prev_callee)
-    prev_callee->next_callee = next_callee;
-  if (next_callee)
-    next_callee->prev_callee = prev_callee;
-  if (!prev_callee)
-    caller->indirect_calls = next_callee;
+  if (edge->prev_callee)
+    edge->prev_callee->next_callee = edge->next_callee;
+  if (edge->next_callee)
+    edge->next_callee->prev_callee = edge->prev_callee;
+  if (!edge->prev_callee)
+    edge->caller->indirect_calls = edge->next_callee;
 
   /* Put it into the normal callee list */
-  prev_callee = NULL;
-  next_callee = caller->callees;
-  if (caller->callees)
-    caller->callees->prev_callee = edge;
-  caller->callees = edge;
+  edge->prev_callee = NULL;
+  edge->next_callee = edge->caller->callees;
+  if (edge->caller->callees)
+    edge->caller->callees->prev_callee = edge;
+  edge->caller->callees = edge;
 
   /* Insert to callers list of the new callee.  */
   edge->set_callee (callee);
 
-  if (call_stmt
-      && !gimple_check_call_matching_types (call_stmt, callee->decl, false))
-    {
-      call_stmt_cannot_inline_p = true;
-      inline_failed = CIF_MISMATCHED_ARGUMENTS;
-    }
-
   /* We need to re-determine the inlining status of the edge.  */
   initialize_inline_failed (edge);
   return edge;
 }
 
+/* Redirect callee of the edge to N.  The function does not update underlying
+   call expression.  */
+
+void
+cgraph_edge::redirect_callee (cgraph_node *n)
+{
+  bool loc = callee->comdat_local_p ();
+  /* Remove from callers list of the current callee.  */
+  remove_callee ();
+
+  /* Insert to callers list of the new callee.  */
+  set_callee (n);
+
+  if (!inline_failed)
+    return;
+  if (!loc && n->comdat_local_p ())
+    {
+      cgraph_node *to = caller->inlined_to ? caller->inlined_to : caller;
+      to->calls_comdat_local = true;
+    }
+  else if (loc && !n->comdat_local_p ())
+    {
+      cgraph_node *to = caller->inlined_to ? caller->inlined_to : caller;
+      gcc_checking_assert (to->calls_comdat_local);
+      to->calls_comdat_local = to->check_calls_comdat_local_p ();
+    }
+}
+
 /* If necessary, change the function declaration in the call statement
-   associated with E so that it corresponds to the edge callee.  */
+   associated with E so that it corresponds to the edge callee.  Speculations
+   can be resolved in the process and EDGE can be removed and deallocated.
+
+   The edge could be one of speculative direct call generated from speculative
+   indirect call.  In this circumstance, decrease the speculative targets
+   count (i.e. num_speculative_call_targets) and redirect call stmt to the
+   corresponding i-th target.  If no speculative direct call left to the
+   speculative indirect call, remove "speculative" of the indirect call and
+   also redirect stmt to it's final direct target.
+
+   It is up to caller to iteratively transform each "speculative"
+   direct call as appropriate.  */
 
 gimple *
-cgraph_edge::redirect_call_stmt_to_callee (void)
+cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e)
 {
-  cgraph_edge *e = this;
-
   tree decl = gimple_call_fndecl (e->call_stmt);
   gcall *new_stmt;
   gimple_stmt_iterator gsi;
-  bool skip_bounds = false;
 
   if (e->speculative)
     {
-      cgraph_edge *e2;
-      gcall *new_stmt;
-      ipa_ref *ref;
-
-      e->speculative_call_info (e, e2, ref);
       /* If there already is an direct call (i.e. as a result of inliner's
         substitution), forget about speculating.  */
       if (decl)
-       e = e->resolve_speculation (decl);
-      /* If types do not match, speculation was likely wrong. 
-         The direct edge was possibly redirected to the clone with a different
-        signature.  We did not update the call statement yet, so compare it 
-        with the reference that still points to the proper type.  */
-      else if (!gimple_check_call_matching_types (e->call_stmt,
-                                                 ref->referred->decl,
-                                                 true))
-       {
-         if (dump_file)
-           fprintf (dump_file, "Not expanding speculative call of %s -> %s\n"
-                    "Type mismatch.\n",
-                    e->caller->dump_name (),
-                    e->callee->dump_name ());
-         e = e->resolve_speculation ();
-         /* We are producing the final function body and will throw away the
-            callgraph edges really soon.  Reset the counts/frequencies to
-            keep verifier happy in the case of roundoff errors.  */
-         e->count = gimple_bb (e->call_stmt)->count;
-       }
-      /* Expand speculation into GIMPLE code.  */
+       e = make_direct (e->speculative_call_indirect_edge (),
+                        cgraph_node::get (decl));
       else
        {
+         /* Be sure we redirect all speculative targets before poking
+            abou tindirect edge.  */
+         gcc_checking_assert (e->callee);
+         cgraph_edge *indirect = e->speculative_call_indirect_edge ();
+         gcall *new_stmt;
+         ipa_ref *ref;
+
+         /* Expand speculation into GIMPLE code.  */
          if (dump_file)
            {
              fprintf (dump_file,
@@ -1314,95 +1438,96 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
              e->count.dump (dump_file);
              fprintf (dump_file, "\n");
            }
-         gcc_assert (e2->speculative);
          push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
 
-         profile_probability prob = e->count.probability_in (e->count
-                                                             + e2->count);
+         profile_count all = indirect->count;
+         for (cgraph_edge *e2 = e->first_speculative_call_target ();
+              e2;
+              e2 = e2->next_speculative_call_target ())
+           all = all + e2->count;
+         profile_probability prob = e->count.probability_in (all);
          if (!prob.initialized_p ())
            prob = profile_probability::even ();
+         ref = e->speculative_call_target_ref ();
          new_stmt = gimple_ic (e->call_stmt,
                                dyn_cast<cgraph_node *> (ref->referred),
                                prob);
          e->speculative = false;
-         e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
-                                                    false);
-
-         /* Fix edges for BUILT_IN_CHKP_BNDRET calls attached to the
-            processed call stmt.  */
-         if (gimple_call_with_bounds_p (new_stmt)
-             && gimple_call_lhs (new_stmt)
-             && chkp_retbnd_call_by_val (gimple_call_lhs (e2->call_stmt)))
+         if (indirect->num_speculative_call_targets_p ())
            {
-             tree dresult = gimple_call_lhs (new_stmt);
-             tree iresult = gimple_call_lhs (e2->call_stmt);
-             gcall *dbndret = chkp_retbnd_call_by_val (dresult);
-             gcall *ibndret = chkp_retbnd_call_by_val (iresult);
-             struct cgraph_edge *iedge
-               = e2->caller->cgraph_node::get_edge (ibndret);
-
-             if (dbndret)
-               iedge->caller->create_edge (iedge->callee, dbndret, e->count);
+             /* The indirect edge has multiple speculative targets, don't
+                remove speculative until all related direct edges are
+                redirected.  */
+             indirect->indirect_info->num_speculative_call_targets--;
+             if (!indirect->indirect_info->num_speculative_call_targets)
+               indirect->speculative = false;
            }
+         else
+           indirect->speculative = false;
+         /* Indirect edges are not both in the call site hash.
+            get it updated.  */
+         update_call_stmt_hash_for_removing_direct_edge (e, indirect);
+         cgraph_edge::set_call_stmt (e, new_stmt, false);
+         e->count = gimple_bb (e->call_stmt)->count;
 
-         e2->speculative = false;
+         /* Once we are done with expanding the sequence, update also indirect
+            call probability.  Until then the basic block accounts for the
+            sum of indirect edge and all non-expanded speculations.  */
+         if (!indirect->speculative)
+           indirect->count = gimple_bb (indirect->call_stmt)->count;
          ref->speculative = false;
          ref->stmt = NULL;
-         /* Indirect edges are not both in the call site hash.
-            get it updated.  */
-         if (e->caller->call_site_hash)
-           cgraph_update_edge_in_call_site_hash (e2);
          pop_cfun ();
          /* Continue redirecting E to proper target.  */
        }
     }
 
-  /* We might propagate instrumented function pointer into
-     not instrumented function and vice versa.  In such a
-     case we need to either fix function declaration or
-     remove bounds from call statement.  */
-  if (flag_check_pointer_bounds && e->callee)
-    skip_bounds = chkp_redirect_edge (e);
 
   if (e->indirect_unknown_callee
-      || (decl == e->callee->decl
-         && !skip_bounds))
+      || decl == e->callee->decl)
     return e->call_stmt;
 
+  if (decl && ipa_saved_clone_sources)
+    {
+      tree *p = ipa_saved_clone_sources->get (e->callee);
+      if (p && decl == *p)
+       {
+         gimple_call_set_fndecl (e->call_stmt, e->callee->decl);
+         return e->call_stmt;
+       }
+    }
   if (flag_checking && decl)
     {
-      cgraph_node *node = cgraph_node::get (decl);
-      gcc_assert (!node || !node->clone.combined_args_to_skip);
+      if (cgraph_node *node = cgraph_node::get (decl))
+       {
+         clone_info *info = clone_info::get (node);
+         gcc_assert (!info || !info->param_adjustments);
+       }
     }
 
+  clone_info *callee_info = clone_info::get (e->callee);
   if (symtab->dump_file)
     {
       fprintf (symtab->dump_file, "updating call of %s -> %s: ",
               e->caller->dump_name (), e->callee->dump_name ());
       print_gimple_stmt (symtab->dump_file, e->call_stmt, 0, dump_flags);
-      if (e->callee->clone.combined_args_to_skip)
-       {
-         fprintf (symtab->dump_file, " combined args to skip: ");
-         dump_bitmap (symtab->dump_file,
-                      e->callee->clone.combined_args_to_skip);
-       }
+      if (callee_info && callee_info->param_adjustments)
+       callee_info->param_adjustments->dump (symtab->dump_file);
     }
 
-  if (e->callee->clone.combined_args_to_skip
-      || skip_bounds)
+  if (ipa_param_adjustments *padjs
+        = callee_info ? callee_info->param_adjustments : NULL)
     {
-      int lp_nr;
-
-      new_stmt = e->call_stmt;
-      if (e->callee->clone.combined_args_to_skip)
-       new_stmt
-         = gimple_call_copy_skip_args (new_stmt,
-                                       e->callee->clone.combined_args_to_skip);
-      if (skip_bounds)
-       new_stmt = chkp_copy_call_skip_bounds (new_stmt);
+      /* We need to defer cleaning EH info on the new statement to
+        fixup-cfg.  We may not have dominator information at this point
+        and thus would end up with unreachable blocks and have no way
+        to communicate that we need to run CFG cleanup then.  */
+      int lp_nr = lookup_stmt_eh_lp (e->call_stmt);
+      if (lp_nr != 0)
+       remove_stmt_from_eh_lp (e->call_stmt);
 
       tree old_fntype = gimple_call_fntype (e->call_stmt);
-      gimple_call_set_fndecl (new_stmt, e->callee->decl);
+      new_stmt = padjs->modify_call (e, false);
       cgraph_node *origin = e->callee;
       while (origin->clone_of)
        origin = origin->clone_of;
@@ -1413,95 +1538,18 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
        gimple_call_set_fntype (new_stmt, TREE_TYPE (e->callee->decl));
       else
        {
-         bitmap skip = e->callee->clone.combined_args_to_skip;
-         tree t = cgraph_build_function_type_skip_args (old_fntype, skip,
-                                                        false);
-         gimple_call_set_fntype (new_stmt, t);
-       }
-
-      if (gimple_vdef (new_stmt)
-         && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
-       SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
-
-      gsi = gsi_for_stmt (e->call_stmt);
-
-      /* For optimized away parameters, add on the caller side
-        before the call
-        DEBUG D#X => parm_Y(D)
-        stmts and associate D#X with parm in decl_debug_args_lookup
-        vector to say for debug info that if parameter parm had been passed,
-        it would have value parm_Y(D).  */
-      if (e->callee->clone.combined_args_to_skip && MAY_HAVE_DEBUG_BIND_STMTS)
-       {
-         vec<tree, va_gc> **debug_args
-           = decl_debug_args_lookup (e->callee->decl);
-         tree old_decl = gimple_call_fndecl (e->call_stmt);
-         if (debug_args && old_decl)
-           {
-             tree parm;
-             unsigned i = 0, num;
-             unsigned len = vec_safe_length (*debug_args);
-             unsigned nargs = gimple_call_num_args (e->call_stmt);
-             for (parm = DECL_ARGUMENTS (old_decl), num = 0;
-                  parm && num < nargs;
-                  parm = DECL_CHAIN (parm), num++)
-               if (bitmap_bit_p (e->callee->clone.combined_args_to_skip, num)
-                   && is_gimple_reg (parm))
-                 {
-                   unsigned last = i;
-
-                   while (i < len && (**debug_args)[i] != DECL_ORIGIN (parm))
-                     i += 2;
-                   if (i >= len)
-                     {
-                       i = 0;
-                       while (i < last
-                              && (**debug_args)[i] != DECL_ORIGIN (parm))
-                         i += 2;
-                       if (i >= last)
-                         continue;
-                     }
-                   tree ddecl = (**debug_args)[i + 1];
-                   tree arg = gimple_call_arg (e->call_stmt, num);
-                   if (!useless_type_conversion_p (TREE_TYPE (ddecl),
-                                                   TREE_TYPE (arg)))
-                     {
-                       tree rhs1;
-                       if (!fold_convertible_p (TREE_TYPE (ddecl), arg))
-                         continue;
-                       if (TREE_CODE (arg) == SSA_NAME
-                           && gimple_assign_cast_p (SSA_NAME_DEF_STMT (arg))
-                           && (rhs1
-                               = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (arg)))
-                           && useless_type_conversion_p (TREE_TYPE (ddecl),
-                                                         TREE_TYPE (rhs1)))
-                         arg = rhs1;
-                       else
-                         arg = fold_convert (TREE_TYPE (ddecl), arg);
-                     }
-
-                   gimple *def_temp
-                     = gimple_build_debug_bind (ddecl, unshare_expr (arg),
-                                                e->call_stmt);
-                   gsi_insert_before (&gsi, def_temp, GSI_SAME_STMT);
-                 }
-           }
+         tree new_fntype = padjs->build_new_function_type (old_fntype, true);
+         gimple_call_set_fntype (new_stmt, new_fntype);
        }
 
-      gsi_replace (&gsi, new_stmt, false);
-      /* We need to defer cleaning EH info on the new statement to
-         fixup-cfg.  We may not have dominator information at this point
-        and thus would end up with unreachable blocks and have no way
-        to communicate that we need to run CFG cleanup then.  */
-      lp_nr = lookup_stmt_eh_lp (e->call_stmt);
       if (lp_nr != 0)
-       {
-         remove_stmt_from_eh_lp (e->call_stmt);
-         add_stmt_to_eh_lp (new_stmt, lp_nr);
-       }
+       add_stmt_to_eh_lp (new_stmt, lp_nr);
     }
   else
     {
+      if (flag_checking
+         && !fndecl_built_in_p (e->callee->decl, BUILT_IN_UNREACHABLE))
+       ipa_verify_edge_has_no_modifications (e);
       new_stmt = e->call_stmt;
       gimple_call_set_fndecl (new_stmt, e->callee->decl);
       update_stmt_fn (DECL_STRUCT_FUNCTION (e->caller->decl), new_stmt);
@@ -1530,7 +1578,7 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
          var = get_or_create_ssa_default_def
                  (DECL_STRUCT_FUNCTION (e->caller->decl), var);
          gimple *set_stmt = gimple_build_assign (lhs, var);
-          gsi = gsi_for_stmt (new_stmt);
+         gsi = gsi_for_stmt (new_stmt);
          gsi_insert_before_without_update (&gsi, set_stmt, GSI_SAME_STMT);
          update_stmt_fn (DECL_STRUCT_FUNCTION (e->caller->decl), set_stmt);
        }
@@ -1587,11 +1635,10 @@ cgraph_update_edges_for_call_stmt_node (cgraph_node *node,
        {
          /* Keep calls marked as dead dead.  */
          if (new_stmt && is_gimple_call (new_stmt) && e->callee
-             && DECL_BUILT_IN_CLASS (e->callee->decl) == BUILT_IN_NORMAL
-             && DECL_FUNCTION_CODE (e->callee->decl) == BUILT_IN_UNREACHABLE)
+             && fndecl_built_in_p (e->callee->decl, BUILT_IN_UNREACHABLE))
            {
-              node->get_edge (old_stmt)->set_call_stmt
-                (as_a <gcall *> (new_stmt));
+             cgraph_edge::set_call_stmt (node->get_edge (old_stmt),
+                                         as_a <gcall *> (new_stmt));
              return;
            }
          /* See if the edge is already there and has the correct callee.  It
@@ -1605,7 +1652,7 @@ cgraph_update_edges_for_call_stmt_node (cgraph_node *node,
                  if (callee->decl == new_call
                      || callee->former_clone_of == new_call)
                    {
-                     e->set_call_stmt (as_a <gcall *> (new_stmt));
+                     cgraph_edge::set_call_stmt (e, as_a <gcall *> (new_stmt));
                      return;
                    }
                  callee = callee->clone_of;
@@ -1617,7 +1664,7 @@ cgraph_update_edges_for_call_stmt_node (cgraph_node *node,
             attached to edge is invalid.  */
          count = e->count;
          if (e->indirect_unknown_callee || e->inline_failed)
-           e->remove ();
+           cgraph_edge::remove (e);
          else
            e->callee->remove_symbol_and_inline_clones ();
        }
@@ -1637,7 +1684,8 @@ cgraph_update_edges_for_call_stmt_node (cgraph_node *node,
     }
   /* We only updated the call stmt; update pointer in cgraph edge..  */
   else if (old_stmt != new_stmt)
-    node->get_edge (old_stmt)->set_call_stmt (as_a <gcall *> (new_stmt));
+    cgraph_edge::set_call_stmt (node->get_edge (old_stmt),
+                               as_a <gcall *> (new_stmt));
 }
 
 /* Update or remove the corresponding cgraph edge if a GIMPLE_CALL
@@ -1656,7 +1704,8 @@ cgraph_update_edges_for_call_stmt (gimple *old_stmt, tree old_decl,
   if (orig->clones)
     for (node = orig->clones; node != orig;)
       {
-        cgraph_update_edges_for_call_stmt_node (node, old_stmt, old_decl, new_stmt);
+       cgraph_update_edges_for_call_stmt_node (node, old_stmt, old_decl,
+                                               new_stmt);
        if (node->clones)
          node = node->clones;
        else if (node->next_sibling_clone)
@@ -1679,6 +1728,8 @@ cgraph_node::remove_callees (void)
 {
   cgraph_edge *e, *f;
 
+  calls_comdat_local = false;
+
   /* It is sufficient to remove the edges from the lists of callers of
      the callees.  The callee list of the node can be zapped with one
      assignment.  */
@@ -1753,14 +1804,14 @@ release_function_body (tree decl)
          gcc_assert (!dom_info_available_p (fn, CDI_DOMINATORS));
          gcc_assert (!dom_info_available_p (fn, CDI_POST_DOMINATORS));
          delete_tree_cfg_annotations (fn);
-         clear_edges (fn);
+         free_cfg (fn);
          fn->cfg = NULL;
        }
       if (fn->value_histograms)
        free_histograms (fn);
       gimple_set_body (decl, NULL);
       /* Struct function hangs a lot of data that would leak if we didn't
-         removed all pointers to it.   */
+        removed all pointers to it.   */
       ggc_free (fn);
       DECL_STRUCT_FUNCTION (decl) = NULL;
     }
@@ -1795,6 +1846,17 @@ cgraph_node::release_body (bool keep_arguments)
       lto_free_function_in_decl_state_for_node (this);
       lto_file_data = NULL;
     }
+  if (flag_checking && clones)
+    {
+      /* It is invalid to release body before materializing clones except
+        for thunks that don't really need a body.  Verify also that we do
+        not leak pointers to the call statements.  */
+      for (cgraph_node *node = clones; node;
+          node = node->next_sibling_clone)
+       gcc_assert (node->thunk && !node->callees->call_stmt);
+    }
+  remove_callees ();
+  remove_all_references ();
 }
 
 /* Remove function from symbol table.  */
@@ -1802,15 +1864,19 @@ cgraph_node::release_body (bool keep_arguments)
 void
 cgraph_node::remove (void)
 {
-  cgraph_node *n;
-  int uid = this->uid;
-
+  bool clone_info_set = false;
+  clone_info *info, saved_info;
   if (symtab->ipa_clones_dump_file && symtab->cloned_nodes.contains (this))
     fprintf (symtab->ipa_clones_dump_file,
             "Callgraph removal;%s;%d;%s;%d;%d\n", asm_name (), order,
             DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl),
             DECL_SOURCE_COLUMN (decl));
 
+  if ((info = clone_info::get (this)) != NULL)
+    {
+      saved_info = *info;
+      clone_info_set = true;
+    }
   symtab->call_cgraph_removal_hooks (this);
   remove_callers ();
   remove_callees ();
@@ -1821,18 +1887,8 @@ cgraph_node::remove (void)
      */
   force_output = false;
   forced_by_abi = false;
-  for (n = nested; n; n = n->next_nested)
-    n->origin = NULL;
-  nested = NULL;
-  if (origin)
-    {
-      cgraph_node **node2 = &origin->nested;
 
-      while (*node2 != this)
-       node2 = &(*node2)->next_nested;
-      *node2 = next_nested;
-    }
-  unregister ();
+  unregister (clone_info_set ? &saved_info : NULL);
   if (prev_sibling_clone)
     prev_sibling_clone->next_sibling_clone = next_sibling_clone;
   else if (clone_of)
@@ -1844,7 +1900,7 @@ cgraph_node::remove (void)
       cgraph_node *n, *next;
 
       if (clone_of)
-        {
+       {
          for (n = clones; n->next_sibling_clone; n = n->next_sibling_clone)
            n->clone_of = clone_of;
          n->clone_of = clone_of;
@@ -1854,7 +1910,7 @@ cgraph_node::remove (void)
          clone_of->clones = clones;
        }
       else
-        {
+       {
          /* We are removing node with clones.  This makes clones inconsistent,
             but assume they will be removed subsequently and just keep clone
             tree intact.  This can happen in unreachable function removal since
@@ -1876,9 +1932,9 @@ cgraph_node::remove (void)
      */
   if (symtab->state != LTO_STREAMING)
     {
-      n = cgraph_node::get (decl);
+      cgraph_node *n = cgraph_node::get (decl);
       if (!n
-         || (!n->clones && !n->clone_of && !n->global.inlined_to
+         || (!n->clones && !n->clone_of && !n->inlined_to
              && ((symtab->global_info_ready || in_lto_p)
                  && (TREE_ASM_WRITTEN (n->decl)
                      || DECL_EXTERNAL (n->decl)
@@ -1899,13 +1955,7 @@ cgraph_node::remove (void)
       call_site_hash = NULL;
     }
 
-  if (instrumented_version)
-    {
-      instrumented_version->instrumented_version = NULL;
-      instrumented_version = NULL;
-    }
-
-  symtab->release_symbol (this, uid);
+  symtab->release_symbol (this);
 }
 
 /* Likewise indicate that a node is having address taken.  */
@@ -1915,7 +1965,7 @@ cgraph_node::mark_address_taken (void)
 {
   /* Indirect inlining can figure out that all uses of the address are
      inlined.  */
-  if (global.inlined_to)
+  if (inlined_to)
     {
       gcc_assert (cfun->after_inlining);
       gcc_assert (callers->indirect_inlining_edge);
@@ -1932,22 +1982,22 @@ cgraph_node::mark_address_taken (void)
   node->address_taken = 1;
 }
 
-/* Return local info for the compiled function.  */
+/* Return local info node for the compiled function.  */
 
-cgraph_local_info *
-cgraph_node::local_info (tree decl)
+cgraph_node *
+cgraph_node::local_info_node (tree decl)
 {
   gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
   cgraph_node *node = get (decl);
   if (!node)
     return NULL;
-  return &node->ultimate_alias_target ()->local;
+  return node->ultimate_alias_target ();
 }
 
-/* Return local info for the compiled function.  */
+/* Return RTL info for the compiled function.  */
 
 cgraph_rtl_info *
-cgraph_node::rtl_info (tree decl)
+cgraph_node::rtl_info (const_tree decl)
 {
   gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
   cgraph_node *node = get (decl);
@@ -1962,7 +2012,10 @@ cgraph_node::rtl_info (tree decl)
     return NULL;
   /* Allocate if it doesn't exist.  */
   if (node->rtl == NULL)
-    node->rtl = ggc_cleared_alloc<cgraph_rtl_info> ();
+    {
+      node->rtl = ggc_cleared_alloc<cgraph_rtl_info> ();
+      SET_HARD_REG_SET (node->rtl->function_used_regs);
+    }
   return node->rtl;
 }
 
@@ -2030,6 +2083,20 @@ cgraph_edge::dump_edge_flags (FILE *f)
     fprintf (f, "(can throw external) ");
 }
 
+/* Dump edge to stderr.  */
+
+void
+cgraph_edge::debug (void)
+{
+  fprintf (stderr, "%s -> %s ", caller->dump_asm_name (),
+          callee == NULL ? "(null)" : callee->dump_asm_name ());
+  dump_edge_flags (stderr);
+  fprintf (stderr, "\n\n");
+  caller->debug ();
+  if (callee != NULL)
+    callee->debug ();
+}
+
 /* Dump call graph node to file F.  */
 
 void
@@ -2039,10 +2106,10 @@ cgraph_node::dump (FILE *f)
 
   dump_base (f);
 
-  if (global.inlined_to)
+  if (inlined_to)
     fprintf (f, "  Function %s is inline copy in %s\n",
             dump_name (),
-            global.inlined_to->dump_name ());
+            inlined_to->dump_name ());
   if (clone_of)
     fprintf (f, "  Clone of %s\n", clone_of->dump_asm_name ());
   if (symtab->function_flags_ready)
@@ -2052,7 +2119,9 @@ cgraph_node::dump (FILE *f)
   if (profile_id)
     fprintf (f, "  Profile id: %i\n",
             profile_id);
-  fprintf (f, "  First run: %i\n", tp_first_run);
+  if (unit_id)
+    fprintf (f, "  Unit id: %i\n",
+            unit_id);
   cgraph_function_version_info *vi = function_version ();
   if (vi != NULL)
     {
@@ -2076,18 +2145,20 @@ cgraph_node::dump (FILE *f)
   fprintf (f, "  Function flags:");
   if (count.initialized_p ())
     {
-      fprintf (f, " count: ");
+      fprintf (f, " count:");
       count.dump (f);
     }
-  if (origin)
-    fprintf (f, " nested in: %s", origin->asm_name ());
+  if (tp_first_run > 0)
+    fprintf (f, " first_run:%" PRId64, (int64_t) tp_first_run);
+  if (cgraph_node *origin = nested_function_origin (this))
+    fprintf (f, " nested in:%s", origin->dump_asm_name ());
   if (gimple_has_body_p (decl))
     fprintf (f, " body");
   if (process)
     fprintf (f, " process");
-  if (local.local)
+  if (local)
     fprintf (f, " local");
-  if (local.redefined_extern_inline)
+  if (redefined_extern_inline)
     fprintf (f, " redefined_extern_inline");
   if (only_called_at_startup)
     fprintf (f, " only_called_at_startup");
@@ -2101,6 +2172,8 @@ cgraph_node::dump (FILE *f)
     fprintf (f, " icf_merged");
   if (merged_comdat)
     fprintf (f, " merged_comdat");
+  if (merged_extern_inline)
+    fprintf (f, " merged_extern_inline");
   if (split_part)
     fprintf (f, " split_part");
   if (indirect_call_target)
@@ -2117,47 +2190,39 @@ cgraph_node::dump (FILE *f)
     fprintf (f, " unlikely_executed");
   if (frequency == NODE_FREQUENCY_EXECUTED_ONCE)
     fprintf (f, " executed_once");
-  if (only_called_at_startup)
-    fprintf (f, " only_called_at_startup");
-  if (only_called_at_exit)
-    fprintf (f, " only_called_at_exit");
   if (opt_for_fn (decl, optimize_size))
     fprintf (f, " optimize_size");
   if (parallelized_function)
     fprintf (f, " parallelized_function");
+  if (DECL_IS_MALLOC (decl))
+    fprintf (f, " decl_is_malloc");
+  if (DECL_IS_OPERATOR_NEW_P (decl))
+    fprintf (f, " %soperator_new",
+            DECL_IS_REPLACEABLE_OPERATOR (decl) ? "replaceable_" : "");
+  if (DECL_IS_OPERATOR_DELETE_P (decl))
+    fprintf (f, " %soperator_delete",
+            DECL_IS_REPLACEABLE_OPERATOR (decl) ? "replaceable_" : "");
 
   fprintf (f, "\n");
 
-  if (thunk.thunk_p)
+  if (thunk)
     {
       fprintf (f, "  Thunk");
-      if (thunk.alias)
-        fprintf (f, "  of %s (asm: %s)",
-                lang_hooks.decl_printable_name (thunk.alias, 2),
-                IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk.alias)));
-      fprintf (f, " fixed offset %i virtual value %i has "
-              "virtual offset %i)\n",
-              (int)thunk.fixed_offset,
-              (int)thunk.virtual_value,
-              (int)thunk.virtual_offset_p);
-    }
-  if (alias && thunk.alias
-      && DECL_P (thunk.alias))
-    {
-      fprintf (f, "  Alias of %s",
-              lang_hooks.decl_printable_name (thunk.alias, 2));
-      if (DECL_ASSEMBLER_NAME_SET_P (thunk.alias))
-        fprintf (f, " (asm: %s)",
-                IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk.alias)));
-      fprintf (f, "\n");
+      thunk_info::get (this)->dump (f);
+    }
+  else if (former_thunk_p ())
+    {
+      fprintf (f, "  Former thunk ");
+      thunk_info::get (this)->dump (f);
     }
+  else gcc_checking_assert (!thunk_info::get (this));
   
   fprintf (f, "  Called by: ");
 
   profile_count sum = profile_count::zero ();
   for (edge = callers; edge; edge = edge->next_caller)
     {
-      fprintf (f, "%s ", edge->caller->dump_name ());
+      fprintf (f, "%s ", edge->caller->dump_asm_name ());
       edge->dump_edge_flags (f);
       if (edge->count.initialized_p ())
        sum += edge->count.ipa ();
@@ -2166,12 +2231,12 @@ cgraph_node::dump (FILE *f)
   fprintf (f, "\n  Calls: ");
   for (edge = callees; edge; edge = edge->next_callee)
     {
-      fprintf (f, "%s ", edge->callee->dump_name ());
+      fprintf (f, "%s ", edge->callee->dump_asm_name ());
       edge->dump_edge_flags (f);
     }
   fprintf (f, "\n");
 
-  if (count.ipa ().initialized_p ())
+  if (!body_removed && count.ipa ().initialized_p ())
     {
       bool ok = true;
       bool min = false;
@@ -2180,8 +2245,8 @@ cgraph_node::dump (FILE *f)
       FOR_EACH_ALIAS (this, ref)
        if (dyn_cast <cgraph_node *> (ref->referring)->count.initialized_p ())
          sum += dyn_cast <cgraph_node *> (ref->referring)->count.ipa ();
-  
-      if (global.inlined_to
+
+      if (inlined_to
          || (symtab->state < EXPANSION
              && ultimate_alias_target () == this && only_called_directly_p ()))
        ok = !count.ipa ().differs_from_p (sum);
@@ -2205,35 +2270,47 @@ cgraph_node::dump (FILE *f)
     {
       if (edge->indirect_info->polymorphic)
        {
-          fprintf (f, "   Polymorphic indirect call of type ");
+         fprintf (f, "   Polymorphic indirect call of type ");
          print_generic_expr (f, edge->indirect_info->otr_type, TDF_SLIM);
          fprintf (f, " token:%i", (int) edge->indirect_info->otr_token);
        }
       else
-        fprintf (f, "   Indirect call");
+       fprintf (f, "   Indirect call");
       edge->dump_edge_flags (f);
       if (edge->indirect_info->param_index != -1)
        {
-         fprintf (f, " of param:%i", edge->indirect_info->param_index);
+         fprintf (f, "of param:%i ", edge->indirect_info->param_index);
          if (edge->indirect_info->agg_contents)
-          fprintf (f, " loaded from %s %s at offset %i",
+          fprintf (f, "loaded from %s %s at offset %i ",
                    edge->indirect_info->member_ptr ? "member ptr" : "aggregate",
                    edge->indirect_info->by_ref ? "passed by reference":"",
                    (int)edge->indirect_info->offset);
          if (edge->indirect_info->vptr_changed)
-           fprintf (f, " (vptr maybe changed)");
+           fprintf (f, "(vptr maybe changed) ");
        }
-      fprintf (f, "\n");
+      fprintf (f, "num speculative call targets: %i\n",
+              edge->indirect_info->num_speculative_call_targets);
       if (edge->indirect_info->polymorphic)
        edge->indirect_info->context.dump (f);
     }
+}
+
+/* Dump call graph node to file F in graphviz format.  */
+
+void
+cgraph_node::dump_graphviz (FILE *f)
+{
+  cgraph_edge *edge;
+
+  for (edge = callees; edge; edge = edge->next_callee)
+    {
+      cgraph_node *callee = edge->callee;
 
-  if (instrumentation_clone)
-    fprintf (f, "  Is instrumented version.\n");
-  else if (instrumented_version)
-    fprintf (f, "  Has instrumented version.\n");
+      fprintf (f, "\t\"%s\" -> \"%s\"\n", dump_name (), callee->dump_name ());
+    }
 }
 
+
 /* Dump call graph node NODE to stderr.  */
 
 DEBUG_FUNCTION void
@@ -2264,19 +2341,6 @@ cgraph_function_possibly_inlined_p (tree decl)
   return DECL_POSSIBLY_INLINED (decl);
 }
 
-/* cgraph_node is no longer nested function; update cgraph accordingly.  */
-void
-cgraph_node::unnest (void)
-{
-  cgraph_node **node2 = &origin->nested;
-  gcc_assert (origin);
-
-  while (*node2 != this)
-    node2 = &(*node2)->next_nested;
-  *node2 = next_nested;
-  origin = NULL;
-}
-
 /* Return function availability.  See cgraph.h for description of individual
    return values.  */
 enum availability
@@ -2286,18 +2350,18 @@ cgraph_node::get_availability (symtab_node *ref)
     {
       cgraph_node *cref = dyn_cast <cgraph_node *> (ref);
       if (cref)
-       ref = cref->global.inlined_to;
+       ref = cref->inlined_to;
     }
   enum availability avail;
-  if (!analyzed)
+  if (!analyzed && !in_other_partition)
     avail = AVAIL_NOT_AVAILABLE;
-  else if (local.local)
+  else if (local)
     avail = AVAIL_LOCAL;
-  else if (global.inlined_to)
+  else if (inlined_to)
     avail = AVAIL_AVAILABLE;
   else if (transparent_alias)
     ultimate_alias_target (&avail, ref);
-  else if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (decl))
+  else if (ifunc_resolver
           || lookup_attribute ("noipa", DECL_ATTRIBUTES (decl)))
     avail = AVAIL_INTERPOSABLE;
   else if (!externally_visible)
@@ -2308,8 +2372,8 @@ cgraph_node::get_availability (symtab_node *ref)
 
      Also comdat groups are always resolved in groups.  */
   else if ((this == ref && !has_aliases_p ())
-           || (ref && get_comdat_group ()
-               && get_comdat_group () == ref->get_comdat_group ()))
+          || (ref && get_comdat_group ()
+              && get_comdat_group () == ref->get_comdat_group ()))
     avail = AVAIL_AVAILABLE;
   /* Inline functions are safe to be analyzed even if their symbol can
      be overwritten at runtime.  It is not meaningful to enforce any sane
@@ -2335,6 +2399,10 @@ static bool
 cgraph_node_cannot_be_local_p_1 (cgraph_node *node, void *)
 {
   return !(!node->force_output
+          && !node->ifunc_resolver
+          /* Limitation of gas requires us to output targets of symver aliases
+             as global symbols.  This is binutils PR 25295.  */
+          && !node->symver
           && ((DECL_COMDAT (node->decl)
                && !node->forced_by_abi
                && !node->used_from_object_file_p ()
@@ -2373,7 +2441,7 @@ cgraph_node::call_for_symbol_thunks_and_aliases (bool (*callback)
       || (avail = get_availability ()) > AVAIL_INTERPOSABLE)
     {
       if (callback (this, data))
-        return true;
+       return true;
     }
   FOR_EACH_ALIAS (this, ref)
     {
@@ -2388,11 +2456,11 @@ cgraph_node::call_for_symbol_thunks_and_aliases (bool (*callback)
   if (avail <= AVAIL_INTERPOSABLE)
     return false;
   for (e = callers; e; e = e->next_caller)
-    if (e->caller->thunk.thunk_p
+    if (e->caller->thunk
        && (include_overwritable
            || e->caller->get_availability () > AVAIL_INTERPOSABLE)
        && !(exclude_virtual_thunks
-            && e->caller->thunk.virtual_offset_p))
+            && thunk_info::get (e->caller)->virtual_offset_p))
       if (e->caller->call_for_symbol_thunks_and_aliases (callback, data,
                                                       include_overwritable,
                                                       exclude_virtual_thunks))
@@ -2414,7 +2482,7 @@ cgraph_node::make_local (cgraph_node *node, void *)
       node->set_comdat_group (NULL);
       node->externally_visible = false;
       node->forced_by_abi = false;
-      node->local.local = true;
+      node->local = true;
       node->set_section (NULL);
       node->unique_name = ((node->resolution == LDPR_PREVAILING_DEF_IRONLY
                           || node->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)
@@ -2444,7 +2512,7 @@ set_nothrow_flag_1 (cgraph_node *node, bool nothrow, bool non_call,
   if (nothrow && !TREE_NOTHROW (node->decl))
     {
       /* With non-call exceptions we can't say for sure if other function body
-        was not possibly optimized to stil throw.  */
+        was not possibly optimized to still throw.  */
       if (!non_call || node->binds_to_current_def_p ())
        {
          TREE_NOTHROW (node->decl) = true;
@@ -2466,7 +2534,7 @@ set_nothrow_flag_1 (cgraph_node *node, bool nothrow, bool non_call,
        set_nothrow_flag_1 (alias, nothrow, non_call, changed);
     }
   for (cgraph_edge *e = node->callers; e; e = e->next_caller)
-    if (e->caller->thunk.thunk_p
+    if (e->caller->thunk
        && (!nothrow || e->caller->get_availability () > AVAIL_INTERPOSABLE))
       set_nothrow_flag_1 (e->caller, nothrow, non_call, changed);
 }
@@ -2515,7 +2583,7 @@ set_malloc_flag_1 (cgraph_node *node, bool malloc_p, bool *changed)
     }
 
   for (cgraph_edge *e = node->callers; e; e = e->next_caller)
-    if (e->caller->thunk.thunk_p
+    if (e->caller->thunk
        && (!malloc_p || e->caller->get_availability () > AVAIL_INTERPOSABLE))
       set_malloc_flag_1 (e->caller, malloc_p, changed);
 }
@@ -2568,8 +2636,8 @@ set_const_flag_1 (cgraph_node *node, bool set_const, bool looping,
     {
       if (TREE_READONLY (node->decl))
        {
-          TREE_READONLY (node->decl) = 0;
-          DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
+         TREE_READONLY (node->decl) = 0;
+         DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
          *changed = true;
        }
     }
@@ -2596,14 +2664,14 @@ set_const_flag_1 (cgraph_node *node, bool set_const, bool looping,
        {
          if (!looping && DECL_LOOPING_CONST_OR_PURE_P (node->decl))
            {
-              DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
+             DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
              *changed = true;
            }
        }
       else if (node->binds_to_current_def_p ())
        {
          TREE_READONLY (node->decl) = true;
-          DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping;
+         DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping;
          DECL_PURE_P (node->decl) = false;
          *changed = true;
        }
@@ -2615,12 +2683,12 @@ set_const_flag_1 (cgraph_node *node, bool set_const, bool looping,
          if (!DECL_PURE_P (node->decl))
            {
              DECL_PURE_P (node->decl) = true;
-              DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping;
+             DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping;
              *changed = true;
            }
          else if (!looping && DECL_LOOPING_CONST_OR_PURE_P (node->decl))
            {
-              DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
+             DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
              *changed = true;
            }
        }
@@ -2634,14 +2702,14 @@ set_const_flag_1 (cgraph_node *node, bool set_const, bool looping,
        set_const_flag_1 (alias, set_const, looping, changed);
     }
   for (cgraph_edge *e = node->callers; e; e = e->next_caller)
-    if (e->caller->thunk.thunk_p
+    if (e->caller->thunk
        && (!set_const || e->caller->get_availability () > AVAIL_INTERPOSABLE))
       {
        /* Virtual thunks access virtual offset in the vtable, so they can
           only be pure, never const.  */
-        if (set_const
-           && (e->caller->thunk.virtual_offset_p
-               || !node->binds_to_current_def_p (e->caller)))
+       if (set_const
+           && (thunk_info::get (e->caller)->virtual_offset_p
+               || !node->binds_to_current_def_p (e->caller)))
          *changed |= e->caller->set_pure_flag (true, looping);
        else
          set_const_flag_1 (e->caller, set_const, looping, changed);
@@ -2652,7 +2720,7 @@ set_const_flag_1 (cgraph_node *node, bool set_const, bool looping,
    If SET_CONST if false, clear the flag.
 
    When setting the flag be careful about possible interposition and
-   do not set the flag for functions that can be interposet and set pure
+   do not set the flag for functions that can be interposed and set pure
    flag for functions that can bind to other definition. 
 
    Return true if any change was done. */
@@ -2711,14 +2779,14 @@ set_pure_flag_1 (cgraph_node *node, void *data)
     {
       if (!DECL_PURE_P (node->decl) && !TREE_READONLY (node->decl))
        {
-          DECL_PURE_P (node->decl) = true;
-          DECL_LOOPING_CONST_OR_PURE_P (node->decl) = info->looping;
+         DECL_PURE_P (node->decl) = true;
+         DECL_LOOPING_CONST_OR_PURE_P (node->decl) = info->looping;
          info->changed = true;
        }
       else if (DECL_LOOPING_CONST_OR_PURE_P (node->decl)
               && !info->looping)
        {
-          DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
+         DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
          info->changed = true;
        }
     }
@@ -2726,8 +2794,8 @@ set_pure_flag_1 (cgraph_node *node, void *data)
     {
       if (DECL_PURE_P (node->decl))
        {
-          DECL_PURE_P (node->decl) = false;
-          DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
+         DECL_PURE_P (node->decl) = false;
+         DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
          info->changed = true;
        }
     }
@@ -2744,13 +2812,11 @@ bool
 cgraph_node::set_pure_flag (bool pure, bool looping)
 {
   struct set_pure_flag_info info = {pure, looping, false};
-  if (!pure)
-    looping = false;
   call_for_symbol_thunks_and_aliases (set_pure_flag_1, &info, !pure, true);
   return info.changed;
 }
 
-/* Return true when cgraph_node can not return or throw and thus
+/* Return true when cgraph_node cannot return or throw and thus
    it is safe to ignore its side effects for IPA analysis.  */
 
 bool
@@ -2764,7 +2830,7 @@ cgraph_node::cannot_return_p (void)
             == (ECF_NORETURN | ECF_NOTHROW));
 }
 
-/* Return true when call of edge can not lead to return from caller
+/* Return true when call of edge cannot lead to return from caller
    and thus it is safe to ignore its side effects for IPA analysis
    when computing side effects of the caller.
    FIXME: We could actually mark all edges that have no reaching
@@ -2787,7 +2853,7 @@ cgraph_edge::cannot_lead_to_return_p (void)
     return callee->cannot_return_p ();
 }
 
-/* Return true if the call can be hot.  */
+/* Return true if the edge may be considered hot.  */
 
 bool
 cgraph_edge::maybe_hot_p (void)
@@ -2806,15 +2872,18 @@ cgraph_edge::maybe_hot_p (void)
     return false;
   if (caller->frequency == NODE_FREQUENCY_HOT)
     return true;
-  /* If profile is now known yet, be conservative.
-     FIXME: this predicate is used by early inliner and can do better there.  */
-  if (symtab->state < IPA_SSA)
+  if (!count.initialized_p ())
     return true;
-  if (caller->frequency == NODE_FREQUENCY_EXECUTED_ONCE
-      && sreal_frequency () * 2 < 3)
+  cgraph_node *where = caller->inlined_to ? caller->inlined_to : caller;
+  if (!where->count.initialized_p ())
     return false;
-  if (PARAM_VALUE (HOT_BB_FREQUENCY_FRACTION) == 0
-      || sreal_frequency () * PARAM_VALUE (HOT_BB_FREQUENCY_FRACTION) <= 1)
+  if (caller->frequency == NODE_FREQUENCY_EXECUTED_ONCE)
+    {
+      if (count.apply_scale (2, 1) < where->count.apply_scale (3, 1))
+       return false;
+    }
+  else if (count.apply_scale (param_hot_bb_frequency_fraction , 1)
+          < where->count)
     return false;
   return true;
 }
@@ -2849,7 +2918,7 @@ cgraph_node::can_remove_if_no_direct_calls_p (bool will_inline)
   if (will_inline && address_taken)
     return false;
 
-  /* Otheriwse check if we can remove the symbol itself and then verify
+  /* Otherwise check if we can remove the symbol itself and then verify
      that only uses of the comdat groups are direct call to THIS
      or its aliases.   */
   if (!can_remove_if_no_direct_calls_and_refs_p ())
@@ -2880,7 +2949,7 @@ cgraph_node::can_remove_if_no_direct_calls_p (bool will_inline)
       /* If function is not being inlined, we care only about
         references outside of the comdat group.  */
       if (!will_inline)
-        for (int i = 0; next->iterate_referring (i, ref); i++)
+       for (int i = 0; next->iterate_referring (i, ref); i++)
          if (ref->referring->get_comdat_group () != get_comdat_group ())
            return false;
     }
@@ -2905,7 +2974,7 @@ bool
 cgraph_node::will_be_removed_from_program_if_no_direct_calls_p
         (bool will_inline)
 {
-  gcc_assert (!global.inlined_to);
+  gcc_assert (!inlined_to);
   if (DECL_EXTERNAL (decl))
     return true;
 
@@ -2983,59 +3052,77 @@ collect_callers_of_node_1 (cgraph_node *node, void *data)
   if (avail > AVAIL_INTERPOSABLE)
     for (cs = node->callers; cs != NULL; cs = cs->next_caller)
       if (!cs->indirect_inlining_edge
-         && !cs->caller->thunk.thunk_p)
-        redirect_callers->safe_push (cs);
+         && !cs->caller->thunk)
+       redirect_callers->safe_push (cs);
   return false;
 }
 
 /* Collect all callers of cgraph_node and its aliases that are known to lead to
    cgraph_node (i.e. are not overwritable).  */
 
-vec<cgraph_edge *>
+auto_vec<cgraph_edge *>
 cgraph_node::collect_callers (void)
 {
-  vec<cgraph_edge *> redirect_callers = vNULL;
+  auto_vec<cgraph_edge *> redirect_callers;
   call_for_symbol_thunks_and_aliases (collect_callers_of_node_1,
                                    &redirect_callers, false);
   return redirect_callers;
 }
 
-/* Return TRUE if NODE2 a clone of NODE or is equivalent to it.  */
+
+/* Return TRUE if NODE2 a clone of NODE or is equivalent to it.  Return
+   optimistically true if this cannot be determined.  */
 
 static bool
 clone_of_p (cgraph_node *node, cgraph_node *node2)
 {
-  bool skipped_thunk = false;
   node = node->ultimate_alias_target ();
   node2 = node2->ultimate_alias_target ();
 
+  if (node2->clone_of == node
+      || node2->former_clone_of == node->decl)
+    return true;
+
+  if (!node->thunk && !node->former_thunk_p ())
+    {
+      while (node2
+            && node->decl != node2->decl
+            && node->decl != node2->former_clone_of)
+       node2 = node2->clone_of;
+      return node2 != NULL;
+    }
+
   /* There are no virtual clones of thunks so check former_clone_of or if we
      might have skipped thunks because this adjustments are no longer
      necessary.  */
-  while (node->thunk.thunk_p)
+  while (node->thunk || node->former_thunk_p ())
     {
-      if (node2->former_clone_of == node->decl)
-       return true;
-      if (!node->thunk.this_adjusting)
+      if (!thunk_info::get (node)->this_adjusting)
        return false;
+      /* In case of instrumented expanded thunks, which can have multiple calls
+        in them, we do not know how to continue and just have to be
+        optimistic.  The same applies if all calls have already been inlined
+        into the thunk.  */
+      if (!node->callees || node->callees->next_callee)
+       return true;
       node = node->callees->callee->ultimate_alias_target ();
-      skipped_thunk = true;
-    }
 
-  if (skipped_thunk)
-    {
-      if (!node2->clone.args_to_skip
-         || !bitmap_bit_p (node2->clone.args_to_skip, 0))
+      clone_info *info = clone_info::get (node2);
+      if (!info || !info->param_adjustments
+         || info->param_adjustments->first_param_intact_p ())
        return false;
-      if (node2->former_clone_of == node->decl)
+      if (node2->former_clone_of == node->decl
+         || node2->former_clone_of == node->former_clone_of)
+       return true;
+
+      cgraph_node *n2 = node2;
+      while (n2 && node->decl != n2->decl)
+       n2 = n2->clone_of;
+      if (n2)
        return true;
-      else if (!node2->clone_of)
-       return false;
     }
 
-  while (node != node2 && node2)
-    node2 = node2->clone_of;
-  return node2 != NULL;
+  return false;
 }
 
 /* Verify edge count and frequency.  */
@@ -3079,7 +3166,7 @@ cgraph_edge::verify_corresponds_to_fndecl (tree decl)
 {
   cgraph_node *node;
 
-  if (!decl || callee->global.inlined_to)
+  if (!decl || callee->inlined_to)
     return false;
   if (symtab->state == LTO_STREAMING)
     return false;
@@ -3100,8 +3187,8 @@ cgraph_edge::verify_corresponds_to_fndecl (tree decl)
 
   /* Optimizers can redirect unreachable calls or calls triggering undefined
      behavior to builtin_unreachable.  */
-  if (DECL_BUILT_IN_CLASS (callee->decl) == BUILT_IN_NORMAL
-      && DECL_FUNCTION_CODE (callee->decl) == BUILT_IN_UNREACHABLE)
+
+  if (fndecl_built_in_p (callee->decl, BUILT_IN_UNREACHABLE))
     return false;
 
   if (callee->former_clone_of != node->decl
@@ -3112,15 +3199,167 @@ cgraph_edge::verify_corresponds_to_fndecl (tree decl)
     return false;
 }
 
-/* Verify cgraph nodes of given cgraph node.  */
-DEBUG_FUNCTION void
-cgraph_node::verify_node (void)
+/* Disable warnings about missing quoting in GCC diagnostics for
+   the verification errors.  Their format strings don't follow GCC
+   diagnostic conventions and the calls are ultimately followed by
+   one to internal_error.  */
+#if __GNUC__ >= 10
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wformat-diag"
+#endif
+
+/* Verify consistency of speculative call in NODE corresponding to STMT
+   and LTO_STMT_UID.  If INDIRECT is set, assume that it is the indirect
+   edge of call sequence. Return true if error is found.
+
+   This function is called to every component of indirect call (direct edges,
+   indirect edge and refs).  To save duplicated work, do full testing only
+   in that case.  */
+static bool
+verify_speculative_call (struct cgraph_node *node, gimple *stmt,
+                        unsigned int lto_stmt_uid,
+                        struct cgraph_edge *indirect)
 {
-  cgraph_edge *e;
-  function *this_cfun = DECL_STRUCT_FUNCTION (decl);
-  basic_block this_block;
-  gimple_stmt_iterator gsi;
+  if (indirect == NULL)
+    {
+      for (indirect = node->indirect_calls; indirect;
+          indirect = indirect->next_callee)
+       if (indirect->call_stmt == stmt
+           && indirect->lto_stmt_uid == lto_stmt_uid)
+         break;
+      if (!indirect)
+       {
+         error ("missing indirect call in speculative call sequence");
+         return true;
+       }
+      if (!indirect->speculative)
+       {
+         error ("indirect call in speculative call sequence has no "
+                "speculative flag");
+         return true;
+       }
+      return false;
+    }
+
+  /* Maximal number of targets.  We probably will never want to have more than
+     this.  */
+  const unsigned int num = 256;
+  cgraph_edge *direct_calls[num];
+  ipa_ref *refs[num];
+
+  for (unsigned int i = 0; i < num; i++)
+    {
+      direct_calls[i] = NULL;
+      refs[i] = NULL;
+    }
+
+  cgraph_edge *first_call = NULL;
+  cgraph_edge *prev_call = NULL;
+
+  for (cgraph_edge *direct = node->callees; direct;
+       direct = direct->next_callee)
+    if (direct->call_stmt == stmt && direct->lto_stmt_uid == lto_stmt_uid)
+      {
+       if (!first_call)
+         first_call = direct;
+       if (prev_call && direct != prev_call->next_callee)
+         {
+           error ("speculative edges are not adjacent");
+           return true;
+         }
+       prev_call = direct;
+       if (!direct->speculative)
+         {
+           error ("direct call to %s in speculative call sequence has no "
+                  "speculative flag", direct->callee->dump_name ());
+           return true;
+         }
+       if (direct->speculative_id >= num)
+         {
+           error ("direct call to %s in speculative call sequence has "
+                  "speculative_id %i out of range",
+                  direct->callee->dump_name (), direct->speculative_id);
+           return true;
+         }
+       if (direct_calls[direct->speculative_id])
+         {
+           error ("duplicate direct call to %s in speculative call sequence "
+                  "with speculative_id %i",
+                  direct->callee->dump_name (), direct->speculative_id);
+           return true;
+         }
+       direct_calls[direct->speculative_id] = direct;
+      }
+
+  if (first_call->call_stmt
+      && first_call != node->get_edge (first_call->call_stmt))
+    {
+      error ("call stmt hash does not point to first direct edge of "
+            "speculative call sequence");
+      return true;
+    }
+
+  ipa_ref *ref;
+  for (int i = 0; node->iterate_reference (i, ref); i++)
+    if (ref->speculative
+       && ref->stmt == stmt && ref->lto_stmt_uid == lto_stmt_uid)
+      {
+       if (ref->speculative_id >= num)
+         {
+           error ("direct call to %s in speculative call sequence has "
+                  "speculative_id %i out of range",
+                  ref->referred->dump_name (), ref->speculative_id);
+           return true;
+         }
+       if (refs[ref->speculative_id])
+         {
+           error ("duplicate reference %s in speculative call sequence "
+                  "with speculative_id %i",
+                  ref->referred->dump_name (), ref->speculative_id);
+           return true;
+         }
+       refs[ref->speculative_id] = ref;
+      }
+
+  int num_targets = 0;
+  for (unsigned int i = 0 ; i < num ; i++)
+    {
+      if (refs[i] && !direct_calls[i])
+       {
+         error ("missing direct call for speculation %i", i);
+         return true;
+       }
+      if (!refs[i] && direct_calls[i])
+       {
+         error ("missing ref for speculation %i", i);
+         return true;
+       }
+      if (refs[i] != NULL)
+       num_targets++;
+    }
+
+  if (num_targets != indirect->num_speculative_call_targets_p ())
+    {
+      error ("number of speculative targets %i mismatched with "
+            "num_speculative_call_targets %i",
+            num_targets,
+            indirect->num_speculative_call_targets_p ());
+      return true;
+    }
+  return false;
+}
+
+/* Verify cgraph nodes of given cgraph node.  */
+DEBUG_FUNCTION void
+cgraph_node::verify_node (void)
+{
+  cgraph_edge *e;
+  function *this_cfun = DECL_STRUCT_FUNCTION (decl);
+  basic_block this_block;
+  gimple_stmt_iterator gsi;
   bool error_found = false;
+  int i;
+  ipa_ref *ref = NULL;
 
   if (seen_error ())
     return;
@@ -3140,31 +3379,63 @@ cgraph_node::verify_node (void)
       error ("cgraph count invalid");
       error_found = true;
     }
-  if (global.inlined_to && same_comdat_group)
+  if (inlined_to && same_comdat_group)
     {
       error ("inline clone in same comdat group list");
       error_found = true;
     }
-  if (!definition && !in_other_partition && local.local)
+  if (inlined_to && !count.compatible_p (inlined_to->count))
+    {
+      error ("inline clone count is not compatible");
+      count.debug ();
+      inlined_to->count.debug ();
+      error_found = true;
+    }
+  if (tp_first_run < 0)
+    {
+      error ("tp_first_run must be non-negative");
+      error_found = true;
+    }
+  if (!definition && !in_other_partition && local)
     {
       error ("local symbols must be defined");
       error_found = true;
     }
-  if (global.inlined_to && externally_visible)
+  if (inlined_to && externally_visible)
     {
       error ("externally visible inline clone");
       error_found = true;
     }
-  if (global.inlined_to && address_taken)
+  if (inlined_to && address_taken)
     {
       error ("inline clone with address taken");
       error_found = true;
     }
-  if (global.inlined_to && force_output)
+  if (inlined_to && force_output)
     {
       error ("inline clone is forced to output");
       error_found = true;
     }
+  if (symtab->state != LTO_STREAMING)
+    {
+      if (calls_comdat_local && !same_comdat_group)
+       {
+         error ("calls_comdat_local is set outside of a comdat group");
+         error_found = true;
+       }
+      if (!inlined_to && calls_comdat_local != check_calls_comdat_local_p ())
+       {
+         error ("invalid calls_comdat_local flag");
+         error_found = true;
+       }
+    }
+  if (DECL_IS_MALLOC (decl)
+      && !POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (decl))))
+    {
+      error ("malloc attribute should be used for a function that "
+            "returns a pointer");
+      error_found = true;
+    }
   for (e = indirect_calls; e; e = e->next_callee)
     {
       if (e->aux)
@@ -3173,6 +3444,13 @@ cgraph_node::verify_node (void)
                 identifier_to_locale (e->caller->name ()));
          error_found = true;
        }
+      if (!e->count.compatible_p (count))
+       {
+         error ("edge count is not compatible with function count");
+         e->count.debug ();
+         count.debug ();
+         error_found = true;
+       }
       if (!e->indirect_unknown_callee
          || !e->indirect_info)
        {
@@ -3182,6 +3460,11 @@ cgraph_node::verify_node (void)
          cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
          error_found = true;
        }
+      if (e->call_stmt && e->lto_stmt_uid)
+       {
+         error ("edge has both call_stmt and lto_stmt_uid set");
+         error_found = true;
+       }
     }
   bool check_comdat = comdat_local_p ();
   for (e = callers; e; e = e->next_caller)
@@ -3197,9 +3480,9 @@ cgraph_node::verify_node (void)
        }
       if (!e->inline_failed)
        {
-         if (global.inlined_to
-             != (e->caller->global.inlined_to
-                 ? e->caller->global.inlined_to : e->caller))
+         if (inlined_to
+             != (e->caller->inlined_to
+                 ? e->caller->inlined_to : e->caller))
            {
              error ("inlined_to pointer is wrong");
              error_found = true;
@@ -3211,7 +3494,7 @@ cgraph_node::verify_node (void)
            }
        }
       else
-       if (global.inlined_to)
+       if (inlined_to)
          {
            error ("inlined_to pointer set for noninline callers");
            error_found = true;
@@ -3221,14 +3504,20 @@ cgraph_node::verify_node (void)
     {
       if (e->verify_count ())
        error_found = true;
+      if (!e->count.compatible_p (count))
+       {
+         error ("edge count is not compatible with function count");
+         e->count.debug ();
+         count.debug ();
+         error_found = true;
+       }
       if (gimple_has_body_p (e->caller->decl)
-         && !e->caller->global.inlined_to
+         && !e->caller->inlined_to
          && !e->speculative
          /* Optimized out calls are redirected to __builtin_unreachable.  */
          && (e->count.nonzero_p ()
              || ! e->callee->decl
-             || DECL_BUILT_IN_CLASS (e->callee->decl) != BUILT_IN_NORMAL
-             || DECL_FUNCTION_CODE (e->callee->decl) != BUILT_IN_UNREACHABLE)
+             || !fndecl_built_in_p (e->callee->decl, BUILT_IN_UNREACHABLE))
          && count
              == ENTRY_BLOCK_PTR_FOR_FN (DECL_STRUCT_FUNCTION (decl))->count
          && (!e->count.ipa_p ()
@@ -3242,13 +3531,22 @@ cgraph_node::verify_node (void)
          fprintf (stderr, "\n");
          error_found = true;
        }
+      if (e->call_stmt && e->lto_stmt_uid)
+       {
+         error ("edge has both call_stmt and lto_stmt_uid set");
+         error_found = true;
+       }
+      if (e->speculative
+         && verify_speculative_call (e->caller, e->call_stmt, e->lto_stmt_uid,
+                                     NULL))
+       error_found = true;
     }
   for (e = indirect_calls; e; e = e->next_callee)
     {
       if (e->verify_count ())
        error_found = true;
       if (gimple_has_body_p (e->caller->decl)
-         && !e->caller->global.inlined_to
+         && !e->caller->inlined_to
          && !e->speculative
          && e->count.ipa_p ()
          && count
@@ -3264,13 +3562,30 @@ cgraph_node::verify_node (void)
          fprintf (stderr, "\n");
          error_found = true;
        }
+      if (e->speculative
+         && verify_speculative_call (e->caller, e->call_stmt, e->lto_stmt_uid,
+                                     e))
+       error_found = true;
     }
-  if (!callers && global.inlined_to)
+  for (i = 0; iterate_reference (i, ref); i++)
+    {
+      if (ref->stmt && ref->lto_stmt_uid)
+       {
+         error ("reference has both stmt and lto_stmt_uid set");
+         error_found = true;
+       }
+      if (ref->speculative
+         && verify_speculative_call (this, ref->stmt,
+                                     ref->lto_stmt_uid, NULL))
+       error_found = true;
+    }
+
+  if (!callers && inlined_to)
     {
       error ("inlined_to pointer is set but no predecessors found");
       error_found = true;
     }
-  if (global.inlined_to == this)
+  if (inlined_to == this)
     {
       error ("inlined_to pointer refers to itself");
       error_found = true;
@@ -3278,14 +3593,14 @@ cgraph_node::verify_node (void)
 
   if (clone_of)
     {
-      cgraph_node *n;
-      for (n = clone_of->clones; n; n = n->next_sibling_clone)
-       if (n == this)
-         break;
-      if (!n)
+      cgraph_node *first_clone = clone_of->clones;
+      if (first_clone != this)
        {
-         error ("cgraph_node has wrong clone_of");
-         error_found = true;
+         if (prev_sibling_clone->clone_of != clone_of)
+           {
+             error ("cgraph_node has wrong clone_of");
+             error_found = true;
+           }
        }
     }
   if (clones)
@@ -3325,12 +3640,10 @@ cgraph_node::verify_node (void)
       if (callees)
        {
          error ("Alias has call edges");
-          error_found = true;
+         error_found = true;
        }
       for (i = 0; iterate_reference (i, ref); i++)
-       if (ref->use == IPA_REF_CHKP)
-         ;
-       else if (ref->use != IPA_REF_ALIAS)
+       if (ref->use != IPA_REF_ALIAS)
          {
            error ("Alias has non-alias reference");
            error_found = true;
@@ -3349,111 +3662,32 @@ cgraph_node::verify_node (void)
        }
     }
 
-  /* Check instrumented version reference.  */
-  if (instrumented_version
-      && instrumented_version->instrumented_version != this)
-    {
-      error ("Instrumentation clone does not reference original node");
-      error_found = true;
-    }
-
-  /* Cannot have orig_decl for not instrumented nodes.  */
-  if (!instrumentation_clone && orig_decl)
-    {
-      error ("Not instrumented node has non-NULL original declaration");
-      error_found = true;
-    }
-
-  /* If original not instrumented node still exists then we may check
-     original declaration is set properly.  */
-  if (instrumented_version
-      && orig_decl
-      && orig_decl != instrumented_version->decl)
-    {
-      error ("Instrumented node has wrong original declaration");
-      error_found = true;
-    }
-
-  /* Check all nodes have chkp reference to their instrumented versions.  */
-  if (analyzed
-      && instrumented_version
-      && !instrumentation_clone)
-    {
-      bool ref_found = false;
-      int i;
-      struct ipa_ref *ref;
-
-      for (i = 0; iterate_reference (i, ref); i++)
-       if (ref->use == IPA_REF_CHKP)
-         {
-           if (ref_found)
-             {
-               error ("Node has more than one chkp reference");
-               error_found = true;
-             }
-           if (ref->referred != instrumented_version)
-             {
-               error ("Wrong node is referenced with chkp reference");
-               error_found = true;
-             }
-           ref_found = true;
-         }
-
-      if (!ref_found)
-       {
-         error ("Analyzed node has no reference to instrumented version");
-         error_found = true;
-       }
-    }
-
-  if (instrumentation_clone
-      && DECL_BUILT_IN_CLASS (decl) == NOT_BUILT_IN)
-    {
-      tree name = DECL_ASSEMBLER_NAME (decl);
-      tree orig_name = DECL_ASSEMBLER_NAME (orig_decl);
-
-      if (!IDENTIFIER_TRANSPARENT_ALIAS (name)
-         || TREE_CHAIN (name) != orig_name)
-       {
-         error ("Alias chain for instrumented node is broken");
-         error_found = true;
-       }
-    }
-
-  if (analyzed && thunk.thunk_p)
+  if (analyzed && thunk)
     {
       if (!callees)
        {
          error ("No edge out of thunk node");
-          error_found = true;
+         error_found = true;
        }
       else if (callees->next_callee)
        {
          error ("More than one edge out of thunk node");
-          error_found = true;
+         error_found = true;
        }
-      if (gimple_has_body_p (decl) && !global.inlined_to)
-        {
-         error ("Thunk is not supposed to have body");
-          error_found = true;
-        }
-      if (thunk.add_pointer_bounds_args
-         && !instrumented_version->semantically_equivalent_p (callees->callee))
+      if (gimple_has_body_p (decl) && !inlined_to)
        {
-         error ("Instrumentation thunk has wrong edge callee");
-          error_found = true;
+         error ("Thunk is not supposed to have body");
+         error_found = true;
        }
     }
   else if (analyzed && gimple_has_body_p (decl)
           && !TREE_ASM_WRITTEN (decl)
-          && (!DECL_EXTERNAL (decl) || global.inlined_to)
+          && (!DECL_EXTERNAL (decl) || inlined_to)
           && !flag_wpa)
     {
       if (this_cfun->cfg)
        {
          hash_set<gimple *> stmts;
-         int i;
-         ipa_ref *ref = NULL;
 
          /* Reach the trees by walking over the CFG, and note the
             enclosing basic-blocks in the call edges.  */
@@ -3524,7 +3758,7 @@ cgraph_node::verify_node (void)
 
       for (e = callees; e; e = e->next_callee)
        {
-         if (!e->aux)
+         if (!e->aux && !e->speculative)
            {
              error ("edge %s->%s has no corresponding call_stmt",
                     identifier_to_locale (e->caller->name ()),
@@ -3546,6 +3780,35 @@ cgraph_node::verify_node (void)
          e->aux = 0;
        }
     }
+
+  if (nested_function_info *info = nested_function_info::get (this))
+    {
+      if (info->nested != NULL)
+       {
+         for (cgraph_node *n = info->nested; n != NULL;
+              n = next_nested_function (n))
+           {
+             nested_function_info *ninfo = nested_function_info::get (n);
+             if (ninfo->origin == NULL)
+               {
+                 error ("missing origin for a node in a nested list");
+                 error_found = true;
+               }
+             else if (ninfo->origin != this)
+               {
+                 error ("origin points to a different parent");
+                 error_found = true;
+                 break;
+               }
+           }
+       }
+      if (info->next_nested != NULL && info->origin == NULL)
+       {
+         error ("missing origin for a node in a nested list");
+         error_found = true;
+       }
+    }
+
   if (error_found)
     {
       dump (stderr);
@@ -3567,6 +3830,10 @@ cgraph_node::verify_cgraph_nodes (void)
     node->verify ();
 }
 
+#if __GNUC__ >= 10
+#  pragma GCC diagnostic pop
+#endif
+
 /* Walk the alias chain to return the function cgraph_node is alias of.
    Walk through thunks, too.
    When AVAILABILITY is non-NULL, get minimal availability in the chain.
@@ -3579,18 +3846,15 @@ cgraph_node::function_symbol (enum availability *availability,
 {
   cgraph_node *node = ultimate_alias_target (availability, ref);
 
-  while (node->thunk.thunk_p)
+  while (node->thunk)
     {
+      enum availability a;
+
       ref = node;
       node = node->callees->callee;
-      if (availability)
-       {
-         enum availability a;
-         a = node->get_availability (ref);
-         if (a < *availability)
-           *availability = a;
-       }
-      node = node->ultimate_alias_target (availability, ref);
+      node = node->ultimate_alias_target (availability ? &a : NULL, ref);
+      if (availability && a < *availability)
+       *availability = a;
     }
   return node;
 }
@@ -3609,33 +3873,41 @@ cgraph_node::function_or_virtual_thunk_symbol
 {
   cgraph_node *node = ultimate_alias_target (availability, ref);
 
-  while (node->thunk.thunk_p && !node->thunk.virtual_offset_p)
+  while (node->thunk && !thunk_info::get (node)->virtual_offset_p)
     {
+      enum availability a;
+
       ref = node;
       node = node->callees->callee;
-      if (availability)
-       {
-         enum availability a;
-         a = node->get_availability (ref);
-         if (a < *availability)
-           *availability = a;
-       }
-      node = node->ultimate_alias_target (availability, ref);
+      node = node->ultimate_alias_target (availability ? &a : NULL, ref);
+      if (availability && a < *availability)
+       *availability = a;
     }
   return node;
 }
 
 /* When doing LTO, read cgraph_node's body from disk if it is not already
-   present.  */
+   present.  Also perform any necessary clone materializations.  */
 
 bool
-cgraph_node::get_untransformed_body (void)
+cgraph_node::get_untransformed_body ()
 {
   lto_file_decl_data *file_data;
   const char *data, *name;
   size_t len;
   tree decl = this->decl;
 
+  /* See if there is clone to be materialized.
+     (inline clones does not need materialization, but we can be seeing
+      an inline clone of real clone).  */
+  cgraph_node *p = this;
+  for (cgraph_node *c = clone_of; c; c = c->clone_of)
+    {
+      if (c->decl != decl)
+       p->materialize_clone ();
+      p = c;
+    }
+
   /* Check if body is already there.  Either we have gimple body or
      the function is thunk and in that case we set DECL_ARGUMENTS.  */
   if (DECL_ARGUMENTS (decl) || gimple_has_body_p (decl))
@@ -3653,15 +3925,22 @@ cgraph_node::get_untransformed_body (void)
   struct lto_in_decl_state *decl_state
         = lto_get_function_in_decl_state (file_data, decl);
 
+  cgraph_node *origin = this;
+  while (origin->clone_of)
+    origin = origin->clone_of;
+
+  int stream_order = origin->order - file_data->order_base;
   data = lto_get_section_data (file_data, LTO_section_function_body,
-                              name, &len, decl_state->compressed);
+                              name, stream_order, &len,
+                              decl_state->compressed);
   if (!data)
-    fatal_error (input_location, "%s: section %s is missing",
-                file_data->file_name,
-                name);
+    fatal_error (input_location, "%s: section %s.%d is missing",
+                file_data->file_name, name, stream_order);
 
   gcc_assert (DECL_STRUCT_FUNCTION (decl) == NULL);
 
+  if (!quiet_flag)
+    fprintf (stderr, " in:%s", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
   lto_input_function_body (file_data, this, data);
   lto_stats.num_function_bodies++;
   lto_free_section_data (file_data, LTO_section_function_body, name,
@@ -3691,7 +3970,7 @@ cgraph_node::get_body (void)
      early.
      TODO: Materializing clones here will likely lead to smaller LTRANS
      footprint. */
-  gcc_assert (!global.inlined_to && !clone_of);
+  gcc_assert (!inlined_to && !clone_of);
   if (ipa_transforms_to_apply.exists ())
     {
       opt_pass *saved_current_pass = current_pass;
@@ -3699,10 +3978,12 @@ cgraph_node::get_body (void)
       const char *saved_dump_file_name = dump_file_name;
       dump_flags_t saved_dump_flags = dump_flags;
       dump_file_name = NULL;
-      dump_file = NULL;
+      set_dump_file (NULL);
 
       push_cfun (DECL_STRUCT_FUNCTION (decl));
-      execute_all_ipa_transforms ();
+
+      update_ssa (TODO_update_ssa_only_virtuals);
+      execute_all_ipa_transforms (true);
       cgraph_edge::rebuild_edges ();
       free_dominance_info (CDI_DOMINATORS);
       free_dominance_info (CDI_POST_DOMINATORS);
@@ -3710,7 +3991,7 @@ cgraph_node::get_body (void)
       updated = true;
 
       current_pass = saved_current_pass;
-      dump_file = saved_dump_file;
+      set_dump_file (saved_dump_file);
       dump_file_name = saved_dump_file_name;
       dump_flags = saved_dump_flags;
     }
@@ -3720,9 +4001,9 @@ cgraph_node::get_body (void)
 /* Return the DECL_STRUCT_FUNCTION of the function.  */
 
 struct function *
-cgraph_node::get_fun (void)
+cgraph_node::get_fun () const
 {
-  cgraph_node *node = this;
+  const cgraph_node *node = this;
   struct function *fun = DECL_STRUCT_FUNCTION (node->decl);
 
   while (!fun && node->clone_of)
@@ -3734,108 +4015,15 @@ cgraph_node::get_fun (void)
   return fun;
 }
 
-/* Verify if the type of the argument matches that of the function
-   declaration.  If we cannot verify this or there is a mismatch,
-   return false.  */
-
-static bool
-gimple_check_call_args (gimple *stmt, tree fndecl, bool args_count_match)
-{
-  tree parms, p;
-  unsigned int i, nargs;
-
-  /* Calls to internal functions always match their signature.  */
-  if (gimple_call_internal_p (stmt))
-    return true;
-
-  nargs = gimple_call_num_args (stmt);
-
-  /* Get argument types for verification.  */
-  if (fndecl)
-    parms = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
-  else
-    parms = TYPE_ARG_TYPES (gimple_call_fntype (stmt));
-
-  /* Verify if the type of the argument matches that of the function
-     declaration.  If we cannot verify this or there is a mismatch,
-     return false.  */
-  if (fndecl && DECL_ARGUMENTS (fndecl))
-    {
-      for (i = 0, p = DECL_ARGUMENTS (fndecl);
-          i < nargs;
-          i++, p = DECL_CHAIN (p))
-       {
-         tree arg;
-         /* We cannot distinguish a varargs function from the case
-            of excess parameters, still deferring the inlining decision
-            to the callee is possible.  */
-         if (!p)
-           break;
-         arg = gimple_call_arg (stmt, i);
-         if (p == error_mark_node
-             || DECL_ARG_TYPE (p) == error_mark_node
-             || arg == error_mark_node
-             || (!types_compatible_p (DECL_ARG_TYPE (p), TREE_TYPE (arg))
-                 && !fold_convertible_p (DECL_ARG_TYPE (p), arg)))
-            return false;
-       }
-      if (args_count_match && p)
-       return false;
-    }
-  else if (parms)
-    {
-      for (i = 0, p = parms; i < nargs; i++, p = TREE_CHAIN (p))
-       {
-         tree arg;
-         /* If this is a varargs function defer inlining decision
-            to callee.  */
-         if (!p)
-           break;
-         arg = gimple_call_arg (stmt, i);
-         if (TREE_VALUE (p) == error_mark_node
-             || arg == error_mark_node
-             || TREE_CODE (TREE_VALUE (p)) == VOID_TYPE
-             || (!types_compatible_p (TREE_VALUE (p), TREE_TYPE (arg))
-                 && !fold_convertible_p (TREE_VALUE (p), arg)))
-            return false;
-       }
-    }
-  else
-    {
-      if (nargs != 0)
-        return false;
-    }
-  return true;
-}
-
-/* Verify if the type of the argument and lhs of CALL_STMT matches
-   that of the function declaration CALLEE. If ARGS_COUNT_MATCH is
-   true, the arg count needs to be the same.
-   If we cannot verify this or there is a mismatch, return false.  */
-
-bool
-gimple_check_call_matching_types (gimple *call_stmt, tree callee,
-                                 bool args_count_match)
-{
-  tree lhs;
-
-  if ((DECL_RESULT (callee)
-       && !DECL_BY_REFERENCE (DECL_RESULT (callee))
-       && (lhs = gimple_call_lhs (call_stmt)) != NULL_TREE
-       && !useless_type_conversion_p (TREE_TYPE (DECL_RESULT (callee)),
-                                      TREE_TYPE (lhs))
-       && !fold_convertible_p (TREE_TYPE (DECL_RESULT (callee)), lhs))
-      || !gimple_check_call_args (call_stmt, callee, args_count_match))
-    return false;
-  return true;
-}
-
 /* Reset all state within cgraph.c so that we can rerun the compiler
    within the same process.  For use by toplev::finalize.  */
 
 void
 cgraph_c_finalize (void)
 {
+  nested_function_info::release ();
+  thunk_info::release ();
+  clone_info::release ();
   symtab = NULL;
 
   x_cgraph_nodes_queue = NULL;
@@ -3844,7 +4032,7 @@ cgraph_c_finalize (void)
   version_info_node = NULL;
 }
 
-/* A wroker for call_for_symbol_and_aliases.  */
+/* A worker for call_for_symbol_and_aliases.  */
 
 bool
 cgraph_node::call_for_symbol_and_aliases_1 (bool (*callback) (cgraph_node *,
@@ -3871,7 +4059,7 @@ bool
 cgraph_node::has_thunk_p (cgraph_node *node, void *)
 {
   for (cgraph_edge *e = node->callers; e; e = e->next_caller)
-    if (e->caller->thunk.thunk_p)
+    if (e->caller->thunk)
       return true;
   return false;
 }
@@ -3881,9 +4069,147 @@ cgraph_node::has_thunk_p (cgraph_node *node, void *)
 sreal
 cgraph_edge::sreal_frequency ()
 {
-  return count.to_sreal_scale (caller->global.inlined_to
-                              ? caller->global.inlined_to->count
+  return count.to_sreal_scale (caller->inlined_to
+                              ? caller->inlined_to->count
                               : caller->count);
 }
 
+
+/* During LTO stream in this can be used to check whether call can possibly
+   be internal to the current translation unit.  */
+
+bool
+cgraph_edge::possibly_call_in_translation_unit_p (void)
+{
+  gcc_checking_assert (in_lto_p && caller->prevailing_p ());
+
+  /* While incremental linking we may end up getting function body later.  */
+  if (flag_incremental_link == INCREMENTAL_LINK_LTO)
+    return true;
+
+  /* We may be smarter here and avoid streaming in indirect calls we can't
+     track, but that would require arranging streaming the indirect call
+     summary first.  */
+  if (!callee)
+    return true;
+
+  /* If callee is local to the original translation unit, it will be
+     defined.  */
+  if (!TREE_PUBLIC (callee->decl) && !DECL_EXTERNAL (callee->decl))
+    return true;
+
+  /* Otherwise we need to lookup prevailing symbol (symbol table is not merged,
+     yet) and see if it is a definition.  In fact we may also resolve aliases,
+     but that is probably not too important.  */
+  symtab_node *node = callee;
+  for (int n = 10; node->previous_sharing_asm_name && n ; n--)
+    node = node->previous_sharing_asm_name;
+  if (node->previous_sharing_asm_name)
+    node = symtab_node::get_for_asmname (DECL_ASSEMBLER_NAME (callee->decl));
+  gcc_assert (TREE_PUBLIC (node->decl));
+  return node->get_availability () >= AVAIL_INTERPOSABLE;
+}
+
+/* Return num_speculative_targets of this edge.  */
+
+int
+cgraph_edge::num_speculative_call_targets_p (void)
+{
+  return indirect_info ? indirect_info->num_speculative_call_targets : 0;
+}
+
+/* Check if function calls comdat local.  This is used to recompute
+   calls_comdat_local flag after function transformations.  */
+bool
+cgraph_node::check_calls_comdat_local_p ()
+{
+  for (cgraph_edge *e = callees; e; e = e->next_callee)
+    if (e->inline_failed
+       ? e->callee->comdat_local_p ()
+       : e->callee->check_calls_comdat_local_p ())
+      return true;
+  return false;
+}
+
+/* Return true if this node represents a former, i.e. an expanded, thunk.  */
+
+bool
+cgraph_node::former_thunk_p (void)
+{
+  if (thunk)
+    return false;
+  thunk_info *i = thunk_info::get (this);
+  if (!i)
+    return false;
+  gcc_checking_assert (i->fixed_offset || i->virtual_offset_p
+                      || i->indirect_offset);
+  return true;
+}
+
+/* A stashed copy of "symtab" for use by selftest::symbol_table_test.
+   This needs to be a global so that it can be a GC root, and thus
+   prevent the stashed copy from being garbage-collected if the GC runs
+   during a symbol_table_test.  */
+
+symbol_table *saved_symtab;
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* class selftest::symbol_table_test.  */
+
+/* Constructor.  Store the old value of symtab, and create a new one.  */
+
+symbol_table_test::symbol_table_test ()
+{
+  gcc_assert (saved_symtab == NULL);
+  saved_symtab = symtab;
+  symtab = new (ggc_alloc<symbol_table> ()) symbol_table ();
+}
+
+/* Destructor.  Restore the old value of symtab.  */
+
+symbol_table_test::~symbol_table_test ()
+{
+  gcc_assert (saved_symtab != NULL);
+  symtab = saved_symtab;
+  saved_symtab = NULL;
+}
+
+/* Verify that symbol_table_test works.  */
+
+static void
+test_symbol_table_test ()
+{
+  /* Simulate running two selftests involving symbol tables.  */
+  for (int i = 0; i < 2; i++)
+    {
+      symbol_table_test stt;
+      tree test_decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL,
+                                  get_identifier ("test_decl"),
+                                  build_function_type_list (void_type_node,
+                                                            NULL_TREE));
+      cgraph_node *node = cgraph_node::get_create (test_decl);
+      gcc_assert (node);
+
+      /* Verify that the node has order 0 on both iterations,
+        and thus that nodes have predictable dump names in selftests.  */
+      ASSERT_EQ (node->order, 0);
+      ASSERT_STREQ (node->dump_name (), "test_decl/0");
+    }
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+cgraph_c_tests ()
+{
+  test_symbol_table_test ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
+
 #include "gt-cgraph.h"