gimple.h: Reorder prototypes to match .c declaration order...
[gcc.git] / gcc / cgraphunit.c
index 64460ac63b7e7a754268de467b62c6b6c0137cfb..863b81e72c0ceeafbb7a33ead42e5262f9c2ca81 100644 (file)
@@ -1,6 +1,5 @@
 /* Driver of optimization process
-   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-   2011, 2012 Free Software Foundation, Inc.
+   Copyright (C) 2003-2013 Free Software Foundation, Inc.
    Contributed by Jan Hubicka
 
 This file is part of GCC.
@@ -84,7 +83,7 @@ along with GCC; see the file COPYING3.  If not see
           b) early small interprocedural passes.
 
              Those are interprocedural passes executed only at compilation
-             time.  These include, for exmaple, transational memory lowering,
+             time.  These include, for example, transational memory lowering,
              unreachable code removal and other simple transformations.
 
           c) IP analysis stage.  All interprocedural passes do their
@@ -165,7 +164,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree.h"
 #include "output.h"
 #include "rtl.h"
-#include "tree-flow.h"
+#include "gimple.h"
+#include "gimplify.h"
+#include "gimple-iterator.h"
+#include "gimplify-me.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "tree-into-ssa.h"
+#include "tree-ssa.h"
 #include "tree-inline.h"
 #include "langhooks.h"
 #include "pointer-set.h"
@@ -174,14 +180,12 @@ along with GCC; see the file COPYING3.  If not see
 #include "ggc.h"
 #include "debug.h"
 #include "target.h"
-#include "cgraph.h"
 #include "diagnostic.h"
 #include "params.h"
 #include "fibheap.h"
 #include "intl.h"
 #include "function.h"
 #include "ipa-prop.h"
-#include "gimple.h"
 #include "tree-iterator.h"
 #include "tree-pass.h"
 #include "tree-dump.h"
@@ -193,7 +197,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "ipa-utils.h"
 #include "lto-streamer.h"
 #include "except.h"
+#include "cfgloop.h"
 #include "regset.h"     /* FIXME: For reg_obstack.  */
+#include "context.h"
+#include "pass_manager.h"
+#include "tree-nested.h"
 
 /* Queue of cgraph nodes scheduled to be added into cgraph.  This is a
    secondary queue used during optimization to accommodate passes that
@@ -203,7 +211,7 @@ 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 analyze_function (struct cgraph_node *);
 static void handle_alias_pairs (void);
 
 FILE *cgraph_dump_file;
@@ -217,37 +225,41 @@ 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 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.  */
-
-static bool
-cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl)
+/* Determine if symbol DECL is needed.  That is, visible to something
+   either outside this translation unit, something magic in the system
+   configury */
+bool
+decide_is_symbol_needed (symtab_node *node)
 {
-  /* If the user told us it is used, then it must be so.  */
-  if (node->symbol.force_output)
-    return true;
+  tree decl = node->decl;
 
   /* Double check that no one output the function into assembly file
      early.  */
   gcc_checking_assert (!DECL_ASSEMBLER_NAME_SET_P (decl)
-                      || (node->thunk.thunk_p || node->same_body_alias)
-                      ||  !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)));
+                      || !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)));
 
+  if (!node->definition)
+    return false;
 
-  /* 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;
+  if (DECL_EXTERNAL (decl))
+    return false;
 
-  /* Externally visible functions must be output.  The exception is
-     COMDAT functions that must be output only when they are needed.  */
+  /* If the user told us it is used, then it must be so.  */
+  if (node->force_output)
+    return true;
 
-  if (TREE_PUBLIC (decl)
-      && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
+  /* ABI forced symbols are needed when they are external.  */
+  if (node->forced_by_abi && TREE_PUBLIC (decl))
+    return true;
+
+ /* Keep constructors, destructors and virtual functions.  */
+   if (TREE_CODE (decl) == FUNCTION_DECL
+       && (DECL_STATIC_CONSTRUCTOR (decl) || DECL_STATIC_DESTRUCTOR (decl)))
+    return true;
+
+  /* Externally visible variables must be output.  The exception is
+     COMDAT variables that must be output only when they are needed.  */
+  if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl))
     return true;
 
   return false;
@@ -255,18 +267,18 @@ cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl)
 
 /* Head of the queue of nodes to be processed while building callgraph */
 
-static symtab_node first = (symtab_node)(void *)1;
+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)
+enqueue_node (symtab_node *node)
 {
-  if (node->symbol.aux)
+  if (node->aux)
     return;
   gcc_checking_assert (first);
-  node->symbol.aux = first;
+  node->aux = first;
   first = node;
 }
 
@@ -290,7 +302,7 @@ cgraph_process_new_functions (void)
   for (csi = csi_start (cgraph_new_nodes); !csi_end_p (csi); csi_next (&csi))
     {
       node = csi_node (csi);
-      fndecl = node->symbol.decl;
+      fndecl = node->decl;
       switch (cgraph_state)
        {
        case CGRAPH_STATE_CONSTRUCTION:
@@ -300,7 +312,7 @@ cgraph_process_new_functions (void)
          cgraph_finalize_function (fndecl, false);
          output = true;
           cgraph_call_function_insertion_hooks (node);
-         enqueue_node ((symtab_node) node);
+         enqueue_node (node);
          break;
 
        case CGRAPH_STATE_IPA:
@@ -311,15 +323,12 @@ cgraph_process_new_functions (void)
 
          gimple_register_cfg_hooks ();
          if (!node->analyzed)
-           cgraph_analyze_function (node);
+           analyze_function (node);
          push_cfun (DECL_STRUCT_FUNCTION (fndecl));
-         if ((cgraph_state == CGRAPH_STATE_IPA_SSA
+         if (cgraph_state == CGRAPH_STATE_IPA_SSA
              && !gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
-             /* When not optimizing, be sure we run early local passes anyway
-                to expand OMP.  */
-             || !optimize)
-           execute_pass_list (pass_early_local_passes.pass.sub);
-         else
+           g->get_passes ()->execute_early_local_passes ();
+         else if (inline_summary_vec != NULL)
            compute_inline_parameters (node, true);
          free_dominance_info (CDI_POST_DOMINATORS);
          free_dominance_info (CDI_DOMINATORS);
@@ -355,7 +364,7 @@ cgraph_process_new_functions (void)
    ??? It may make more sense to use one body for inlining and other
    body for expanding the function but this is difficult to do.  */
 
-static void
+void
 cgraph_reset_node (struct cgraph_node *node)
 {
   /* If node->process is set, then we have already begun whole-unit analysis.
@@ -370,45 +379,53 @@ cgraph_reset_node (struct cgraph_node *node)
   memset (&node->global, 0, sizeof (node->global));
   memset (&node->rtl, 0, sizeof (node->rtl));
   node->analyzed = false;
-  node->local.finalized = false;
+  node->definition = false;
+  node->alias = false;
+  node->weakref = false;
+  node->cpp_implicit_alias = false;
 
   cgraph_node_remove_callees (node);
+  ipa_remove_all_references (&node->ref_list);
 }
 
 /* Return true when there are references to NODE.  */
 
 static bool
-referred_to_p (symtab_node node)
+referred_to_p (symtab_node *node)
 {
   struct ipa_ref *ref;
 
   /* See if there are any references at all.  */
-  if (ipa_ref_list_referring_iterate (&node->symbol.ref_list, 0, ref))
+  if (ipa_ref_list_referring_iterate (&node->ref_list, 0, ref))
     return true;
   /* For functions check also calls.  */
-  if (symtab_function_p (node) && cgraph (node)->callers)
+  cgraph_node *cn = dyn_cast <cgraph_node> (node);
+  if (cn && cn->callers)
     return true;
   return false;
 }
 
 /* DECL has been parsed.  Take it, queue it, compile it at the whim of the
-   logic in effect.  If NESTED is true, then our caller cannot stand to have
+   logic in effect.  If NO_COLLECT is true, then our caller cannot stand to have
    the garbage collector run at the moment.  We would need to either create
    a new GC context, or just not compile right now.  */
 
 void
-cgraph_finalize_function (tree decl, bool nested)
+cgraph_finalize_function (tree decl, bool no_collect)
 {
   struct cgraph_node *node = cgraph_get_create_node (decl);
 
-  if (node->local.finalized)
+  if (node->definition)
     {
+      /* Nested functions should only be defined once.  */
+      gcc_assert (!DECL_CONTEXT (decl)
+                 || TREE_CODE (DECL_CONTEXT (decl)) != FUNCTION_DECL);
       cgraph_reset_node (node);
       node->local.redefined_extern_inline = true;
     }
 
   notice_global_symbol (decl);
-  node->local.finalized = true;
+  node->definition = true;
   node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL;
 
   /* With -fkeep-inline-functions we are keeping all inline functions except
@@ -417,7 +434,7 @@ cgraph_finalize_function (tree decl, bool nested)
       && DECL_DECLARED_INLINE_P (decl)
       && !DECL_EXTERNAL (decl)
       && !DECL_DISREGARD_INLINE_LIMITS (decl))
-    node->symbol.force_output = 1;
+    node->force_output = 1;
 
   /* When not optimizing, also output the static functions. (see
      PR24561), but don't do so for always_inline functions, functions
@@ -425,13 +442,13 @@ cgraph_finalize_function (tree decl, bool nested)
      in the original implementation and it is unclear whether we want
      to change the behavior here.  */
   if ((!optimize
-       && !node->same_body_alias
+       && !node->cpp_implicit_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;
+    node->force_output = 1;
 
   /* If we've not yet emitted decl, tell the debug info about it.  */
   if (!TREE_ASM_WRITTEN (decl))
@@ -441,13 +458,13 @@ cgraph_finalize_function (tree decl, bool nested)
   if (warn_unused_parameter)
     do_warn_unused_parameter (decl);
 
-  if (!nested)
+  if (!no_collect)
     ggc_collect ();
 
   if (cgraph_state == CGRAPH_STATE_CONSTRUCTION
-      && (cgraph_decide_is_function_needed (node, decl)
-         || referred_to_p ((symtab_node)node)))
-    enqueue_node ((symtab_node)node);
+      && (decide_is_symbol_needed (node)
+         || referred_to_p (node)))
+    enqueue_node (node);
 }
 
 /* Add the function FNDECL to the call graph.
@@ -465,6 +482,7 @@ cgraph_finalize_function (tree decl, bool nested)
 void
 cgraph_add_new_function (tree fndecl, bool lowered)
 {
+  gcc::pass_manager *passes = g->get_passes ();
   struct cgraph_node *node;
   switch (cgraph_state)
     {
@@ -488,15 +506,15 @@ cgraph_add_new_function (tree fndecl, bool lowered)
           analyzing and compilation.  */
        node = cgraph_get_create_node (fndecl);
        node->local.local = false;
-       node->local.finalized = true;
-       node->symbol.force_output = true;
+       node->definition = true;
+       node->force_output = true;
        if (!lowered && cgraph_state == CGRAPH_STATE_EXPANSION)
          {
            push_cfun (DECL_STRUCT_FUNCTION (fndecl));
            gimple_register_cfg_hooks ();
            bitmap_obstack_initialize (NULL);
-           execute_pass_list (all_lowering_passes);
-           execute_pass_list (pass_early_local_passes.pass.sub);
+           execute_pass_list (passes->all_lowering_passes);
+           passes->execute_early_local_passes ();
            bitmap_obstack_release (NULL);
            pop_cfun ();
 
@@ -515,12 +533,13 @@ cgraph_add_new_function (tree fndecl, bool lowered)
        node = cgraph_create_node (fndecl);
        if (lowered)
          node->lowered = true;
-       cgraph_analyze_function (node);
+       node->definition = true;
+       analyze_function (node);
        push_cfun (DECL_STRUCT_FUNCTION (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);
+         g->get_passes ()->execute_early_local_passes ();
        bitmap_obstack_release (NULL);
        pop_cfun ();
        expand_function (node);
@@ -571,69 +590,49 @@ output_asm_statements (void)
   asm_nodes = NULL;
 }
 
-/* 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)
+analyze_function (struct cgraph_node *node)
 {
-  tree decl = node->symbol.decl;
+  tree decl = node->decl;
   location_t saved_loc = input_location;
   input_location = DECL_SOURCE_LOCATION (decl);
 
-  if (node->alias && node->thunk.alias)
+  if (node->thunk.thunk_p)
     {
-      struct cgraph_node *tgt = cgraph_get_node (node->thunk.alias);
-      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_DECLARED_INLINE_P (node->symbol.decl)
-            = DECL_DECLARED_INLINE_P (node->thunk.alias);
-         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);
+      cgraph_create_edge (node, cgraph_get_node (node->thunk.alias),
+                         NULL, 0, CGRAPH_FREQ_BASE);
+      if (!expand_thunk (node, false))
+       {
+         node->thunk.alias = NULL;
+         node->analyzed = true;
+         return;
        }
-
-      if (node->symbol.address_taken)
-       cgraph_mark_address_taken_node (cgraph_alias_aliased_node (node));
+      node->thunk.alias = NULL;
     }
-  else if (node->thunk.thunk_p)
+  if (node->alias)
+    symtab_resolve_alias
+       (node, cgraph_get_node (node->alias_target));
+  else if (node->dispatcher_function)
     {
-      cgraph_create_edge (node, cgraph_get_node (node->thunk.alias),
-                         NULL, 0, CGRAPH_FREQ_BASE);
+      /* Generate the dispatcher body of multi-versioned functions.  */
+      struct cgraph_function_version_info *dispatcher_version_info
+       = get_cgraph_node_version (node);
+      if (dispatcher_version_info != NULL
+          && (dispatcher_version_info->dispatcher_resolver
+             == NULL_TREE))
+       {
+         tree resolver = NULL_TREE;
+         gcc_assert (targetm.generate_version_dispatcher_body);
+         resolver = targetm.generate_version_dispatcher_body (node);
+         gcc_assert (resolver != NULL_TREE);
+       }
     }
   else
     {
       push_cfun (DECL_STRUCT_FUNCTION (decl));
 
-      assign_assembler_name_if_neeeded (node->symbol.decl);
+      assign_assembler_name_if_neeeded (node->decl);
 
       /* Make sure to gimplify bodies only once.  During analyzing a
         function we lower it, which will require gimplified nested
@@ -647,12 +646,12 @@ cgraph_analyze_function (struct cgraph_node *node)
       if (!node->lowered)
        {
          if (node->nested)
-           lower_nested_functions (node->symbol.decl);
+           lower_nested_functions (node->decl);
          gcc_assert (!node->nested);
 
          gimple_register_cfg_hooks ();
          bitmap_obstack_initialize (NULL);
-         execute_pass_list (all_lowering_passes);
+         execute_pass_list (g->get_passes ()->all_lowering_passes);
          free_dominance_info (CDI_POST_DOMINATORS);
          free_dominance_info (CDI_DOMINATORS);
          compact_blocks ();
@@ -676,16 +675,15 @@ cgraph_analyze_function (struct cgraph_node *node)
 void
 cgraph_process_same_body_aliases (void)
 {
-  struct cgraph_node *node;
-  FOR_EACH_FUNCTION (node)
-    if (node->same_body_alias
-       && !VEC_length (ipa_ref_t, node->symbol.ref_list.references))
-      {
-        struct cgraph_node *tgt = cgraph_get_node (node->thunk.alias);
-        ipa_record_reference ((symtab_node)node, (symtab_node)tgt,
-                             IPA_REF_ALIAS, NULL);
-      }
-  same_body_aliases_done = true;
+  symtab_node *node;
+  FOR_EACH_SYMBOL (node)
+    if (node->cpp_implicit_alias && !node->analyzed)
+      symtab_resolve_alias
+        (node,
+        TREE_CODE (node->alias_target) == VAR_DECL
+        ? (symtab_node *)varpool_node_for_decl (node->alias_target)
+        : (symtab_node *)cgraph_get_create_node (node->alias_target));
+  cpp_implicit_aliases_done = true;
 }
 
 /* Process attributes common for vars and functions.  */
@@ -740,20 +738,20 @@ process_function_and_variable_attributes (struct cgraph_node *first,
   for (node = cgraph_first_function (); node != first;
        node = cgraph_next_function (node))
     {
-      tree decl = node->symbol.decl;
+      tree decl = node->decl;
       if (DECL_PRESERVE_P (decl))
        cgraph_mark_force_output_node (node);
       else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
        {
-         if (! TREE_PUBLIC (node->symbol.decl))
-           warning_at (DECL_SOURCE_LOCATION (node->symbol.decl), OPT_Wattributes,
+         if (! TREE_PUBLIC (node->decl))
+           warning_at (DECL_SOURCE_LOCATION (node->decl), OPT_Wattributes,
                        "%<externally_visible%>"
                        " attribute have effect only on public objects");
        }
       if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))
-         && (node->local.finalized && !node->alias))
+         && (node->definition && !node->alias))
        {
-         warning_at (DECL_SOURCE_LOCATION (node->symbol.decl), OPT_Wattributes,
+         warning_at (DECL_SOURCE_LOCATION (node->decl), OPT_Wattributes,
                      "%<weakref%> attribute ignored"
                      " because function is defined");
          DECL_WEAK (decl) = 0;
@@ -773,25 +771,24 @@ process_function_and_variable_attributes (struct cgraph_node *first,
   for (vnode = varpool_first_variable (); vnode != first_var;
        vnode = varpool_next_variable (vnode))
     {
-      tree decl = vnode->symbol.decl;
+      tree decl = vnode->decl;
       if (DECL_EXTERNAL (decl)
-         && DECL_INITIAL (decl)
-         && const_value_known_p (decl))
+         && DECL_INITIAL (decl))
        varpool_finalize_decl (decl);
       if (DECL_PRESERVE_P (decl))
-       vnode->symbol.force_output = true;
+       vnode->force_output = true;
       else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
        {
-         if (! TREE_PUBLIC (vnode->symbol.decl))
-           warning_at (DECL_SOURCE_LOCATION (vnode->symbol.decl), OPT_Wattributes,
+         if (! TREE_PUBLIC (vnode->decl))
+           warning_at (DECL_SOURCE_LOCATION (vnode->decl), OPT_Wattributes,
                        "%<externally_visible%>"
                        " attribute have effect only on public objects");
        }
       if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))
-         && vnode->finalized
+         && vnode->definition
          && DECL_INITIAL (decl))
        {
-         warning_at (DECL_SOURCE_LOCATION (vnode->symbol.decl), OPT_Wattributes,
+         warning_at (DECL_SOURCE_LOCATION (vnode->decl), OPT_Wattributes,
                      "%<weakref%> attribute ignored"
                      " because variable is initialized");
          DECL_WEAK (decl) = 0;
@@ -809,25 +806,25 @@ process_function_and_variable_attributes (struct cgraph_node *first,
 void
 varpool_finalize_decl (tree decl)
 {
-  struct varpool_node *node = varpool_node (decl);
+  struct varpool_node *node = varpool_node_for_decl (decl);
 
   gcc_assert (TREE_STATIC (decl) || DECL_EXTERNAL (decl));
 
-  if (node->finalized)
+  if (node->definition)
     return;
   notice_global_symbol (decl);
-  node->finalized = true;
+  node->definition = 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;
+      || (!flag_toplevel_reorder && !DECL_COMDAT (node->decl)
+         && !DECL_ARTIFICIAL (node->decl)))
+    node->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);
+      && (decide_is_symbol_needed (node)
+         || referred_to_p (node)))
+    enqueue_node (node);
   if (cgraph_state >= CGRAPH_STATE_IPA_SSA)
     varpool_analyze_node (node);
   /* Some frontends produce various interface variables after compilation
@@ -836,11 +833,88 @@ varpool_finalize_decl (tree decl)
     varpool_assemble_decl (node);
 }
 
+/* EDGE is an polymorphic call.  Mark all possible targets as reachable
+   and if there is only one target, perform trivial devirtualization. 
+   REACHABLE_CALL_TARGETS collects target lists we already walked to
+   avoid udplicate work.  */
+
+static void
+walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
+                              struct cgraph_edge *edge)
+{
+  unsigned int i;
+  void *cache_token;
+  bool final;
+  vec <cgraph_node *>targets
+    = possible_polymorphic_call_targets
+       (edge, &final, &cache_token);
+
+  if (!pointer_set_insert (reachable_call_targets,
+                          cache_token))
+    {
+      if (cgraph_dump_file)
+       dump_possible_polymorphic_call_targets 
+         (cgraph_dump_file, edge);
+
+      for (i = 0; i < targets.length (); i++)
+       {
+         /* Do not bother to mark virtual methods in anonymous namespace;
+            either we will find use of virtual table defining it, or it is
+            unused.  */
+         if (targets[i]->definition
+             && TREE_CODE
+                 (TREE_TYPE (targets[i]->decl))
+                  == METHOD_TYPE
+             && !type_in_anonymous_namespace_p
+                  (method_class_type
+                    (TREE_TYPE (targets[i]->decl))))
+         enqueue_node (targets[i]);
+       }
+    }
+
+  /* Very trivial devirtualization; when the type is
+     final or anonymous (so we know all its derivation)
+     and there is only one possible virtual call target,
+     make the edge direct.  */
+  if (final)
+    {
+      if (targets.length () <= 1)
+       {
+         cgraph_node *target;
+         if (targets.length () == 1)
+           target = targets[0];
+         else
+           target = cgraph_get_create_node
+                      (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
+
+         if (cgraph_dump_file)
+           {
+             fprintf (cgraph_dump_file,
+                      "Devirtualizing call: ");
+             print_gimple_stmt (cgraph_dump_file,
+                                edge->call_stmt, 0,
+                                TDF_SLIM);
+           }
+         cgraph_make_edge_direct (edge, target);
+         cgraph_redirect_edge_call_stmt_to_callee (edge);
+         if (cgraph_dump_file)
+           {
+             fprintf (cgraph_dump_file,
+                      "Devirtualized as: ");
+             print_gimple_stmt (cgraph_dump_file,
+                                edge->call_stmt, 0,
+                                TDF_SLIM);
+           }
+       }
+    }
+}
+
+
 /* 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)
+analyze_functions (void)
 {
   /* Keep track of already processed nodes when called multiple times for
      intermodule optimization.  */
@@ -848,14 +922,27 @@ cgraph_analyze_functions (void)
   struct cgraph_node *first_handled = first_analyzed;
   static struct varpool_node *first_analyzed_var;
   struct varpool_node *first_handled_var = first_analyzed_var;
+  struct pointer_set_t *reachable_call_targets = pointer_set_create ();
 
-  symtab_node node, next;
+  symtab_node *node;
+  symtab_node *next;
   int i;
   struct ipa_ref *ref;
   bool changed = true;
+  location_t saved_loc = input_location;
 
   bitmap_obstack_initialize (NULL);
   cgraph_state = CGRAPH_STATE_CONSTRUCTION;
+  input_location = UNKNOWN_LOCATION;
+
+  /* Ugly, but the fixup can not happen at a time same body alias is created;
+     C++ FE is confused about the COMDAT groups being right.  */
+  if (cpp_implicit_aliases_done)
+    FOR_EACH_SYMBOL (node)
+      if (node->cpp_implicit_alias)
+         fixup_same_cpp_alias_visibility (node, symtab_alias_target (node));
+  if (optimize && flag_devirtualize)
+    build_type_inheritance_graph ();
 
   /* Analysis adds static variables that in turn adds references to new functions.
      So we need to iterate the process until it stabilize.  */
@@ -867,16 +954,10 @@ cgraph_analyze_functions (void)
 
       /* 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)
+          node != first_analyzed
+          && node != first_analyzed_var; node = node->next)
        {
-         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)))
+         if (decide_is_symbol_needed (node))
            {
              enqueue_node (node);
              if (!changed && cgraph_dump_file)
@@ -884,9 +965,11 @@ cgraph_analyze_functions (void)
              changed = true;
              if (cgraph_dump_file)
                fprintf (cgraph_dump_file, " %s", symtab_node_asm_name (node));
+             if (!changed && cgraph_dump_file)
+               fprintf (cgraph_dump_file, "\n");
            }
-         if (node == (symtab_node)first_analyzed
-             || node == (symtab_node)first_analyzed_var)
+         if (node == first_analyzed
+             || node == first_analyzed_var)
            break;
        }
       cgraph_process_new_functions ();
@@ -898,26 +981,24 @@ cgraph_analyze_functions (void)
 
       /* Lower representation, build callgraph edges and references for all trivially
          needed symbols and all symbols referred by them.  */
-      while (first != (symtab_node)(void *)1)
+      while (first != (symtab_node *)(void *)1)
        {
          changed = true;
          node = first;
-         first = (symtab_node)first->symbol.aux;
-         if (symtab_function_p (node) && cgraph (node)->local.finalized)
+         first = (symtab_node *)first->aux;
+         cgraph_node *cnode = dyn_cast <cgraph_node> (node);
+         if (cnode && cnode->definition)
            {
              struct cgraph_edge *edge;
-             struct cgraph_node *cnode;
-             tree decl;
-
-             cnode = cgraph (node);
-             decl = cnode->symbol.decl;
+             tree decl = cnode->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  */
+             /* ??? 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)
+                 && !cnode->alias
+                 && !cnode->thunk.thunk_p
+                 && !cnode->dispatcher_function)
                {
                  cgraph_reset_node (cnode);
                  cnode->local.redefined_extern_inline = true;
@@ -925,43 +1006,58 @@ cgraph_analyze_functions (void)
                }
 
              if (!cnode->analyzed)
-               cgraph_analyze_function (cnode);
+               analyze_function (cnode);
 
              for (edge = cnode->callees; edge; edge = edge->next_callee)
-               if (edge->callee->local.finalized)
-                 enqueue_node ((symtab_node)edge->callee);
+               if (edge->callee->definition)
+                  enqueue_node (edge->callee);
+             if (optimize && flag_devirtualize)
+               {
+                 struct cgraph_edge *next;
+
+                 for (edge = cnode->indirect_calls; edge; edge = next)
+                   {
+                     next = edge->next_callee;
+                     if (edge->indirect_info->polymorphic)
+                       walk_polymorphic_call_targets (reachable_call_targets,
+                                                      edge);
+                   }
+               }
 
-             /* 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 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;
+                 struct cgraph_node *origin_node
+                 = cgraph_get_node (DECL_ABSTRACT_ORIGIN (decl));
+                 origin_node->used_as_abstract_origin = true;
                }
-
            }
-         else if (symtab_variable_p (node)
-                  && varpool (node)->finalized)
-           varpool_analyze_node (varpool (node));
+         else
+           {
+             varpool_node *vnode = dyn_cast <varpool_node> (node);
+             if (vnode && vnode->definition && !vnode->analyzed)
+               varpool_analyze_node (vnode);
+           }
 
-         if (node->symbol.same_comdat_group)
+         if (node->same_comdat_group)
            {
-             symtab_node next;
-             for (next = node->symbol.same_comdat_group;
+             symtab_node *next;
+             for (next = node->same_comdat_group;
                   next != node;
-                  next = next->symbol.same_comdat_group)
+                  next = next->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))
+         for (i = 0; ipa_ref_list_reference_iterate (&node->ref_list, i, ref); i++)
+           if (ref->referred->definition)
              enqueue_node (ref->referred);
           cgraph_process_new_functions ();
        }
     }
+  if (optimize && flag_devirtualize)
+    update_type_inheritance_graph ();
 
   /* Collect entry points to the unit.  */
   if (cgraph_dump_file)
@@ -974,34 +1070,35 @@ cgraph_analyze_functions (void)
     fprintf (cgraph_dump_file, "\nRemoving unused symbols:");
 
   for (node = symtab_nodes;
-       node != (symtab_node)first_handled
-       && node != (symtab_node)first_handled_var; node = next)
+       node != first_handled
+       && node != first_handled_var; node = next)
     {
-      next = node->symbol.next;
-      if (!node->symbol.aux && !referred_to_p (node))
+      next = node->next;
+      if (!node->aux && !referred_to_p (node))
        {
          if (cgraph_dump_file)
            fprintf (cgraph_dump_file, " %s", symtab_node_name (node));
          symtab_remove_node (node);
          continue;
        }
-      if (symtab_function_p (node))
+      if (cgraph_node *cnode = dyn_cast <cgraph_node> (node))
        {
-         tree decl = node->symbol.decl;
-         struct cgraph_node *cnode = cgraph (node);
+         tree decl = node->decl;
 
-         if (cnode->local.finalized && !gimple_has_body_p (decl)
-             && (!cnode->alias || !cnode->thunk.alias)
+         if (cnode->definition && !gimple_has_body_p (decl)
+             && !cnode->alias
              && !cnode->thunk.thunk_p)
            cgraph_reset_node (cnode);
 
-         gcc_assert (!cnode->local.finalized || cnode->thunk.thunk_p
+         gcc_assert (!cnode->definition || cnode->thunk.thunk_p
                      || cnode->alias
                      || gimple_has_body_p (decl));
-         gcc_assert (cnode->analyzed == cnode->local.finalized);
+         gcc_assert (cnode->analyzed == cnode->definition);
        }
-      node->symbol.aux = NULL;
+      node->aux = NULL;
     }
+  for (;node; node = node->next)
+    node->aux = NULL;
   first_analyzed = cgraph_first_function ();
   first_analyzed_var = varpool_first_variable ();
   if (cgraph_dump_file)
@@ -1010,12 +1107,20 @@ cgraph_analyze_functions (void)
       dump_symtab (cgraph_dump_file);
     }
   bitmap_obstack_release (NULL);
+  pointer_set_destroy (reachable_call_targets);
   ggc_collect ();
+  /* Initialize assembler name hash, in particular we want to trigger C++
+     mangling and same body alias creation before we free DECL_ARGUMENTS
+     used by it.  */
+  if (!seen_error ())
+    symtab_initialize_asm_name_hash ();
+
+  input_location = saved_loc;
 }
 
 /* Translate the ugly representation of aliases as alias pairs into nice
    representation in callgraph.  We don't handle all cases yet,
-   unforutnately.  */
+   unfortunately.  */
 
 static void
 handle_alias_pairs (void)
@@ -1023,46 +1128,42 @@ handle_alias_pairs (void)
   alias_pair *p;
   unsigned i;
   
-  for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p);)
+  for (i = 0; alias_pairs && alias_pairs->iterate (i, &p);)
     {
-      symtab_node target_node = symtab_node_for_asm (p->target);
+      symtab_node *target_node = symtab_node_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)
+      /* 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);
+         symtab_node *node = symtab_get_node (p->decl);
+         if (node)
+           {
+             node->alias_target = p->target;
+             node->weakref = true;
+             node->alias = true;
+           }
+         alias_pairs->unordered_remove (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);
+         symtab_node *node = symtab_get_node (p->decl);
+         if (node)
+           node->alias = false;
+         alias_pairs->unordered_remove (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)
+      if (DECL_EXTERNAL (target_node->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))
+         && (! TREE_CODE (target_node->decl) == FUNCTION_DECL
+             || ! DECL_VIRTUAL_P (target_node->decl))
          && ! lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)))
        {
          error ("%q+D aliased to external symbol %qE",
@@ -1070,30 +1171,30 @@ handle_alias_pairs (void)
        }
 
       if (TREE_CODE (p->decl) == FUNCTION_DECL
-          && target_node && symtab_function_p (target_node))
+          && target_node && is_a <cgraph_node> (target_node))
        {
          struct cgraph_node *src_node = cgraph_get_node (p->decl);
-         if (src_node && src_node->local.finalized)
+         if (src_node && src_node->definition)
             cgraph_reset_node (src_node);
-         cgraph_create_function_alias (p->decl, target_node->symbol.decl);
-         VEC_unordered_remove (alias_pair, alias_pairs, i);
+         cgraph_create_function_alias (p->decl, target_node->decl);
+         alias_pairs->unordered_remove (i);
        }
       else if (TREE_CODE (p->decl) == VAR_DECL
-              && target_node && symtab_variable_p (target_node))
+              && target_node && is_a <varpool_node> (target_node))
        {
-         varpool_create_variable_alias (p->decl, target_node->symbol.decl);
-         VEC_unordered_remove (alias_pair, alias_pairs, i);
+         varpool_create_variable_alias (p->decl, target_node->decl);
+         alias_pairs->unordered_remove (i);
        }
       else
        {
          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);
+                  target_node->decl);
+         alias_pairs->unordered_remove (i);
        }
     }
-  VEC_free (alias_pair, gc, alias_pairs);
+  vec_free (alias_pairs);
 }
 
 
@@ -1112,9 +1213,9 @@ mark_functions_to_output (void)
 
   FOR_EACH_FUNCTION (node)
     {
-      tree decl = node->symbol.decl;
+      tree decl = node->decl;
 
-      gcc_assert (!node->process || node->symbol.same_comdat_group);
+      gcc_assert (!node->process || node->same_comdat_group);
       if (node->process)
        continue;
 
@@ -1129,17 +1230,17 @@ mark_functions_to_output (void)
          && !DECL_EXTERNAL (decl))
        {
          node->process = 1;
-         if (node->symbol.same_comdat_group)
+         if (node->same_comdat_group)
            {
              struct cgraph_node *next;
-             for (next = cgraph (node->symbol.same_comdat_group);
+             for (next = cgraph (node->same_comdat_group);
                   next != node;
-                  next = cgraph (next->symbol.same_comdat_group))
+                  next = cgraph (next->same_comdat_group))
                if (!next->thunk.thunk_p && !next->alias)
                  next->process = 1;
            }
        }
-      else if (node->symbol.same_comdat_group)
+      else if (node->same_comdat_group)
        {
 #ifdef ENABLE_CHECKING
          check_same_comdat_groups = true;
@@ -1154,7 +1255,7 @@ 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->symbol.in_other_partition
+             && !node->in_other_partition
              && !node->alias
              && !node->clones
              && !DECL_EXTERNAL (decl))
@@ -1165,7 +1266,7 @@ mark_functions_to_output (void)
 #endif
          gcc_assert (node->global.inlined_to
                      || !gimple_has_body_p (decl)
-                     || node->symbol.in_other_partition
+                     || node->in_other_partition
                      || node->clones
                      || DECL_ARTIFICIAL (decl)
                      || DECL_EXTERNAL (decl));
@@ -1176,16 +1277,16 @@ mark_functions_to_output (void)
 #ifdef ENABLE_CHECKING
   if (check_same_comdat_groups)
     FOR_EACH_FUNCTION (node)
-      if (node->symbol.same_comdat_group && !node->process)
+      if (node->same_comdat_group && !node->process)
        {
-         tree decl = node->symbol.decl;
+         tree decl = node->decl;
          if (!node->global.inlined_to
              && gimple_has_body_p (decl)
              /* 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->in_other_partition
              && !node->clones
              && !DECL_EXTERNAL (decl))
            {
@@ -1198,13 +1299,13 @@ mark_functions_to_output (void)
 }
 
 /* DECL is FUNCTION_DECL.  Initialize datastructures so DECL is a function
-   in lowered gimple form.
+   in lowered gimple form.  IN_SSA is true if the gimple is in SSA.
    
    Set current_function_decl and cfun to newly constructed empty function body.
    return basic block in the function body.  */
 
-static basic_block
-init_lowered_empty_function (tree decl)
+basic_block
+init_lowered_empty_function (tree decl, bool in_ssa)
 {
   basic_block bb;
 
@@ -1212,19 +1313,30 @@ init_lowered_empty_function (tree decl)
   allocate_struct_function (decl, false);
   gimple_register_cfg_hooks ();
   init_empty_tree_cfg ();
-  init_tree_ssa (cfun);
-  init_ssa_operands (cfun);
-  cfun->gimple_df->in_ssa_p = true;
+
+  if (in_ssa)
+    {
+      init_tree_ssa (cfun);
+      init_ssa_operands (cfun);
+      cfun->gimple_df->in_ssa_p = true;
+      cfun->curr_properties |= PROP_ssa;
+    }
+
   DECL_INITIAL (decl) = make_node (BLOCK);
 
   DECL_SAVED_TREE (decl) = error_mark_node;
-  cfun->curr_properties |=
-    (PROP_gimple_lcf | PROP_gimple_leh | PROP_cfg | PROP_ssa | PROP_gimple_any);
+  cfun->curr_properties |= (PROP_gimple_lcf | PROP_gimple_leh | PROP_gimple_any
+                           | PROP_cfg | PROP_loops);
+
+  set_loops_for_fn (cfun, ggc_alloc_cleared_loops ());
+  init_loops_structure (cfun, loops_for_fn (cfun), 1);
+  loops_for_fn (cfun)->state |= LOOPS_MAY_HAVE_MULTIPLE_LATCHES;
 
   /* Create BB for body of the function and connect it properly.  */
   bb = create_basic_block (NULL, (void *) 0, ENTRY_BLOCK_PTR);
-  make_edge (ENTRY_BLOCK_PTR, bb, 0);
+  make_edge (ENTRY_BLOCK_PTR, bb, EDGE_FALLTHRU);
   make_edge (bb, EXIT_BLOCK_PTR, 0);
+  add_bb_to_loop (bb, ENTRY_BLOCK_PTR->loop_father);
 
   return bb;
 }
@@ -1333,23 +1445,21 @@ thunk_adjust (gimple_stmt_iterator * bsi,
   return ret;
 }
 
-/* Produce assembler for thunk NODE.  */
+/* Expand thunk NODE to gimple if possible.
+   When OUTPUT_ASM_THUNK is true, also produce assembler for
+   thunks that are not lowered.  */
 
-static void
-assemble_thunk (struct cgraph_node *node)
+bool
+expand_thunk (struct cgraph_node *node, bool output_asm_thunks)
 {
   bool this_adjusting = node->thunk.this_adjusting;
   HOST_WIDE_INT fixed_offset = node->thunk.fixed_offset;
   HOST_WIDE_INT virtual_value = node->thunk.virtual_value;
   tree virtual_offset = NULL;
-  tree alias = node->thunk.alias;
-  tree thunk_fndecl = node->symbol.decl;
-  tree a = DECL_ARGUMENTS (thunk_fndecl);
-
-  current_function_decl = thunk_fndecl;
+  tree alias = node->callees->callee->decl;
+  tree thunk_fndecl = node->decl;
+  tree a;
 
-  /* Ensure thunks are emitted in their correct sections.  */
-  resolve_unique_section (thunk_fndecl, 0, flag_function_sections);
 
   if (this_adjusting
       && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
@@ -1358,10 +1468,23 @@ assemble_thunk (struct cgraph_node *node)
       const char *fnname;
       tree fn_block;
       tree restype = TREE_TYPE (TREE_TYPE (thunk_fndecl));
+
+      if (!output_asm_thunks)
+       return false;
+
+      if (in_lto_p)
+       cgraph_get_body (node);
+      a = DECL_ARGUMENTS (thunk_fndecl);
       
+      current_function_decl = thunk_fndecl;
+
+      /* Ensure thunks are emitted in their correct sections.  */
+      resolve_unique_section (thunk_fndecl, 0, flag_function_sections);
+
       DECL_RESULT (thunk_fndecl)
        = build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
                      RESULT_DECL, 0, restype);
+      DECL_CONTEXT (DECL_RESULT (thunk_fndecl)) = thunk_fndecl;
       fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl));
 
       /* The back end expects DECL_INITIAL to contain a BLOCK, so we
@@ -1371,16 +1494,16 @@ assemble_thunk (struct cgraph_node *node)
       DECL_INITIAL (thunk_fndecl) = fn_block;
       init_function_start (thunk_fndecl);
       cfun->is_thunk = 1;
+      insn_locations_init ();
+      set_curr_insn_location (DECL_SOURCE_LOCATION (thunk_fndecl));
+      prologue_location = curr_insn_location ();
       assemble_start_function (thunk_fndecl, fnname);
-      (*debug_hooks->source_line) (DECL_SOURCE_LINE (thunk_fndecl),
-                                  DECL_SOURCE_FILE (thunk_fndecl),
-                                  /* discriminator */ 0,
-                                  /* is_stmt */ 1);
 
       targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
                                       fixed_offset, virtual_value, alias);
 
       assemble_end_function (thunk_fndecl, fnname);
+      insn_locations_finalize ();
       init_insn_lengths ();
       free_after_compilation (cfun);
       set_cfun (NULL);
@@ -1398,11 +1521,20 @@ assemble_thunk (struct cgraph_node *node)
       int i;
       tree resdecl;
       tree restmp = NULL;
-      VEC(tree, heap) *vargs;
+      vec<tree> vargs;
 
       gimple call;
       gimple ret;
 
+      if (in_lto_p)
+       cgraph_get_body (node);
+      a = DECL_ARGUMENTS (thunk_fndecl);
+      
+      current_function_decl = thunk_fndecl;
+
+      /* Ensure thunks are emitted in their correct sections.  */
+      resolve_unique_section (thunk_fndecl, 0, flag_function_sections);
+
       DECL_IGNORED_P (thunk_fndecl) = 1;
       bitmap_obstack_initialize (NULL);
 
@@ -1417,18 +1549,21 @@ assemble_thunk (struct cgraph_node *node)
          DECL_ARTIFICIAL (resdecl) = 1;
          DECL_IGNORED_P (resdecl) = 1;
          DECL_RESULT (thunk_fndecl) = resdecl;
+          DECL_CONTEXT (DECL_RESULT (thunk_fndecl)) = thunk_fndecl;
        }
       else
        resdecl = DECL_RESULT (thunk_fndecl);
 
-      bb = then_bb = else_bb = return_bb = init_lowered_empty_function (thunk_fndecl);
+      bb = then_bb = else_bb = return_bb = init_lowered_empty_function (thunk_fndecl, true);
 
       bsi = gsi_start_bb (bb);
 
       /* Build call to the function being thunked.  */
       if (!VOID_TYPE_P (restype))
        {
-         if (!is_gimple_reg_type (restype))
+         if (DECL_BY_REFERENCE (resdecl))
+           restmp = gimple_fold_indirect_ref (resdecl);
+         else if (!is_gimple_reg_type (restype))
            {
              restmp = resdecl;
              add_local_decl (cfun, restmp);
@@ -1440,86 +1575,106 @@ assemble_thunk (struct cgraph_node *node)
 
       for (arg = a; arg; arg = DECL_CHAIN (arg))
         nargs++;
-      vargs = VEC_alloc (tree, heap, nargs);
+      vargs.create (nargs);
       if (this_adjusting)
-        VEC_quick_push (tree, vargs,
-                       thunk_adjust (&bsi,
-                                     a, 1, fixed_offset,
-                                     virtual_offset));
-      else
-        VEC_quick_push (tree, vargs, a);
-      for (i = 1, arg = DECL_CHAIN (a); i < nargs; i++, arg = DECL_CHAIN (arg))
-       VEC_quick_push (tree, vargs, arg);
+        vargs.quick_push (thunk_adjust (&bsi, a, 1, fixed_offset,
+                                       virtual_offset));
+      else if (nargs)
+        vargs.quick_push (a);
+
+      if (nargs)
+        for (i = 1, arg = DECL_CHAIN (a); i < nargs; i++, arg = DECL_CHAIN (arg))
+         vargs.quick_push (arg);
       call = gimple_build_call_vec (build_fold_addr_expr_loc (0, alias), vargs);
-      VEC_free (tree, heap, vargs);
+      node->callees->call_stmt = call;
+      vargs.release ();
       gimple_call_set_from_thunk (call, true);
       if (restmp)
-        gimple_call_set_lhs (call, restmp);
+       {
+          gimple_call_set_lhs (call, restmp);
+         gcc_assert (useless_type_conversion_p (TREE_TYPE (restmp),
+                                                TREE_TYPE (TREE_TYPE (alias))));
+       }
       gsi_insert_after (&bsi, call, GSI_NEW_STMT);
+      if (!(gimple_call_flags (call) & ECF_NORETURN))
+       {
+         if (restmp && !this_adjusting
+             && (fixed_offset || virtual_offset))
+           {
+             tree true_label = NULL_TREE;
 
-      if (restmp && !this_adjusting)
-        {
-         tree true_label = NULL_TREE;
+             if (TREE_CODE (TREE_TYPE (restmp)) == POINTER_TYPE)
+               {
+                 gimple stmt;
+                 /* If the return type is a pointer, we need to
+                    protect against NULL.  We know there will be an
+                    adjustment, because that's why we're emitting a
+                    thunk.  */
+                 then_bb = create_basic_block (NULL, (void *) 0, bb);
+                 return_bb = create_basic_block (NULL, (void *) 0, then_bb);
+                 else_bb = create_basic_block (NULL, (void *) 0, else_bb);
+                 add_bb_to_loop (then_bb, bb->loop_father);
+                 add_bb_to_loop (return_bb, bb->loop_father);
+                 add_bb_to_loop (else_bb, bb->loop_father);
+                 remove_edge (single_succ_edge (bb));
+                 true_label = gimple_block_label (then_bb);
+                 stmt = gimple_build_cond (NE_EXPR, restmp,
+                                           build_zero_cst (TREE_TYPE (restmp)),
+                                           NULL_TREE, NULL_TREE);
+                 gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
+                 make_edge (bb, then_bb, EDGE_TRUE_VALUE);
+                 make_edge (bb, else_bb, EDGE_FALSE_VALUE);
+                 make_edge (return_bb, EXIT_BLOCK_PTR, 0);
+                 make_edge (then_bb, return_bb, EDGE_FALLTHRU);
+                 make_edge (else_bb, return_bb, EDGE_FALLTHRU);
+                 bsi = gsi_last_bb (then_bb);
+               }
 
-         if (TREE_CODE (TREE_TYPE (restmp)) == POINTER_TYPE)
-           {
-             gimple stmt;
-             /* If the return type is a pointer, we need to
-                protect against NULL.  We know there will be an
-                adjustment, because that's why we're emitting a
-                thunk.  */
-             then_bb = create_basic_block (NULL, (void *) 0, bb);
-             return_bb = create_basic_block (NULL, (void *) 0, then_bb);
-             else_bb = create_basic_block (NULL, (void *) 0, else_bb);
-             remove_edge (single_succ_edge (bb));
-             true_label = gimple_block_label (then_bb);
-             stmt = gimple_build_cond (NE_EXPR, restmp,
-                                       build_zero_cst (TREE_TYPE (restmp)),
-                                       NULL_TREE, NULL_TREE);
-             gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
-             make_edge (bb, then_bb, EDGE_TRUE_VALUE);
-             make_edge (bb, else_bb, EDGE_FALSE_VALUE);
-             make_edge (return_bb, EXIT_BLOCK_PTR, 0);
-             make_edge (then_bb, return_bb, EDGE_FALLTHRU);
-             make_edge (else_bb, return_bb, EDGE_FALLTHRU);
-             bsi = gsi_last_bb (then_bb);
+             restmp = thunk_adjust (&bsi, restmp, /*this_adjusting=*/0,
+                                    fixed_offset, virtual_offset);
+             if (true_label)
+               {
+                 gimple stmt;
+                 bsi = gsi_last_bb (else_bb);
+                 stmt = gimple_build_assign (restmp,
+                                             build_zero_cst (TREE_TYPE (restmp)));
+                 gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
+                 bsi = gsi_last_bb (return_bb);
+               }
            }
+         else
+           gimple_call_set_tail (call, true);
 
-         restmp = thunk_adjust (&bsi, restmp, /*this_adjusting=*/0,
-                                fixed_offset, virtual_offset);
-         if (true_label)
-           {
-             gimple stmt;
-             bsi = gsi_last_bb (else_bb);
-             stmt = gimple_build_assign (restmp,
-                                         build_zero_cst (TREE_TYPE (restmp)));
-             gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
-             bsi = gsi_last_bb (return_bb);
-           }
+         /* Build return value.  */
+         ret = gimple_build_return (restmp);
+         gsi_insert_after (&bsi, ret, GSI_NEW_STMT);
        }
       else
-        gimple_call_set_tail (call, true);
-
-      /* Build return value.  */
-      ret = gimple_build_return (restmp);
-      gsi_insert_after (&bsi, ret, GSI_NEW_STMT);
+       {
+         gimple_call_set_tail (call, true);
+         remove_edge (single_succ_edge (bb));
+       }
 
+      cfun->gimple_df->in_ssa_p = true;
+      /* FIXME: C++ FE should stop setting TREE_ASM_WRITTEN on thunks.  */
+      TREE_ASM_WRITTEN (thunk_fndecl) = false;
       delete_unreachable_blocks ();
       update_ssa (TODO_update_ssa);
+#ifdef ENABLE_CHECKING
+      verify_flow_info ();
+#endif
 
       /* Since we want to emit the thunk, we explicitly mark its name as
         referenced.  */
       node->thunk.thunk_p = false;
-      cgraph_node_remove_callees (node);
-      cgraph_add_new_function (thunk_fndecl, true);
+      node->lowered = true;
       bitmap_obstack_release (NULL);
     }
   current_function_decl = NULL;
   set_cfun (NULL);
+  return true;
 }
 
-
-
 /* Assemble thunks and aliases associated to NODE.  */
 
 static void
@@ -1536,24 +1691,24 @@ assemble_thunks_and_aliases (struct cgraph_node *node)
 
        e = e->next_caller;
        assemble_thunks_and_aliases (thunk);
-        assemble_thunk (thunk);
+        expand_thunk (thunk, true);
       }
     else
       e = e->next_caller;
-  for (i = 0; ipa_ref_list_referring_iterate (&node->symbol.ref_list,
+  for (i = 0; ipa_ref_list_referring_iterate (&node->ref_list,
                                             i, ref); i++)
     if (ref->use == IPA_REF_ALIAS)
       {
        struct cgraph_node *alias = ipa_ref_referring_node (ref);
-        bool saved_written = TREE_ASM_WRITTEN (alias->thunk.alias);
+        bool saved_written = TREE_ASM_WRITTEN (node->decl);
 
        /* Force assemble_alias to really output the alias this time instead
           of buffering it in same alias pairs.  */
-       TREE_ASM_WRITTEN (alias->thunk.alias) = 1;
-       do_assemble_alias (alias->symbol.decl,
-                          DECL_ASSEMBLER_NAME (alias->thunk.alias));
+       TREE_ASM_WRITTEN (node->decl) = 1;
+       do_assemble_alias (alias->decl,
+                          DECL_ASSEMBLER_NAME (node->decl));
        assemble_thunks_and_aliases (alias);
-       TREE_ASM_WRITTEN (alias->thunk.alias) = saved_written;
+       TREE_ASM_WRITTEN (node->decl) = saved_written;
       }
 }
 
@@ -1562,7 +1717,7 @@ assemble_thunks_and_aliases (struct cgraph_node *node)
 static void
 expand_function (struct cgraph_node *node)
 {
-  tree decl = node->symbol.decl;
+  tree decl = node->decl;
   location_t saved_loc;
 
   /* We ought to not compile any inline clones.  */
@@ -1571,6 +1726,7 @@ expand_function (struct cgraph_node *node)
   announce_function (decl);
   node->process = 0;
   gcc_assert (node->lowered);
+  cgraph_get_body (node);
 
   /* Generate RTL for the body of DECL.  */
 
@@ -1598,7 +1754,7 @@ expand_function (struct cgraph_node *node)
   /* Signal the start of passes.  */
   invoke_plugin_callbacks (PLUGIN_ALL_PASSES_START, NULL);
 
-  execute_pass_list (all_passes);
+  execute_pass_list (g->get_passes ()->all_passes);
 
   /* Signal the end of passes.  */
   invoke_plugin_callbacks (PLUGIN_ALL_PASSES_END, NULL);
@@ -1666,6 +1822,7 @@ 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);
+  ipa_remove_all_references (&node->ref_list);
 }
 
 
@@ -1755,7 +1912,7 @@ output_in_order (void)
     {
       if (pf->process && !pf->thunk.thunk_p && !pf->alias)
        {
-         i = pf->symbol.order;
+         i = pf->order;
          gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
          nodes[i].kind = ORDER_FUNCTION;
          nodes[i].u.f = pf;
@@ -1763,9 +1920,9 @@ output_in_order (void)
     }
 
   FOR_EACH_DEFINED_VARIABLE (pv)
-    if (!DECL_EXTERNAL (pv->symbol.decl))
+    if (!DECL_EXTERNAL (pv->decl))
       {
-       i = pv->symbol.order;
+       i = pv->order;
        gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
        nodes[i].kind = ORDER_VAR;
        nodes[i].u.v = pv;
@@ -1817,6 +1974,8 @@ output_in_order (void)
 static void
 ipa_passes (void)
 {
+  gcc::pass_manager *passes = g->get_passes ();
+
   set_cfun (NULL);
   current_function_decl = NULL;
   gimple_register_cfg_hooks ();
@@ -1826,7 +1985,7 @@ ipa_passes (void)
 
   if (!in_lto_p)
     {
-      execute_ipa_pass_list (all_small_ipa_passes);
+      execute_ipa_pass_list (passes->all_small_ipa_passes);
       if (seen_error ())
        return;
     }
@@ -1853,14 +2012,15 @@ ipa_passes (void)
       cgraph_process_new_functions ();
 
       execute_ipa_summary_passes
-       ((struct ipa_opt_pass_d *) all_regular_ipa_passes);
+       ((struct ipa_opt_pass_d *) passes->all_regular_ipa_passes);
     }
 
   /* Some targets need to handle LTO assembler output specially.  */
   if (flag_generate_lto)
     targetm.asm_out.lto_start ();
 
-  execute_ipa_summary_passes ((struct ipa_opt_pass_d *) all_lto_gen_passes);
+  execute_ipa_summary_passes ((struct ipa_opt_pass_d *)
+                             passes->all_lto_gen_passes);
 
   if (!in_lto_p)
     ipa_write_summaries ();
@@ -1869,7 +2029,7 @@ ipa_passes (void)
     targetm.asm_out.lto_end ();
 
   if (!flag_ltrans && (in_lto_p || !flag_lto || flag_fat_lto_objects))
-    execute_ipa_pass_list (all_regular_ipa_passes);
+    execute_ipa_pass_list (passes->all_regular_ipa_passes);
   invoke_plugin_callbacks (PLUGIN_ALL_IPA_PASSES_END, NULL);
 
   bitmap_obstack_release (NULL);
@@ -1893,22 +2053,32 @@ get_alias_symbol (tree decl)
 static void
 output_weakrefs (void)
 {
-  struct cgraph_node *node;
-  struct varpool_node *vnode;
-  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));
+  symtab_node *node;
+  FOR_EACH_SYMBOL (node)
+    if (node->alias
+        && !TREE_ASM_WRITTEN (node->decl)
+       && node->weakref)
+      {
+       tree target;
+
+       /* Weakrefs are special by not requiring target definition in current
+          compilation unit.  It is thus bit hard to work out what we want to
+          alias.
+          When alias target is defined, we need to fetch it from symtab reference,
+          otherwise it is pointed to by alias_target.  */
+       if (node->alias_target)
+         target = (DECL_P (node->alias_target)
+                   ? DECL_ASSEMBLER_NAME (node->alias_target)
+                   : node->alias_target);
+       else if (node->analyzed)
+         target = DECL_ASSEMBLER_NAME (symtab_alias_target (node)->decl);
+       else
+         {
+           gcc_unreachable ();
+           target = get_alias_symbol (node->decl);
+         }
+        do_assemble_alias (node->decl, target);
+      }
 }
 
 /* Initialize callgraph dump file.  */
@@ -1985,7 +2155,7 @@ compile (void)
 
   cgraph_materialize_all_clones ();
   bitmap_obstack_initialize (NULL);
-  execute_ipa_pass_list (all_late_ipa_passes);
+  execute_ipa_pass_list (g->get_passes ()->all_late_ipa_passes);
   symtab_remove_unreachable_nodes (true, dump_file);
 #ifdef ENABLE_CHECKING
   verify_symtab ();
@@ -1993,6 +2163,32 @@ compile (void)
   bitmap_obstack_release (NULL);
   mark_functions_to_output ();
 
+  /* When weakref support is missing, we autmatically translate all
+     references to NODE to references to its ultimate alias target.
+     The renaming mechanizm uses flag IDENTIFIER_TRANSPARENT_ALIAS and
+     TREE_CHAIN.
+
+     Set up this mapping before we output any assembler but once we are sure
+     that all symbol renaming is done.
+
+     FIXME: All this uglyness can go away if we just do renaming at gimple
+     level by physically rewritting the IL.  At the moment we can only redirect
+     calls, so we need infrastructure for renaming references as well.  */
+#ifndef ASM_OUTPUT_WEAKREF
+  symtab_node *node;
+
+  FOR_EACH_SYMBOL (node)
+    if (node->alias
+       && lookup_attribute ("weakref", DECL_ATTRIBUTES (node->decl)))
+      {
+       IDENTIFIER_TRANSPARENT_ALIAS
+          (DECL_ASSEMBLER_NAME (node->decl)) = 1;
+       TREE_CHAIN (DECL_ASSEMBLER_NAME (node->decl))
+          = (node->alias_target ? node->alias_target
+             : DECL_ASSEMBLER_NAME (symtab_alias_target (node)->decl));
+      }
+#endif
+
   cgraph_state = CGRAPH_STATE_EXPANSION;
   if (!flag_toplevel_reorder)
     output_in_order ();
@@ -2024,7 +2220,7 @@ compile (void)
 
       FOR_EACH_DEFINED_FUNCTION (node)
        if (node->global.inlined_to
-           || gimple_has_body_p (node->symbol.decl))
+           || gimple_has_body_p (node->decl))
          {
            error_found = true;
            dump_cgraph_node (stderr, node);
@@ -2068,13 +2264,13 @@ finalize_compilation_unit (void)
 
   /* Gimplify and lower all functions, compute reachability and
      remove unreachable nodes.  */
-  cgraph_analyze_functions ();
+  analyze_functions ();
 
   /* Mark alias targets necessary and emit diagnostics.  */
   handle_alias_pairs ();
 
   /* Gimplify and lower thunks.  */
-  cgraph_analyze_functions ();
+  analyze_functions ();
 
   /* Finally drive the pass manager.  */
   compile ();