Add VEC_WIDEN_MULT_EVEN/ODD_EXPR
[platform/upstream/gcc.git] / gcc / cgraphunit.c
index 26c69ae..e47008f 100644 (file)
@@ -1,6 +1,6 @@
-/* Callgraph based interprocedural optimizations.
+/* Driver of optimization process
    Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-   2011 Free Software Foundation, Inc.
+   2011, 2012 Free Software Foundation, Inc.
    Contributed by Jan Hubicka
 
 This file is part of GCC.
@@ -19,11 +19,10 @@ You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
 
-/* This module implements main driver of compilation process as well as
-   few basic interprocedural optimizers.
+/* This module implements main driver of compilation process.
 
    The main scope of this file is to act as an interface in between
-   tree based frontends and the backend (and middle end)
+   tree based frontends and the backend.
 
    The front-end is supposed to use following functionality:
 
@@ -40,77 +39,131 @@ along with GCC; see the file COPYING3.  If not see
       This function has same behavior as the above but is used for static
       variables.
 
-    - cgraph_finalize_compilation_unit
+    - add_asm_node
+
+      Insert new toplevel ASM statement
+
+    - finalize_compilation_unit
 
       This function is called once (source level) compilation unit is finalized
       and it will no longer change.
 
-      In the call-graph construction and local function analysis takes
-      place here.  Bodies of unreachable functions are released to
-      conserve memory usage.
+      The symbol table is constructed starting from the trivially needed
+      symbols finalized by the frontend.  Functions are lowered into
+      GIMPLE representation and callgraph/reference lists are constructed.
+      Those are used to discover other necessary functions and variables.
+
+      At the end the bodies of unreachable functions are removed.
 
       The function can be called multiple times when multiple source level
-      compilation units are combined (such as in C frontend)
+      compilation units are combined.
+
+    - compile
+
+      This passes control to the back-end.  Optimizations are performed and
+      final assembler is generated.  This is done in the following way. Note
+      that with link time optimization the process is split into three
+      stages (compile time, linktime analysis and parallel linktime as
+      indicated bellow).
+
+      Compile time:
+
+       1) Inter-procedural optimization.
+          (ipa_passes)
+
+          This part is further split into:
+
+          a) early optimizations. These are local passes executed in
+             the topological order on the callgraph.
+
+             The purpose of early optimiations is to optimize away simple
+             things that may otherwise confuse IP analysis. Very simple
+             propagation across the callgraph is done i.e. to discover
+             functions without side effects and simple inlining is performed.
 
-    - cgraph_optimize
+          b) early small interprocedural passes.
 
-      In this unit-at-a-time compilation the intra procedural analysis takes
-      place here.  In particular the static functions whose address is never
-      taken are marked as local.  Backend can then use this information to
-      modify calling conventions, do better inlining or similar optimizations.
+             Those are interprocedural passes executed only at compilation
+             time.  These include, for exmaple, transational memory lowering,
+             unreachable code removal and other simple transformations.
 
-    - cgraph_mark_needed_node
-    - varpool_mark_needed_node
+          c) IP analysis stage.  All interprocedural passes do their
+             analysis.
 
-      When function or variable is referenced by some hidden way the call-graph
-      data structure must be updated accordingly by this function.
-      There should be little need to call this function and all the references
-      should be made explicit to cgraph code.  At present these functions are
-      used by C++ frontend to explicitly mark the keyed methods.
+             Interprocedural passes differ from small interprocedural
+             passes by their ability to operate across whole program
+             at linktime.  Their analysis stage is performed early to
+             both reduce linking times and linktime memory usage by    
+             not having to represent whole program in memory.
 
-    - analyze_expr callback
+          d) LTO sreaming.  When doing LTO, everything important gets
+             streamed into the object file.
 
-      This function is responsible for lowering tree nodes not understood by
-      generic code into understandable ones or alternatively marking
-      callgraph and varpool nodes referenced by the as needed.
+       Compile time and or linktime analysis stage (WPA):
 
-      ??? On the tree-ssa genericizing should take place here and we will avoid
-      need for these hooks (replacing them by genericizing hook)
+             At linktime units gets streamed back and symbol table is
+             merged.  Function bodies are not streamed in and not
+             available.
+          e) IP propagation stage.  All IP passes execute their
+             IP propagation. This is done based on the earlier analysis
+             without having function bodies at hand.
+          f) Ltrans streaming.  When doing WHOPR LTO, the program
+             is partitioned and streamed into multple object files.
 
-        Analyzing of all functions is deferred
-       to cgraph_finalize_compilation_unit and expansion into cgraph_optimize.
+       Compile time and/or parallel linktime stage (ltrans)
 
-       In cgraph_finalize_compilation_unit the reachable functions are
-       analyzed.  During analysis the call-graph edges from reachable
-       functions are constructed and their destinations are marked as
-       reachable.  References to functions and variables are discovered too
-       and variables found to be needed output to the assembly file.  Via
-       mark_referenced call in assemble_variable functions referenced by
-       static variables are noticed too.
+             Each of the object files is streamed back and compiled
+             separately.  Now the function bodies becomes available
+             again.
 
-       The intra-procedural information is produced and its existence
-       indicated by global_info_ready.  Once this flag is set it is impossible
-       to change function from !reachable to reachable and thus
-       assemble_variable no longer call mark_referenced.
+        2) Virtual clone materialization
+           (cgraph_materialize_clone)
 
-       Finally the call-graph is topologically sorted and all reachable functions
-       that has not been completely inlined or are not external are output.
+           IP passes can produce copies of existing functoins (such
+           as versioned clones or inline clones) without actually
+           manipulating their bodies by creating virtual clones in
+           the callgraph. At this time the virtual clones are
+           turned into real functions
+        3) IP transformation
 
-       ??? It is possible that reference to function or variable is optimized
-       out.  We can not deal with this nicely because topological order is not
-       suitable for it.  For tree-ssa we may consider another pass doing
-       optimization and re-discovering reachable functions.
+           All IP passes transform function bodies based on earlier
+           decision of the IP propagation.
 
-       ??? Reorganize code so variables are output very last and only if they
-       really has been referenced by produced code, so we catch more cases
-       where reference has been optimized out.  */
+        4) late small IP passes
 
+           Simple IP passes working within single program partition.
+
+        5) Expansion
+           (expand_all_functions)
+
+           At this stage functions that needs to be output into
+           assembler are identified and compiled in topological order
+        6) Output of variables and aliases
+           Now it is known what variable references was not optimized
+           out and thus all variables are output to the file.
+
+           Note that with -fno-toplevel-reorder passes 5 and 6
+           are combined together in cgraph_output_in_order.  
+
+   Finally there are functions to manipulate the callgraph from
+   backend.
+    - cgraph_add_new_function is used to add backend produced
+      functions introduced after the unit is finalized.
+      The functions are enqueue for later processing and inserted
+      into callgraph with cgraph_process_new_functions.
+
+    - cgraph_function_versioning
+
+      produces a copy of function into new one (a version)
+      and apply simple transformations
+*/
 
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
 #include "tree.h"
+#include "output.h"
 #include "rtl.h"
 #include "tree-flow.h"
 #include "tree-inline.h"
@@ -123,8 +176,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "target.h"
 #include "cgraph.h"
 #include "diagnostic.h"
-#include "tree-pretty-print.h"
-#include "gimple-pretty-print.h"
 #include "timevar.h"
 #include "params.h"
 #include "fibheap.h"
@@ -135,78 +186,91 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-iterator.h"
 #include "tree-pass.h"
 #include "tree-dump.h"
+#include "gimple-pretty-print.h"
 #include "output.h"
 #include "coverage.h"
 #include "plugin.h"
 #include "ipa-inline.h"
 #include "ipa-utils.h"
 #include "lto-streamer.h"
+#include "except.h"
+#include "regset.h"     /* FIXME: For reg_obstack.  */
 
-static void cgraph_expand_all_functions (void);
-static void cgraph_mark_functions_to_output (void);
-static void cgraph_expand_function (struct cgraph_node *);
-static void cgraph_output_pending_asms (void);
+/* 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;
+
+static void expand_all_functions (void);
+static void mark_functions_to_output (void);
+static void expand_function (struct cgraph_node *);
+static void cgraph_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 function DECL is needed.  That is, visible to something
-   either outside this translation unit, something magic in the system
-   configury.  */
+/* Determine if function DECL is trivially needed and should stay in the
+   compilation unit.  This is used at the symbol table construction time
+   and differs from later logic removing unnecessary functions that can
+   take into account results of analysis, whole program info etc.  */
 
-bool
+static bool
 cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl)
 {
   /* If the user told us it is used, then it must be so.  */
-  if (node->local.externally_visible)
+  if (node->symbol.force_output)
     return true;
 
-  /* ??? If the assembler name is set by hand, it is possible to assemble
-     the name later after finalizing the function and the fact is noticed
-     in assemble_name then.  This is arguably a bug.  */
-  if (DECL_ASSEMBLER_NAME_SET_P (decl)
-      && (!node->thunk.thunk_p && !node->same_body_alias)
-      && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
-    return true;
+  /* Double check that no one output the function into assembly file
+     early.  */
+  gcc_checking_assert (!DECL_ASSEMBLER_NAME_SET_P (decl)
+                      || (node->thunk.thunk_p || node->same_body_alias)
+                      ||  !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)));
 
-  /* With -fkeep-inline-functions we are keeping all inline functions except
-     for extern inline ones.  */
-  if (flag_keep_inline_functions
-      && DECL_DECLARED_INLINE_P (decl)
-      && !DECL_EXTERNAL (decl)
-      && !DECL_DISREGARD_INLINE_LIMITS (decl))
-     return true;
 
-  /* If we decided it was needed before, but at the time we didn't have
-     the body of the function available, then it's still needed.  We have
-     to go back and re-check its dependencies now.  */
-  if (node->needed)
-    return true;
+  /* Keep constructors, destructors and virtual functions.  */
+  if (DECL_STATIC_CONSTRUCTOR (decl)
+      || DECL_STATIC_DESTRUCTOR (decl)
+      || (DECL_VIRTUAL_P (decl)
+         && optimize && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl))))
+     return true;
 
   /* Externally visible functions must be output.  The exception is
-     COMDAT functions that must be output only when they are needed.
+     COMDAT functions that must be output only when they are needed.  */
 
-     When not optimizing, also output the static functions. (see
-     PR24561), but don't do so for always_inline functions, functions
-     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 (((TREE_PUBLIC (decl)
-       || (!optimize
-           && !DECL_DISREGARD_INLINE_LIMITS (decl)
-           && !DECL_DECLARED_INLINE_P (decl)
-           && !(DECL_CONTEXT (decl)
-                && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)))
-       && !flag_whole_program
-       && !flag_lto)
+  if (TREE_PUBLIC (decl)
       && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
     return true;
 
   return false;
 }
 
+/* Head of the queue of nodes to be processed while building callgraph */
+
+static symtab_node first = (symtab_node)(void *)1;
+
+/* Add NODE to queue starting at FIRST. 
+   The queue is linked via AUX pointers and terminated by pointer to 1.  */
+
+static void
+enqueue_node (symtab_node node)
+{
+  if (node->symbol.aux)
+    return;
+  gcc_checking_assert (first);
+  node->symbol.aux = first;
+  first = 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.  */
@@ -217,26 +281,27 @@ cgraph_process_new_functions (void)
   bool output = false;
   tree fndecl;
   struct cgraph_node *node;
+  cgraph_node_set_iterator csi;
 
-  varpool_analyze_pending_decls ();
+  if (!cgraph_new_nodes)
+    return false;
+  handle_alias_pairs ();
   /*  Note that this queue may grow as its being processed, as the new
       functions may generate new ones.  */
-  while (cgraph_new_nodes)
+  for (csi = csi_start (cgraph_new_nodes); !csi_end_p (csi); csi_next (&csi))
     {
-      node = cgraph_new_nodes;
-      fndecl = node->decl;
-      cgraph_new_nodes = cgraph_new_nodes->next_needed;
+      node = csi_node (csi);
+      fndecl = node->symbol.decl;
       switch (cgraph_state)
        {
        case CGRAPH_STATE_CONSTRUCTION:
          /* At construction time we just need to finalize function and move
             it into reachable functions list.  */
 
-         node->next_needed = NULL;
          cgraph_finalize_function (fndecl, false);
-         cgraph_mark_reachable_node (node);
          output = true;
           cgraph_call_function_insertion_hooks (node);
+         enqueue_node ((symtab_node) node);
          break;
 
        case CGRAPH_STATE_IPA:
@@ -270,15 +335,16 @@ cgraph_process_new_functions (void)
             directly.  */
          node->process = 0;
           cgraph_call_function_insertion_hooks (node);
-         cgraph_expand_function (node);
+         expand_function (node);
          break;
 
        default:
          gcc_unreachable ();
          break;
        }
-      varpool_analyze_pending_decls ();
     }
+  free_cgraph_node_set (cgraph_new_nodes);
+  cgraph_new_nodes = NULL;
   return output;
 }
 
@@ -312,18 +378,20 @@ cgraph_reset_node (struct cgraph_node *node)
   cgraph_node_remove_callees (node);
 }
 
-static void
-cgraph_lower_function (struct cgraph_node *node)
-{
-  if (node->lowered)
-    return;
+/* Return true when there are references to NODE.  */
 
-  if (node->nested)
-    lower_nested_functions (node->decl);
-  gcc_assert (!node->nested);
+static bool
+referred_to_p (symtab_node node)
+{
+  struct ipa_ref *ref;
 
-  tree_lowering_passes (node->decl);
-  node->lowered = true;
+  /* See if there are any references at all.  */
+  if (ipa_ref_list_referring_iterate (&node->symbol.ref_list, 0, ref))
+    return true;
+  /* For functions check also calls.  */
+  if (symtab_function_p (node) && cgraph (node)->callers)
+    return true;
+  return false;
 }
 
 /* DECL has been parsed.  Take it, queue it, compile it at the whim of the
@@ -346,23 +414,27 @@ cgraph_finalize_function (tree decl, bool nested)
   node->local.finalized = true;
   node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL;
 
-  if (cgraph_decide_is_function_needed (node, decl))
-    cgraph_mark_needed_node (node);
+  /* With -fkeep-inline-functions we are keeping all inline functions except
+     for extern inline ones.  */
+  if (flag_keep_inline_functions
+      && DECL_DECLARED_INLINE_P (decl)
+      && !DECL_EXTERNAL (decl)
+      && !DECL_DISREGARD_INLINE_LIMITS (decl))
+    node->symbol.force_output = 1;
 
-  /* Since we reclaim unreachable nodes at the end of every language
-     level unit, we need to be conservative about possible entry points
-     there.  */
-  if ((TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
-      || DECL_STATIC_CONSTRUCTOR (decl)
-      || DECL_STATIC_DESTRUCTOR (decl)
-      /* COMDAT virtual functions may be referenced by vtable from
-        other compilation unit.  Still we want to devirtualize calls
-        to those so we need to analyze them.
-        FIXME: We should introduce may edges for this purpose and update
-        their handling in unreachable function removal and inliner too.  */
-      || (DECL_VIRTUAL_P (decl)
-         && optimize && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl))))
-    cgraph_mark_reachable_node (node);
+  /* When not optimizing, also output the static functions. (see
+     PR24561), but don't do so for always_inline functions, functions
+     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
+       && !node->same_body_alias
+       && !DECL_DISREGARD_INLINE_LIMITS (decl)
+       && !DECL_DECLARED_INLINE_P (decl)
+       && !(DECL_CONTEXT (decl)
+           && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL))
+      && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
+    node->symbol.force_output = 1;
 
   /* If we've not yet emitted decl, tell the debug info about it.  */
   if (!TREE_ASM_WRITTEN (decl))
@@ -374,509 +446,191 @@ cgraph_finalize_function (tree decl, bool nested)
 
   if (!nested)
     ggc_collect ();
-}
-
-/* C99 extern inline keywords allow changing of declaration after function
-   has been finalized.  We need to re-decide if we want to mark the function as
-   needed then.   */
 
-void
-cgraph_mark_if_needed (tree decl)
-{
-  struct cgraph_node *node = cgraph_get_node (decl);
-  if (node->local.finalized && cgraph_decide_is_function_needed (node, decl))
-    cgraph_mark_needed_node (node);
-}
-
-/* Return TRUE if NODE2 is equivalent to NODE or its clone.  */
-static bool
-clone_of_p (struct cgraph_node *node, struct cgraph_node *node2)
-{
-  node = cgraph_function_or_thunk_node (node, NULL);
-  node2 = cgraph_function_or_thunk_node (node2, NULL);
-  while (node != node2 && node2)
-    node2 = node2->clone_of;
-  return node2 != NULL;
+  if (cgraph_state == CGRAPH_STATE_CONSTRUCTION
+      && (cgraph_decide_is_function_needed (node, decl)
+         || referred_to_p ((symtab_node)node)))
+    enqueue_node ((symtab_node)node);
 }
 
-/* Verify edge E count and frequency.  */
+/* Add the function FNDECL to the call graph.
+   Unlike cgraph_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.
 
-static bool
-verify_edge_count_and_frequency (struct cgraph_edge *e)
-{
-  bool error_found = false;
-  if (e->count < 0)
-    {
-      error ("caller edge count is negative");
-      error_found = true;
-    }
-  if (e->frequency < 0)
-    {
-      error ("caller edge frequency is negative");
-      error_found = true;
-    }
-  if (e->frequency > CGRAPH_FREQ_MAX)
-    {
-      error ("caller edge frequency is too large");
-      error_found = true;
-    }
-  if (gimple_has_body_p (e->caller->decl)
-      && !e->caller->global.inlined_to
-      /* FIXME: Inline-analysis sets frequency to 0 when edge is optimized out.
-        Remove this once edges are actualy removed from the function at that time.  */
-      && (e->frequency
-         || (inline_edge_summary_vec
-             && ((VEC_length(inline_edge_summary_t, inline_edge_summary_vec)
-                 <= (unsigned) e->uid)
-                 || !inline_edge_summary (e)->predicate)))
-      && (e->frequency
-         != compute_call_stmt_bb_frequency (e->caller->decl,
-                                            gimple_bb (e->call_stmt))))
-    {
-      error ("caller edge frequency %i does not match BB frequency %i",
-            e->frequency,
-            compute_call_stmt_bb_frequency (e->caller->decl,
-                                            gimple_bb (e->call_stmt)));
-      error_found = true;
-    }
-  return error_found;
-}
-
-/* Switch to THIS_CFUN if needed and print STMT to stderr.  */
-static void
-cgraph_debug_gimple_stmt (struct function *this_cfun, gimple stmt)
-{
-  /* debug_gimple_stmt needs correct cfun */
-  if (cfun != this_cfun)
-    set_cfun (this_cfun);
-  debug_gimple_stmt (stmt);
-}
+   The function is assumed to be reachable and have address taken (so no
+   API breaking optimizations are performed on it).
 
-/* Verify that call graph edge E corresponds to DECL from the associated
-   statement.  Return true if the verification should fail.  */
+   Main work done by this function is to enqueue the function for later
+   processing to avoid need the passes to be re-entrant.  */
 
-static bool
-verify_edge_corresponds_to_fndecl (struct cgraph_edge *e, tree decl)
+void
+cgraph_add_new_function (tree fndecl, bool lowered)
 {
   struct cgraph_node *node;
-
-  if (!decl || e->callee->global.inlined_to)
-    return false;
-  node = cgraph_get_node (decl);
-
-  /* We do not know if a node from a different partition is an alias or what it
-     aliases and therefore cannot do the former_clone_of check reliably.  */
-  if (!node || node->in_other_partition)
-    return false;
-  node = cgraph_function_or_thunk_node (node, NULL);
-
-  if ((e->callee->former_clone_of != node->decl)
-      /* IPA-CP sometimes redirect edge to clone and then back to the former
-        function.  This ping-pong has to go, eventaully.  */
-      && (node != cgraph_function_or_thunk_node (e->callee, NULL))
-      && !clone_of_p (node, e->callee))
-    return true;
-  else
-    return false;
-}
-
-/* Verify cgraph nodes of given cgraph node.  */
-DEBUG_FUNCTION void
-verify_cgraph_node (struct cgraph_node *node)
-{
-  struct cgraph_edge *e;
-  struct function *this_cfun = DECL_STRUCT_FUNCTION (node->decl);
-  basic_block this_block;
-  gimple_stmt_iterator gsi;
-  bool error_found = false;
-
-  if (seen_error ())
-    return;
-
-  timevar_push (TV_CGRAPH_VERIFY);
-  for (e = node->callees; e; e = e->next_callee)
-    if (e->aux)
-      {
-       error ("aux field set for edge %s->%s",
-              identifier_to_locale (cgraph_node_name (e->caller)),
-              identifier_to_locale (cgraph_node_name (e->callee)));
-       error_found = true;
-      }
-  if (node->count < 0)
-    {
-      error ("execution count is negative");
-      error_found = true;
-    }
-  if (node->global.inlined_to && node->local.externally_visible)
-    {
-      error ("externally visible inline clone");
-      error_found = true;
-    }
-  if (node->global.inlined_to && node->address_taken)
-    {
-      error ("inline clone with address taken");
-      error_found = true;
-    }
-  if (node->global.inlined_to && node->needed)
-    {
-      error ("inline clone is needed");
-      error_found = true;
-    }
-  for (e = node->indirect_calls; e; e = e->next_callee)
-    {
-      if (e->aux)
-       {
-         error ("aux field set for indirect edge from %s",
-                identifier_to_locale (cgraph_node_name (e->caller)));
-         error_found = true;
-       }
-      if (!e->indirect_unknown_callee
-         || !e->indirect_info)
-       {
-         error ("An indirect edge from %s is not marked as indirect or has "
-                "associated indirect_info, the corresponding statement is: ",
-                identifier_to_locale (cgraph_node_name (e->caller)));
-         cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
-         error_found = true;
-       }
-    }
-  for (e = node->callers; e; e = e->next_caller)
-    {
-      if (verify_edge_count_and_frequency (e))
-       error_found = true;
-      if (!e->inline_failed)
-       {
-         if (node->global.inlined_to
-             != (e->caller->global.inlined_to
-                 ? e->caller->global.inlined_to : e->caller))
-           {
-             error ("inlined_to pointer is wrong");
-             error_found = true;
-           }
-         if (node->callers->next_caller)
-           {
-             error ("multiple inline callers");
-             error_found = true;
-           }
-       }
-      else
-       if (node->global.inlined_to)
+  switch (cgraph_state)
+    {
+      case CGRAPH_STATE_PARSING:
+       cgraph_finalize_function (fndecl, false);
+       break;
+      case CGRAPH_STATE_CONSTRUCTION:
+       /* Just enqueue function to be processed at nearest occurrence.  */
+       node = cgraph_create_node (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);
+        break;
+
+      case CGRAPH_STATE_IPA:
+      case CGRAPH_STATE_IPA_SSA:
+      case CGRAPH_STATE_EXPANSION:
+       /* Bring the function into finalized state and enqueue for later
+          analyzing and compilation.  */
+       node = cgraph_get_create_node (fndecl);
+       node->local.local = false;
+       node->local.finalized = true;
+       node->symbol.force_output = true;
+       if (!lowered && cgraph_state == CGRAPH_STATE_EXPANSION)
          {
-           error ("inlined_to pointer set for noninline callers");
-           error_found = true;
-         }
-    }
-  for (e = node->indirect_calls; e; e = e->next_callee)
-    if (verify_edge_count_and_frequency (e))
-      error_found = true;
-  if (!node->callers && node->global.inlined_to)
-    {
-      error ("inlined_to pointer is set but no predecessors found");
-      error_found = true;
-    }
-  if (node->global.inlined_to == node)
-    {
-      error ("inlined_to pointer refers to itself");
-      error_found = true;
-    }
-
-  if (!cgraph_get_node (node->decl))
-    {
-      error ("node not found in cgraph_hash");
-      error_found = true;
-    }
-
-  if (node->clone_of)
-    {
-      struct cgraph_node *n;
-      for (n = node->clone_of->clones; n; n = n->next_sibling_clone)
-        if (n == node)
-         break;
-      if (!n)
-       {
-         error ("node has wrong clone_of");
-         error_found = true;
-       }
-    }
-  if (node->clones)
-    {
-      struct cgraph_node *n;
-      for (n = node->clones; n; n = n->next_sibling_clone)
-        if (n->clone_of != node)
-         break;
-      if (n)
-       {
-         error ("node has wrong clone list");
-         error_found = true;
-       }
-    }
-  if ((node->prev_sibling_clone || node->next_sibling_clone) && !node->clone_of)
-    {
-       error ("node is in clone list but it is not clone");
-       error_found = true;
-    }
-  if (!node->prev_sibling_clone && node->clone_of && node->clone_of->clones != node)
-    {
-      error ("node has wrong prev_clone pointer");
-      error_found = true;
-    }
-  if (node->prev_sibling_clone && node->prev_sibling_clone->next_sibling_clone != node)
-    {
-      error ("double linked list of clones corrupted");
-      error_found = true;
-    }
-  if (node->same_comdat_group)
-    {
-      struct cgraph_node *n = node->same_comdat_group;
-
-      if (!DECL_ONE_ONLY (node->decl))
-       {
-         error ("non-DECL_ONE_ONLY node in a same_comdat_group list");
-         error_found = true;
-       }
-      if (n == node)
-       {
-         error ("node is alone in a comdat group");
-         error_found = true;
-       }
-      do
-       {
-         if (!n->same_comdat_group)
-           {
-             error ("same_comdat_group is not a circular list");
-             error_found = true;
-             break;
-           }
-         n = n->same_comdat_group;
-       }
-      while (n != node);
-    }
-
-  if (node->analyzed && node->alias)
-    {
-      bool ref_found = false;
-      int i;
-      struct ipa_ref *ref;
+           push_cfun (DECL_STRUCT_FUNCTION (fndecl));
+           current_function_decl = fndecl;
+           gimple_register_cfg_hooks ();
+           bitmap_obstack_initialize (NULL);
+           execute_pass_list (all_lowering_passes);
+           execute_pass_list (pass_early_local_passes.pass.sub);
+           bitmap_obstack_release (NULL);
+           pop_cfun ();
+           current_function_decl = NULL;
 
-      if (node->callees)
-       {
-         error ("Alias has call edges");
-          error_found = true;
-       }
-      for (i = 0; ipa_ref_list_reference_iterate (&node->ref_list, i, ref); i++)
-       if (ref->use != IPA_REF_ALIAS)
-         {
-           error ("Alias has non-alias refernece");
-           error_found = true;
-         }
-       else if (ref_found)
-         {
-           error ("Alias has more than one alias reference");
-           error_found = true;
+           lowered = true;
          }
-       else
-         ref_found = true;
-       if (!ref_found)
-         {
-           error ("Analyzed alias has no reference");
-           error_found = true;
-         }
-    }
-  if (node->analyzed && node->thunk.thunk_p)
-    {
-      if (!node->callees)
-       {
-         error ("No edge out of thunk node");
-          error_found = true;
-       }
-      else if (node->callees->next_callee)
-       {
-         error ("More than one edge out of thunk node");
-          error_found = true;
-       }
-      if (gimple_has_body_p (node->decl))
-        {
-         error ("Thunk is not supposed to have body");
-          error_found = true;
-        }
-    }
-  else if (node->analyzed && gimple_has_body_p (node->decl)
-           && !TREE_ASM_WRITTEN (node->decl)
-           && (!DECL_EXTERNAL (node->decl) || node->global.inlined_to)
-           && !flag_wpa)
-    {
-      if (this_cfun->cfg)
-       {
-         /* The nodes we're interested in are never shared, so walk
-            the tree ignoring duplicates.  */
-         struct pointer_set_t *visited_nodes = pointer_set_create ();
-         /* Reach the trees by walking over the CFG, and note the
-            enclosing basic-blocks in the call edges.  */
-         FOR_EACH_BB_FN (this_block, this_cfun)
-           for (gsi = gsi_start_bb (this_block);
-                 !gsi_end_p (gsi);
-                 gsi_next (&gsi))
-             {
-               gimple stmt = gsi_stmt (gsi);
-               if (is_gimple_call (stmt))
-                 {
-                   struct cgraph_edge *e = cgraph_edge (node, stmt);
-                   tree decl = gimple_call_fndecl (stmt);
-                   if (e)
-                     {
-                       if (e->aux)
-                         {
-                           error ("shared call_stmt:");
-                           cgraph_debug_gimple_stmt (this_cfun, stmt);
-                           error_found = true;
-                         }
-                       if (!e->indirect_unknown_callee)
-                         {
-                           if (verify_edge_corresponds_to_fndecl (e, decl))
-                             {
-                               error ("edge points to wrong declaration:");
-                               debug_tree (e->callee->decl);
-                               fprintf (stderr," Instead of:");
-                               debug_tree (decl);
-                               error_found = true;
-                             }
-                         }
-                       else if (decl)
-                         {
-                           error ("an indirect edge with unknown callee "
-                                  "corresponding to a call_stmt with "
-                                  "a known declaration:");
-                           error_found = true;
-                           cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
-                         }
-                       e->aux = (void *)1;
-                     }
-                   else if (decl)
-                     {
-                       error ("missing callgraph edge for call stmt:");
-                       cgraph_debug_gimple_stmt (this_cfun, stmt);
-                       error_found = true;
-                     }
-                 }
-             }
-         pointer_set_destroy (visited_nodes);
-       }
-      else
-       /* No CFG available?!  */
+       if (lowered)
+         node->lowered = true;
+       if (!cgraph_new_nodes)
+         cgraph_new_nodes = cgraph_node_set_new ();
+       cgraph_node_set_add (cgraph_new_nodes, node);
+        break;
+
+      case CGRAPH_STATE_FINISHED:
+       /* At the very end of compilation we have to do all the work up
+          to expansion.  */
+       node = cgraph_create_node (fndecl);
+       if (lowered)
+         node->lowered = true;
+       cgraph_analyze_function (node);
+       push_cfun (DECL_STRUCT_FUNCTION (fndecl));
+       current_function_decl = fndecl;
+       gimple_register_cfg_hooks ();
+       bitmap_obstack_initialize (NULL);
+       if (!gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
+         execute_pass_list (pass_early_local_passes.pass.sub);
+       bitmap_obstack_release (NULL);
+       pop_cfun ();
+       expand_function (node);
+       current_function_decl = NULL;
+       break;
+
+      default:
        gcc_unreachable ();
-
-      for (e = node->callees; e; e = e->next_callee)
-       {
-         if (!e->aux)
-           {
-             error ("edge %s->%s has no corresponding call_stmt",
-                    identifier_to_locale (cgraph_node_name (e->caller)),
-                    identifier_to_locale (cgraph_node_name (e->callee)));
-             cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
-             error_found = true;
-           }
-         e->aux = 0;
-       }
-      for (e = node->indirect_calls; e; e = e->next_callee)
-       {
-         if (!e->aux)
-           {
-             error ("an indirect edge from %s has no corresponding call_stmt",
-                    identifier_to_locale (cgraph_node_name (e->caller)));
-             cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
-             error_found = true;
-           }
-         e->aux = 0;
-       }
     }
-  if (error_found)
-    {
-      dump_cgraph_node (stderr, node);
-      internal_error ("verify_cgraph_node failed");
-    }
-  timevar_pop (TV_CGRAPH_VERIFY);
-}
 
-/* Verify whole cgraph structure.  */
-DEBUG_FUNCTION void
-verify_cgraph (void)
-{
-  struct cgraph_node *node;
+  /* Set a personality if required and we already passed EH lowering.  */
+  if (lowered
+      && (function_needs_eh_personality (DECL_STRUCT_FUNCTION (fndecl))
+         == eh_personality_lang))
+    DECL_FUNCTION_PERSONALITY (fndecl) = lang_hooks.eh_personality ();
+}
 
-  if (seen_error ())
-    return;
+/* Add a top-level asm statement to the list.  */
 
-  for (node = cgraph_nodes; node; node = node->next)
-    verify_cgraph_node (node);
+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
-cgraph_output_pending_asms (void)
+output_asm_statements (void)
 {
-  struct cgraph_asm_node *can;
+  struct asm_node *can;
 
   if (seen_error ())
     return;
 
-  for (can = cgraph_asm_nodes; can; can = can->next)
+  for (can = asm_nodes; can; can = can->next)
     assemble_asm (can->asm_str);
-  cgraph_asm_nodes = NULL;
+  asm_nodes = NULL;
 }
 
-/* Analyze the function scheduled to be output.  */
+/* C++ FE sometimes change linkage flags after producing same body aliases.  */
 void
+fixup_same_cpp_alias_visibility (symtab_node node, symtab_node target, tree alias)
+{
+  DECL_VIRTUAL_P (node->symbol.decl) = DECL_VIRTUAL_P (alias);
+  if (TREE_PUBLIC (node->symbol.decl))
+    {
+      DECL_EXTERNAL (node->symbol.decl) = DECL_EXTERNAL (alias);
+      DECL_COMDAT (node->symbol.decl) = DECL_COMDAT (alias);
+      DECL_COMDAT_GROUP (node->symbol.decl) = DECL_COMDAT_GROUP (alias);
+      if (DECL_ONE_ONLY (alias)
+         && !node->symbol.same_comdat_group)
+       symtab_add_to_same_comdat_group ((symtab_node)node, (symtab_node)target);
+    }
+}
+
+/* Analyze the function scheduled to be output.  */
+static void
 cgraph_analyze_function (struct cgraph_node *node)
 {
   tree save = current_function_decl;
-  tree decl = node->decl;
+  tree decl = node->symbol.decl;
+  location_t saved_loc = input_location;
+  input_location = DECL_SOURCE_LOCATION (decl);
 
   if (node->alias && node->thunk.alias)
     {
       struct cgraph_node *tgt = cgraph_get_node (node->thunk.alias);
-      if (!VEC_length (ipa_ref_t, node->ref_list.references))
-        ipa_record_reference (node, NULL, tgt, NULL, IPA_REF_ALIAS, NULL);
+      struct cgraph_node *n;
+
+      for (n = tgt; n && n->alias;
+          n = n->analyzed ? cgraph_alias_aliased_node (n) : NULL)
+       if (n == node)
+         {
+           error ("function %q+D part of alias cycle", node->symbol.decl);
+           node->alias = false;
+           input_location = saved_loc;
+           return;
+         }
+      if (!VEC_length (ipa_ref_t, node->symbol.ref_list.references))
+        ipa_record_reference ((symtab_node)node, (symtab_node)tgt,
+                             IPA_REF_ALIAS, NULL);
       if (node->same_body_alias)
        { 
-         DECL_VIRTUAL_P (node->decl) = DECL_VIRTUAL_P (node->thunk.alias);
-         DECL_DECLARED_INLINE_P (node->decl)
+         DECL_DECLARED_INLINE_P (node->symbol.decl)
             = DECL_DECLARED_INLINE_P (node->thunk.alias);
-         DECL_DISREGARD_INLINE_LIMITS (node->decl)
+         DECL_DISREGARD_INLINE_LIMITS (node->symbol.decl)
             = DECL_DISREGARD_INLINE_LIMITS (node->thunk.alias);
+         fixup_same_cpp_alias_visibility ((symtab_node) node, (symtab_node) tgt, node->thunk.alias);
        }
 
-      /* Fixup visibility nonsences C++ frontend produce on same body aliases.  */
-      if (TREE_PUBLIC (node->decl) && node->same_body_alias)
-       {
-          DECL_EXTERNAL (node->decl) = DECL_EXTERNAL (node->thunk.alias);
-         if (DECL_ONE_ONLY (node->thunk.alias))
-           {
-             DECL_COMDAT (node->decl) = DECL_COMDAT (node->thunk.alias);
-             DECL_COMDAT_GROUP (node->decl) = DECL_COMDAT_GROUP (node->thunk.alias);
-             if (DECL_ONE_ONLY (node->thunk.alias) && !node->same_comdat_group)
-               {
-                 struct cgraph_node *tgt = cgraph_get_node (node->thunk.alias);
-                 node->same_comdat_group = tgt;
-                 if (!tgt->same_comdat_group)
-                   tgt->same_comdat_group = node;
-                 else
-                   {
-                     struct cgraph_node *n;
-                     for (n = tgt->same_comdat_group;
-                          n->same_comdat_group != tgt;
-                          n = n->same_comdat_group)
-                       ;
-                     n->same_comdat_group = node;
-                   }
-               }
-           }
-       }
-      cgraph_mark_reachable_node (cgraph_alias_aliased_node (node));
-      if (node->address_taken)
+      if (node->symbol.address_taken)
        cgraph_mark_address_taken_node (cgraph_alias_aliased_node (node));
-      if (cgraph_decide_is_function_needed (node, node->decl))
-       cgraph_mark_needed_node (node);
     }
   else if (node->thunk.thunk_p)
     {
@@ -888,22 +642,39 @@ cgraph_analyze_function (struct cgraph_node *node)
       current_function_decl = decl;
       push_cfun (DECL_STRUCT_FUNCTION (decl));
 
-      assign_assembler_name_if_neeeded (node->decl);
+      assign_assembler_name_if_neeeded (node->symbol.decl);
 
       /* Make sure to gimplify bodies only once.  During analyzing a
         function we lower it, which will require gimplified nested
         functions, so we can end up here with an already gimplified
         body.  */
-      if (!gimple_body (decl))
+      if (!gimple_has_body_p (decl))
        gimplify_function_tree (decl);
       dump_function (TDI_generic, decl);
 
-      cgraph_lower_function (node);
+      /* Lower the function.  */
+      if (!node->lowered)
+       {
+         if (node->nested)
+           lower_nested_functions (node->symbol.decl);
+         gcc_assert (!node->nested);
+
+         gimple_register_cfg_hooks ();
+         bitmap_obstack_initialize (NULL);
+         execute_pass_list (all_lowering_passes);
+         free_dominance_info (CDI_POST_DOMINATORS);
+         free_dominance_info (CDI_DOMINATORS);
+         compact_blocks ();
+         bitmap_obstack_release (NULL);
+         node->lowered = true;
+       }
+
       pop_cfun ();
     }
   node->analyzed = true;
 
   current_function_decl = save;
+  input_location = saved_loc;
 }
 
 /* C++ frontend produce same body aliases all over the place, even before PCH
@@ -916,12 +687,13 @@ void
 cgraph_process_same_body_aliases (void)
 {
   struct cgraph_node *node;
-  for (node = cgraph_nodes; node; node = node->next)
+  FOR_EACH_FUNCTION (node)
     if (node->same_body_alias
-       && !VEC_length (ipa_ref_t, node->ref_list.references))
+       && !VEC_length (ipa_ref_t, node->symbol.ref_list.references))
       {
         struct cgraph_node *tgt = cgraph_get_node (node->thunk.alias);
-        ipa_record_reference (node, NULL, tgt, NULL, IPA_REF_ALIAS, NULL);
+        ipa_record_reference ((symtab_node)node, (symtab_node)tgt,
+                             IPA_REF_ALIAS, NULL);
       }
   same_body_aliases_done = true;
 }
@@ -975,31 +747,23 @@ process_function_and_variable_attributes (struct cgraph_node *first,
   struct cgraph_node *node;
   struct varpool_node *vnode;
 
-  for (node = cgraph_nodes; node != first; node = node->next)
+  for (node = cgraph_first_function (); node != first;
+       node = cgraph_next_function (node))
     {
-      tree decl = node->decl;
+      tree decl = node->symbol.decl;
       if (DECL_PRESERVE_P (decl))
-       cgraph_mark_needed_node (node);
-      if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
-         && lookup_attribute ("dllexport", DECL_ATTRIBUTES (decl))
-         && TREE_PUBLIC (node->decl))
-       {
-         if (node->local.finalized)
-           cgraph_mark_needed_node (node);
-       }
+       cgraph_mark_force_output_node (node);
       else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
        {
-         if (! TREE_PUBLIC (node->decl))
-           warning_at (DECL_SOURCE_LOCATION (node->decl), OPT_Wattributes,
+         if (! TREE_PUBLIC (node->symbol.decl))
+           warning_at (DECL_SOURCE_LOCATION (node->symbol.decl), OPT_Wattributes,
                        "%<externally_visible%>"
                        " attribute have effect only on public objects");
-         else if (node->local.finalized)
-            cgraph_mark_needed_node (node);
        }
       if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))
          && (node->local.finalized && !node->alias))
        {
-         warning_at (DECL_SOURCE_LOCATION (node->decl), OPT_Wattributes,
+         warning_at (DECL_SOURCE_LOCATION (node->symbol.decl), OPT_Wattributes,
                      "%<weakref%> attribute ignored"
                      " because function is defined");
          DECL_WEAK (decl) = 0;
@@ -1016,36 +780,28 @@ process_function_and_variable_attributes (struct cgraph_node *first,
      
       process_common_attributes (decl);
     }
-  for (vnode = varpool_nodes; vnode != first_var; vnode = vnode->next)
+  for (vnode = varpool_first_variable (); vnode != first_var;
+       vnode = varpool_next_variable (vnode))
     {
-      tree decl = vnode->decl;
+      tree decl = vnode->symbol.decl;
+      if (DECL_EXTERNAL (decl)
+         && DECL_INITIAL (decl)
+         && const_value_known_p (decl))
+       varpool_finalize_decl (decl);
       if (DECL_PRESERVE_P (decl))
-       {
-         vnode->force_output = true;
-         if (vnode->finalized)
-           varpool_mark_needed_node (vnode);
-       }
-      if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
-         && lookup_attribute ("dllexport", DECL_ATTRIBUTES (decl))
-         && TREE_PUBLIC (vnode->decl))
-       {
-         if (vnode->finalized)
-           varpool_mark_needed_node (vnode);
-       }
+       vnode->symbol.force_output = true;
       else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
        {
-         if (! TREE_PUBLIC (vnode->decl))
-           warning_at (DECL_SOURCE_LOCATION (vnode->decl), OPT_Wattributes,
+         if (! TREE_PUBLIC (vnode->symbol.decl))
+           warning_at (DECL_SOURCE_LOCATION (vnode->symbol.decl), OPT_Wattributes,
                        "%<externally_visible%>"
                        " attribute have effect only on public objects");
-         else if (vnode->finalized)
-           varpool_mark_needed_node (vnode);
        }
       if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))
          && vnode->finalized
          && DECL_INITIAL (decl))
        {
-         warning_at (DECL_SOURCE_LOCATION (vnode->decl), OPT_Wattributes,
+         warning_at (DECL_SOURCE_LOCATION (vnode->symbol.decl), OPT_Wattributes,
                      "%<weakref%> attribute ignored"
                      " because variable is initialized");
          DECL_WEAK (decl) = 0;
@@ -1056,10 +812,42 @@ process_function_and_variable_attributes (struct cgraph_node *first,
     }
 }
 
-/* Process CGRAPH_NODES_NEEDED queue, analyze each function (and transitively
-   each reachable functions) and build cgraph.
-   The function can be called multiple times after inserting new nodes
-   into beginning of queue.  Just the new part of queue is re-scanned then.  */
+/* Mark DECL as finalized.  By finalizing the declaration, frontend instruct the
+   middle end to output the variable to asm file, if needed or externally
+   visible.  */
+
+void
+varpool_finalize_decl (tree decl)
+{
+  struct varpool_node *node = varpool_node (decl);
+
+  gcc_assert (TREE_STATIC (decl) || DECL_EXTERNAL (decl));
+
+  if (node->finalized)
+    return;
+  notice_global_symbol (decl);
+  node->finalized = true;
+  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->symbol.decl)
+         && !DECL_ARTIFICIAL (node->symbol.decl)))
+    node->symbol.force_output = true;
+
+  if (cgraph_state == CGRAPH_STATE_CONSTRUCTION
+      && (decide_is_variable_needed (node, decl)
+         || referred_to_p ((symtab_node)node)))
+    enqueue_node ((symtab_node)node);
+  if (cgraph_state >= CGRAPH_STATE_IPA_SSA)
+    varpool_analyze_node (node);
+  /* Some frontends produce various interface variables after compilation
+     finished.  */
+  if (cgraph_state == CGRAPH_STATE_FINISHED)
+    varpool_assemble_decl (node);
+}
+
+/* Discover all functions and variables that are trivially needed, analyze
+   them as well as all functions and variables referred by them  */
 
 static void
 cgraph_analyze_functions (void)
@@ -1067,139 +855,171 @@ cgraph_analyze_functions (void)
   /* Keep track of already processed nodes when called multiple times for
      intermodule optimization.  */
   static struct cgraph_node *first_analyzed;
-  struct cgraph_node *first_processed = first_analyzed;
+  struct cgraph_node *first_handled = first_analyzed;
   static struct varpool_node *first_analyzed_var;
-  struct cgraph_node *node, *next;
+  struct varpool_node *first_handled_var = first_analyzed_var;
+
+  symtab_node node, next;
+  int i;
+  struct ipa_ref *ref;
+  bool changed = true;
 
   bitmap_obstack_initialize (NULL);
-  process_function_and_variable_attributes (first_processed,
-                                           first_analyzed_var);
-  first_processed = cgraph_nodes;
-  first_analyzed_var = varpool_nodes;
-  varpool_analyze_pending_decls ();
-  if (cgraph_dump_file)
-    {
-      fprintf (cgraph_dump_file, "Initial entry points:");
-      for (node = cgraph_nodes; node != first_analyzed; node = node->next)
-       if (node->needed)
-         fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
-      fprintf (cgraph_dump_file, "\n");
-    }
-  cgraph_process_new_functions ();
+  cgraph_state = CGRAPH_STATE_CONSTRUCTION;
 
-  /* Propagate reachability flag and lower representation of all reachable
-     functions.  In the future, lowering will introduce new functions and
-     new entry points on the way (by template instantiation and virtual
-     method table generation for instance).  */
-  while (cgraph_nodes_queue)
+  /* Analysis adds static variables that in turn adds references to new functions.
+     So we need to iterate the process until it stabilize.  */
+  while (changed)
     {
-      struct cgraph_edge *edge;
-      tree decl = cgraph_nodes_queue->decl;
-
-      node = cgraph_nodes_queue;
-      cgraph_nodes_queue = cgraph_nodes_queue->next_needed;
-      node->next_needed = NULL;
-
-      /* ??? It is possible to create extern inline function and later using
-        weak alias attribute to kill its body. See
-        gcc.c-torture/compile/20011119-1.c  */
-      if (!DECL_STRUCT_FUNCTION (decl)
-         && (!node->alias || !node->thunk.alias)
-         && !node->thunk.thunk_p)
+      changed = false;
+      process_function_and_variable_attributes (first_analyzed,
+                                               first_analyzed_var);
+
+      /* First identify the trivially needed symbols.  */
+      for (node = symtab_nodes;
+          node != (symtab_node)first_analyzed
+          && node != (symtab_node)first_analyzed_var; node = node->symbol.next)
        {
-         cgraph_reset_node (node);
-          node->local.redefined_extern_inline = true;
-         continue;
+         if ((symtab_function_p (node)
+              && cgraph (node)->local.finalized
+              && cgraph_decide_is_function_needed (cgraph (node), node->symbol.decl))
+             || (symtab_variable_p (node)
+                 && varpool (node)->finalized
+                 && !DECL_EXTERNAL (node->symbol.decl)
+                 && decide_is_variable_needed (varpool (node), node->symbol.decl)))
+           {
+             enqueue_node (node);
+             if (!changed && cgraph_dump_file)
+               fprintf (cgraph_dump_file, "Trivially needed symbols:");
+             changed = true;
+             if (cgraph_dump_file)
+               fprintf (cgraph_dump_file, " %s", symtab_node_asm_name (node));
+           }
+         if (node == (symtab_node)first_analyzed
+             || node == (symtab_node)first_analyzed_var)
+           break;
        }
+      cgraph_process_new_functions ();
+      first_analyzed_var = varpool_first_variable ();
+      first_analyzed = cgraph_first_function ();
 
-      if (!node->analyzed)
-       cgraph_analyze_function (node);
-
-      for (edge = node->callees; edge; edge = edge->next_callee)
-       if (!edge->callee->reachable)
-         cgraph_mark_reachable_node (edge->callee);
-      for (edge = node->callers; edge; edge = edge->next_caller)
-       if (!edge->caller->reachable && edge->caller->thunk.thunk_p)
-         cgraph_mark_reachable_node (edge->caller);
+      if (changed && dump_file)
+       fprintf (cgraph_dump_file, "\n");
 
-      if (node->same_comdat_group)
+      /* Lower representation, build callgraph edges and references for all trivially
+         needed symbols and all symbols referred by them.  */
+      while (first != (symtab_node)(void *)1)
        {
-         for (next = node->same_comdat_group;
-              next != node;
-              next = next->same_comdat_group)
-           cgraph_mark_reachable_node (next);
-       }
+         changed = true;
+         node = first;
+         first = (symtab_node)first->symbol.aux;
+         if (symtab_function_p (node) && cgraph (node)->local.finalized)
+           {
+             struct cgraph_edge *edge;
+             struct cgraph_node *cnode;
+             tree decl;
+
+             cnode = cgraph (node);
+             decl = cnode->symbol.decl;
+
+             /* ??? It is possible to create extern inline function and later using
+                weak alias attribute to kill its body. See
+                gcc.c-torture/compile/20011119-1.c  */
+             if (!DECL_STRUCT_FUNCTION (decl)
+                 && (!cnode->alias || !cnode->thunk.alias)
+                 && !cnode->thunk.thunk_p)
+               {
+                 cgraph_reset_node (cnode);
+                 cnode->local.redefined_extern_inline = true;
+                 continue;
+               }
 
-      /* If decl is a clone of an abstract function, mark that abstract
-        function so that we don't release its body. The DECL_INITIAL() of that
-        abstract function declaration will be later needed to output debug
-        info.  */
-      if (DECL_ABSTRACT_ORIGIN (decl))
-       {
-         struct cgraph_node *origin_node;
-         origin_node = cgraph_get_node (DECL_ABSTRACT_ORIGIN (decl));
-         origin_node->abstract_and_needed = true;
-       }
+             if (!cnode->analyzed)
+               cgraph_analyze_function (cnode);
 
-      /* We finalize local static variables during constructing callgraph
-         edges.  Process their attributes too.  */
-      process_function_and_variable_attributes (first_processed,
-                                               first_analyzed_var);
-      first_processed = cgraph_nodes;
-      first_analyzed_var = varpool_nodes;
-      varpool_analyze_pending_decls ();
-      cgraph_process_new_functions ();
+             for (edge = cnode->callees; edge; edge = edge->next_callee)
+               if (edge->callee->local.finalized)
+                 enqueue_node ((symtab_node)edge->callee);
+
+             /* If decl is a clone of an abstract function, mark that abstract
+                function so that we don't release its body. The DECL_INITIAL() of that
+                abstract function declaration will be later needed to output debug
+                info.  */
+             if (DECL_ABSTRACT_ORIGIN (decl))
+               {
+                 struct cgraph_node *origin_node;
+                 origin_node = cgraph_get_node (DECL_ABSTRACT_ORIGIN (decl));
+                 origin_node->abstract_and_needed = true;
+               }
+
+           }
+         else if (symtab_variable_p (node)
+                  && varpool (node)->finalized)
+           varpool_analyze_node (varpool (node));
+
+         if (node->symbol.same_comdat_group)
+           {
+             symtab_node next;
+             for (next = node->symbol.same_comdat_group;
+                  next != node;
+                  next = next->symbol.same_comdat_group)
+               enqueue_node (next);
+           }
+         for (i = 0; ipa_ref_list_reference_iterate (&node->symbol.ref_list, i, ref); i++)
+           if ((symtab_function_p (ref->referred) && cgraph (ref->referred)->local.finalized)
+               || (symtab_variable_p (ref->referred) && varpool (ref->referred)->finalized))
+             enqueue_node (ref->referred);
+          cgraph_process_new_functions ();
+       }
     }
 
   /* Collect entry points to the unit.  */
   if (cgraph_dump_file)
     {
-      fprintf (cgraph_dump_file, "Unit entry points:");
-      for (node = cgraph_nodes; node != first_analyzed; node = node->next)
-       if (node->needed)
-         fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
       fprintf (cgraph_dump_file, "\n\nInitial ");
-      dump_cgraph (cgraph_dump_file);
-      dump_varpool (cgraph_dump_file);
+      dump_symtab (cgraph_dump_file);
     }
 
   if (cgraph_dump_file)
-    fprintf (cgraph_dump_file, "\nReclaiming functions:");
+    fprintf (cgraph_dump_file, "\nRemoving unused symbols:");
 
-  for (node = cgraph_nodes; node != first_analyzed; node = next)
+  for (node = symtab_nodes;
+       node != (symtab_node)first_handled
+       && node != (symtab_node)first_handled_var; node = next)
     {
-      tree decl = node->decl;
-      next = node->next;
-
-      if (node->local.finalized && !gimple_has_body_p (decl)
-         && (!node->alias || !node->thunk.alias)
-         && !node->thunk.thunk_p)
-       cgraph_reset_node (node);
-
-      if (!node->reachable
-         && (gimple_has_body_p (decl) || node->thunk.thunk_p
-             || (node->alias && node->thunk.alias)))
+      next = node->symbol.next;
+      if (!node->symbol.aux && !referred_to_p (node))
        {
          if (cgraph_dump_file)
-           fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
-         cgraph_remove_node (node);
+           fprintf (cgraph_dump_file, " %s", symtab_node_name (node));
+         symtab_remove_node (node);
          continue;
        }
-      else
-       node->next_needed = NULL;
-      gcc_assert (!node->local.finalized || node->thunk.thunk_p
-                 || node->alias
-                 || gimple_has_body_p (decl));
-      gcc_assert (node->analyzed == node->local.finalized);
+      if (symtab_function_p (node))
+       {
+         tree decl = node->symbol.decl;
+         struct cgraph_node *cnode = cgraph (node);
+
+         if (cnode->local.finalized && !gimple_has_body_p (decl)
+             && (!cnode->alias || !cnode->thunk.alias)
+             && !cnode->thunk.thunk_p)
+           cgraph_reset_node (cnode);
+
+         gcc_assert (!cnode->local.finalized || cnode->thunk.thunk_p
+                     || cnode->alias
+                     || gimple_has_body_p (decl));
+         gcc_assert (cnode->analyzed == cnode->local.finalized);
+       }
+      node->symbol.aux = NULL;
     }
+  first_analyzed = cgraph_first_function ();
+  first_analyzed_var = varpool_first_variable ();
   if (cgraph_dump_file)
     {
       fprintf (cgraph_dump_file, "\n\nReclaimed ");
-      dump_cgraph (cgraph_dump_file);
-      dump_varpool (cgraph_dump_file);
+      dump_symtab (cgraph_dump_file);
     }
   bitmap_obstack_release (NULL);
-  first_analyzed = cgraph_nodes;
   ggc_collect ();
 }
 
@@ -1212,135 +1032,102 @@ handle_alias_pairs (void)
 {
   alias_pair *p;
   unsigned i;
-  struct cgraph_node *target_node;
-  struct cgraph_node *src_node;
-  struct varpool_node *target_vnode;
   
   for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p);)
     {
+      symtab_node target_node = symtab_node_for_asm (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 alias flag
+        to later output the weakref pseudo op into asm file.  */
+      if (!target_node && lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)) != NULL)
+       {
+         if (TREE_CODE (p->decl) == FUNCTION_DECL)
+           cgraph_get_create_node (p->decl)->alias = true;
+         else
+           varpool_get_node (p->decl)->alias = true;
+         DECL_EXTERNAL (p->decl) = 1;
+         VEC_unordered_remove (alias_pair, alias_pairs, i);
+         continue;
+       }
+      else if (!target_node)
+       {
+         error ("%q+D aliased to undefined symbol %qE", p->decl, p->target);
+         VEC_unordered_remove (alias_pair, alias_pairs, i);
+         continue;
+       }
+
+      /* Normally EXTERNAL flag is used to mark external inlines,
+        however for aliases it seems to be allowed to use it w/o
+        any meaning. See gcc.dg/attr-alias-3.c  
+        However for weakref we insist on EXTERNAL flag being set.
+        See gcc.dg/attr-alias-5.c  */
+      if (DECL_EXTERNAL (p->decl))
+       DECL_EXTERNAL (p->decl)
+         = lookup_attribute ("weakref",
+                             DECL_ATTRIBUTES (p->decl)) != NULL;
+
+      if (DECL_EXTERNAL (target_node->symbol.decl)
+         /* 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->symbol.decl) == FUNCTION_DECL
+             || ! DECL_VIRTUAL_P (target_node->symbol.decl))
+         && ! lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)))
+       {
+         error ("%q+D aliased to external symbol %qE",
+                p->decl, p->target);
+       }
+
       if (TREE_CODE (p->decl) == FUNCTION_DECL
-         && (target_node = cgraph_node_for_asm (p->target)) != NULL)
+          && target_node && symtab_function_p (target_node))
        {
-         src_node = cgraph_get_node (p->decl);
+         struct cgraph_node *src_node = cgraph_get_node (p->decl);
          if (src_node && src_node->local.finalized)
             cgraph_reset_node (src_node);
-         /* Normally EXTERNAL flag is used to mark external inlines,
-            however for aliases it seems to be allowed to use it w/o
-            any meaning. See gcc.dg/attr-alias-3.c  
-            However for weakref we insist on EXTERNAL flag being set.
-            See gcc.dg/attr-alias-5.c  */
-         if (DECL_EXTERNAL (p->decl))
-           DECL_EXTERNAL (p->decl) = lookup_attribute ("weakref",
-                                                       DECL_ATTRIBUTES (p->decl)) != NULL;
-         cgraph_create_function_alias (p->decl, target_node->decl);
+         cgraph_create_function_alias (p->decl, target_node->symbol.decl);
          VEC_unordered_remove (alias_pair, alias_pairs, i);
        }
       else if (TREE_CODE (p->decl) == VAR_DECL
-              && (target_vnode = varpool_node_for_asm (p->target)) != NULL)
+              && target_node && symtab_variable_p (target_node))
        {
-         /* Normally EXTERNAL flag is used to mark external inlines,
-            however for aliases it seems to be allowed to use it w/o
-            any meaning. See gcc.dg/attr-alias-3.c  
-            However for weakref we insist on EXTERNAL flag being set.
-            See gcc.dg/attr-alias-5.c  */
-         if (DECL_EXTERNAL (p->decl))
-           DECL_EXTERNAL (p->decl) = lookup_attribute ("weakref",
-                                                       DECL_ATTRIBUTES (p->decl)) != NULL;
-         varpool_create_variable_alias (p->decl, target_vnode->decl);
+         varpool_create_variable_alias (p->decl, target_node->symbol.decl);
          VEC_unordered_remove (alias_pair, alias_pairs, i);
        }
       else
        {
-         if (dump_file)
-           fprintf (dump_file, "Unhandled alias %s->%s\n",
-                    IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (p->decl)),
-                    IDENTIFIER_POINTER (p->target));
-
-         i++;
+         error ("%q+D alias in between function and variable is not supported",
+                p->decl);
+         warning (0, "%q+D aliased declaration",
+                  target_node->symbol.decl);
+         VEC_unordered_remove (alias_pair, alias_pairs, i);
        }
     }
-}
-
-
-/* Analyze the whole compilation unit once it is parsed completely.  */
-
-void
-cgraph_finalize_compilation_unit (void)
-{
-  timevar_push (TV_CGRAPH);
-
-  /* If LTO is enabled, initialize the streamer hooks needed by GIMPLE.  */
-  if (flag_lto)
-    lto_streamer_hooks_init ();
-
-  /* If we're here there's no current function anymore.  Some frontends
-     are lazy in clearing these.  */
-  current_function_decl = NULL;
-  set_cfun (NULL);
-
-  /* Do not skip analyzing the functions if there were errors, we
-     miss diagnostics for following functions otherwise.  */
-
-  /* Emit size functions we didn't inline.  */
-  finalize_size_functions ();
-
-  /* Mark alias targets necessary and emit diagnostics.  */
-  finish_aliases_1 ();
-  handle_alias_pairs ();
-
-  if (!quiet_flag)
-    {
-      fprintf (stderr, "\nAnalyzing compilation unit\n");
-      fflush (stderr);
-    }
-
-  if (flag_dump_passes)
-    dump_passes ();
-
-  /* Gimplify and lower all functions, compute reachability and
-     remove unreachable nodes.  */
-  cgraph_analyze_functions ();
-
-  /* Mark alias targets necessary and emit diagnostics.  */
-  finish_aliases_1 ();
-  handle_alias_pairs ();
-
-  /* Gimplify and lower thunks.  */
-  cgraph_analyze_functions ();
-
-  /* Finally drive the pass manager.  */
-  cgraph_optimize ();
-
-  timevar_pop (TV_CGRAPH);
+  VEC_free (alias_pair, gc, alias_pairs);
 }
 
 
 /* Figure out what functions we want to assemble.  */
 
 static void
-cgraph_mark_functions_to_output (void)
+mark_functions_to_output (void)
 {
   struct cgraph_node *node;
 #ifdef ENABLE_CHECKING
   bool check_same_comdat_groups = false;
 
-  for (node = cgraph_nodes; node; node = node->next)
+  FOR_EACH_FUNCTION (node)
     gcc_assert (!node->process);
 #endif
 
-  for (node = cgraph_nodes; node; node = node->next)
+  FOR_EACH_FUNCTION (node)
     {
-      tree decl = node->decl;
-      struct cgraph_edge *e;
+      tree decl = node->symbol.decl;
 
-      gcc_assert (!node->process || node->same_comdat_group);
+      gcc_assert (!node->process || node->symbol.same_comdat_group);
       if (node->process)
        continue;
 
-      for (e = node->callers; e; e = e->next_caller)
-       if (e->inline_failed)
-         break;
-
       /* We need to output all local functions that are used and not
         always inlined, as well as those that are reachable from
         outside the current compilation unit.  */
@@ -1348,24 +1135,21 @@ cgraph_mark_functions_to_output (void)
          && !node->thunk.thunk_p
          && !node->alias
          && !node->global.inlined_to
-         && (!cgraph_only_called_directly_p (node)
-             || ((e || ipa_ref_has_aliases_p (&node->ref_list))
-                 && node->reachable))
          && !TREE_ASM_WRITTEN (decl)
          && !DECL_EXTERNAL (decl))
        {
          node->process = 1;
-         if (node->same_comdat_group)
+         if (node->symbol.same_comdat_group)
            {
              struct cgraph_node *next;
-             for (next = node->same_comdat_group;
+             for (next = cgraph (node->symbol.same_comdat_group);
                   next != node;
-                  next = next->same_comdat_group)
+                  next = cgraph (next->symbol.same_comdat_group))
                if (!next->thunk.thunk_p && !next->alias)
                  next->process = 1;
            }
        }
-      else if (node->same_comdat_group)
+      else if (node->symbol.same_comdat_group)
        {
 #ifdef ENABLE_CHECKING
          check_same_comdat_groups = true;
@@ -1380,8 +1164,9 @@ cgraph_mark_functions_to_output (void)
              /* FIXME: in ltrans unit when offline copy is outside partition but inline copies
                 are inside partition, we can end up not removing the body since we no longer
                 have analyzed node pointing to it.  */
-             && !node->in_other_partition
+             && !node->symbol.in_other_partition
              && !node->alias
+             && !node->clones
              && !DECL_EXTERNAL (decl))
            {
              dump_cgraph_node (stderr, node);
@@ -1390,7 +1175,9 @@ cgraph_mark_functions_to_output (void)
 #endif
          gcc_assert (node->global.inlined_to
                      || !gimple_has_body_p (decl)
-                     || node->in_other_partition
+                     || node->symbol.in_other_partition
+                     || node->clones
+                     || DECL_ARTIFICIAL (decl)
                      || DECL_EXTERNAL (decl));
 
        }
@@ -1398,20 +1185,23 @@ cgraph_mark_functions_to_output (void)
     }
 #ifdef ENABLE_CHECKING
   if (check_same_comdat_groups)
-    for (node = cgraph_nodes; node; node = node->next)
-      if (node->same_comdat_group && !node->process)
+    FOR_EACH_FUNCTION (node)
+      if (node->symbol.same_comdat_group && !node->process)
        {
-         tree decl = node->decl;
+         tree decl = node->symbol.decl;
          if (!node->global.inlined_to
              && gimple_has_body_p (decl)
-             /* FIXME: in ltrans unit when offline copy is outside partition but inline copies
-                are inside partition, we can end up not removing the body since we no longer
-                have analyzed node pointing to it.  */
-             && !node->in_other_partition
+             /* FIXME: in an ltrans unit when the offline copy is outside a
+                partition but inline copies are inside a partition, we can
+                end up not removing the body since we no longer have an
+                analyzed node pointing to it.  */
+             && !node->symbol.in_other_partition
+             && !node->clones
              && !DECL_EXTERNAL (decl))
            {
              dump_cgraph_node (stderr, node);
-             internal_error ("failed to reclaim unneeded functionin same comdat group");
+             internal_error ("failed to reclaim unneeded function in same "
+                             "comdat group");
            }
        }
 #endif
@@ -1433,7 +1223,7 @@ init_lowered_empty_function (tree decl)
   gimple_register_cfg_hooks ();
   init_empty_tree_cfg ();
   init_tree_ssa (cfun);
-  init_ssa_operands ();
+  init_ssa_operands (cfun);
   cfun->gimple_df->in_ssa_p = true;
   DECL_INITIAL (decl) = make_node (BLOCK);
 
@@ -1492,25 +1282,21 @@ thunk_adjust (gimple_stmt_iterator * bsi,
        }
 
       vtabletmp =
-       create_tmp_var (build_pointer_type
-                       (build_pointer_type (vtable_entry_type)), "vptr");
+       make_rename_temp (build_pointer_type
+                         (build_pointer_type (vtable_entry_type)), "vptr");
 
       /* The vptr is always at offset zero in the object.  */
       stmt = gimple_build_assign (vtabletmp,
                                  build1 (NOP_EXPR, TREE_TYPE (vtabletmp),
                                          ptr));
       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
-      mark_symbols_for_renaming (stmt);
-      find_referenced_vars_in (stmt);
 
       /* Form the vtable address.  */
-      vtabletmp2 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp)),
-                                  "vtableaddr");
+      vtabletmp2 = make_rename_temp (TREE_TYPE (TREE_TYPE (vtabletmp)),
+                                    "vtableaddr");
       stmt = gimple_build_assign (vtabletmp2,
                                  build_simple_mem_ref (vtabletmp));
       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
-      mark_symbols_for_renaming (stmt);
-      find_referenced_vars_in (stmt);
 
       /* Find the entry with the vcall offset.  */
       stmt = gimple_build_assign (vtabletmp2,
@@ -1520,13 +1306,11 @@ thunk_adjust (gimple_stmt_iterator * bsi,
       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
 
       /* Get the offset itself.  */
-      vtabletmp3 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp2)),
-                                  "vcalloffset");
+      vtabletmp3 = make_rename_temp (TREE_TYPE (TREE_TYPE (vtabletmp2)),
+                                    "vcalloffset");
       stmt = gimple_build_assign (vtabletmp3,
                                  build_simple_mem_ref (vtabletmp2));
       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
-      mark_symbols_for_renaming (stmt);
-      find_referenced_vars_in (stmt);
 
       /* Adjust the `this' pointer.  */
       ptr = fold_build_pointer_plus_loc (input_location, ptr, vtabletmp3);
@@ -1544,21 +1328,17 @@ thunk_adjust (gimple_stmt_iterator * bsi,
         ptrtmp = ptr;
       else
         {
-          ptrtmp = create_tmp_var (TREE_TYPE (ptr), "ptr");
+          ptrtmp = make_rename_temp (TREE_TYPE (ptr), "ptr");
           stmt = gimple_build_assign (ptrtmp, ptr);
          gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
-         mark_symbols_for_renaming (stmt);
-         find_referenced_vars_in (stmt);
        }
       ptr = fold_build_pointer_plus_hwi_loc (input_location,
                                             ptrtmp, fixed_offset);
     }
 
   /* Emit the statement and gimplify the adjustment expression.  */
-  ret = create_tmp_var (TREE_TYPE (ptr), "adjusted_this");
+  ret = make_rename_temp (TREE_TYPE (ptr), "adjusted_this");
   stmt = gimple_build_assign (ret, ptr);
-  mark_symbols_for_renaming (stmt);
-  find_referenced_vars_in (stmt);
   gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
 
   return ret;
@@ -1574,7 +1354,7 @@ assemble_thunk (struct cgraph_node *node)
   HOST_WIDE_INT virtual_value = node->thunk.virtual_value;
   tree virtual_offset = NULL;
   tree alias = node->thunk.alias;
-  tree thunk_fndecl = node->decl;
+  tree thunk_fndecl = node->symbol.decl;
   tree a = DECL_ARGUMENTS (thunk_fndecl);
 
   current_function_decl = thunk_fndecl;
@@ -1662,7 +1442,7 @@ assemble_thunk (struct cgraph_node *node)
              BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp;
            }
          else
-            restmp = create_tmp_var_raw (restype, "retval");
+            restmp = make_rename_temp (restype, "retval");
        }
 
       for (arg = a; arg; arg = DECL_CHAIN (arg))
@@ -1675,18 +1455,22 @@ assemble_thunk (struct cgraph_node *node)
                                      virtual_offset));
       else
         VEC_quick_push (tree, vargs, a);
+      add_referenced_var (a);
+      if (is_gimple_reg (a))
+       mark_sym_for_renaming (a);
       for (i = 1, arg = DECL_CHAIN (a); i < nargs; i++, arg = DECL_CHAIN (arg))
-        VEC_quick_push (tree, vargs, arg);
+       {
+         add_referenced_var (arg);
+         if (is_gimple_reg (arg))
+           mark_sym_for_renaming (arg);
+         VEC_quick_push (tree, vargs, arg);
+       }
       call = gimple_build_call_vec (build_fold_addr_expr_loc (0, alias), vargs);
       VEC_free (tree, heap, vargs);
-      gimple_call_set_cannot_inline (call, true);
       gimple_call_set_from_thunk (call, true);
       if (restmp)
         gimple_call_set_lhs (call, restmp);
       gsi_insert_after (&bsi, call, GSI_NEW_STMT);
-      mark_symbols_for_renaming (call);
-      find_referenced_vars_in (call);
-      update_stmt (call);
 
       if (restmp && !this_adjusting)
         {
@@ -1750,7 +1534,7 @@ assemble_thunk (struct cgraph_node *node)
 
 
 
-/* Assemble thunks and aliases asociated to NODE.  */
+/* Assemble thunks and aliases associated to NODE.  */
 
 static void
 assemble_thunks_and_aliases (struct cgraph_node *node)
@@ -1770,17 +1554,18 @@ assemble_thunks_and_aliases (struct cgraph_node *node)
       }
     else
       e = e->next_caller;
-  for (i = 0; ipa_ref_list_refering_iterate (&node->ref_list, i, ref); i++)
+  for (i = 0; ipa_ref_list_referring_iterate (&node->symbol.ref_list,
+                                            i, ref); i++)
     if (ref->use == IPA_REF_ALIAS)
       {
-       struct cgraph_node *alias = ipa_ref_refering_node (ref);
+       struct cgraph_node *alias = ipa_ref_referring_node (ref);
         bool saved_written = TREE_ASM_WRITTEN (alias->thunk.alias);
 
        /* Force assemble_alias to really output the alias this time instead
           of buffering it in same alias pairs.  */
        TREE_ASM_WRITTEN (alias->thunk.alias) = 1;
-       assemble_alias (alias->decl,
-                       DECL_ASSEMBLER_NAME (alias->thunk.alias));
+       do_assemble_alias (alias->symbol.decl,
+                          DECL_ASSEMBLER_NAME (alias->thunk.alias));
        assemble_thunks_and_aliases (alias);
        TREE_ASM_WRITTEN (alias->thunk.alias) = saved_written;
       }
@@ -1789,9 +1574,10 @@ assemble_thunks_and_aliases (struct cgraph_node *node)
 /* Expand function specified by NODE.  */
 
 static void
-cgraph_expand_function (struct cgraph_node *node)
+expand_function (struct cgraph_node *node)
 {
-  tree decl = node->decl;
+  tree decl = node->symbol.decl;
+  location_t saved_loc;
 
   /* We ought to not compile any inline clones.  */
   gcc_assert (!node->global.inlined_to);
@@ -1801,17 +1587,93 @@ cgraph_expand_function (struct cgraph_node *node)
   gcc_assert (node->lowered);
 
   /* Generate RTL for the body of DECL.  */
-  tree_rest_of_compilation (decl);
+
+  timevar_push (TV_REST_OF_COMPILATION);
+
+  gcc_assert (cgraph_global_info_ready);
+
+  /* Initialize the default bitmap obstack.  */
+  bitmap_obstack_initialize (NULL);
+
+  /* Initialize the RTL code for the function.  */
+  current_function_decl = decl;
+  saved_loc = input_location;
+  input_location = DECL_SOURCE_LOCATION (decl);
+  init_function_start (decl);
+
+  gimple_register_cfg_hooks ();
+
+  bitmap_obstack_initialize (&reg_obstack); /* FIXME, only at RTL generation*/
+
+  execute_all_ipa_transforms ();
+
+  /* Perform all tree transforms and optimizations.  */
+
+  /* Signal the start of passes.  */
+  invoke_plugin_callbacks (PLUGIN_ALL_PASSES_START, NULL);
+
+  execute_pass_list (all_passes);
+
+  /* Signal the end of passes.  */
+  invoke_plugin_callbacks (PLUGIN_ALL_PASSES_END, NULL);
+
+  bitmap_obstack_release (&reg_obstack);
+
+  /* Release the default bitmap obstack.  */
+  bitmap_obstack_release (NULL);
+
+  set_cfun (NULL);
+
+  /* If requested, warn about function definitions where the function will
+     return a value (usually of some struct or union type) which itself will
+     take up a lot of stack space.  */
+  if (warn_larger_than && !DECL_EXTERNAL (decl) && TREE_TYPE (decl))
+    {
+      tree ret_type = TREE_TYPE (TREE_TYPE (decl));
+
+      if (ret_type && TYPE_SIZE_UNIT (ret_type)
+         && TREE_CODE (TYPE_SIZE_UNIT (ret_type)) == INTEGER_CST
+         && 0 < compare_tree_int (TYPE_SIZE_UNIT (ret_type),
+                                  larger_than_size))
+       {
+         unsigned int size_as_int
+           = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ret_type));
+
+         if (compare_tree_int (TYPE_SIZE_UNIT (ret_type), size_as_int) == 0)
+           warning (OPT_Wlarger_than_, "size of return value of %q+D is %u bytes",
+                     decl, size_as_int);
+         else
+           warning (OPT_Wlarger_than_, "size of return value of %q+D is larger than %wd bytes",
+                     decl, larger_than_size);
+       }
+    }
+
+  gimple_set_body (decl, NULL);
+  if (DECL_STRUCT_FUNCTION (decl) == 0
+      && !cgraph_get_node (decl)->origin)
+    {
+      /* Stop pointing to the local nodes about to be freed.
+        But DECL_INITIAL must remain nonzero so we know this
+        was an actual function definition.
+        For a nested function, this is done in c_pop_function_context.
+        If rest_of_compilation set this to 0, leave it 0.  */
+      if (DECL_INITIAL (decl) != 0)
+       DECL_INITIAL (decl) = error_mark_node;
+    }
+
+  input_location = saved_loc;
+
+  ggc_collect ();
+  timevar_pop (TV_REST_OF_COMPILATION);
 
   /* Make sure that BE didn't give up on compiling.  */
   gcc_assert (TREE_ASM_WRITTEN (decl));
   current_function_decl = NULL;
-  gcc_assert (!cgraph_preserve_function_body_p (node));
 
   /* It would make a lot more sense to output thunks before function body to get more
-     forward and lest backwarding jumps.  This is however would need solving problem
+     forward and lest backwarding jumps.  This however would need solving problem
      with comdats. See PR48668.  Also aliases must come after function itself to
-     make one pass assemblers, like one on AIX happy.  See PR 50689.
+     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);
@@ -1819,21 +1681,9 @@ cgraph_expand_function (struct cgraph_node *node)
   /* Eliminate all call edges.  This is important so the GIMPLE_CALL no longer
      points to the dead function body.  */
   cgraph_node_remove_callees (node);
-
-  cgraph_function_flags_ready = true;
-}
-
-/* Return true when CALLER_DECL should be inlined into CALLEE_DECL.  */
-
-bool
-cgraph_inline_p (struct cgraph_edge *e, cgraph_inline_failed_t *reason)
-{
-  *reason = e->inline_failed;
-  return !e->inline_failed;
 }
 
 
-
 /* Expand all functions that must be output.
 
    Attempt to topologically sort the nodes so function is output when
@@ -1845,7 +1695,7 @@ cgraph_inline_p (struct cgraph_edge *e, cgraph_inline_failed_t *reason)
    order).  */
 
 static void
-cgraph_expand_all_functions (void)
+expand_all_functions (void)
 {
   struct cgraph_node *node;
   struct cgraph_node **order = XCNEWVEC (struct cgraph_node *, cgraph_n_nodes);
@@ -1866,9 +1716,8 @@ cgraph_expand_all_functions (void)
       node = order[i];
       if (node->process)
        {
-         gcc_assert (node->reachable);
          node->process = 0;
-         cgraph_expand_function (node);
+         expand_function (node);
        }
     }
   cgraph_process_new_functions ();
@@ -1894,7 +1743,7 @@ struct cgraph_order_sort
   {
     struct cgraph_node *f;
     struct varpool_node *v;
-    struct cgraph_asm_node *a;
+    struct asm_node *a;
   } u;
 };
 
@@ -1905,40 +1754,39 @@ struct cgraph_order_sort
    need to be output.  */
 
 static void
-cgraph_output_in_order (void)
+output_in_order (void)
 {
   int max;
   struct cgraph_order_sort *nodes;
   int i;
   struct cgraph_node *pf;
   struct varpool_node *pv;
-  struct cgraph_asm_node *pa;
+  struct asm_node *pa;
 
-  max = cgraph_order;
+  max = symtab_order;
   nodes = XCNEWVEC (struct cgraph_order_sort, max);
 
-  varpool_analyze_pending_decls ();
-
-  for (pf = cgraph_nodes; pf; pf = pf->next)
+  FOR_EACH_DEFINED_FUNCTION (pf)
     {
       if (pf->process && !pf->thunk.thunk_p && !pf->alias)
        {
-         i = pf->order;
+         i = pf->symbol.order;
          gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
          nodes[i].kind = ORDER_FUNCTION;
          nodes[i].u.f = pf;
        }
     }
 
-  for (pv = varpool_nodes_queue; pv; pv = pv->next_needed)
-    {
-      i = pv->order;
-      gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
-      nodes[i].kind = ORDER_VAR;
-      nodes[i].u.v = pv;
-    }
+  FOR_EACH_DEFINED_VARIABLE (pv)
+    if (!DECL_EXTERNAL (pv->symbol.decl))
+      {
+       i = pv->symbol.order;
+       gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
+       nodes[i].kind = ORDER_VAR;
+       nodes[i].u.v = pv;
+      }
 
-  for (pa = cgraph_asm_nodes; pa; pa = pa->next)
+  for (pa = asm_nodes; pa; pa = pa->next)
     {
       i = pa->order;
       gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
@@ -1947,14 +1795,6 @@ cgraph_output_in_order (void)
     }
 
   /* In toplevel reorder mode we output all statics; mark them as needed.  */
-  for (i = 0; i < max; ++i)
-    {
-      if (nodes[i].kind == ORDER_VAR)
-        {
-         varpool_mark_needed_node (nodes[i].u.v);
-       }
-    }
-  varpool_empty_needed_queue ();
 
   for (i = 0; i < max; ++i)
     if (nodes[i].kind == ORDER_VAR)
@@ -1966,7 +1806,7 @@ cgraph_output_in_order (void)
        {
        case ORDER_FUNCTION:
          nodes[i].u.f->process = 0;
-         cgraph_expand_function (nodes[i].u.f);
+         expand_function (nodes[i].u.f);
          break;
 
        case ORDER_VAR:
@@ -1985,24 +1825,10 @@ cgraph_output_in_order (void)
        }
     }
 
-  cgraph_asm_nodes = NULL;
+  asm_nodes = NULL;
   free (nodes);
 }
 
-/* Return true when function body of DECL still needs to be kept around
-   for later re-use.  */
-bool
-cgraph_preserve_function_body_p (struct cgraph_node *node)
-{
-  gcc_assert (cgraph_global_info_ready);
-  gcc_assert (!node->alias && !node->thunk.thunk_p);
-
-  /* Look if there is any clone around.  */
-  if (node->clones)
-    return true;
-  return false;
-}
-
 static void
 ipa_passes (void)
 {
@@ -2024,7 +1850,7 @@ ipa_passes (void)
      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.  */
-  cgraph_remove_unreachable_nodes (true, cgraph_dump_file);
+  symtab_remove_unreachable_nodes (true, cgraph_dump_file);
 
   /* If pass_all_early_optimizations was not scheduled, the state of
      the cgraph will not be properly updated.  Update it now.  */
@@ -2064,43 +1890,64 @@ ipa_passes (void)
   bitmap_obstack_release (NULL);
 }
 
+
+/* Return string alias is alias of.  */
+
+static tree
+get_alias_symbol (tree decl)
+{
+  tree alias = lookup_attribute ("alias", DECL_ATTRIBUTES (decl));
+  return get_identifier (TREE_STRING_POINTER
+                         (TREE_VALUE (TREE_VALUE (alias))));
+}
+
+
 /* Weakrefs may be associated to external decls and thus not output
-   at expansion time.  Emit all neccesary aliases.  */
+   at expansion time.  Emit all necessary aliases.  */
 
-void
+static void
 output_weakrefs (void)
 {
   struct cgraph_node *node;
   struct varpool_node *vnode;
-  for (node = cgraph_nodes; node; node = node->next)
-    if (node->alias && node->thunk.alias && DECL_EXTERNAL (node->decl)
-        && !TREE_ASM_WRITTEN (node->decl))
-      assemble_alias (node->decl,
-                     DECL_ASSEMBLER_NAME (node->thunk.alias));
-  for (vnode = varpool_nodes; vnode; vnode = vnode->next)
-    if (vnode->alias && vnode->alias_of && DECL_EXTERNAL (vnode->decl)
-        && !TREE_ASM_WRITTEN (vnode->decl))
-      assemble_alias (vnode->decl,
-                     DECL_ASSEMBLER_NAME (vnode->alias_of));
+  FOR_EACH_FUNCTION (node)
+    if (node->alias && DECL_EXTERNAL (node->symbol.decl)
+        && !TREE_ASM_WRITTEN (node->symbol.decl)
+       && lookup_attribute ("weakref", DECL_ATTRIBUTES (node->symbol.decl)))
+      do_assemble_alias (node->symbol.decl,
+                        node->thunk.alias ? DECL_ASSEMBLER_NAME (node->thunk.alias)
+                        : get_alias_symbol (node->symbol.decl));
+  FOR_EACH_VARIABLE (vnode)
+    if (vnode->alias && DECL_EXTERNAL (vnode->symbol.decl)
+        && !TREE_ASM_WRITTEN (vnode->symbol.decl)
+       && lookup_attribute ("weakref", DECL_ATTRIBUTES (vnode->symbol.decl)))
+      do_assemble_alias (vnode->symbol.decl,
+                        vnode->alias_of ? DECL_ASSEMBLER_NAME (vnode->alias_of)
+                        : get_alias_symbol (vnode->symbol.decl));
+}
+
+/* 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
-cgraph_optimize (void)
+compile (void)
 {
   if (seen_error ())
     return;
 
 #ifdef ENABLE_CHECKING
-  verify_cgraph ();
+  verify_symtab ();
 #endif
 
-  /* Frontend may output common variables after the unit has been finalized.
-     It is safe to deal with them here as they are always zero initialized.  */
-  varpool_analyze_pending_decls ();
-
   timevar_push (TV_CGRAPHOPT);
   if (pre_ipa_mem_report)
     {
@@ -2111,6 +1958,10 @@ cgraph_optimize (void)
     fprintf (stderr, "Performing interprocedural optimizations\n");
   cgraph_state = CGRAPH_STATE_IPA;
 
+  /* If LTO is enabled, initialize the streamer hooks needed by GIMPLE.  */
+  if (flag_lto)
+    lto_streamer_hooks_init ();
+
   /* Don't run the IPA passes if there was any error or sorry messages.  */
   if (!seen_error ())
     ipa_passes ();
@@ -2125,13 +1976,12 @@ cgraph_optimize (void)
 
   /* This pass remove bodies of extern inline functions we never inlined.
      Do this later so other IPA passes see what is really going on.  */
-  cgraph_remove_unreachable_nodes (false, dump_file);
+  symtab_remove_unreachable_nodes (false, dump_file);
   cgraph_global_info_ready = true;
   if (cgraph_dump_file)
     {
       fprintf (cgraph_dump_file, "Optimized ");
-      dump_cgraph (cgraph_dump_file);
-      dump_varpool (cgraph_dump_file);
+      dump_symtab (cgraph_dump_file);
     }
   if (post_ipa_mem_report)
     {
@@ -2145,44 +1995,41 @@ cgraph_optimize (void)
   if (!quiet_flag)
     fprintf (stderr, "Assembling functions:\n");
 #ifdef ENABLE_CHECKING
-  verify_cgraph ();
+  verify_symtab ();
 #endif
 
   cgraph_materialize_all_clones ();
   bitmap_obstack_initialize (NULL);
   execute_ipa_pass_list (all_late_ipa_passes);
-  cgraph_remove_unreachable_nodes (true, dump_file);
+  symtab_remove_unreachable_nodes (true, dump_file);
 #ifdef ENABLE_CHECKING
-  verify_cgraph ();
+  verify_symtab ();
 #endif
   bitmap_obstack_release (NULL);
-  cgraph_mark_functions_to_output ();
+  mark_functions_to_output ();
 
   cgraph_state = CGRAPH_STATE_EXPANSION;
   if (!flag_toplevel_reorder)
-    cgraph_output_in_order ();
+    output_in_order ();
   else
     {
-      cgraph_output_pending_asms ();
-
-      cgraph_expand_all_functions ();
-      varpool_remove_unreferenced_decls ();
+      output_asm_statements ();
 
-      varpool_assemble_pending_decls ();
+      expand_all_functions ();
+      varpool_output_variables ();
     }
 
-  output_weakrefs ();
   cgraph_process_new_functions ();
   cgraph_state = CGRAPH_STATE_FINISHED;
+  output_weakrefs ();
 
   if (cgraph_dump_file)
     {
       fprintf (cgraph_dump_file, "\nFinal ");
-      dump_cgraph (cgraph_dump_file);
-      dump_varpool (cgraph_dump_file);
+      dump_symtab (cgraph_dump_file);
     }
 #ifdef ENABLE_CHECKING
-  verify_cgraph ();
+  verify_symtab ();
   /* Double check that all inline clones are gone and that all
      function bodies have been released from memory.  */
   if (!seen_error ())
@@ -2190,10 +2037,9 @@ cgraph_optimize (void)
       struct cgraph_node *node;
       bool error_found = false;
 
-      for (node = cgraph_nodes; node; node = node->next)
-       if (node->analyzed
-           && (node->global.inlined_to
-               || gimple_has_body_p (node->decl)))
+      FOR_EACH_DEFINED_FUNCTION (node)
+       if (node->global.inlined_to
+           || gimple_has_body_p (node->symbol.decl))
          {
            error_found = true;
            dump_cgraph_node (stderr, node);
@@ -2204,368 +2050,52 @@ cgraph_optimize (void)
 #endif
 }
 
-void
-init_cgraph (void)
-{
-  if (!cgraph_dump_file)
-    cgraph_dump_file = dump_begin (TDI_cgraph, NULL);
-}
-
-/* The edges representing the callers of the NEW_VERSION node were
-   fixed by cgraph_function_versioning (), now the call_expr in their
-   respective tree code should be updated to call the NEW_VERSION.  */
-
-static void
-update_call_expr (struct cgraph_node *new_version)
-{
-  struct cgraph_edge *e;
-
-  gcc_assert (new_version);
-
-  /* Update the call expr on the edges to call the new version.  */
-  for (e = new_version->callers; e; e = e->next_caller)
-    {
-      struct function *inner_function = DECL_STRUCT_FUNCTION (e->caller->decl);
-      gimple_call_set_fndecl (e->call_stmt, new_version->decl);
-      maybe_clean_eh_stmt_fn (inner_function, e->call_stmt);
-    }
-}
 
+/* Analyze the whole compilation unit once it is parsed completely.  */
 
-/* Create a new cgraph node which is the new version of
-   OLD_VERSION node.  REDIRECT_CALLERS holds the callers
-   edges which should be redirected to point to
-   NEW_VERSION.  ALL the callees edges of OLD_VERSION
-   are cloned to the new version node.  Return the new
-   version node. 
-
-   If non-NULL BLOCK_TO_COPY determine what basic blocks 
-   was copied to prevent duplications of calls that are dead
-   in the clone.  */
-
-static struct cgraph_node *
-cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
-                                tree new_decl,
-                                VEC(cgraph_edge_p,heap) *redirect_callers,
-                                bitmap bbs_to_copy)
- {
-   struct cgraph_node *new_version;
-   struct cgraph_edge *e;
-   unsigned i;
-
-   gcc_assert (old_version);
-
-   new_version = cgraph_create_node (new_decl);
-
-   new_version->analyzed = true;
-   new_version->local = old_version->local;
-   new_version->local.externally_visible = false;
-   new_version->local.local = true;
-   new_version->global = old_version->global;
-   new_version->rtl = old_version->rtl;
-   new_version->reachable = true;
-   new_version->count = old_version->count;
-
-   for (e = old_version->callees; e; e=e->next_callee)
-     if (!bbs_to_copy
-        || bitmap_bit_p (bbs_to_copy, gimple_bb (e->call_stmt)->index))
-       cgraph_clone_edge (e, new_version, e->call_stmt,
-                         e->lto_stmt_uid, REG_BR_PROB_BASE,
-                         CGRAPH_FREQ_BASE,
-                         true);
-   for (e = old_version->indirect_calls; e; e=e->next_callee)
-     if (!bbs_to_copy
-        || bitmap_bit_p (bbs_to_copy, gimple_bb (e->call_stmt)->index))
-       cgraph_clone_edge (e, new_version, e->call_stmt,
-                         e->lto_stmt_uid, REG_BR_PROB_BASE,
-                         CGRAPH_FREQ_BASE,
-                         true);
-   FOR_EACH_VEC_ELT (cgraph_edge_p, redirect_callers, i, e)
-     {
-       /* Redirect calls to the old version node to point to its new
-         version.  */
-       cgraph_redirect_edge_callee (e, new_version);
-     }
-
-   return new_version;
- }
-
- /* Perform function versioning.
-    Function versioning includes copying of the tree and
-    a callgraph update (creating a new cgraph node and updating
-    its callees and callers).
-
-    REDIRECT_CALLERS varray includes the edges to be redirected
-    to the new version.
-
-    TREE_MAP is a mapping of tree nodes we want to replace with
-    new ones (according to results of prior analysis).
-    OLD_VERSION_NODE is the node that is versioned.
-    It returns the new version's cgraph node.
-    If non-NULL ARGS_TO_SKIP determine function parameters to remove
-    from new version.
-    If non-NULL BLOCK_TO_COPY determine what basic blocks to copy.
-    If non_NULL NEW_ENTRY determine new entry BB of the clone.  */
-
-struct cgraph_node *
-cgraph_function_versioning (struct cgraph_node *old_version_node,
-                           VEC(cgraph_edge_p,heap) *redirect_callers,
-                           VEC (ipa_replace_map_p,gc)* tree_map,
-                           bitmap args_to_skip,
-                           bitmap bbs_to_copy,
-                           basic_block new_entry_block,
-                           const char *clone_name)
+void
+finalize_compilation_unit (void)
 {
-  tree old_decl = old_version_node->decl;
-  struct cgraph_node *new_version_node = NULL;
-  tree new_decl;
+  timevar_push (TV_CGRAPH);
 
-  if (!tree_versionable_function_p (old_decl))
-    return NULL;
+  /* If we're here there's no current function anymore.  Some frontends
+     are lazy in clearing these.  */
+  current_function_decl = NULL;
+  set_cfun (NULL);
 
-  gcc_assert (old_version_node->local.can_change_signature || !args_to_skip);
+  /* Do not skip analyzing the functions if there were errors, we
+     miss diagnostics for following functions otherwise.  */
 
-  /* Make a new FUNCTION_DECL tree node for the
-     new version. */
-  if (!args_to_skip)
-    new_decl = copy_node (old_decl);
-  else
-    new_decl = build_function_decl_skip_args (old_decl, args_to_skip);
-
-  /* Generate a new name for the new version. */
-  DECL_NAME (new_decl) = clone_function_name (old_decl, clone_name);
-  SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
-  SET_DECL_RTL (new_decl, NULL);
-
-  /* Create the new version's call-graph node.
-     and update the edges of the new node. */
-  new_version_node =
-    cgraph_copy_node_for_versioning (old_version_node, new_decl,
-                                    redirect_callers, bbs_to_copy);
-
-  /* Copy the OLD_VERSION_NODE function tree to the new version.  */
-  tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip,
-                           bbs_to_copy, new_entry_block);
-
-  /* Update the new version's properties.
-     Make The new version visible only within this translation unit.  Make sure
-     that is not weak also.
-     ??? We cannot use COMDAT linkage because there is no
-     ABI support for this.  */
-  cgraph_make_decl_local (new_version_node->decl);
-  DECL_VIRTUAL_P (new_version_node->decl) = 0;
-  new_version_node->local.externally_visible = 0;
-  new_version_node->local.local = 1;
-  new_version_node->lowered = true;
-
-  /* Update the call_expr on the edges to call the new version node. */
-  update_call_expr (new_version_node);
-
-  cgraph_call_function_insertion_hooks (new_version_node);
-  return new_version_node;
-}
+  /* Emit size functions we didn't inline.  */
+  finalize_size_functions ();
 
-/* Given virtual clone, turn it into actual clone.  */
-static void
-cgraph_materialize_clone (struct cgraph_node *node)
-{
-  bitmap_obstack_initialize (NULL);
-  node->former_clone_of = node->clone_of->decl;
-  if (node->clone_of->former_clone_of)
-    node->former_clone_of = node->clone_of->former_clone_of;
-  /* Copy the OLD_VERSION_NODE function tree to the new version.  */
-  tree_function_versioning (node->clone_of->decl, node->decl,
-                           node->clone.tree_map, true,
-                           node->clone.args_to_skip, NULL, NULL);
-  if (cgraph_dump_file)
-    {
-      dump_function_to_file (node->clone_of->decl, cgraph_dump_file, dump_flags);
-      dump_function_to_file (node->decl, cgraph_dump_file, dump_flags);
-    }
+  /* Mark alias targets necessary and emit diagnostics.  */
+  handle_alias_pairs ();
 
-  /* Function is no longer clone.  */
-  if (node->next_sibling_clone)
-    node->next_sibling_clone->prev_sibling_clone = node->prev_sibling_clone;
-  if (node->prev_sibling_clone)
-    node->prev_sibling_clone->next_sibling_clone = node->next_sibling_clone;
-  else
-    node->clone_of->clones = node->next_sibling_clone;
-  node->next_sibling_clone = NULL;
-  node->prev_sibling_clone = NULL;
-  if (!node->clone_of->analyzed && !node->clone_of->clones)
+  if (!quiet_flag)
     {
-      cgraph_release_function_body (node->clone_of);
-      cgraph_node_remove_callees (node->clone_of);
-      ipa_remove_all_references (&node->clone_of->ref_list);
+      fprintf (stderr, "\nAnalyzing compilation unit\n");
+      fflush (stderr);
     }
-  node->clone_of = NULL;
-  bitmap_obstack_release (NULL);
-}
-
-/* If necessary, change the function declaration in the call statement
-   associated with E so that it corresponds to the edge callee.  */
-
-gimple
-cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e)
-{
-  tree decl = gimple_call_fndecl (e->call_stmt);
-  gimple new_stmt;
-  gimple_stmt_iterator gsi;
-#ifdef ENABLE_CHECKING
-  struct cgraph_node *node;
-#endif
 
-  if (e->indirect_unknown_callee
-      || decl == e->callee->decl)
-    return e->call_stmt;
+  if (flag_dump_passes)
+    dump_passes ();
 
-#ifdef ENABLE_CHECKING
-  if (decl)
-    {
-      node = cgraph_get_node (decl);
-      gcc_assert (!node || !node->clone.combined_args_to_skip);
-    }
-#endif
+  /* Gimplify and lower all functions, compute reachability and
+     remove unreachable nodes.  */
+  cgraph_analyze_functions ();
 
-  if (cgraph_dump_file)
-    {
-      fprintf (cgraph_dump_file, "updating call of %s/%i -> %s/%i: ",
-              cgraph_node_name (e->caller), e->caller->uid,
-              cgraph_node_name (e->callee), e->callee->uid);
-      print_gimple_stmt (cgraph_dump_file, e->call_stmt, 0, dump_flags);
-      if (e->callee->clone.combined_args_to_skip)
-       {
-         fprintf (cgraph_dump_file, " combined args to skip: ");
-         dump_bitmap (cgraph_dump_file,
-                      e->callee->clone.combined_args_to_skip);
-       }
-    }
+  /* Mark alias targets necessary and emit diagnostics.  */
+  handle_alias_pairs ();
 
-  if (e->callee->clone.combined_args_to_skip)
-    {
-      int lp_nr;
-
-      new_stmt
-       = gimple_call_copy_skip_args (e->call_stmt,
-                                     e->callee->clone.combined_args_to_skip);
-      gimple_call_set_fndecl (new_stmt, e->callee->decl);
-
-      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);
-      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);
-       }
-    }
-  else
-    {
-      new_stmt = e->call_stmt;
-      gimple_call_set_fndecl (new_stmt, e->callee->decl);
-      update_stmt (new_stmt);
-    }
+  /* Gimplify and lower thunks.  */
+  cgraph_analyze_functions ();
 
-  cgraph_set_call_stmt_including_clones (e->caller, e->call_stmt, new_stmt);
+  /* Finally drive the pass manager.  */
+  compile ();
 
-  if (cgraph_dump_file)
-    {
-      fprintf (cgraph_dump_file, "  updated to:");
-      print_gimple_stmt (cgraph_dump_file, e->call_stmt, 0, dump_flags);
-    }
-  return new_stmt;
+  timevar_pop (TV_CGRAPH);
 }
 
-/* Once all functions from compilation unit are in memory, produce all clones
-   and update all calls.  We might also do this on demand if we don't want to
-   bring all functions to memory prior compilation, but current WHOPR
-   implementation does that and it is is bit easier to keep everything right in
-   this order.  */
-void
-cgraph_materialize_all_clones (void)
-{
-  struct cgraph_node *node;
-  bool stabilized = false;
-
-  if (cgraph_dump_file)
-    fprintf (cgraph_dump_file, "Materializing clones\n");
-#ifdef ENABLE_CHECKING
-  verify_cgraph ();
-#endif
-
-  /* We can also do topological order, but number of iterations should be
-     bounded by number of IPA passes since single IPA pass is probably not
-     going to create clones of clones it created itself.  */
-  while (!stabilized)
-    {
-      stabilized = true;
-      for (node = cgraph_nodes; node; node = node->next)
-        {
-         if (node->clone_of && node->decl != node->clone_of->decl
-             && !gimple_has_body_p (node->decl))
-           {
-             if (gimple_has_body_p (node->clone_of->decl))
-               {
-                 if (cgraph_dump_file)
-                   {
-                     fprintf (cgraph_dump_file, "cloning %s to %s\n",
-                              cgraph_node_name (node->clone_of),
-                              cgraph_node_name (node));
-                     if (node->clone.tree_map)
-                       {
-                         unsigned int i;
-                         fprintf (cgraph_dump_file, "   replace map: ");
-                         for (i = 0; i < VEC_length (ipa_replace_map_p,
-                                                     node->clone.tree_map);
-                                                     i++)
-                           {
-                             struct ipa_replace_map *replace_info;
-                             replace_info = VEC_index (ipa_replace_map_p,
-                                                       node->clone.tree_map,
-                                                       i);
-                             print_generic_expr (cgraph_dump_file, replace_info->old_tree, 0);
-                             fprintf (cgraph_dump_file, " -> ");
-                             print_generic_expr (cgraph_dump_file, replace_info->new_tree, 0);
-                             fprintf (cgraph_dump_file, "%s%s;",
-                                      replace_info->replace_p ? "(replace)":"",
-                                      replace_info->ref_p ? "(ref)":"");
-                           }
-                         fprintf (cgraph_dump_file, "\n");
-                       }
-                     if (node->clone.args_to_skip)
-                       {
-                         fprintf (cgraph_dump_file, "   args_to_skip: ");
-                         dump_bitmap (cgraph_dump_file, node->clone.args_to_skip);
-                       }
-                     if (node->clone.args_to_skip)
-                       {
-                         fprintf (cgraph_dump_file, "   combined_args_to_skip:");
-                         dump_bitmap (cgraph_dump_file, node->clone.combined_args_to_skip);
-                       }
-                   }
-                 cgraph_materialize_clone (node);
-                 stabilized = false;
-               }
-           }
-       }
-    }
-  for (node = cgraph_nodes; node; node = node->next)
-    if (!node->analyzed && node->callees)
-      cgraph_node_remove_callees (node);
-  if (cgraph_dump_file)
-    fprintf (cgraph_dump_file, "Materialization Call site updates done.\n");
-#ifdef ENABLE_CHECKING
-  verify_cgraph ();
-#endif
-  cgraph_remove_unreachable_nodes (false, cgraph_dump_file);
-}
 
 #include "gt-cgraphunit.h"