re PR debug/66691 (ICE on valid code at -O3 with -g enabled in simplify_subreg, at...
[gcc.git] / gcc / cgraphunit.c
index c3a8967e843af60f9508e8431c1ecb6493cfe1f4..7e78bf789155c779f9620a4a5185e7f8465b85a7 100644 (file)
@@ -1,5 +1,5 @@
 /* Driver of optimization process
-   Copyright (C) 2003-2013 Free Software Foundation, Inc.
+   Copyright (C) 2003-2015 Free Software Foundation, Inc.
    Contributed by Jan Hubicka
 
 This file is part of GCC.
@@ -25,7 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 
    The front-end is supposed to use following functionality:
 
-    - cgraph_finalize_function
+    - finalize_function
 
       This function is called once front-end has parsed whole body of function
       and it is certain that the function body nor the declaration will change.
@@ -161,27 +161,46 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
+#include "alias.h"
+#include "symtab.h"
 #include "tree.h"
+#include "fold-const.h"
+#include "varasm.h"
+#include "stor-layout.h"
+#include "stringpool.h"
 #include "output.h"
 #include "rtl.h"
+#include "predict.h"
+#include "hard-reg-set.h"
+#include "function.h"
+#include "basic-block.h"
+#include "dominance.h"
+#include "cfgcleanup.h"
+#include "cfg.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "gimple-expr.h"
 #include "gimple.h"
+#include "gimplify.h"
+#include "gimple-iterator.h"
+#include "gimplify-me.h"
 #include "gimple-ssa.h"
 #include "tree-cfg.h"
 #include "tree-into-ssa.h"
 #include "tree-ssa.h"
 #include "tree-inline.h"
 #include "langhooks.h"
-#include "pointer-set.h"
 #include "toplev.h"
 #include "flags.h"
-#include "ggc.h"
 #include "debug.h"
 #include "target.h"
 #include "diagnostic.h"
 #include "params.h"
-#include "fibheap.h"
 #include "intl.h"
-#include "function.h"
+#include "cgraph.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
 #include "ipa-prop.h"
 #include "tree-iterator.h"
 #include "tree-pass.h"
@@ -198,57 +217,52 @@ along with GCC; see the file COPYING3.  If not see
 #include "regset.h"     /* FIXME: For reg_obstack.  */
 #include "context.h"
 #include "pass_manager.h"
+#include "tree-nested.h"
+#include "gimplify.h"
+#include "dbgcnt.h"
+#include "tree-chkp.h"
+#include "lto-section-names.h"
+#include "omp-low.h"
+#include "print-tree.h"
 
 /* Queue of cgraph nodes scheduled to be added into cgraph.  This is a
    secondary queue used during optimization to accommodate passes that
    may generate new functions that need to be optimized and expanded.  */
-cgraph_node_set cgraph_new_nodes;
+vec<cgraph_node *> cgraph_new_nodes;
 
 static void expand_all_functions (void);
 static void mark_functions_to_output (void);
-static void expand_function (struct cgraph_node *);
-static void analyze_function (struct cgraph_node *);
 static void handle_alias_pairs (void);
 
-FILE *cgraph_dump_file;
-
-/* Linked list of cgraph asm nodes.  */
-struct asm_node *asm_nodes;
-
-/* Last node in cgraph_asm_nodes.  */
-static GTY(()) struct asm_node *asm_last_node;
-
 /* Used for vtable lookup in thunk adjusting.  */
 static GTY (()) tree vtable_entry_type;
 
-/* Determine if symbol DECL is needed.  That is, visible to something
+/* Determine if symbol declaration is needed.  That is, visible to something
    either outside this translation unit, something magic in the system
    configury */
 bool
-decide_is_symbol_needed (symtab_node *node)
+symtab_node::needed_p (void)
 {
-  tree decl = node->decl;
-
   /* Double check that no one output the function into assembly file
      early.  */
   gcc_checking_assert (!DECL_ASSEMBLER_NAME_SET_P (decl)
                       || !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)));
 
-  if (!node->definition)
+  if (!definition)
     return false;
 
   if (DECL_EXTERNAL (decl))
     return false;
 
   /* If the user told us it is used, then it must be so.  */
-  if (node->force_output)
+  if (force_output)
     return true;
 
   /* ABI forced symbols are needed when they are external.  */
-  if (node->forced_by_abi && TREE_PUBLIC (decl))
+  if (forced_by_abi && TREE_PUBLIC (decl))
     return true;
 
- /* Keep constructors, destructors and virtual functions.  */
 /* Keep constructors, destructors and virtual functions.  */
    if (TREE_CODE (decl) == FUNCTION_DECL
        && (DECL_STATIC_CONSTRUCTOR (decl) || DECL_STATIC_DESTRUCTOR (decl)))
     return true;
@@ -261,11 +275,13 @@ decide_is_symbol_needed (symtab_node *node)
   return false;
 }
 
-/* Head of the queue of nodes to be processed while building callgraph */
+/* Head and terminator of the queue of nodes to be processed while building
+   callgraph.  */
 
-static symtab_node *first = (symtab_node *)(void *)1;
+static symtab_node symtab_terminator;
+static symtab_node *queued_nodes = &symtab_terminator;
 
-/* Add NODE to queue starting at FIRST
+/* Add NODE to queue starting at QUEUED_NODES
    The queue is linked via AUX pointers and terminated by pointer to 1.  */
 
 static void
@@ -273,71 +289,69 @@ enqueue_node (symtab_node *node)
 {
   if (node->aux)
     return;
-  gcc_checking_assert (first);
-  node->aux = first;
-  first = node;
+  gcc_checking_assert (queued_nodes);
+  node->aux = queued_nodes;
+  queued_nodes = node;
 }
 
 /* Process CGRAPH_NEW_FUNCTIONS and perform actions necessary to add these
    functions into callgraph in a way so they look like ordinary reachable
    functions inserted into callgraph already at construction time.  */
 
-bool
-cgraph_process_new_functions (void)
+void
+symbol_table::process_new_functions (void)
 {
-  bool output = false;
   tree fndecl;
-  struct cgraph_node *node;
-  cgraph_node_set_iterator csi;
 
-  if (!cgraph_new_nodes)
-    return false;
+  if (!cgraph_new_nodes.exists ())
+    return;
+
   handle_alias_pairs ();
   /*  Note that this queue may grow as its being processed, as the new
       functions may generate new ones.  */
-  for (csi = csi_start (cgraph_new_nodes); !csi_end_p (csi); csi_next (&csi))
+  for (unsigned i = 0; i < cgraph_new_nodes.length (); i++)
     {
-      node = csi_node (csi);
+      cgraph_node *node = cgraph_new_nodes[i];
       fndecl = node->decl;
-      switch (cgraph_state)
+      switch (state)
        {
-       case CGRAPH_STATE_CONSTRUCTION:
+       case CONSTRUCTION:
          /* At construction time we just need to finalize function and move
             it into reachable functions list.  */
 
-         cgraph_finalize_function (fndecl, false);
-         output = true;
-          cgraph_call_function_insertion_hooks (node);
+         cgraph_node::finalize_function (fndecl, false);
+         call_cgraph_insertion_hooks (node);
          enqueue_node (node);
          break;
 
-       case CGRAPH_STATE_IPA:
-       case CGRAPH_STATE_IPA_SSA:
+       case IPA:
+       case IPA_SSA:
+       case IPA_SSA_AFTER_INLINING:
          /* When IPA optimization already started, do all essential
             transformations that has been already performed on the whole
             cgraph but not on this function.  */
 
          gimple_register_cfg_hooks ();
          if (!node->analyzed)
-           analyze_function (node);
+           node->analyze ();
          push_cfun (DECL_STRUCT_FUNCTION (fndecl));
-         if (cgraph_state == CGRAPH_STATE_IPA_SSA
+         if ((state == IPA_SSA || state == IPA_SSA_AFTER_INLINING)
              && !gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
            g->get_passes ()->execute_early_local_passes ();
-         else if (inline_summary_vec != NULL)
+         else if (inline_summaries != NULL)
            compute_inline_parameters (node, true);
          free_dominance_info (CDI_POST_DOMINATORS);
          free_dominance_info (CDI_DOMINATORS);
          pop_cfun ();
-          cgraph_call_function_insertion_hooks (node);
+         call_cgraph_insertion_hooks (node);
          break;
 
-       case CGRAPH_STATE_EXPANSION:
+       case EXPANSION:
          /* Functions created during expansion shall be compiled
             directly.  */
          node->process = 0;
-          cgraph_call_function_insertion_hooks (node);
-         expand_function (node);
+         call_cgraph_insertion_hooks (node);
+         node->expand ();
          break;
 
        default:
@@ -345,9 +359,8 @@ cgraph_process_new_functions (void)
          break;
        }
     }
-  free_cgraph_node_set (cgraph_new_nodes);
-  cgraph_new_nodes = NULL;
-  return output;
+
+  cgraph_new_nodes.release ();
 }
 
 /* As an GCC extension we allow redefinition of the function.  The
@@ -361,43 +374,50 @@ cgraph_process_new_functions (void)
    body for expanding the function but this is difficult to do.  */
 
 void
-cgraph_reset_node (struct cgraph_node *node)
+cgraph_node::reset (void)
 {
-  /* If node->process is set, then we have already begun whole-unit analysis.
+  /* If process is set, then we have already begun whole-unit analysis.
      This is *not* testing for whether we've already emitted the function.
      That case can be sort-of legitimately seen with real function redefinition
      errors.  I would argue that the front end should never present us with
      such a case, but don't enforce that for now.  */
-  gcc_assert (!node->process);
+  gcc_assert (!process);
 
   /* Reset our data structures so we can analyze the function again.  */
-  memset (&node->local, 0, sizeof (node->local));
-  memset (&node->global, 0, sizeof (node->global));
-  memset (&node->rtl, 0, sizeof (node->rtl));
-  node->analyzed = false;
-  node->definition = false;
-  node->alias = false;
-  node->weakref = false;
-  node->cpp_implicit_alias = false;
-
-  cgraph_node_remove_callees (node);
-  ipa_remove_all_references (&node->ref_list);
+  memset (&local, 0, sizeof (local));
+  memset (&global, 0, sizeof (global));
+  memset (&rtl, 0, sizeof (rtl));
+  analyzed = false;
+  definition = false;
+  alias = false;
+  weakref = false;
+  cpp_implicit_alias = false;
+
+  remove_callees ();
+  remove_all_references ();
 }
 
-/* Return true when there are references to NODE.  */
+/* Return true when there are references to the node.  INCLUDE_SELF is
+   true if a self reference counts as a reference.  */
 
-static bool
-referred_to_p (symtab_node *node)
+bool
+symtab_node::referred_to_p (bool include_self)
 {
-  struct ipa_ref *ref;
+  ipa_ref *ref = NULL;
 
   /* See if there are any references at all.  */
-  if (ipa_ref_list_referring_iterate (&node->ref_list, 0, ref))
+  if (iterate_referring (0, ref))
     return true;
   /* For functions check also calls.  */
-  cgraph_node *cn = dyn_cast <cgraph_node> (node);
+  cgraph_node *cn = dyn_cast <cgraph_node *> (this);
   if (cn && cn->callers)
-    return true;
+    {
+      if (include_self)
+       return true;
+      for (cgraph_edge *e = cn->callers; e; e = e->next_caller)
+       if (e->caller != this)
+         return true;
+    }
   return false;
 }
 
@@ -407,21 +427,23 @@ referred_to_p (symtab_node *node)
    a new GC context, or just not compile right now.  */
 
 void
-cgraph_finalize_function (tree decl, bool no_collect)
+cgraph_node::finalize_function (tree decl, bool no_collect)
 {
-  struct cgraph_node *node = cgraph_get_create_node (decl);
+  cgraph_node *node = cgraph_node::get_create (decl);
 
   if (node->definition)
     {
       /* Nested functions should only be defined once.  */
       gcc_assert (!DECL_CONTEXT (decl)
                  || TREE_CODE (DECL_CONTEXT (decl)) != FUNCTION_DECL);
-      cgraph_reset_node (node);
+      node->reset ();
       node->local.redefined_extern_inline = true;
     }
 
-  notice_global_symbol (decl);
+  /* Set definition first before calling notice_global_symbol so that
+     it is available to notice_global_symbol.  */
   node->definition = true;
+  notice_global_symbol (decl);
   node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL;
 
   /* With -fkeep-inline-functions we are keeping all inline functions except
@@ -437,7 +459,7 @@ cgraph_finalize_function (tree decl, bool no_collect)
      declared inline and nested functions.  These were optimized out
      in the original implementation and it is unclear whether we want
      to change the behavior here.  */
-  if ((!optimize
+  if ((!opt_for_fn (decl, optimize)
        && !node->cpp_implicit_alias
        && !DECL_DISREGARD_INLINE_LIMITS (decl)
        && !DECL_DECLARED_INLINE_P (decl)
@@ -450,21 +472,16 @@ cgraph_finalize_function (tree decl, bool no_collect)
   if (!TREE_ASM_WRITTEN (decl))
     (*debug_hooks->deferred_inline_function) (decl);
 
-  /* Possibly warn about unused parameters.  */
-  if (warn_unused_parameter)
-    do_warn_unused_parameter (decl);
-
   if (!no_collect)
     ggc_collect ();
 
-  if (cgraph_state == CGRAPH_STATE_CONSTRUCTION
-      && (decide_is_symbol_needed (node)
-         || referred_to_p (node)))
+  if (symtab->state == CONSTRUCTION
+      && (node->needed_p () || node->referred_to_p ()))
     enqueue_node (node);
 }
 
 /* Add the function FNDECL to the call graph.
-   Unlike cgraph_finalize_function, this function is intended to be used
+   Unlike finalize_function, this function is intended to be used
    by middle end and allows insertion of new function at arbitrary point
    of compilation.  The function can be either in high, low or SSA form
    GIMPLE.
@@ -476,40 +493,56 @@ cgraph_finalize_function (tree decl, bool no_collect)
    processing to avoid need the passes to be re-entrant.  */
 
 void
-cgraph_add_new_function (tree fndecl, bool lowered)
+cgraph_node::add_new_function (tree fndecl, bool lowered)
 {
   gcc::pass_manager *passes = g->get_passes ();
-  struct cgraph_node *node;
-  switch (cgraph_state)
+  cgraph_node *node;
+
+  if (dump_file)
     {
-      case CGRAPH_STATE_PARSING:
-       cgraph_finalize_function (fndecl, false);
+      struct function *fn = DECL_STRUCT_FUNCTION (fndecl);
+      const char *function_type = ((gimple_has_body_p (fndecl))
+                                  ? (lowered
+                                     ? (gimple_in_ssa_p (fn)
+                                        ? "ssa gimple"
+                                        : "low gimple")
+                                     : "high gimple")
+                                  : "to-be-gimplified");
+      fprintf (dump_file,
+              "Added new %s function %s to callgraph\n",
+              function_type,
+              fndecl_name (fndecl));
+    }
+
+  switch (symtab->state)
+    {
+      case PARSING:
+       cgraph_node::finalize_function (fndecl, false);
        break;
-      case CGRAPH_STATE_CONSTRUCTION:
+      case CONSTRUCTION:
        /* Just enqueue function to be processed at nearest occurrence.  */
-       node = cgraph_create_node (fndecl);
+       node = cgraph_node::get_create (fndecl);
        if (lowered)
          node->lowered = true;
-       if (!cgraph_new_nodes)
-         cgraph_new_nodes = cgraph_node_set_new ();
-       cgraph_node_set_add (cgraph_new_nodes, node);
+       cgraph_new_nodes.safe_push (node);
         break;
 
-      case CGRAPH_STATE_IPA:
-      case CGRAPH_STATE_IPA_SSA:
-      case CGRAPH_STATE_EXPANSION:
+      case IPA:
+      case IPA_SSA:
+      case IPA_SSA_AFTER_INLINING:
+      case EXPANSION:
        /* Bring the function into finalized state and enqueue for later
           analyzing and compilation.  */
-       node = cgraph_get_create_node (fndecl);
+       node = cgraph_node::get_create (fndecl);
        node->local.local = false;
        node->definition = true;
        node->force_output = true;
-       if (!lowered && cgraph_state == CGRAPH_STATE_EXPANSION)
+       if (!lowered && symtab->state == EXPANSION)
          {
            push_cfun (DECL_STRUCT_FUNCTION (fndecl));
            gimple_register_cfg_hooks ();
            bitmap_obstack_initialize (NULL);
-           execute_pass_list (passes->all_lowering_passes);
+           execute_pass_list (cfun, passes->all_lowering_passes);
            passes->execute_early_local_passes ();
            bitmap_obstack_release (NULL);
            pop_cfun ();
@@ -518,19 +551,17 @@ cgraph_add_new_function (tree fndecl, bool lowered)
          }
        if (lowered)
          node->lowered = true;
-       if (!cgraph_new_nodes)
-         cgraph_new_nodes = cgraph_node_set_new ();
-       cgraph_node_set_add (cgraph_new_nodes, node);
+       cgraph_new_nodes.safe_push (node);
         break;
 
-      case CGRAPH_STATE_FINISHED:
+      case FINISHED:
        /* At the very end of compilation we have to do all the work up
           to expansion.  */
-       node = cgraph_create_node (fndecl);
+       node = cgraph_node::create (fndecl);
        if (lowered)
          node->lowered = true;
        node->definition = true;
-       analyze_function (node);
+       node->analyze ();
        push_cfun (DECL_STRUCT_FUNCTION (fndecl));
        gimple_register_cfg_hooks ();
        bitmap_obstack_initialize (NULL);
@@ -538,7 +569,7 @@ cgraph_add_new_function (tree fndecl, bool lowered)
          g->get_passes ()->execute_early_local_passes ();
        bitmap_obstack_release (NULL);
        pop_cfun ();
-       expand_function (node);
+       node->expand ();
        break;
 
       default:
@@ -552,75 +583,50 @@ cgraph_add_new_function (tree fndecl, bool lowered)
     DECL_FUNCTION_PERSONALITY (fndecl) = lang_hooks.eh_personality ();
 }
 
-/* Add a top-level asm statement to the list.  */
-
-struct asm_node *
-add_asm_node (tree asm_str)
-{
-  struct asm_node *node;
-
-  node = ggc_alloc_cleared_asm_node ();
-  node->asm_str = asm_str;
-  node->order = symtab_order++;
-  node->next = NULL;
-  if (asm_nodes == NULL)
-    asm_nodes = node;
-  else
-    asm_last_node->next = node;
-  asm_last_node = node;
-  return node;
-}
-
-/* Output all asm statements we have stored up to be output.  */
-
-static void
-output_asm_statements (void)
-{
-  struct asm_node *can;
-
-  if (seen_error ())
-    return;
-
-  for (can = asm_nodes; can; can = can->next)
-    assemble_asm (can->asm_str);
-  asm_nodes = NULL;
-}
-
 /* Analyze the function scheduled to be output.  */
-static void
-analyze_function (struct cgraph_node *node)
+void
+cgraph_node::analyze (void)
 {
-  tree decl = node->decl;
+  tree decl = this->decl;
   location_t saved_loc = input_location;
   input_location = DECL_SOURCE_LOCATION (decl);
 
-  if (node->thunk.thunk_p)
+  if (thunk.thunk_p)
     {
-      cgraph_create_edge (node, cgraph_get_node (node->thunk.alias),
-                         NULL, 0, CGRAPH_FREQ_BASE);
-      if (!expand_thunk (node, false))
+      cgraph_node *t = cgraph_node::get (thunk.alias);
+
+      create_edge (t, NULL, 0, CGRAPH_FREQ_BASE);
+      /* Target code in expand_thunk may need the thunk's target
+        to be analyzed, so recurse here.  */
+      if (!t->analyzed)
+       t->analyze ();
+      if (t->alias)
        {
-         node->thunk.alias = NULL;
-         node->analyzed = true;
+         t = t->get_alias_target ();
+         if (!t->analyzed)
+           t->analyze ();
+       }
+      if (!expand_thunk (false, false))
+       {
+         thunk.alias = NULL;
          return;
        }
-      node->thunk.alias = NULL;
+      thunk.alias = NULL;
     }
-  if (node->alias)
-    symtab_resolve_alias
-       (node, cgraph_get_node (node->alias_target));
-  else if (node->dispatcher_function)
+  if (alias)
+    resolve_alias (cgraph_node::get (alias_target));
+  else if (dispatcher_function)
     {
       /* Generate the dispatcher body of multi-versioned functions.  */
-      struct cgraph_function_version_info *dispatcher_version_info
-       = get_cgraph_node_version (node);
+      cgraph_function_version_info *dispatcher_version_info
+       = function_version ();
       if (dispatcher_version_info != NULL
           && (dispatcher_version_info->dispatcher_resolver
              == NULL_TREE))
        {
          tree resolver = NULL_TREE;
          gcc_assert (targetm.generate_version_dispatcher_body);
-         resolver = targetm.generate_version_dispatcher_body (node);
+         resolver = targetm.generate_version_dispatcher_body (this);
          gcc_assert (resolver != NULL_TREE);
        }
     }
@@ -628,7 +634,7 @@ analyze_function (struct cgraph_node *node)
     {
       push_cfun (DECL_STRUCT_FUNCTION (decl));
 
-      assign_assembler_name_if_neeeded (node->decl);
+      assign_assembler_name_if_neeeded (decl);
 
       /* Make sure to gimplify bodies only once.  During analyzing a
         function we lower it, which will require gimplified nested
@@ -636,28 +642,27 @@ analyze_function (struct cgraph_node *node)
         body.  */
       if (!gimple_has_body_p (decl))
        gimplify_function_tree (decl);
-      dump_function (TDI_generic, decl);
 
       /* Lower the function.  */
-      if (!node->lowered)
+      if (!lowered)
        {
-         if (node->nested)
-           lower_nested_functions (node->decl);
-         gcc_assert (!node->nested);
+         if (nested)
+           lower_nested_functions (decl);
+         gcc_assert (!nested);
 
          gimple_register_cfg_hooks ();
          bitmap_obstack_initialize (NULL);
-         execute_pass_list (g->get_passes ()->all_lowering_passes);
+         execute_pass_list (cfun, g->get_passes ()->all_lowering_passes);
          free_dominance_info (CDI_POST_DOMINATORS);
          free_dominance_info (CDI_DOMINATORS);
          compact_blocks ();
          bitmap_obstack_release (NULL);
-         node->lowered = true;
+         lowered = true;
        }
 
       pop_cfun ();
     }
-  node->analyzed = true;
+  analyzed = true;
 
   input_location = saved_loc;
 }
@@ -669,23 +674,22 @@ analyze_function (struct cgraph_node *node)
    PCH we build the links via this function.  */
 
 void
-cgraph_process_same_body_aliases (void)
+symbol_table::process_same_body_aliases (void)
 {
   symtab_node *node;
   FOR_EACH_SYMBOL (node)
     if (node->cpp_implicit_alias && !node->analyzed)
-      symtab_resolve_alias
-        (node,
-        TREE_CODE (node->alias_target) == VAR_DECL
-        ? (symtab_node *)varpool_node_for_decl (node->alias_target)
-        : (symtab_node *)cgraph_get_create_node (node->alias_target));
+      node->resolve_alias
+       (TREE_CODE (node->alias_target) == VAR_DECL
+        ? (symtab_node *)varpool_node::get_create (node->alias_target)
+        : (symtab_node *)cgraph_node::get_create (node->alias_target));
   cpp_implicit_aliases_done = true;
 }
 
 /* Process attributes common for vars and functions.  */
 
 static void
-process_common_attributes (tree decl)
+process_common_attributes (symtab_node *node, tree decl)
 {
   tree weakref = lookup_attribute ("weakref", DECL_ATTRIBUTES (decl));
 
@@ -698,6 +702,9 @@ process_common_attributes (tree decl)
       DECL_ATTRIBUTES (decl) = remove_attribute ("weakref",
                                                 DECL_ATTRIBUTES (decl));
     }
+
+  if (lookup_attribute ("no_reorder", DECL_ATTRIBUTES (decl)))
+    node->no_reorder = 1;
 }
 
 /* Look for externally_visible and used attributes and mark cgraph nodes
@@ -714,7 +721,7 @@ process_common_attributes (tree decl)
    declaration -- but the front end will subsequently merge that declaration
    with the original declaration and discard the second declaration.
 
-   Furthermore, we can't mark these nodes in cgraph_finalize_function because:
+   Furthermore, we can't mark these nodes in finalize_function because:
 
     void f() {}
     void f() __attribute__((externally_visible));
@@ -725,18 +732,18 @@ process_common_attributes (tree decl)
    attributes at that point.  */
 
 static void
-process_function_and_variable_attributes (struct cgraph_node *first,
-                                          struct varpool_node *first_var)
+process_function_and_variable_attributes (cgraph_node *first,
+                                          varpool_node *first_var)
 {
-  struct cgraph_node *node;
-  struct varpool_node *vnode;
+  cgraph_node *node;
+  varpool_node *vnode;
 
-  for (node = cgraph_first_function (); node != first;
-       node = cgraph_next_function (node))
+  for (node = symtab->first_function (); node != first;
+       node = symtab->next_function (node))
     {
       tree decl = node->decl;
       if (DECL_PRESERVE_P (decl))
-       cgraph_mark_force_output_node (node);
+       node->mark_force_output ();
       else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
        {
          if (! TREE_PUBLIC (node->decl))
@@ -762,15 +769,15 @@ process_function_and_variable_attributes (struct cgraph_node *first,
        warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
                    "always_inline function might not be inlinable");
      
-      process_common_attributes (decl);
+      process_common_attributes (node, decl);
     }
-  for (vnode = varpool_first_variable (); vnode != first_var;
-       vnode = varpool_next_variable (vnode))
+  for (vnode = symtab->first_variable (); vnode != first_var;
+       vnode = symtab->next_variable (vnode))
     {
       tree decl = vnode->decl;
       if (DECL_EXTERNAL (decl)
          && DECL_INITIAL (decl))
-       varpool_finalize_decl (decl);
+       varpool_node::finalize_decl (decl);
       if (DECL_PRESERVE_P (decl))
        vnode->force_output = true;
       else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
@@ -791,7 +798,7 @@ process_function_and_variable_attributes (struct cgraph_node *first,
          DECL_ATTRIBUTES (decl) = remove_attribute ("weakref",
                                                      DECL_ATTRIBUTES (decl));
        }
-      process_common_attributes (decl);
+      process_common_attributes (vnode, decl);
     }
 }
 
@@ -800,33 +807,41 @@ process_function_and_variable_attributes (struct cgraph_node *first,
    visible.  */
 
 void
-varpool_finalize_decl (tree decl)
+varpool_node::finalize_decl (tree decl)
 {
-  struct varpool_node *node = varpool_node_for_decl (decl);
+  varpool_node *node = varpool_node::get_create (decl);
 
   gcc_assert (TREE_STATIC (decl) || DECL_EXTERNAL (decl));
 
   if (node->definition)
     return;
-  notice_global_symbol (decl);
+  /* Set definition first before calling notice_global_symbol so that
+     it is available to notice_global_symbol.  */
   node->definition = true;
+  notice_global_symbol (decl);
   if (TREE_THIS_VOLATILE (decl) || DECL_PRESERVE_P (decl)
       /* Traditionally we do not eliminate static variables when not
         optimizing and when not doing toplevel reoder.  */
-      || (!flag_toplevel_reorder && !DECL_COMDAT (node->decl)
-         && !DECL_ARTIFICIAL (node->decl)))
+      || node->no_reorder
+      || ((!flag_toplevel_reorder
+          && !DECL_COMDAT (node->decl)
+          && !DECL_ARTIFICIAL (node->decl))))
     node->force_output = true;
 
-  if (cgraph_state == CGRAPH_STATE_CONSTRUCTION
-      && (decide_is_symbol_needed (node)
-         || referred_to_p (node)))
+  if (symtab->state == CONSTRUCTION
+      && (node->needed_p () || node->referred_to_p ()))
     enqueue_node (node);
-  if (cgraph_state >= CGRAPH_STATE_IPA_SSA)
-    varpool_analyze_node (node);
+  if (symtab->state >= IPA_SSA)
+    node->analyze ();
   /* Some frontends produce various interface variables after compilation
      finished.  */
-  if (cgraph_state == CGRAPH_STATE_FINISHED)
-    varpool_assemble_decl (node);
+  if (symtab->state == FINISHED
+      || (!flag_toplevel_reorder
+       && symtab->state == EXPANSION))
+    node->assemble_decl ();
+
+  if (DECL_INITIAL (decl))
+    chkp_register_var_initializer (decl);
 }
 
 /* EDGE is an polymorphic call.  Mark all possible targets as reachable
@@ -835,8 +850,8 @@ varpool_finalize_decl (tree decl)
    avoid udplicate work.  */
 
 static void
-walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
-                              struct cgraph_edge *edge)
+walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
+                              cgraph_edge *edge)
 {
   unsigned int i;
   void *cache_token;
@@ -845,12 +860,11 @@ walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
     = possible_polymorphic_call_targets
        (edge, &final, &cache_token);
 
-  if (!pointer_set_insert (reachable_call_targets,
-                          cache_token))
+  if (!reachable_call_targets->add (cache_token))
     {
-      if (cgraph_dump_file)
+      if (symtab->dump_file)
        dump_possible_polymorphic_call_targets 
-         (cgraph_dump_file, edge);
+         (symtab->dump_file, edge);
 
       for (i = 0; i < targets.length (); i++)
        {
@@ -862,9 +876,8 @@ walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
                  (TREE_TYPE (targets[i]->decl))
                   == METHOD_TYPE
              && !type_in_anonymous_namespace_p
-                  (method_class_type
-                    (TREE_TYPE (targets[i]->decl))))
-         enqueue_node (targets[i]);
+                  (TYPE_METHOD_BASETYPE (TREE_TYPE (targets[i]->decl))))
+           enqueue_node (targets[i]);
        }
     }
 
@@ -874,30 +887,43 @@ walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
      make the edge direct.  */
   if (final)
     {
-      if (targets.length () <= 1)
+      if (targets.length () <= 1 && dbg_cnt (devirt))
        {
          cgraph_node *target;
          if (targets.length () == 1)
            target = targets[0];
          else
-           target = cgraph_get_create_node
-                      (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
+           target = cgraph_node::create
+                       (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
 
-         if (cgraph_dump_file)
+         if (symtab->dump_file)
            {
-             fprintf (cgraph_dump_file,
+             fprintf (symtab->dump_file,
                       "Devirtualizing call: ");
-             print_gimple_stmt (cgraph_dump_file,
+             print_gimple_stmt (symtab->dump_file,
                                 edge->call_stmt, 0,
                                 TDF_SLIM);
            }
-         cgraph_make_edge_direct (edge, target);
-         cgraph_redirect_edge_call_stmt_to_callee (edge);
-         if (cgraph_dump_file)
+          if (dump_enabled_p ())
+            {
+             location_t locus = gimple_location_safe (edge->call_stmt);
+             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+                              "devirtualizing call in %s to %s\n",
+                              edge->caller->name (), target->name ());
+           }
+
+         edge->make_direct (target);
+         edge->redirect_call_stmt_to_callee ();
+
+         /* Call to __builtin_unreachable shouldn't be instrumented.  */
+         if (!targets.length ())
+           gimple_call_set_with_bounds (edge->call_stmt, false);
+
+         if (symtab->dump_file)
            {
-             fprintf (cgraph_dump_file,
+             fprintf (symtab->dump_file,
                       "Devirtualized as: ");
-             print_gimple_stmt (cgraph_dump_file,
+             print_gimple_stmt (symtab->dump_file,
                                 edge->call_stmt, 0,
                                 TDF_SLIM);
            }
@@ -908,37 +934,40 @@ walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
 
 /* Discover all functions and variables that are trivially needed, analyze
    them as well as all functions and variables referred by them  */
+static cgraph_node *first_analyzed;
+static varpool_node *first_analyzed_var;
+
+/* FIRST_TIME is set to TRUE for the first time we are called for a
+   translation unit from finalize_compilation_unit() or false
+   otherwise.  */
 
 static void
-analyze_functions (void)
+analyze_functions (bool first_time)
 {
   /* Keep track of already processed nodes when called multiple times for
      intermodule optimization.  */
-  static struct cgraph_node *first_analyzed;
-  struct cgraph_node *first_handled = first_analyzed;
-  static struct varpool_node *first_analyzed_var;
-  struct varpool_node *first_handled_var = first_analyzed_var;
-  struct pointer_set_t *reachable_call_targets = pointer_set_create ();
+  cgraph_node *first_handled = first_analyzed;
+  varpool_node *first_handled_var = first_analyzed_var;
+  hash_set<void *> reachable_call_targets;
 
   symtab_node *node;
   symtab_node *next;
   int i;
-  struct ipa_ref *ref;
+  ipa_ref *ref;
   bool changed = true;
   location_t saved_loc = input_location;
 
   bitmap_obstack_initialize (NULL);
-  cgraph_state = CGRAPH_STATE_CONSTRUCTION;
+  symtab->state = CONSTRUCTION;
   input_location = UNKNOWN_LOCATION;
 
   /* Ugly, but the fixup can not happen at a time same body alias is created;
      C++ FE is confused about the COMDAT groups being right.  */
-  if (cpp_implicit_aliases_done)
+  if (symtab->cpp_implicit_aliases_done)
     FOR_EACH_SYMBOL (node)
       if (node->cpp_implicit_alias)
-         fixup_same_cpp_alias_visibility (node, symtab_alias_target (node));
-  if (optimize && flag_devirtualize)
-    build_type_inheritance_graph ();
+         node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
+  build_type_inheritance_graph ();
 
   /* Analysis adds static variables that in turn adds references to new functions.
      So we need to iterate the process until it stabilize.  */
@@ -949,43 +978,45 @@ analyze_functions (void)
                                                first_analyzed_var);
 
       /* First identify the trivially needed symbols.  */
-      for (node = symtab_nodes;
+      for (node = symtab->first_symbol ();
           node != first_analyzed
           && node != first_analyzed_var; node = node->next)
        {
-         if (decide_is_symbol_needed (node))
+         /* Convert COMDAT group designators to IDENTIFIER_NODEs.  */
+         node->get_comdat_group_id ();
+         if (node->needed_p ())
            {
              enqueue_node (node);
-             if (!changed && cgraph_dump_file)
-               fprintf (cgraph_dump_file, "Trivially needed symbols:");
+             if (!changed && symtab->dump_file)
+               fprintf (symtab->dump_file, "Trivially needed symbols:");
              changed = true;
-             if (cgraph_dump_file)
-               fprintf (cgraph_dump_file, " %s", symtab_node_asm_name (node));
-             if (!changed && cgraph_dump_file)
-               fprintf (cgraph_dump_file, "\n");
+             if (symtab->dump_file)
+               fprintf (symtab->dump_file, " %s", node->asm_name ());
+             if (!changed && symtab->dump_file)
+               fprintf (symtab->dump_file, "\n");
            }
          if (node == first_analyzed
              || node == first_analyzed_var)
            break;
        }
-      cgraph_process_new_functions ();
-      first_analyzed_var = varpool_first_variable ();
-      first_analyzed = cgraph_first_function ();
+      symtab->process_new_functions ();
+      first_analyzed_var = symtab->first_variable ();
+      first_analyzed = symtab->first_function ();
 
-      if (changed && dump_file)
-       fprintf (cgraph_dump_file, "\n");
+      if (changed && symtab->dump_file)
+       fprintf (symtab->dump_file, "\n");
 
       /* Lower representation, build callgraph edges and references for all trivially
          needed symbols and all symbols referred by them.  */
-      while (first != (symtab_node *)(void *)1)
+      while (queued_nodes != &symtab_terminator)
        {
          changed = true;
-         node = first;
-         first = (symtab_node *)first->aux;
-         cgraph_node *cnode = dyn_cast <cgraph_node> (node);
+         node = queued_nodes;
+         queued_nodes = (symtab_node *)queued_nodes->aux;
+         cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
          if (cnode && cnode->definition)
            {
-             struct cgraph_edge *edge;
+             cgraph_edge *edge;
              tree decl = cnode->decl;
 
              /* ??? It is possible to create extern inline function
@@ -996,26 +1027,40 @@ analyze_functions (void)
                  && !cnode->thunk.thunk_p
                  && !cnode->dispatcher_function)
                {
-                 cgraph_reset_node (cnode);
+                 cnode->reset ();
                  cnode->local.redefined_extern_inline = true;
                  continue;
                }
 
              if (!cnode->analyzed)
-               analyze_function (cnode);
+               cnode->analyze ();
 
              for (edge = cnode->callees; edge; edge = edge->next_callee)
-               if (edge->callee->definition)
+               if (edge->callee->definition
+                   && (!DECL_EXTERNAL (edge->callee->decl)
+                       /* When not optimizing, do not try to analyze extern
+                          inline functions.  Doing so is pointless.  */
+                       || opt_for_fn (edge->callee->decl, optimize)
+                       /* Weakrefs needs to be preserved.  */
+                       || edge->callee->alias
+                       /* always_inline functions are inlined aven at -O0.  */
+                       || lookup_attribute
+                                ("always_inline",
+                                 DECL_ATTRIBUTES (edge->callee->decl))
+                       /* Multiversioned functions needs the dispatcher to
+                          be produced locally even for extern functions.  */
+                       || edge->callee->function_version ()))
                   enqueue_node (edge->callee);
-             if (optimize && flag_devirtualize)
+             if (opt_for_fn (cnode->decl, optimize)
+                 && opt_for_fn (cnode->decl, flag_devirtualize))
                {
-                 struct cgraph_edge *next;
+                 cgraph_edge *next;
 
                  for (edge = cnode->indirect_calls; edge; edge = next)
                    {
                      next = edge->next_callee;
                      if (edge->indirect_info->polymorphic)
-                       walk_polymorphic_call_targets (reachable_call_targets,
+                       walk_polymorphic_call_targets (&reachable_call_targets,
                                                       edge);
                    }
                }
@@ -1026,16 +1071,16 @@ analyze_functions (void)
              will be later needed to output debug info.  */
              if (DECL_ABSTRACT_ORIGIN (decl))
                {
-                 struct cgraph_node *origin_node
-                 = cgraph_get_node (DECL_ABSTRACT_ORIGIN (decl));
+                 cgraph_node *origin_node
+                   = cgraph_node::get_create (DECL_ABSTRACT_ORIGIN (decl));
                  origin_node->used_as_abstract_origin = true;
                }
            }
          else
            {
-             varpool_node *vnode = dyn_cast <varpool_node> (node);
+             varpool_node *vnode = dyn_cast <varpool_node *> (node);
              if (vnode && vnode->definition && !vnode->analyzed)
-               varpool_analyze_node (vnode);
+               vnode->analyze ();
            }
 
          if (node->same_comdat_group)
@@ -1044,47 +1089,74 @@ analyze_functions (void)
              for (next = node->same_comdat_group;
                   next != node;
                   next = next->same_comdat_group)
-               enqueue_node (next);
+               if (!next->comdat_local_p ())
+                 enqueue_node (next);
            }
-         for (i = 0; ipa_ref_list_reference_iterate (&node->ref_list, i, ref); i++)
-           if (ref->referred->definition)
+         for (i = 0; node->iterate_reference (i, ref); i++)
+           if (ref->referred->definition
+               && (!DECL_EXTERNAL (ref->referred->decl)
+                   || ((TREE_CODE (ref->referred->decl) != FUNCTION_DECL
+                        && optimize)
+                       || (TREE_CODE (ref->referred->decl) == FUNCTION_DECL
+                           && opt_for_fn (ref->referred->decl, optimize))
+                   || node->alias
+                   || ref->referred->alias)))
              enqueue_node (ref->referred);
-          cgraph_process_new_functions ();
+         symtab->process_new_functions ();
        }
     }
-  if (optimize && flag_devirtualize)
-    update_type_inheritance_graph ();
+  update_type_inheritance_graph ();
 
   /* Collect entry points to the unit.  */
-  if (cgraph_dump_file)
+  if (symtab->dump_file)
     {
-      fprintf (cgraph_dump_file, "\n\nInitial ");
-      dump_symtab (cgraph_dump_file);
+      fprintf (symtab->dump_file, "\n\nInitial ");
+      symtab_node::dump_table (symtab->dump_file);
     }
 
-  if (cgraph_dump_file)
-    fprintf (cgraph_dump_file, "\nRemoving unused symbols:");
+  if (first_time)
+    {
+      symtab_node *snode;
+      FOR_EACH_SYMBOL (snode)
+       check_global_declaration (snode->decl);
+    }
+
+  if (symtab->dump_file)
+    fprintf (symtab->dump_file, "\nRemoving unused symbols:");
 
-  for (node = symtab_nodes;
+  for (node = symtab->first_symbol ();
        node != first_handled
        && node != first_handled_var; node = next)
     {
       next = node->next;
-      if (!node->aux && !referred_to_p (node))
+      if (!node->aux && !node->referred_to_p ())
        {
-         if (cgraph_dump_file)
-           fprintf (cgraph_dump_file, " %s", symtab_node_name (node));
-         symtab_remove_node (node);
+         if (symtab->dump_file)
+           fprintf (symtab->dump_file, " %s", node->name ());
+
+         /* See if the debugger can use anything before the DECL
+            passes away.  Perhaps it can notice a DECL that is now a
+            constant and can tag the early DIE with an appropriate
+            attribute.
+
+            Otherwise, this is the last chance the debug_hooks have
+            at looking at optimized away DECLs, since
+            late_global_decl will subsequently be called from the
+            contents of the now pruned symbol table.  */
+         if (!decl_function_context (node->decl))
+           (*debug_hooks->late_global_decl) (node->decl);
+
+         node->remove ();
          continue;
        }
-      if (cgraph_node *cnode = dyn_cast <cgraph_node> (node))
+      if (cgraph_node *cnode = dyn_cast <cgraph_node *> (node))
        {
          tree decl = node->decl;
 
          if (cnode->definition && !gimple_has_body_p (decl)
              && !cnode->alias
              && !cnode->thunk.thunk_p)
-           cgraph_reset_node (cnode);
+           cnode->reset ();
 
          gcc_assert (!cnode->definition || cnode->thunk.thunk_p
                      || cnode->alias
@@ -1095,21 +1167,20 @@ analyze_functions (void)
     }
   for (;node; node = node->next)
     node->aux = NULL;
-  first_analyzed = cgraph_first_function ();
-  first_analyzed_var = varpool_first_variable ();
-  if (cgraph_dump_file)
+  first_analyzed = symtab->first_function ();
+  first_analyzed_var = symtab->first_variable ();
+  if (symtab->dump_file)
     {
-      fprintf (cgraph_dump_file, "\n\nReclaimed ");
-      dump_symtab (cgraph_dump_file);
+      fprintf (symtab->dump_file, "\n\nReclaimed ");
+      symtab_node::dump_table (symtab->dump_file);
     }
   bitmap_obstack_release (NULL);
-  pointer_set_destroy (reachable_call_targets);
   ggc_collect ();
   /* Initialize assembler name hash, in particular we want to trigger C++
      mangling and same body alias creation before we free DECL_ARGUMENTS
      used by it.  */
   if (!seen_error ())
-    symtab_initialize_asm_name_hash ();
+    symtab->symtab_initialize_asm_name_hash ();
 
   input_location = saved_loc;
 }
@@ -1126,7 +1197,7 @@ handle_alias_pairs (void)
   
   for (i = 0; alias_pairs && alias_pairs->iterate (i, &p);)
     {
-      symtab_node *target_node = symtab_node_for_asm (p->target);
+      symtab_node *target_node = symtab_node::get_for_asmname (p->target);
 
       /* Weakrefs with target not defined in current unit are easy to handle:
         they behave just as external variables except we need to note the
@@ -1134,7 +1205,7 @@ handle_alias_pairs (void)
       if (!target_node
          && lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)) != NULL)
        {
-         symtab_node *node = symtab_get_node (p->decl);
+         symtab_node *node = symtab_node::get (p->decl);
          if (node)
            {
              node->alias_target = p->target;
@@ -1147,7 +1218,7 @@ handle_alias_pairs (void)
       else if (!target_node)
        {
          error ("%q+D aliased to undefined symbol %qE", p->decl, p->target);
-         symtab_node *node = symtab_get_node (p->decl);
+         symtab_node *node = symtab_node::get (p->decl);
          if (node)
            node->alias = false;
          alias_pairs->unordered_remove (i);
@@ -1158,7 +1229,7 @@ handle_alias_pairs (void)
          /* We use local aliases for C++ thunks to force the tailcall
             to bind locally.  This is a hack - to keep it working do
             the following (which is not strictly correct).  */
-         && (! TREE_CODE (target_node->decl) == FUNCTION_DECL
+         && (TREE_CODE (target_node->decl) != FUNCTION_DECL
              || ! DECL_VIRTUAL_P (target_node->decl))
          && ! lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)))
        {
@@ -1167,18 +1238,18 @@ handle_alias_pairs (void)
        }
 
       if (TREE_CODE (p->decl) == FUNCTION_DECL
-          && target_node && is_a <cgraph_node> (target_node))
+          && target_node && is_a <cgraph_node *> (target_node))
        {
-         struct cgraph_node *src_node = cgraph_get_node (p->decl);
+         cgraph_node *src_node = cgraph_node::get (p->decl);
          if (src_node && src_node->definition)
-            cgraph_reset_node (src_node);
-         cgraph_create_function_alias (p->decl, target_node->decl);
+           src_node->reset ();
+         cgraph_node::create_alias (p->decl, target_node->decl);
          alias_pairs->unordered_remove (i);
        }
       else if (TREE_CODE (p->decl) == VAR_DECL
-              && target_node && is_a <varpool_node> (target_node))
+              && target_node && is_a <varpool_node *> (target_node))
        {
-         varpool_create_variable_alias (p->decl, target_node->decl);
+         varpool_node::create_alias (p->decl, target_node->decl);
          alias_pairs->unordered_remove (i);
        }
       else
@@ -1199,7 +1270,7 @@ handle_alias_pairs (void)
 static void
 mark_functions_to_output (void)
 {
-  struct cgraph_node *node;
+  cgraph_node *node;
 #ifdef ENABLE_CHECKING
   bool check_same_comdat_groups = false;
 
@@ -1228,11 +1299,12 @@ mark_functions_to_output (void)
          node->process = 1;
          if (node->same_comdat_group)
            {
-             struct cgraph_node *next;
-             for (next = cgraph (node->same_comdat_group);
+             cgraph_node *next;
+             for (next = dyn_cast<cgraph_node *> (node->same_comdat_group);
                   next != node;
-                  next = cgraph (next->same_comdat_group))
-               if (!next->thunk.thunk_p && !next->alias)
+                  next = dyn_cast<cgraph_node *> (next->same_comdat_group))
+               if (!next->thunk.thunk_p && !next->alias
+                   && !next->comdat_local_p ())
                  next->process = 1;
            }
        }
@@ -1256,7 +1328,7 @@ mark_functions_to_output (void)
              && !node->clones
              && !DECL_EXTERNAL (decl))
            {
-             dump_cgraph_node (stderr, node);
+             node->debug ();
              internal_error ("failed to reclaim unneeded function");
            }
 #endif
@@ -1286,7 +1358,7 @@ mark_functions_to_output (void)
              && !node->clones
              && !DECL_EXTERNAL (decl))
            {
-             dump_cgraph_node (stderr, node);
+             node->debug ();
              internal_error ("failed to reclaim unneeded function in same "
                              "comdat group");
            }
@@ -1301,9 +1373,10 @@ mark_functions_to_output (void)
    return basic block in the function body.  */
 
 basic_block
-init_lowered_empty_function (tree decl, bool in_ssa)
+init_lowered_empty_function (tree decl, bool in_ssa, gcov_type count)
 {
   basic_block bb;
+  edge e;
 
   current_function_decl = decl;
   allocate_struct_function (decl, false);
@@ -1324,15 +1397,25 @@ init_lowered_empty_function (tree decl, bool in_ssa)
   cfun->curr_properties |= (PROP_gimple_lcf | PROP_gimple_leh | PROP_gimple_any
                            | PROP_cfg | PROP_loops);
 
-  set_loops_for_fn (cfun, ggc_alloc_cleared_loops ());
+  set_loops_for_fn (cfun, ggc_cleared_alloc<loops> ());
   init_loops_structure (cfun, loops_for_fn (cfun), 1);
   loops_for_fn (cfun)->state |= LOOPS_MAY_HAVE_MULTIPLE_LATCHES;
 
   /* Create BB for body of the function and connect it properly.  */
-  bb = create_basic_block (NULL, (void *) 0, ENTRY_BLOCK_PTR);
-  make_edge (ENTRY_BLOCK_PTR, bb, EDGE_FALLTHRU);
-  make_edge (bb, EXIT_BLOCK_PTR, 0);
-  add_bb_to_loop (bb, ENTRY_BLOCK_PTR->loop_father);
+  ENTRY_BLOCK_PTR_FOR_FN (cfun)->count = count;
+  ENTRY_BLOCK_PTR_FOR_FN (cfun)->frequency = REG_BR_PROB_BASE;
+  EXIT_BLOCK_PTR_FOR_FN (cfun)->count = count;
+  EXIT_BLOCK_PTR_FOR_FN (cfun)->frequency = REG_BR_PROB_BASE;
+  bb = create_basic_block (NULL, ENTRY_BLOCK_PTR_FOR_FN (cfun));
+  bb->count = count;
+  bb->frequency = BB_FREQ_MAX;
+  e = make_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun), bb, EDGE_FALLTHRU);
+  e->count = count;
+  e->probability = REG_BR_PROB_BASE;
+  e = make_edge (bb, EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
+  e->count = count;
+  e->probability = REG_BR_PROB_BASE;
+  add_bb_to_loop (bb, ENTRY_BLOCK_PTR_FOR_FN (cfun)->loop_father);
 
   return bb;
 }
@@ -1347,7 +1430,7 @@ thunk_adjust (gimple_stmt_iterator * bsi,
              tree ptr, bool this_adjusting,
              HOST_WIDE_INT fixed_offset, tree virtual_offset)
 {
-  gimple stmt;
+  gassign *stmt;
   tree ret;
 
   if (this_adjusting
@@ -1442,22 +1525,28 @@ thunk_adjust (gimple_stmt_iterator * bsi,
 }
 
 /* Expand thunk NODE to gimple if possible.
+   When FORCE_GIMPLE_THUNK is true, gimple thunk is created and
+   no assembler is produced.
    When OUTPUT_ASM_THUNK is true, also produce assembler for
    thunks that are not lowered.  */
 
 bool
-expand_thunk (struct cgraph_node *node, bool output_asm_thunks)
+cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
 {
-  bool this_adjusting = node->thunk.this_adjusting;
-  HOST_WIDE_INT fixed_offset = node->thunk.fixed_offset;
-  HOST_WIDE_INT virtual_value = node->thunk.virtual_value;
+  bool this_adjusting = thunk.this_adjusting;
+  HOST_WIDE_INT fixed_offset = thunk.fixed_offset;
+  HOST_WIDE_INT virtual_value = thunk.virtual_value;
   tree virtual_offset = NULL;
-  tree alias = node->callees->callee->decl;
-  tree thunk_fndecl = node->decl;
+  tree alias = callees->callee->decl;
+  tree thunk_fndecl = decl;
   tree a;
 
+  /* Instrumentation thunk is the same function with
+     a different signature.  Never need to expand it.  */
+  if (thunk.add_pointer_bounds_args)
+    return false;
 
-  if (this_adjusting
+  if (!force_gimple_thunk && this_adjusting
       && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
                                              virtual_value, alias))
     {
@@ -1466,16 +1555,20 @@ expand_thunk (struct cgraph_node *node, bool output_asm_thunks)
       tree restype = TREE_TYPE (TREE_TYPE (thunk_fndecl));
 
       if (!output_asm_thunks)
-       return false;
+       {
+         analyzed = true;
+         return false;
+       }
 
       if (in_lto_p)
-       cgraph_get_body (node);
+       get_untransformed_body ();
       a = DECL_ARGUMENTS (thunk_fndecl);
       
       current_function_decl = thunk_fndecl;
 
       /* Ensure thunks are emitted in their correct sections.  */
-      resolve_unique_section (thunk_fndecl, 0, flag_function_sections);
+      resolve_unique_section (thunk_fndecl, 0,
+                             flag_function_sections);
 
       DECL_RESULT (thunk_fndecl)
        = build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
@@ -1504,8 +1597,16 @@ expand_thunk (struct cgraph_node *node, bool output_asm_thunks)
       free_after_compilation (cfun);
       set_cfun (NULL);
       TREE_ASM_WRITTEN (thunk_fndecl) = 1;
-      node->thunk.thunk_p = false;
-      node->analyzed = false;
+      thunk.thunk_p = false;
+      analyzed = false;
+    }
+  else if (stdarg_p (TREE_TYPE (thunk_fndecl)))
+    {
+      error ("generic thunk code fails for method %qD which uses %<...%>",
+            thunk_fndecl);
+      TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+      analyzed = true;
+      return false;
     }
   else
     {
@@ -1517,24 +1618,26 @@ expand_thunk (struct cgraph_node *node, bool output_asm_thunks)
       int i;
       tree resdecl;
       tree restmp = NULL;
-      vec<tree> vargs;
+      tree resbnd = NULL;
 
-      gimple call;
-      gimple ret;
+      gcall *call;
+      greturn *ret;
+      bool alias_is_noreturn = TREE_THIS_VOLATILE (alias);
 
       if (in_lto_p)
-       cgraph_get_body (node);
+       get_untransformed_body ();
       a = DECL_ARGUMENTS (thunk_fndecl);
-      
+
       current_function_decl = thunk_fndecl;
 
       /* Ensure thunks are emitted in their correct sections.  */
-      resolve_unique_section (thunk_fndecl, 0, flag_function_sections);
+      resolve_unique_section (thunk_fndecl, 0,
+                             flag_function_sections);
 
       DECL_IGNORED_P (thunk_fndecl) = 1;
       bitmap_obstack_initialize (NULL);
 
-      if (node->thunk.virtual_offset_p)
+      if (thunk.virtual_offset_p)
         virtual_offset = size_int (virtual_value);
 
       /* Build the return declaration for the function.  */
@@ -1550,20 +1653,36 @@ expand_thunk (struct cgraph_node *node, bool output_asm_thunks)
       else
        resdecl = DECL_RESULT (thunk_fndecl);
 
-      bb = then_bb = else_bb = return_bb = init_lowered_empty_function (thunk_fndecl, true);
+      bb = then_bb = else_bb = return_bb
+       = init_lowered_empty_function (thunk_fndecl, true, count);
 
       bsi = gsi_start_bb (bb);
 
       /* Build call to the function being thunked.  */
-      if (!VOID_TYPE_P (restype))
+      if (!VOID_TYPE_P (restype) && !alias_is_noreturn)
        {
          if (DECL_BY_REFERENCE (resdecl))
-           restmp = gimple_fold_indirect_ref (resdecl);
+           {
+             restmp = gimple_fold_indirect_ref (resdecl);
+             if (!restmp)
+               restmp = build2 (MEM_REF,
+                                TREE_TYPE (TREE_TYPE (DECL_RESULT (alias))),
+                                resdecl,
+                                build_int_cst (TREE_TYPE
+                                  (DECL_RESULT (alias)), 0));
+           }
          else if (!is_gimple_reg_type (restype))
            {
-             restmp = resdecl;
-             add_local_decl (cfun, restmp);
-             BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp;
+             if (aggregate_value_p (resdecl, TREE_TYPE (thunk_fndecl)))
+               {
+                 restmp = resdecl;
+
+                 if (TREE_CODE (restmp) == VAR_DECL)
+                   add_local_decl (cfun, restmp);
+                 BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp;
+               }
+             else
+               restmp = create_tmp_var (restype, "retval");
            }
          else
            restmp = create_tmp_reg (restype, "retval");
@@ -1571,29 +1690,62 @@ expand_thunk (struct cgraph_node *node, bool output_asm_thunks)
 
       for (arg = a; arg; arg = DECL_CHAIN (arg))
         nargs++;
-      vargs.create (nargs);
+      auto_vec<tree> vargs (nargs);
+      i = 0;
+      arg = a;
       if (this_adjusting)
-        vargs.quick_push (thunk_adjust (&bsi, a, 1, fixed_offset,
-                                       virtual_offset));
-      else if (nargs)
-        vargs.quick_push (a);
+       {
+         vargs.quick_push (thunk_adjust (&bsi, a, 1, fixed_offset,
+                                         virtual_offset));
+         arg = DECL_CHAIN (a);
+         i = 1;
+       }
 
       if (nargs)
-        for (i = 1, arg = DECL_CHAIN (a); i < nargs; i++, arg = DECL_CHAIN (arg))
-         vargs.quick_push (arg);
+       for (; i < nargs; i++, arg = DECL_CHAIN (arg))
+         {
+           tree tmp = arg;
+           if (!is_gimple_val (arg))
+             {
+               tmp = create_tmp_reg (TYPE_MAIN_VARIANT
+                                     (TREE_TYPE (arg)), "arg");
+               gimple stmt = gimple_build_assign (tmp, arg);
+               gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
+             }
+           vargs.quick_push (tmp);
+         }
       call = gimple_build_call_vec (build_fold_addr_expr_loc (0, alias), vargs);
-      node->callees->call_stmt = call;
-      vargs.release ();
+      callees->call_stmt = call;
       gimple_call_set_from_thunk (call, true);
-      if (restmp)
+      gimple_call_set_with_bounds (call, instrumentation_clone);
+
+      /* Return slot optimization is always possible and in fact requred to
+         return values with DECL_BY_REFERENCE.  */
+      if (aggregate_value_p (resdecl, TREE_TYPE (thunk_fndecl))
+         && (!is_gimple_reg_type (TREE_TYPE (resdecl))
+             || DECL_BY_REFERENCE (resdecl)))
+        gimple_call_set_return_slot_opt (call, true);
+
+      if (restmp && !alias_is_noreturn)
        {
           gimple_call_set_lhs (call, restmp);
          gcc_assert (useless_type_conversion_p (TREE_TYPE (restmp),
                                                 TREE_TYPE (TREE_TYPE (alias))));
        }
       gsi_insert_after (&bsi, call, GSI_NEW_STMT);
-      if (!(gimple_call_flags (call) & ECF_NORETURN))
+      if (!alias_is_noreturn)
        {
+         if (instrumentation_clone
+             && !DECL_BY_REFERENCE (resdecl)
+             && restmp
+             && BOUNDED_P (restmp))
+           {
+             resbnd = chkp_insert_retbnd_call (NULL, restmp, &bsi);
+             create_edge (get_create (gimple_call_fndecl (gsi_stmt (bsi))),
+                          as_a <gcall *> (gsi_stmt (bsi)),
+                          callees->count, callees->frequency);
+           }
+
          if (restmp && !this_adjusting
              && (fixed_offset || virtual_offset))
            {
@@ -1602,13 +1754,20 @@ expand_thunk (struct cgraph_node *node, bool output_asm_thunks)
              if (TREE_CODE (TREE_TYPE (restmp)) == POINTER_TYPE)
                {
                  gimple stmt;
+                 edge e;
                  /* If the return type is a pointer, we need to
                     protect against NULL.  We know there will be an
                     adjustment, because that's why we're emitting a
                     thunk.  */
-                 then_bb = create_basic_block (NULL, (void *) 0, bb);
-                 return_bb = create_basic_block (NULL, (void *) 0, then_bb);
-                 else_bb = create_basic_block (NULL, (void *) 0, else_bb);
+                 then_bb = create_basic_block (NULL, bb);
+                 then_bb->count = count - count / 16;
+                 then_bb->frequency = BB_FREQ_MAX - BB_FREQ_MAX / 16;
+                 return_bb = create_basic_block (NULL, then_bb);
+                 return_bb->count = count;
+                 return_bb->frequency = BB_FREQ_MAX;
+                 else_bb = create_basic_block (NULL, else_bb);
+                 then_bb->count = count / 16;
+                 then_bb->frequency = BB_FREQ_MAX / 16;
                  add_bb_to_loop (then_bb, bb->loop_father);
                  add_bb_to_loop (return_bb, bb->loop_father);
                  add_bb_to_loop (else_bb, bb->loop_father);
@@ -1618,11 +1777,21 @@ expand_thunk (struct cgraph_node *node, bool output_asm_thunks)
                                            build_zero_cst (TREE_TYPE (restmp)),
                                            NULL_TREE, NULL_TREE);
                  gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
-                 make_edge (bb, then_bb, EDGE_TRUE_VALUE);
-                 make_edge (bb, else_bb, EDGE_FALSE_VALUE);
-                 make_edge (return_bb, EXIT_BLOCK_PTR, 0);
-                 make_edge (then_bb, return_bb, EDGE_FALLTHRU);
-                 make_edge (else_bb, return_bb, EDGE_FALLTHRU);
+                 e = make_edge (bb, then_bb, EDGE_TRUE_VALUE);
+                 e->probability = REG_BR_PROB_BASE - REG_BR_PROB_BASE / 16;
+                 e->count = count - count / 16;
+                 e = make_edge (bb, else_bb, EDGE_FALSE_VALUE);
+                 e->probability = REG_BR_PROB_BASE / 16;
+                 e->count = count / 16;
+                 e = make_edge (return_bb, EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
+                 e->probability = REG_BR_PROB_BASE;
+                 e->count = count;
+                 e = make_edge (then_bb, return_bb, EDGE_FALLTHRU);
+                 e->probability = REG_BR_PROB_BASE;
+                 e->count = count - count / 16;
+                 e = make_edge (else_bb, return_bb, EDGE_FALLTHRU);
+                 e->probability = REG_BR_PROB_BASE;
+                 e->count = count / 16;
                  bsi = gsi_last_bb (then_bb);
                }
 
@@ -1642,7 +1811,12 @@ expand_thunk (struct cgraph_node *node, bool output_asm_thunks)
            gimple_call_set_tail (call, true);
 
          /* Build return value.  */
-         ret = gimple_build_return (restmp);
+         if (!DECL_BY_REFERENCE (resdecl))
+           ret = gimple_build_return (restmp);
+         else
+           ret = gimple_build_return (resdecl);
+         gimple_return_set_retbnd (ret, resbnd);
+
          gsi_insert_after (&bsi, ret, GSI_NEW_STMT);
        }
       else
@@ -1652,6 +1826,8 @@ expand_thunk (struct cgraph_node *node, bool output_asm_thunks)
        }
 
       cfun->gimple_df->in_ssa_p = true;
+      profile_status_for_fn (cfun)
+        = count ? PROFILE_READ : PROFILE_GUESSED;
       /* FIXME: C++ FE should stop setting TREE_ASM_WRITTEN on thunks.  */
       TREE_ASM_WRITTEN (thunk_fndecl) = false;
       delete_unreachable_blocks ();
@@ -1659,11 +1835,12 @@ expand_thunk (struct cgraph_node *node, bool output_asm_thunks)
 #ifdef ENABLE_CHECKING
       verify_flow_info ();
 #endif
+      free_dominance_info (CDI_DOMINATORS);
 
       /* Since we want to emit the thunk, we explicitly mark its name as
         referenced.  */
-      node->thunk.thunk_p = false;
-      node->lowered = true;
+      thunk.thunk_p = false;
+      lowered = true;
       bitmap_obstack_release (NULL);
     }
   current_function_decl = NULL;
@@ -1671,64 +1848,62 @@ expand_thunk (struct cgraph_node *node, bool output_asm_thunks)
   return true;
 }
 
-/* Assemble thunks and aliases associated to NODE.  */
+/* Assemble thunks and aliases associated to node.  */
 
-static void
-assemble_thunks_and_aliases (struct cgraph_node *node)
+void
+cgraph_node::assemble_thunks_and_aliases (void)
 {
-  struct cgraph_edge *e;
-  int i;
-  struct ipa_ref *ref;
+  cgraph_edge *e;
+  ipa_ref *ref;
 
-  for (e = node->callers; e;)
-    if (e->caller->thunk.thunk_p)
+  for (e = callers; e;)
+    if (e->caller->thunk.thunk_p
+       && !e->caller->thunk.add_pointer_bounds_args)
       {
-       struct cgraph_node *thunk = e->caller;
+       cgraph_node *thunk = e->caller;
 
        e = e->next_caller;
-       assemble_thunks_and_aliases (thunk);
-        expand_thunk (thunk, true);
+       thunk->expand_thunk (true, false);
+       thunk->assemble_thunks_and_aliases ();
       }
     else
       e = e->next_caller;
-  for (i = 0; ipa_ref_list_referring_iterate (&node->ref_list,
-                                            i, ref); i++)
-    if (ref->use == IPA_REF_ALIAS)
-      {
-       struct cgraph_node *alias = ipa_ref_referring_node (ref);
-        bool saved_written = TREE_ASM_WRITTEN (node->decl);
-
-       /* Force assemble_alias to really output the alias this time instead
-          of buffering it in same alias pairs.  */
-       TREE_ASM_WRITTEN (node->decl) = 1;
-       do_assemble_alias (alias->decl,
-                          DECL_ASSEMBLER_NAME (node->decl));
-       assemble_thunks_and_aliases (alias);
-       TREE_ASM_WRITTEN (node->decl) = saved_written;
-      }
+
+  FOR_EACH_ALIAS (this, ref)
+    {
+      cgraph_node *alias = dyn_cast <cgraph_node *> (ref->referring);
+      bool saved_written = TREE_ASM_WRITTEN (decl);
+
+      /* Force assemble_alias to really output the alias this time instead
+        of buffering it in same alias pairs.  */
+      TREE_ASM_WRITTEN (decl) = 1;
+      do_assemble_alias (alias->decl,
+                        DECL_ASSEMBLER_NAME (decl));
+      alias->assemble_thunks_and_aliases ();
+      TREE_ASM_WRITTEN (decl) = saved_written;
+    }
 }
 
-/* Expand function specified by NODE.  */
+/* Expand function specified by node.  */
 
-static void
-expand_function (struct cgraph_node *node)
+void
+cgraph_node::expand (void)
 {
-  tree decl = node->decl;
   location_t saved_loc;
 
   /* We ought to not compile any inline clones.  */
-  gcc_assert (!node->global.inlined_to);
+  gcc_assert (!global.inlined_to);
 
   announce_function (decl);
-  node->process = 0;
-  gcc_assert (node->lowered);
-  cgraph_get_body (node);
+  process = 0;
+  gcc_assert (lowered);
+  get_untransformed_body ();
 
   /* Generate RTL for the body of DECL.  */
 
   timevar_push (TV_REST_OF_COMPILATION);
 
-  gcc_assert (cgraph_global_info_ready);
+  gcc_assert (symtab->global_info_ready);
 
   /* Initialize the default bitmap obstack.  */
   bitmap_obstack_initialize (NULL);
@@ -1750,7 +1925,7 @@ expand_function (struct cgraph_node *node)
   /* Signal the start of passes.  */
   invoke_plugin_callbacks (PLUGIN_ALL_PASSES_START, NULL);
 
-  execute_pass_list (g->get_passes ()->all_passes);
+  execute_pass_list (cfun, g->get_passes ()->all_passes);
 
   /* Signal the end of passes.  */
   invoke_plugin_callbacks (PLUGIN_ALL_PASSES_END, NULL);
@@ -1786,7 +1961,7 @@ expand_function (struct cgraph_node *node)
 
   gimple_set_body (decl, NULL);
   if (DECL_STRUCT_FUNCTION (decl) == 0
-      && !cgraph_get_node (decl)->origin)
+      && !cgraph_node::get (decl)->origin)
     {
       /* Stop pointing to the local nodes about to be freed.
         But DECL_INITIAL must remain nonzero so we know this
@@ -1813,14 +1988,31 @@ expand_function (struct cgraph_node *node)
      make one pass assemblers, like one on AIX, happy.  See PR 50689.
      FIXME: Perhaps thunks should be move before function IFF they are not in comdat
      groups.  */
-  assemble_thunks_and_aliases (node);
-  cgraph_release_function_body (node);
+  assemble_thunks_and_aliases ();
+  release_body ();
   /* Eliminate all call edges.  This is important so the GIMPLE_CALL no longer
      points to the dead function body.  */
-  cgraph_node_remove_callees (node);
-  ipa_remove_all_references (&node->ref_list);
+  remove_callees ();
+  remove_all_references ();
 }
 
+/* Node comparer that is responsible for the order that corresponds
+   to time when a function was launched for the first time.  */
+
+static int
+node_cmp (const void *pa, const void *pb)
+{
+  const cgraph_node *a = *(const cgraph_node * const *) pa;
+  const cgraph_node *b = *(const cgraph_node * const *) pb;
+
+  /* Functions with time profile must be before these without profile.  */
+  if (!a->tp_first_run || !b->tp_first_run)
+    return a->tp_first_run - b->tp_first_run;
+
+  return a->tp_first_run != b->tp_first_run
+        ? b->tp_first_run - a->tp_first_run
+        : b->order - a->order;
+}
 
 /* Expand all functions that must be output.
 
@@ -1835,13 +2027,15 @@ expand_function (struct cgraph_node *node)
 static void
 expand_all_functions (void)
 {
-  struct cgraph_node *node;
-  struct cgraph_node **order = XCNEWVEC (struct cgraph_node *, cgraph_n_nodes);
+  cgraph_node *node;
+  cgraph_node **order = XCNEWVEC (cgraph_node *,
+                                        symtab->cgraph_count);
+  unsigned int expanded_func_count = 0, profiled_func_count = 0;
   int order_pos, new_order_pos = 0;
   int i;
 
   order_pos = ipa_reverse_postorder (order);
-  gcc_assert (order_pos == cgraph_n_nodes);
+  gcc_assert (order_pos == symtab->cgraph_count);
 
   /* Garbage collector may remove inline clones we eliminate during
      optimization.  So we must be sure to not reference them.  */
@@ -1849,19 +2043,40 @@ expand_all_functions (void)
     if (order[i]->process)
       order[new_order_pos++] = order[i];
 
+  if (flag_profile_reorder_functions)
+    qsort (order, new_order_pos, sizeof (cgraph_node *), node_cmp);
+
   for (i = new_order_pos - 1; i >= 0; i--)
     {
       node = order[i];
+
       if (node->process)
        {
+         expanded_func_count++;
+         if(node->tp_first_run)
+           profiled_func_count++;
+
+         if (symtab->dump_file)
+           fprintf (symtab->dump_file,
+                    "Time profile order in expand_all_functions:%s:%d\n",
+                    node->asm_name (), node->tp_first_run);
          node->process = 0;
-         expand_function (node);
+         node->expand ();
        }
     }
-  cgraph_process_new_functions ();
 
-  free (order);
+    if (dump_file)
+      fprintf (dump_file, "Expanded functions with time profile (%s):%u/%u\n",
+               main_input_filename, profiled_func_count, expanded_func_count);
+
+  if (symtab->dump_file && flag_profile_reorder_functions)
+    fprintf (symtab->dump_file, "Expanded functions with time profile:%u/%u\n",
+             profiled_func_count, expanded_func_count);
 
+  symtab->process_new_functions ();
+  free_gimplify_stack ();
+
+  free (order);
 }
 
 /* This is used to sort the node types by the cgraph order number.  */
@@ -1879,9 +2094,9 @@ struct cgraph_order_sort
   enum cgraph_order_sort_kind kind;
   union
   {
-    struct cgraph_node *f;
-    struct varpool_node *v;
-    struct asm_node *a;
+    cgraph_node *f;
+    varpool_node *v;
+    asm_node *a;
   } u;
 };
 
@@ -1889,25 +2104,27 @@ struct cgraph_order_sort
    according to their order fields, which is the order in which they
    appeared in the file.  This implements -fno-toplevel-reorder.  In
    this mode we may output functions and variables which don't really
-   need to be output.  */
+   need to be output.
+   When NO_REORDER is true only do this for symbols marked no reorder. */
 
 static void
-output_in_order (void)
+output_in_order (bool no_reorder)
 {
   int max;
-  struct cgraph_order_sort *nodes;
+  cgraph_order_sort *nodes;
   int i;
-  struct cgraph_node *pf;
-  struct varpool_node *pv;
-  struct asm_node *pa;
-
-  max = symtab_order;
-  nodes = XCNEWVEC (struct cgraph_order_sort, max);
+  cgraph_node *pf;
+  varpool_node *pv;
+  asm_node *pa;
+  max = symtab->order;
+  nodes = XCNEWVEC (cgraph_order_sort, max);
 
   FOR_EACH_DEFINED_FUNCTION (pf)
     {
       if (pf->process && !pf->thunk.thunk_p && !pf->alias)
        {
+         if (no_reorder && !pf->no_reorder)
+           continue;
          i = pf->order;
          gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
          nodes[i].kind = ORDER_FUNCTION;
@@ -1918,13 +2135,15 @@ output_in_order (void)
   FOR_EACH_DEFINED_VARIABLE (pv)
     if (!DECL_EXTERNAL (pv->decl))
       {
+       if (no_reorder && !pv->no_reorder)
+           continue;
        i = pv->order;
        gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
        nodes[i].kind = ORDER_VAR;
        nodes[i].u.v = pv;
       }
 
-  for (pa = asm_nodes; pa; pa = pa->next)
+  for (pa = symtab->first_asm_symbol (); pa; pa = pa->next)
     {
       i = pa->order;
       gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
@@ -1936,7 +2155,7 @@ output_in_order (void)
 
   for (i = 0; i < max; ++i)
     if (nodes[i].kind == ORDER_VAR)
-      varpool_finalize_named_section_flags (nodes[i].u.v);
+      nodes[i].u.v->finalize_named_section_flags ();
 
   for (i = 0; i < max; ++i)
     {
@@ -1944,11 +2163,11 @@ output_in_order (void)
        {
        case ORDER_FUNCTION:
          nodes[i].u.f->process = 0;
-         expand_function (nodes[i].u.f);
+         nodes[i].u.f->expand ();
          break;
 
        case ORDER_VAR:
-         varpool_assemble_decl (nodes[i].u.v);
+         nodes[i].u.v->assemble_decl ();
          break;
 
        case ORDER_ASM:
@@ -1963,7 +2182,8 @@ output_in_order (void)
        }
     }
 
-  asm_nodes = NULL;
+  symtab->clear_asm_symbols ();
+
   free (nodes);
 }
 
@@ -1986,16 +2206,14 @@ ipa_passes (void)
        return;
     }
 
-  /* We never run removal of unreachable nodes after early passes.  This is
-     because TODO is run before the subpasses.  It is important to remove
-     the unreachable functions to save works at IPA level and to get LTO
-     symbol tables right.  */
-  symtab_remove_unreachable_nodes (true, cgraph_dump_file);
+  /* This extra symtab_remove_unreachable_nodes pass tends to catch some
+     devirtualization and other changes where removal iterate.  */
+  symtab->remove_unreachable_nodes (symtab->dump_file);
 
   /* If pass_all_early_optimizations was not scheduled, the state of
      the cgraph will not be properly updated.  Update it now.  */
-  if (cgraph_state < CGRAPH_STATE_IPA_SSA)
-    cgraph_state = CGRAPH_STATE_IPA_SSA;
+  if (symtab->state < IPA_SSA)
+    symtab->state = IPA_SSA;
 
   if (!in_lto_p)
     {
@@ -2005,23 +2223,34 @@ ipa_passes (void)
       /* Process new functions added.  */
       set_cfun (NULL);
       current_function_decl = NULL;
-      cgraph_process_new_functions ();
+      symtab->process_new_functions ();
 
       execute_ipa_summary_passes
-       ((struct ipa_opt_pass_d *) passes->all_regular_ipa_passes);
+       ((ipa_opt_pass_d *) passes->all_regular_ipa_passes);
     }
 
   /* Some targets need to handle LTO assembler output specially.  */
-  if (flag_generate_lto)
+  if (flag_generate_lto || flag_generate_offload)
     targetm.asm_out.lto_start ();
 
-  execute_ipa_summary_passes ((struct ipa_opt_pass_d *)
-                             passes->all_lto_gen_passes);
-
   if (!in_lto_p)
-    ipa_write_summaries ();
+    {
+      if (g->have_offload)
+       {
+         section_name_prefix = OFFLOAD_SECTION_NAME_PREFIX;
+         lto_stream_offload_p = true;
+         ipa_write_summaries ();
+         lto_stream_offload_p = false;
+       }
+      if (flag_lto)
+       {
+         section_name_prefix = LTO_SECTION_NAME_PREFIX;
+         lto_stream_offload_p = false;
+         ipa_write_summaries ();
+       }
+    }
 
-  if (flag_generate_lto)
+  if (flag_generate_lto || flag_generate_offload)
     targetm.asm_out.lto_end ();
 
   if (!flag_ltrans && (in_lto_p || !flag_lto || flag_fat_lto_objects))
@@ -2046,13 +2275,17 @@ get_alias_symbol (tree decl)
 /* Weakrefs may be associated to external decls and thus not output
    at expansion time.  Emit all necessary aliases.  */
 
-static void
-output_weakrefs (void)
+void
+symbol_table::output_weakrefs (void)
 {
   symtab_node *node;
+  cgraph_node *cnode;
   FOR_EACH_SYMBOL (node)
     if (node->alias
         && !TREE_ASM_WRITTEN (node->decl)
+       && (!(cnode = dyn_cast <cgraph_node *> (node))
+           || !cnode->instrumented_version
+           || !TREE_ASM_WRITTEN (cnode->instrumented_version->decl))
        && node->weakref)
       {
        tree target;
@@ -2067,7 +2300,7 @@ output_weakrefs (void)
                    ? DECL_ASSEMBLER_NAME (node->alias_target)
                    : node->alias_target);
        else if (node->analyzed)
-         target = DECL_ASSEMBLER_NAME (symtab_alias_target (node)->decl);
+         target = DECL_ASSEMBLER_NAME (node->get_alias_target ()->decl);
        else
          {
            gcc_unreachable ();
@@ -2077,26 +2310,16 @@ output_weakrefs (void)
       }
 }
 
-/* Initialize callgraph dump file.  */
-
-void
-init_cgraph (void)
-{
-  if (!cgraph_dump_file)
-    cgraph_dump_file = dump_begin (TDI_cgraph, NULL);
-}
-
-
 /* Perform simple optimizations based on callgraph.  */
 
 void
-compile (void)
+symbol_table::compile (void)
 {
   if (seen_error ())
     return;
 
 #ifdef ENABLE_CHECKING
-  verify_symtab ();
+  symtab_node::verify_symtab_nodes ();
 #endif
 
   timevar_push (TV_CGRAPHOPT);
@@ -2107,10 +2330,14 @@ compile (void)
     }
   if (!quiet_flag)
     fprintf (stderr, "Performing interprocedural optimizations\n");
-  cgraph_state = CGRAPH_STATE_IPA;
+  state = IPA;
+
+  /* Offloading requires LTO infrastructure.  */
+  if (!in_lto_p && g->have_offload)
+    flag_generate_offload = 1;
 
   /* If LTO is enabled, initialize the streamer hooks needed by GIMPLE.  */
-  if (flag_lto)
+  if (flag_generate_lto || flag_generate_offload)
     lto_streamer_hooks_init ();
 
   /* Don't run the IPA passes if there was any error or sorry messages.  */
@@ -2125,14 +2352,11 @@ compile (void)
       return;
     }
 
-  /* This pass remove bodies of extern inline functions we never inlined.
-     Do this later so other IPA passes see what is really going on.  */
-  symtab_remove_unreachable_nodes (false, dump_file);
-  cgraph_global_info_ready = true;
-  if (cgraph_dump_file)
+  global_info_ready = true;
+  if (dump_file)
     {
-      fprintf (cgraph_dump_file, "Optimized ");
-      dump_symtab (cgraph_dump_file);
+      fprintf (dump_file, "Optimized ");
+      symtab_node:: dump_table (dump_file);
     }
   if (post_ipa_mem_report)
     {
@@ -2146,16 +2370,12 @@ compile (void)
   if (!quiet_flag)
     fprintf (stderr, "Assembling functions:\n");
 #ifdef ENABLE_CHECKING
-  verify_symtab ();
+  symtab_node::verify_symtab_nodes ();
 #endif
 
-  cgraph_materialize_all_clones ();
+  materialize_all_clones ();
   bitmap_obstack_initialize (NULL);
   execute_ipa_pass_list (g->get_passes ()->all_late_ipa_passes);
-  symtab_remove_unreachable_nodes (true, dump_file);
-#ifdef ENABLE_CHECKING
-  verify_symtab ();
-#endif
   bitmap_obstack_release (NULL);
   mark_functions_to_output ();
 
@@ -2181,37 +2401,39 @@ compile (void)
           (DECL_ASSEMBLER_NAME (node->decl)) = 1;
        TREE_CHAIN (DECL_ASSEMBLER_NAME (node->decl))
           = (node->alias_target ? node->alias_target
-             : DECL_ASSEMBLER_NAME (symtab_alias_target (node)->decl));
+             : DECL_ASSEMBLER_NAME (node->get_alias_target ()->decl));
       }
 #endif
 
-  cgraph_state = CGRAPH_STATE_EXPANSION;
+  state = EXPANSION;
+
   if (!flag_toplevel_reorder)
-    output_in_order ();
+    output_in_order (false);
   else
     {
-      output_asm_statements ();
-
+      /* Output first asm statements and anything ordered. The process
+         flag is cleared for these nodes, so we skip them later.  */
+      output_in_order (true);
       expand_all_functions ();
-      varpool_output_variables ();
+      output_variables ();
     }
 
-  cgraph_process_new_functions ();
-  cgraph_state = CGRAPH_STATE_FINISHED;
+  process_new_functions ();
+  state = FINISHED;
   output_weakrefs ();
 
-  if (cgraph_dump_file)
+  if (dump_file)
     {
-      fprintf (cgraph_dump_file, "\nFinal ");
-      dump_symtab (cgraph_dump_file);
+      fprintf (dump_file, "\nFinal ");
+      symtab_node::dump_table (dump_file);
     }
 #ifdef ENABLE_CHECKING
-  verify_symtab ();
+  symtab_node::verify_symtab_nodes ();
   /* Double check that all inline clones are gone and that all
      function bodies have been released from memory.  */
   if (!seen_error ())
     {
-      struct cgraph_node *node;
+      cgraph_node *node;
       bool error_found = false;
 
       FOR_EACH_DEFINED_FUNCTION (node)
@@ -2219,7 +2441,7 @@ compile (void)
            || gimple_has_body_p (node->decl))
          {
            error_found = true;
-           dump_cgraph_node (stderr, node);
+           node->debug ();
          }
       if (error_found)
        internal_error ("nodes with unreleased memory found");
@@ -2231,7 +2453,7 @@ compile (void)
 /* Analyze the whole compilation unit once it is parsed completely.  */
 
 void
-finalize_compilation_unit (void)
+symbol_table::finalize_compilation_unit (void)
 {
   timevar_push (TV_CGRAPH);
 
@@ -2260,13 +2482,23 @@ finalize_compilation_unit (void)
 
   /* Gimplify and lower all functions, compute reachability and
      remove unreachable nodes.  */
-  analyze_functions ();
+  analyze_functions (/*first_time=*/true);
 
   /* Mark alias targets necessary and emit diagnostics.  */
   handle_alias_pairs ();
 
   /* Gimplify and lower thunks.  */
-  analyze_functions ();
+  analyze_functions (/*first_time=*/false);
+
+  /* Emit early debug for reachable functions, and by consequence,
+     locally scoped symbols.  */
+  struct cgraph_node *cnode;
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cnode)
+    (*debug_hooks->early_global_decl) (cnode->decl);
+
+  /* Clean up anything that needs cleaning up after initial debug
+     generation.  */
+  (*debug_hooks->early_finish) ();
 
   /* Finally drive the pass manager.  */
   compile ();
@@ -2274,5 +2506,62 @@ finalize_compilation_unit (void)
   timevar_pop (TV_CGRAPH);
 }
 
+/* Reset all state within cgraphunit.c so that we can rerun the compiler
+   within the same process.  For use by toplev::finalize.  */
+
+void
+cgraphunit_c_finalize (void)
+{
+  gcc_assert (cgraph_new_nodes.length () == 0);
+  cgraph_new_nodes.truncate (0);
+
+  vtable_entry_type = NULL;
+  queued_nodes = &symtab_terminator;
+
+  first_analyzed = NULL;
+  first_analyzed_var = NULL;
+}
+
+/* Creates a wrapper from cgraph_node to TARGET node. Thunk is used for this
+   kind of wrapper method.  */
+
+void
+cgraph_node::create_wrapper (cgraph_node *target)
+{
+  /* Preserve DECL_RESULT so we get right by reference flag.  */
+  tree decl_result = DECL_RESULT (decl);
+
+  /* Remove the function's body but keep arguments to be reused
+     for thunk.  */
+  release_body (true);
+  reset ();
+
+  DECL_UNINLINABLE (decl) = false;
+  DECL_RESULT (decl) = decl_result;
+  DECL_INITIAL (decl) = NULL;
+  allocate_struct_function (decl, false);
+  set_cfun (NULL);
+
+  /* Turn alias into thunk and expand it into GIMPLE representation.  */
+  definition = true;
+
+  memset (&thunk, 0, sizeof (cgraph_thunk_info));
+  thunk.thunk_p = true;
+  create_edge (target, NULL, count, CGRAPH_FREQ_BASE);
+
+  tree arguments = DECL_ARGUMENTS (decl);
+
+  while (arguments)
+    {
+      TREE_ADDRESSABLE (arguments) = false;
+      arguments = TREE_CHAIN (arguments);
+    }
+
+  expand_thunk (false, true);
+
+  /* Inline summary set-up.  */
+  analyze ();
+  inline_analyze_function (this);
+}
 
 #include "gt-cgraphunit.h"