OSDN Git Service

Add a blank line in ChangeLog
[pf3gnuchains/gcc-fork.git] / gcc / cgraphunit.c
index 5a69afa..ae30605 100644 (file)
@@ -1,6 +1,6 @@
 /* Callgraph based interprocedural optimizations.
 /* Callgraph based interprocedural optimizations.
-   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
-   Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+   2011, 2012 Free Software Foundation, Inc.
    Contributed by Jan Hubicka
 
 This file is part of GCC.
    Contributed by Jan Hubicka
 
 This file is part of GCC.
@@ -45,9 +45,9 @@ along with GCC; see the file COPYING3.  If not see
       This function is called once (source level) compilation unit is finalized
       and it will no longer change.
 
       This function is called once (source level) compilation unit is finalized
       and it will no longer change.
 
-      In the the call-graph construction and local function
-      analysis takes place here.  Bodies of unreachable functions are released
-      to conserve memory usage.
+      In the call-graph construction and local function analysis takes
+      place here.  Bodies of unreachable functions are released to
+      conserve memory usage.
 
       The function can be called multiple times when multiple source level
       compilation units are combined (such as in C frontend)
 
       The function can be called multiple times when multiple source level
       compilation units are combined (such as in C frontend)
@@ -138,183 +138,20 @@ along with GCC; see the file COPYING3.  If not see
 #include "output.h"
 #include "coverage.h"
 #include "plugin.h"
 #include "output.h"
 #include "coverage.h"
 #include "plugin.h"
+#include "ipa-inline.h"
+#include "ipa-utils.h"
+#include "lto-streamer.h"
 
 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);
 
 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);
-static void cgraph_analyze_function (struct cgraph_node *);
 
 FILE *cgraph_dump_file;
 
 
 FILE *cgraph_dump_file;
 
-/* A vector of FUNCTION_DECLs declared as static constructors.  */
-static GTY (()) VEC(tree, gc) *static_ctors;
-/* A vector of FUNCTION_DECLs declared as static destructors.  */
-static GTY (()) VEC(tree, gc) *static_dtors;
-
 /* Used for vtable lookup in thunk adjusting.  */
 static GTY (()) tree vtable_entry_type;
 
 /* Used for vtable lookup in thunk adjusting.  */
 static GTY (()) tree vtable_entry_type;
 
-/* When target does not have ctors and dtors, we call all constructor
-   and destructor by special initialization/destruction function
-   recognized by collect2.
-
-   When we are going to build this function, collect all constructors and
-   destructors and turn them into normal functions.  */
-
-static void
-record_cdtor_fn (tree fndecl)
-{
-  struct cgraph_node *node;
-  if (targetm.have_ctors_dtors
-      || (!DECL_STATIC_CONSTRUCTOR (fndecl)
-         && !DECL_STATIC_DESTRUCTOR (fndecl)))
-    return;
-
-  if (DECL_STATIC_CONSTRUCTOR (fndecl))
-    {
-      VEC_safe_push (tree, gc, static_ctors, fndecl);
-      DECL_STATIC_CONSTRUCTOR (fndecl) = 0;
-    }
-  if (DECL_STATIC_DESTRUCTOR (fndecl))
-    {
-      VEC_safe_push (tree, gc, static_dtors, fndecl);
-      DECL_STATIC_DESTRUCTOR (fndecl) = 0;
-    }
-  node = cgraph_node (fndecl);
-  node->local.disregard_inline_limits = 1;
-  cgraph_mark_reachable_node (node);
-}
-
-/* Define global constructors/destructor functions for the CDTORS, of
-   which they are LEN.  The CDTORS are sorted by initialization
-   priority.  If CTOR_P is true, these are constructors; otherwise,
-   they are destructors.  */
-
-static void
-build_cdtor (bool ctor_p, tree *cdtors, size_t len)
-{
-  size_t i;
-
-  i = 0;
-  while (i < len)
-    {
-      tree body;
-      tree fn;
-      priority_type priority;
-
-      priority = 0;
-      body = NULL_TREE;
-      /* Find the next batch of constructors/destructors with the same
-        initialization priority.  */
-      do
-       {
-         priority_type p;
-         fn = cdtors[i];
-         p = ctor_p ? DECL_INIT_PRIORITY (fn) : DECL_FINI_PRIORITY (fn);
-         if (!body)
-           priority = p;
-         else if (p != priority)
-           break;
-         append_to_statement_list (build_function_call_expr (UNKNOWN_LOCATION,
-                                                             fn, 0),
-                                   &body);
-         ++i;
-       }
-      while (i < len);
-      gcc_assert (body != NULL_TREE);
-      /* Generate a function to call all the function of like
-        priority.  */
-      cgraph_build_static_cdtor (ctor_p ? 'I' : 'D', body, priority);
-    }
-}
-
-/* Comparison function for qsort.  P1 and P2 are actually of type
-   "tree *" and point to static constructors.  DECL_INIT_PRIORITY is
-   used to determine the sort order.  */
-
-static int
-compare_ctor (const void *p1, const void *p2)
-{
-  tree f1;
-  tree f2;
-  int priority1;
-  int priority2;
-
-  f1 = *(const tree *)p1;
-  f2 = *(const tree *)p2;
-  priority1 = DECL_INIT_PRIORITY (f1);
-  priority2 = DECL_INIT_PRIORITY (f2);
-
-  if (priority1 < priority2)
-    return -1;
-  else if (priority1 > priority2)
-    return 1;
-  else
-    /* Ensure a stable sort.  */
-    return (const tree *)p1 - (const tree *)p2;
-}
-
-/* Comparison function for qsort.  P1 and P2 are actually of type
-   "tree *" and point to static destructors.  DECL_FINI_PRIORITY is
-   used to determine the sort order.  */
-
-static int
-compare_dtor (const void *p1, const void *p2)
-{
-  tree f1;
-  tree f2;
-  int priority1;
-  int priority2;
-
-  f1 = *(const tree *)p1;
-  f2 = *(const tree *)p2;
-  priority1 = DECL_FINI_PRIORITY (f1);
-  priority2 = DECL_FINI_PRIORITY (f2);
-
-  if (priority1 < priority2)
-    return -1;
-  else if (priority1 > priority2)
-    return 1;
-  else
-    /* Ensure a stable sort.  */
-    return (const tree *)p1 - (const tree *)p2;
-}
-
-/* Generate functions to call static constructors and destructors
-   for targets that do not support .ctors/.dtors sections.  These
-   functions have magic names which are detected by collect2.  */
-
-static void
-cgraph_build_cdtor_fns (void)
-{
-  if (!VEC_empty (tree, static_ctors))
-    {
-      gcc_assert (!targetm.have_ctors_dtors);
-      qsort (VEC_address (tree, static_ctors),
-            VEC_length (tree, static_ctors),
-            sizeof (tree),
-            compare_ctor);
-      build_cdtor (/*ctor_p=*/true,
-                  VEC_address (tree, static_ctors),
-                  VEC_length (tree, static_ctors));
-      VEC_truncate (tree, static_ctors, 0);
-    }
-
-  if (!VEC_empty (tree, static_dtors))
-    {
-      gcc_assert (!targetm.have_ctors_dtors);
-      qsort (VEC_address (tree, static_dtors),
-            VEC_length (tree, static_dtors),
-            sizeof (tree),
-            compare_dtor);
-      build_cdtor (/*ctor_p=*/false,
-                  VEC_address (tree, static_dtors),
-                  VEC_length (tree, static_dtors));
-      VEC_truncate (tree, static_dtors, 0);
-    }
-}
-
 /* 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 needed.  That is, visible to something
    either outside this translation unit, something magic in the system
    configury.  */
@@ -330,6 +167,7 @@ cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl)
      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)
      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;
 
       && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
     return true;
 
@@ -338,7 +176,7 @@ cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl)
   if (flag_keep_inline_functions
       && DECL_DECLARED_INLINE_P (decl)
       && !DECL_EXTERNAL (decl)
   if (flag_keep_inline_functions
       && DECL_DECLARED_INLINE_P (decl)
       && !DECL_EXTERNAL (decl)
-      && !lookup_attribute ("always_inline", DECL_ATTRIBUTES (decl)))
+      && !DECL_DISREGARD_INLINE_LIMITS (decl))
      return true;
 
   /* If we decided it was needed before, but at the time we didn't have
      return true;
 
   /* If we decided it was needed before, but at the time we didn't have
@@ -357,21 +195,16 @@ cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl)
      to change the behavior here.  */
   if (((TREE_PUBLIC (decl)
        || (!optimize
      to change the behavior here.  */
   if (((TREE_PUBLIC (decl)
        || (!optimize
-           && !node->local.disregard_inline_limits
+           && !node->same_body_alias
+           && !DECL_DISREGARD_INLINE_LIMITS (decl)
            && !DECL_DECLARED_INLINE_P (decl)
            && !(DECL_CONTEXT (decl)
                 && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)))
        && !flag_whole_program
            && !DECL_DECLARED_INLINE_P (decl)
            && !(DECL_CONTEXT (decl)
                 && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)))
        && !flag_whole_program
-       && !flag_lto
-       && !flag_whopr)
+       && !flag_lto)
       && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
     return true;
 
       && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
     return true;
 
-  /* Constructors and destructors are reachable from the runtime by
-     some mechanism.  */
-  if (DECL_STATIC_CONSTRUCTOR (decl) || DECL_STATIC_DESTRUCTOR (decl))
-    return true;
-
   return false;
 }
 
   return false;
 }
 
@@ -404,6 +237,7 @@ cgraph_process_new_functions (void)
          cgraph_finalize_function (fndecl, false);
          cgraph_mark_reachable_node (node);
          output = true;
          cgraph_finalize_function (fndecl, false);
          cgraph_mark_reachable_node (node);
          output = true;
+          cgraph_call_function_insertion_hooks (node);
          break;
 
        case CGRAPH_STATE_IPA:
          break;
 
        case CGRAPH_STATE_IPA:
@@ -417,23 +251,26 @@ cgraph_process_new_functions (void)
            cgraph_analyze_function (node);
          push_cfun (DECL_STRUCT_FUNCTION (fndecl));
          current_function_decl = fndecl;
            cgraph_analyze_function (node);
          push_cfun (DECL_STRUCT_FUNCTION (fndecl));
          current_function_decl = fndecl;
-         compute_inline_parameters (node);
          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);
          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
+           compute_inline_parameters (node, true);
          free_dominance_info (CDI_POST_DOMINATORS);
          free_dominance_info (CDI_DOMINATORS);
          pop_cfun ();
          current_function_decl = NULL;
          free_dominance_info (CDI_POST_DOMINATORS);
          free_dominance_info (CDI_DOMINATORS);
          pop_cfun ();
          current_function_decl = NULL;
+          cgraph_call_function_insertion_hooks (node);
          break;
 
        case CGRAPH_STATE_EXPANSION:
          /* Functions created during expansion shall be compiled
             directly.  */
          node->process = 0;
          break;
 
        case CGRAPH_STATE_EXPANSION:
          /* Functions created during expansion shall be compiled
             directly.  */
          node->process = 0;
+          cgraph_call_function_insertion_hooks (node);
          cgraph_expand_function (node);
          break;
 
          cgraph_expand_function (node);
          break;
 
@@ -441,7 +278,6 @@ cgraph_process_new_functions (void)
          gcc_unreachable ();
          break;
        }
          gcc_unreachable ();
          break;
        }
-      cgraph_call_function_insertion_hooks (node);
       varpool_analyze_pending_decls ();
     }
   return output;
       varpool_analyze_pending_decls ();
     }
   return output;
@@ -472,24 +308,9 @@ cgraph_reset_node (struct cgraph_node *node)
   memset (&node->global, 0, sizeof (node->global));
   memset (&node->rtl, 0, sizeof (node->rtl));
   node->analyzed = false;
   memset (&node->global, 0, sizeof (node->global));
   memset (&node->rtl, 0, sizeof (node->rtl));
   node->analyzed = false;
-  node->local.redefined_extern_inline = true;
   node->local.finalized = false;
 
   cgraph_node_remove_callees (node);
   node->local.finalized = false;
 
   cgraph_node_remove_callees (node);
-
-  /* We may need to re-queue the node for assembling in case
-     we already proceeded it and ignored as not needed or got
-     a re-declaration in IMA mode.  */
-  if (node->reachable)
-    {
-      struct cgraph_node *n;
-
-      for (n = cgraph_nodes_queue; n; n = n->next_needed)
-       if (n == node)
-         break;
-      if (!n)
-       node->reachable = 0;
-    }
 }
 
 static void
 }
 
 static void
@@ -514,17 +335,17 @@ cgraph_lower_function (struct cgraph_node *node)
 void
 cgraph_finalize_function (tree decl, bool nested)
 {
 void
 cgraph_finalize_function (tree decl, bool nested)
 {
-  struct cgraph_node *node = cgraph_node (decl);
+  struct cgraph_node *node = cgraph_get_create_node (decl);
 
   if (node->local.finalized)
 
   if (node->local.finalized)
-    cgraph_reset_node (node);
+    {
+      cgraph_reset_node (node);
+      node->local.redefined_extern_inline = true;
+    }
 
 
-  node->pid = cgraph_max_pid ++;
   notice_global_symbol (decl);
   node->local.finalized = true;
   node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL;
   notice_global_symbol (decl);
   node->local.finalized = true;
   node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL;
-  node->finalized_by_frontend = true;
-  record_cdtor_fn (node->decl);
 
   if (cgraph_decide_is_function_needed (node, decl))
     cgraph_mark_needed_node (node);
 
   if (cgraph_decide_is_function_needed (node, decl))
     cgraph_mark_needed_node (node);
@@ -532,7 +353,16 @@ cgraph_finalize_function (tree decl, bool nested)
   /* Since we reclaim unreachable nodes at the end of every language
      level unit, we need to be conservative about possible entry points
      there.  */
   /* 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)))
+  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);
 
   /* If we've not yet emitted decl, tell the debug info about it.  */
     cgraph_mark_reachable_node (node);
 
   /* If we've not yet emitted decl, tell the debug info about it.  */
@@ -554,21 +384,108 @@ cgraph_finalize_function (tree decl, bool nested)
 void
 cgraph_mark_if_needed (tree decl)
 {
 void
 cgraph_mark_if_needed (tree decl)
 {
-  struct cgraph_node *node = cgraph_node (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);
 }
 
   if (node->local.finalized && cgraph_decide_is_function_needed (node, decl))
     cgraph_mark_needed_node (node);
 }
 
-#ifdef ENABLE_CHECKING
 /* Return TRUE if NODE2 is equivalent to NODE or its clone.  */
 static bool
 clone_of_p (struct cgraph_node *node, struct cgraph_node *node2)
 {
 /* 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;
 }
   while (node != node2 && node2)
     node2 = node2->clone_of;
   return node2 != NULL;
 }
-#endif
+
+/* Verify edge E count and frequency.  */
+
+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);
+}
+
+/* Verify that call graph edge E corresponds to DECL from the associated
+   statement.  Return true if the verification should fail.  */
+
+static bool
+verify_edge_corresponds_to_fndecl (struct cgraph_edge *e, tree decl)
+{
+  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
+       && (!node->same_body_alias
+          || e->callee->former_clone_of != node->thunk.alias))
+      /* IPA-CP sometimes redirect edge to clone and then back to the former
+        function.  This ping-pong has to go, eventually.  */
+      && (node != cgraph_function_or_thunk_node (e->callee, NULL))
+      && !clone_of_p (node, e->callee)
+      /* If decl is a same body alias of some other decl, allow e->callee to be
+        a clone of a clone of that other decl too.  */
+      && (!node->same_body_alias
+         || !clone_of_p (cgraph_get_node (node->thunk.alias), e->callee)))
+    return true;
+  else
+    return false;
+}
 
 /* Verify cgraph nodes of given cgraph node.  */
 DEBUG_FUNCTION void
 
 /* Verify cgraph nodes of given cgraph node.  */
 DEBUG_FUNCTION void
@@ -576,7 +493,6 @@ verify_cgraph_node (struct cgraph_node *node)
 {
   struct cgraph_edge *e;
   struct function *this_cfun = DECL_STRUCT_FUNCTION (node->decl);
 {
   struct cgraph_edge *e;
   struct function *this_cfun = DECL_STRUCT_FUNCTION (node->decl);
-  struct function *saved_cfun = cfun;
   basic_block this_block;
   gimple_stmt_iterator gsi;
   bool error_found = false;
   basic_block this_block;
   gimple_stmt_iterator gsi;
   bool error_found = false;
@@ -585,8 +501,6 @@ verify_cgraph_node (struct cgraph_node *node)
     return;
 
   timevar_push (TV_CGRAPH_VERIFY);
     return;
 
   timevar_push (TV_CGRAPH_VERIFY);
-  /* debug_generic_stmt needs correct cfun */
-  set_cfun (this_cfun);
   for (e = node->callees; e; e = e->next_callee)
     if (e->aux)
       {
   for (e = node->callees; e; e = e->next_callee)
     if (e->aux)
       {
@@ -597,22 +511,22 @@ verify_cgraph_node (struct cgraph_node *node)
       }
   if (node->count < 0)
     {
       }
   if (node->count < 0)
     {
-      error ("Execution count is negative");
+      error ("execution count is negative");
       error_found = true;
     }
   if (node->global.inlined_to && node->local.externally_visible)
     {
       error_found = true;
     }
   if (node->global.inlined_to && node->local.externally_visible)
     {
-      error ("Externally visible inline clone");
+      error ("externally visible inline clone");
       error_found = true;
     }
   if (node->global.inlined_to && node->address_taken)
     {
       error_found = true;
     }
   if (node->global.inlined_to && node->address_taken)
     {
-      error ("Inline clone with address taken");
+      error ("inline clone with address taken");
       error_found = true;
     }
   if (node->global.inlined_to && node->needed)
     {
       error_found = true;
     }
   if (node->global.inlined_to && node->needed)
     {
-      error ("Inline clone is needed");
+      error ("inline clone is needed");
       error_found = true;
     }
   for (e = node->indirect_calls; e; e = e->next_callee)
       error_found = true;
     }
   for (e = node->indirect_calls; e; e = e->next_callee)
@@ -629,39 +543,14 @@ verify_cgraph_node (struct cgraph_node *node)
          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)));
          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)));
-         debug_gimple_stmt (e->call_stmt);
+         cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
          error_found = true;
        }
     }
   for (e = node->callers; e; e = e->next_caller)
     {
          error_found = true;
        }
     }
   for (e = node->callers; e; e = e->next_caller)
     {
-      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
-          && (e->frequency
-             != compute_call_stmt_bb_frequency (e->caller->decl,
-                                                gimple_bb (e->call_stmt))))
-       {
-         error ("caller edge frequency %i does not match BB freqency %i",
-                e->frequency,
-                compute_call_stmt_bb_frequency (e->caller->decl,
-                                                gimple_bb (e->call_stmt)));
-         error_found = true;
-       }
+      if (verify_edge_count_and_frequency (e))
+       error_found = true;
       if (!e->inline_failed)
        {
          if (node->global.inlined_to
       if (!e->inline_failed)
        {
          if (node->global.inlined_to
@@ -684,6 +573,9 @@ verify_cgraph_node (struct cgraph_node *node)
            error_found = true;
          }
     }
            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");
   if (!node->callers && node->global.inlined_to)
     {
       error ("inlined_to pointer is set but no predecessors found");
@@ -695,7 +587,7 @@ verify_cgraph_node (struct cgraph_node *node)
       error_found = true;
     }
 
       error_found = true;
     }
 
-  if (!cgraph_node (node->decl))
+  if (!cgraph_get_node (node->decl))
     {
       error ("node not found in cgraph_hash");
       error_found = true;
     {
       error ("node not found in cgraph_hash");
       error_found = true;
@@ -767,10 +659,58 @@ verify_cgraph_node (struct cgraph_node *node)
       while (n != node);
     }
 
       while (n != node);
     }
 
-  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 (node->analyzed && node->alias)
+    {
+      bool ref_found = false;
+      int i;
+      struct ipa_ref *ref;
+
+      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 reference");
+           error_found = true;
+         }
+       else if (ref_found)
+         {
+           error ("Alias has more than one alias reference");
+           error_found = 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)
        {
     {
       if (this_cfun->cfg)
        {
@@ -794,25 +734,12 @@ verify_cgraph_node (struct cgraph_node *node)
                        if (e->aux)
                          {
                            error ("shared call_stmt:");
                        if (e->aux)
                          {
                            error ("shared call_stmt:");
-                           debug_gimple_stmt (stmt);
+                           cgraph_debug_gimple_stmt (this_cfun, stmt);
                            error_found = true;
                          }
                        if (!e->indirect_unknown_callee)
                          {
                            error_found = true;
                          }
                        if (!e->indirect_unknown_callee)
                          {
-                           if (e->callee->same_body_alias)
-                             {
-                               error ("edge points to same body alias:");
-                               debug_tree (e->callee->decl);
-                               error_found = true;
-                             }
-#ifdef ENABLE_CHECKING
-                           else if (!e->callee->global.inlined_to
-                                    && decl
-                                    && cgraph_get_node (decl)
-                                    && (e->callee->former_clone_of
-                                        != cgraph_get_node (decl)->decl)
-                                    && !clone_of_p (cgraph_node (decl),
-                                                    e->callee))
+                           if (verify_edge_corresponds_to_fndecl (e, decl))
                              {
                                error ("edge points to wrong declaration:");
                                debug_tree (e->callee->decl);
                              {
                                error ("edge points to wrong declaration:");
                                debug_tree (e->callee->decl);
@@ -820,7 +747,6 @@ verify_cgraph_node (struct cgraph_node *node)
                                debug_tree (decl);
                                error_found = true;
                              }
                                debug_tree (decl);
                                error_found = true;
                              }
-#endif
                          }
                        else if (decl)
                          {
                          }
                        else if (decl)
                          {
@@ -828,14 +754,14 @@ verify_cgraph_node (struct cgraph_node *node)
                                   "corresponding to a call_stmt with "
                                   "a known declaration:");
                            error_found = true;
                                   "corresponding to a call_stmt with "
                                   "a known declaration:");
                            error_found = true;
-                           debug_gimple_stmt (e->call_stmt);
+                           cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
                          }
                        e->aux = (void *)1;
                      }
                    else if (decl)
                      {
                        error ("missing callgraph edge for call stmt:");
                          }
                        e->aux = (void *)1;
                      }
                    else if (decl)
                      {
                        error ("missing callgraph edge for call stmt:");
-                       debug_gimple_stmt (stmt);
+                       cgraph_debug_gimple_stmt (this_cfun, stmt);
                        error_found = true;
                      }
                  }
                        error_found = true;
                      }
                  }
@@ -853,7 +779,7 @@ verify_cgraph_node (struct cgraph_node *node)
              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)));
              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)));
-             debug_gimple_stmt (e->call_stmt);
+             cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
              error_found = true;
            }
          e->aux = 0;
              error_found = true;
            }
          e->aux = 0;
@@ -864,7 +790,7 @@ verify_cgraph_node (struct cgraph_node *node)
            {
              error ("an indirect edge from %s has no corresponding call_stmt",
                     identifier_to_locale (cgraph_node_name (e->caller)));
            {
              error ("an indirect edge from %s has no corresponding call_stmt",
                     identifier_to_locale (cgraph_node_name (e->caller)));
-             debug_gimple_stmt (e->call_stmt);
+             cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
              error_found = true;
            }
          e->aux = 0;
              error_found = true;
            }
          e->aux = 0;
@@ -875,7 +801,6 @@ verify_cgraph_node (struct cgraph_node *node)
       dump_cgraph_node (stderr, node);
       internal_error ("verify_cgraph_node failed");
     }
       dump_cgraph_node (stderr, node);
       internal_error ("verify_cgraph_node failed");
     }
-  set_cfun (saved_cfun);
   timevar_pop (TV_CGRAPH_VERIFY);
 }
 
   timevar_pop (TV_CGRAPH_VERIFY);
 }
 
@@ -908,32 +833,134 @@ cgraph_output_pending_asms (void)
 }
 
 /* Analyze the function scheduled to be output.  */
 }
 
 /* Analyze the function scheduled to be output.  */
-static void
+void
 cgraph_analyze_function (struct cgraph_node *node)
 {
   tree save = current_function_decl;
   tree decl = node->decl;
 
 cgraph_analyze_function (struct cgraph_node *node)
 {
   tree save = current_function_decl;
   tree decl = node->decl;
 
-  current_function_decl = decl;
-  push_cfun (DECL_STRUCT_FUNCTION (decl));
+  if (node->alias && node->thunk.alias)
+    {
+      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->decl);
+           node->alias = false;
+           return;
+         }
+      if (!VEC_length (ipa_ref_t, node->ref_list.references))
+        ipa_record_reference (node, NULL, tgt, NULL, 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->thunk.alias);
+         DECL_DISREGARD_INLINE_LIMITS (node->decl)
+            = DECL_DISREGARD_INLINE_LIMITS (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)
+       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)
+    {
+      cgraph_create_edge (node, cgraph_get_node (node->thunk.alias),
+                         NULL, 0, CGRAPH_FREQ_BASE);
+    }
+  else
+    {
+      current_function_decl = decl;
+      push_cfun (DECL_STRUCT_FUNCTION (decl));
 
 
-  assign_assembler_name_if_neeeded (node->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
-     functions, so we can end up here with an already gimplified
-     body.  */
-  if (!gimple_body (decl))
-    gimplify_function_tree (decl);
-  dump_function (TDI_generic, 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))
+       gimplify_function_tree (decl);
+      dump_function (TDI_generic, decl);
 
 
-  cgraph_lower_function (node);
+      cgraph_lower_function (node);
+      pop_cfun ();
+    }
   node->analyzed = true;
 
   node->analyzed = true;
 
-  pop_cfun ();
   current_function_decl = save;
 }
 
   current_function_decl = save;
 }
 
+/* C++ frontend produce same body aliases all over the place, even before PCH
+   gets streamed out. It relies on us linking the aliases with their function
+   in order to do the fixups, but ipa-ref is not PCH safe.  Consequentely we
+   first produce aliases without links, but once C++ FE is sure he won't sream
+   PCH we build the links via this function.  */
+
+void
+cgraph_process_same_body_aliases (void)
+{
+  struct cgraph_node *node;
+  for (node = cgraph_nodes; node; node = node->next)
+    if (node->same_body_alias
+       && !VEC_length (ipa_ref_t, node->ref_list.references))
+      {
+        struct cgraph_node *tgt = cgraph_get_node (node->thunk.alias);
+        ipa_record_reference (node, NULL, tgt, NULL, IPA_REF_ALIAS, NULL);
+      }
+  same_body_aliases_done = true;
+}
+
+/* Process attributes common for vars and functions.  */
+
+static void
+process_common_attributes (tree decl)
+{
+  tree weakref = lookup_attribute ("weakref", DECL_ATTRIBUTES (decl));
+
+  if (weakref && !lookup_attribute ("alias", DECL_ATTRIBUTES (decl)))
+    {
+      warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+                 "%<weakref%> attribute should be accompanied with"
+                 " an %<alias%> attribute");
+      DECL_WEAK (decl) = 0;
+      DECL_ATTRIBUTES (decl) = remove_attribute ("weakref",
+                                                DECL_ATTRIBUTES (decl));
+    }
+}
+
 /* Look for externally_visible and used attributes and mark cgraph nodes
    accordingly.
 
 /* Look for externally_visible and used attributes and mark cgraph nodes
    accordingly.
 
@@ -970,7 +997,14 @@ process_function_and_variable_attributes (struct cgraph_node *first,
       tree decl = node->decl;
       if (DECL_PRESERVE_P (decl))
        cgraph_mark_needed_node (node);
       tree decl = node->decl;
       if (DECL_PRESERVE_P (decl))
        cgraph_mark_needed_node (node);
-      if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
+      if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+         && lookup_attribute ("dllexport", DECL_ATTRIBUTES (decl))
+         && TREE_PUBLIC (node->decl))
+       {
+         if (node->local.finalized)
+           cgraph_mark_needed_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->decl))
            warning_at (DECL_SOURCE_LOCATION (node->decl), OPT_Wattributes,
@@ -979,6 +1013,25 @@ process_function_and_variable_attributes (struct cgraph_node *first,
          else if (node->local.finalized)
             cgraph_mark_needed_node (node);
        }
          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,
+                     "%<weakref%> attribute ignored"
+                     " because function is defined");
+         DECL_WEAK (decl) = 0;
+         DECL_ATTRIBUTES (decl) = remove_attribute ("weakref",
+                                                    DECL_ATTRIBUTES (decl));
+       }
+
+      if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (decl))
+         && !DECL_DECLARED_INLINE_P (decl)
+         /* redefining extern inline function makes it DECL_UNINLINABLE.  */
+         && !DECL_UNINLINABLE (decl))
+       warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+                   "always_inline function might not be inlinable");
+     
+      process_common_attributes (decl);
     }
   for (vnode = varpool_nodes; vnode != first_var; vnode = vnode->next)
     {
     }
   for (vnode = varpool_nodes; vnode != first_var; vnode = vnode->next)
     {
@@ -989,7 +1042,14 @@ process_function_and_variable_attributes (struct cgraph_node *first,
          if (vnode->finalized)
            varpool_mark_needed_node (vnode);
        }
          if (vnode->finalized)
            varpool_mark_needed_node (vnode);
        }
-      if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
+      if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+         && lookup_attribute ("dllexport", DECL_ATTRIBUTES (decl))
+         && TREE_PUBLIC (vnode->decl))
+       {
+         if (vnode->finalized)
+           varpool_mark_needed_node (vnode);
+       }
+      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->decl))
            warning_at (DECL_SOURCE_LOCATION (vnode->decl), OPT_Wattributes,
@@ -998,6 +1058,18 @@ process_function_and_variable_attributes (struct cgraph_node *first,
          else if (vnode->finalized)
            varpool_mark_needed_node (vnode);
        }
          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,
+                     "%<weakref%> attribute ignored"
+                     " because variable is initialized");
+         DECL_WEAK (decl) = 0;
+         DECL_ATTRIBUTES (decl) = remove_attribute ("weakref",
+                                                     DECL_ATTRIBUTES (decl));
+       }
+      process_common_attributes (decl);
     }
 }
 
     }
 }
 
@@ -1016,6 +1088,7 @@ cgraph_analyze_functions (void)
   static struct varpool_node *first_analyzed_var;
   struct cgraph_node *node, *next;
 
   static struct varpool_node *first_analyzed_var;
   struct cgraph_node *node, *next;
 
+  bitmap_obstack_initialize (NULL);
   process_function_and_variable_attributes (first_processed,
                                            first_analyzed_var);
   first_processed = cgraph_nodes;
   process_function_and_variable_attributes (first_processed,
                                            first_analyzed_var);
   first_processed = cgraph_nodes;
@@ -1047,9 +1120,12 @@ cgraph_analyze_functions (void)
       /* ??? 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))
+      if (!DECL_STRUCT_FUNCTION (decl)
+         && (!node->alias || !node->thunk.alias)
+         && !node->thunk.thunk_p)
        {
          cgraph_reset_node (node);
        {
          cgraph_reset_node (node);
+          node->local.redefined_extern_inline = true;
          continue;
        }
 
          continue;
        }
 
@@ -1059,6 +1135,9 @@ cgraph_analyze_functions (void)
       for (edge = node->callees; edge; edge = edge->next_callee)
        if (!edge->callee->reachable)
          cgraph_mark_reachable_node (edge->callee);
       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 (node->same_comdat_group)
        {
 
       if (node->same_comdat_group)
        {
@@ -1070,10 +1149,12 @@ cgraph_analyze_functions (void)
 
       /* 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
 
       /* 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.  */
+        abstract function declaration will be later needed to output debug
+        info.  */
       if (DECL_ABSTRACT_ORIGIN (decl))
        {
       if (DECL_ABSTRACT_ORIGIN (decl))
        {
-         struct cgraph_node *origin_node = cgraph_node (DECL_ABSTRACT_ORIGIN (decl));
+         struct cgraph_node *origin_node;
+         origin_node = cgraph_get_node (DECL_ABSTRACT_ORIGIN (decl));
          origin_node->abstract_and_needed = true;
        }
 
          origin_node->abstract_and_needed = true;
        }
 
@@ -1096,6 +1177,7 @@ cgraph_analyze_functions (void)
          fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
       fprintf (cgraph_dump_file, "\n\nInitial ");
       dump_cgraph (cgraph_dump_file);
          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);
     }
 
   if (cgraph_dump_file)
     }
 
   if (cgraph_dump_file)
@@ -1106,10 +1188,14 @@ cgraph_analyze_functions (void)
       tree decl = node->decl;
       next = node->next;
 
       tree decl = node->decl;
       next = node->next;
 
-      if (node->local.finalized && !gimple_has_body_p (decl))
+      if (node->local.finalized && !gimple_has_body_p (decl)
+         && (!node->alias || !node->thunk.alias)
+         && !node->thunk.thunk_p)
        cgraph_reset_node (node);
 
        cgraph_reset_node (node);
 
-      if (!node->reachable && gimple_has_body_p (decl))
+      if (!node->reachable
+         && (gimple_has_body_p (decl) || node->thunk.thunk_p
+             || (node->alias && node->thunk.alias)))
        {
          if (cgraph_dump_file)
            fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
        {
          if (cgraph_dump_file)
            fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
@@ -1118,18 +1204,95 @@ cgraph_analyze_functions (void)
        }
       else
        node->next_needed = NULL;
        }
       else
        node->next_needed = NULL;
-      gcc_assert (!node->local.finalized || gimple_has_body_p (decl));
+      gcc_assert (!node->local.finalized || node->thunk.thunk_p
+                 || node->alias
+                 || gimple_has_body_p (decl));
       gcc_assert (node->analyzed == node->local.finalized);
     }
   if (cgraph_dump_file)
     {
       fprintf (cgraph_dump_file, "\n\nReclaimed ");
       dump_cgraph (cgraph_dump_file);
       gcc_assert (node->analyzed == node->local.finalized);
     }
   if (cgraph_dump_file)
     {
       fprintf (cgraph_dump_file, "\n\nReclaimed ");
       dump_cgraph (cgraph_dump_file);
+      dump_varpool (cgraph_dump_file);
     }
     }
+  bitmap_obstack_release (NULL);
   first_analyzed = cgraph_nodes;
   ggc_collect ();
 }
 
   first_analyzed = cgraph_nodes;
   ggc_collect ();
 }
 
+/* Translate the ugly representation of aliases as alias pairs into nice
+   representation in callgraph.  We don't handle all cases yet,
+   unforutnately.  */
+
+static void
+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);)
+    {
+      if (TREE_CODE (p->decl) == FUNCTION_DECL
+         && (target_node = cgraph_node_for_asm (p->target)) != NULL)
+       {
+         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);
+         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)
+       {
+         /* 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);
+         VEC_unordered_remove (alias_pair, alias_pairs, i);
+       }
+      /* 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.  */
+      else if (lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)) != NULL
+              && (TREE_CODE (p->decl) == FUNCTION_DECL
+                  ? (varpool_node_for_asm (p->target) == NULL)
+                  : (cgraph_node_for_asm (p->target) == 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);
+       }
+      else
+       {
+         if (dump_file)
+           fprintf (dump_file, "Unhandled alias %s->%s\n",
+                    IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (p->decl)),
+                    IDENTIFIER_POINTER (p->target));
+
+         i++;
+       }
+    }
+}
+
 
 /* Analyze the whole compilation unit once it is parsed completely.  */
 
 
 /* Analyze the whole compilation unit once it is parsed completely.  */
 
@@ -1138,18 +1301,24 @@ cgraph_finalize_compilation_unit (void)
 {
   timevar_push (TV_CGRAPH);
 
 {
   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 ();
 
   /* 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 ();
 
-  /* Call functions declared with the "constructor" or "destructor"
-     attribute.  */
-  cgraph_build_cdtor_fns ();
-
   /* Mark alias targets necessary and emit diagnostics.  */
   finish_aliases_1 ();
   /* Mark alias targets necessary and emit diagnostics.  */
   finish_aliases_1 ();
+  handle_alias_pairs ();
 
   if (!quiet_flag)
     {
 
   if (!quiet_flag)
     {
@@ -1157,12 +1326,16 @@ cgraph_finalize_compilation_unit (void)
       fflush (stderr);
     }
 
       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 ();
   /* 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 ();
 
   /* Gimplify and lower thunks.  */
   cgraph_analyze_functions ();
@@ -1204,10 +1377,12 @@ cgraph_mark_functions_to_output (void)
         always inlined, as well as those that are reachable from
         outside the current compilation unit.  */
       if (node->analyzed
         always inlined, as well as those that are reachable from
         outside the current compilation unit.  */
       if (node->analyzed
+         && !node->thunk.thunk_p
+         && !node->alias
          && !node->global.inlined_to
          && !node->global.inlined_to
-         && (node->needed || node->reachable_from_other_partition
-             || node->address_taken
-             || (e && node->reachable))
+         && (!cgraph_only_called_directly_p (node)
+             || ((e || ipa_ref_has_aliases_p (&node->ref_list))
+                 && node->reachable))
          && !TREE_ASM_WRITTEN (decl)
          && !DECL_EXTERNAL (decl))
        {
          && !TREE_ASM_WRITTEN (decl)
          && !DECL_EXTERNAL (decl))
        {
@@ -1218,7 +1393,8 @@ cgraph_mark_functions_to_output (void)
              for (next = node->same_comdat_group;
                   next != node;
                   next = next->same_comdat_group)
              for (next = node->same_comdat_group;
                   next != node;
                   next = next->same_comdat_group)
-               next->process = 1;
+               if (!next->thunk.thunk_p && !next->alias)
+                 next->process = 1;
            }
        }
       else if (node->same_comdat_group)
            }
        }
       else if (node->same_comdat_group)
@@ -1237,6 +1413,7 @@ cgraph_mark_functions_to_output (void)
                 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
                 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->alias
              && !DECL_EXTERNAL (decl))
            {
              dump_cgraph_node (stderr, node);
              && !DECL_EXTERNAL (decl))
            {
              dump_cgraph_node (stderr, node);
@@ -1259,14 +1436,16 @@ cgraph_mark_functions_to_output (void)
          tree decl = node->decl;
          if (!node->global.inlined_to
              && gimple_has_body_p (decl)
          tree decl = node->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.  */
+             /* 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->in_other_partition
              && !DECL_EXTERNAL (decl))
            {
              dump_cgraph_node (stderr, node);
              && !node->in_other_partition
              && !DECL_EXTERNAL (decl))
            {
              dump_cgraph_node (stderr, node);
-             internal_error ("failed to reclaim unneeded function");
+             internal_error ("failed to reclaim unneeded function in same "
+                             "comdat group");
            }
        }
 #endif
            }
        }
 #endif
@@ -1295,7 +1474,7 @@ init_lowered_empty_function (tree decl)
   DECL_SAVED_TREE (decl) = error_mark_node;
   cfun->curr_properties |=
     (PROP_gimple_lcf | PROP_gimple_leh | PROP_cfg | PROP_referenced_vars |
   DECL_SAVED_TREE (decl) = error_mark_node;
   cfun->curr_properties |=
     (PROP_gimple_lcf | PROP_gimple_leh | PROP_cfg | PROP_referenced_vars |
-     PROP_ssa);
+     PROP_ssa | PROP_gimple_any);
 
   /* Create BB for body of the function and connect it properly.  */
   bb = create_basic_block (NULL, (void *) 0, ENTRY_BLOCK_PTR);
 
   /* Create BB for body of the function and connect it properly.  */
   bb = create_basic_block (NULL, (void *) 0, ENTRY_BLOCK_PTR);
@@ -1321,11 +1500,10 @@ thunk_adjust (gimple_stmt_iterator * bsi,
   if (this_adjusting
       && fixed_offset != 0)
     {
   if (this_adjusting
       && fixed_offset != 0)
     {
-      stmt = gimple_build_assign (ptr,
-                                 fold_build2_loc (input_location,
-                                                  POINTER_PLUS_EXPR,
-                                                  TREE_TYPE (ptr), ptr,
-                                                  size_int (fixed_offset)));
+      stmt = gimple_build_assign
+               (ptr, fold_build_pointer_plus_hwi_loc (input_location,
+                                                      ptr,
+                                                      fixed_offset));
       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
     }
 
       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
     }
 
@@ -1336,7 +1514,6 @@ thunk_adjust (gimple_stmt_iterator * bsi,
       tree vtabletmp;
       tree vtabletmp2;
       tree vtabletmp3;
       tree vtabletmp;
       tree vtabletmp2;
       tree vtabletmp3;
-      tree offsettmp;
 
       if (!vtable_entry_type)
        {
 
       if (!vtable_entry_type)
        {
@@ -1371,12 +1548,9 @@ thunk_adjust (gimple_stmt_iterator * bsi,
 
       /* Find the entry with the vcall offset.  */
       stmt = gimple_build_assign (vtabletmp2,
 
       /* Find the entry with the vcall offset.  */
       stmt = gimple_build_assign (vtabletmp2,
-                                 fold_build2_loc (input_location,
-                                                  POINTER_PLUS_EXPR,
-                                                  TREE_TYPE (vtabletmp2),
-                                                  vtabletmp2,
-                                                  fold_convert (sizetype,
-                                                                virtual_offset)));
+                                 fold_build_pointer_plus_loc (input_location,
+                                                              vtabletmp2,
+                                                              virtual_offset));
       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
 
       /* Get the offset itself.  */
       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
 
       /* Get the offset itself.  */
@@ -1388,17 +1562,10 @@ thunk_adjust (gimple_stmt_iterator * bsi,
       mark_symbols_for_renaming (stmt);
       find_referenced_vars_in (stmt);
 
       mark_symbols_for_renaming (stmt);
       find_referenced_vars_in (stmt);
 
-      /* Cast to sizetype.  */
-      offsettmp = create_tmp_var (sizetype, "offset");
-      stmt = gimple_build_assign (offsettmp, fold_convert (sizetype, vtabletmp3));
-      gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
-      mark_symbols_for_renaming (stmt);
-      find_referenced_vars_in (stmt);
-
       /* Adjust the `this' pointer.  */
       /* Adjust the `this' pointer.  */
-      ptr = fold_build2_loc (input_location,
-                            POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
-                            offsettmp);
+      ptr = fold_build_pointer_plus_loc (input_location, ptr, vtabletmp3);
+      ptr = force_gimple_operand_gsi (bsi, ptr, true, NULL_TREE, false,
+                                     GSI_CONTINUE_LINKING);
     }
 
   if (!this_adjusting
     }
 
   if (!this_adjusting
@@ -1417,9 +1584,8 @@ thunk_adjust (gimple_stmt_iterator * bsi,
          mark_symbols_for_renaming (stmt);
          find_referenced_vars_in (stmt);
        }
          mark_symbols_for_renaming (stmt);
          find_referenced_vars_in (stmt);
        }
-      ptr = fold_build2_loc (input_location,
-                            POINTER_PLUS_EXPR, TREE_TYPE (ptrtmp), ptrtmp,
-                            size_int (fixed_offset));
+      ptr = fold_build_pointer_plus_hwi_loc (input_location,
+                                            ptrtmp, fixed_offset);
     }
 
   /* Emit the statement and gimplify the adjustment expression.  */
     }
 
   /* Emit the statement and gimplify the adjustment expression.  */
@@ -1447,16 +1613,20 @@ assemble_thunk (struct cgraph_node *node)
 
   current_function_decl = thunk_fndecl;
 
 
   current_function_decl = thunk_fndecl;
 
+  /* 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,
                                              virtual_value, alias))
     {
       const char *fnname;
       tree fn_block;
   if (this_adjusting
       && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
                                              virtual_value, alias))
     {
       const char *fnname;
       tree fn_block;
+      tree restype = TREE_TYPE (TREE_TYPE (thunk_fndecl));
       
       DECL_RESULT (thunk_fndecl)
        = build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
       
       DECL_RESULT (thunk_fndecl)
        = build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
-                     RESULT_DECL, 0, integer_type_node);
+                     RESULT_DECL, 0, restype);
       fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl));
 
       /* The back end expects DECL_INITIAL to contain a BLOCK, so we
       fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl));
 
       /* The back end expects DECL_INITIAL to contain a BLOCK, so we
@@ -1476,6 +1646,8 @@ assemble_thunk (struct cgraph_node *node)
       free_after_compilation (cfun);
       set_cfun (NULL);
       TREE_ASM_WRITTEN (thunk_fndecl) = 1;
       free_after_compilation (cfun);
       set_cfun (NULL);
       TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+      node->thunk.thunk_p = false;
+      node->analyzed = false;
     }
   else
     {
     }
   else
     {
@@ -1520,14 +1692,14 @@ assemble_thunk (struct cgraph_node *node)
          if (!is_gimple_reg_type (restype))
            {
              restmp = resdecl;
          if (!is_gimple_reg_type (restype))
            {
              restmp = resdecl;
-             cfun->local_decls = tree_cons (NULL_TREE, restmp, cfun->local_decls);
+             add_local_decl (cfun, restmp);
              BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp;
            }
          else
             restmp = create_tmp_var_raw (restype, "retval");
        }
 
              BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp;
            }
          else
             restmp = create_tmp_var_raw (restype, "retval");
        }
 
-      for (arg = a; arg; arg = TREE_CHAIN (arg))
+      for (arg = a; arg; arg = DECL_CHAIN (arg))
         nargs++;
       vargs = VEC_alloc (tree, heap, nargs);
       if (this_adjusting)
         nargs++;
       vargs = VEC_alloc (tree, heap, nargs);
       if (this_adjusting)
@@ -1537,11 +1709,10 @@ assemble_thunk (struct cgraph_node *node)
                                      virtual_offset));
       else
         VEC_quick_push (tree, vargs, a);
                                      virtual_offset));
       else
         VEC_quick_push (tree, vargs, a);
-      for (i = 1, arg = TREE_CHAIN (a); i < nargs; i++, arg = TREE_CHAIN (arg))
+      for (i = 1, arg = DECL_CHAIN (a); i < nargs; i++, arg = DECL_CHAIN (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);
         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);
       gimple_call_set_from_thunk (call, true);
       if (restmp)
         gimple_call_set_lhs (call, restmp);
@@ -1567,8 +1738,7 @@ assemble_thunk (struct cgraph_node *node)
              remove_edge (single_succ_edge (bb));
              true_label = gimple_block_label (then_bb);
              stmt = gimple_build_cond (NE_EXPR, restmp,
              remove_edge (single_succ_edge (bb));
              true_label = gimple_block_label (then_bb);
              stmt = gimple_build_cond (NE_EXPR, restmp,
-                                       fold_convert (TREE_TYPE (restmp),
-                                                     integer_zero_node),
+                                       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);
                                        NULL_TREE, NULL_TREE);
              gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
              make_edge (bb, then_bb, EDGE_TRUE_VALUE);
@@ -1585,8 +1755,8 @@ assemble_thunk (struct cgraph_node *node)
            {
              gimple stmt;
              bsi = gsi_last_bb (else_bb);
            {
              gimple stmt;
              bsi = gsi_last_bb (else_bb);
-             stmt = gimple_build_assign (restmp, fold_convert (TREE_TYPE (restmp),
-                                                               integer_zero_node));
+             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);
            }
              gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
              bsi = gsi_last_bb (return_bb);
            }
@@ -1601,15 +1771,54 @@ assemble_thunk (struct cgraph_node *node)
       delete_unreachable_blocks ();
       update_ssa (TODO_update_ssa);
 
       delete_unreachable_blocks ();
       update_ssa (TODO_update_ssa);
 
-      cgraph_remove_same_body_alias (node);
       /* Since we want to emit the thunk, we explicitly mark its name as
         referenced.  */
       /* 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);
       bitmap_obstack_release (NULL);
     }
   current_function_decl = NULL;
 }
 
       cgraph_add_new_function (thunk_fndecl, true);
       bitmap_obstack_release (NULL);
     }
   current_function_decl = NULL;
 }
 
+
+
+/* Assemble thunks and aliases asociated to NODE.  */
+
+static void
+assemble_thunks_and_aliases (struct cgraph_node *node)
+{
+  struct cgraph_edge *e;
+  int i;
+  struct ipa_ref *ref;
+
+  for (e = node->callers; e;)
+    if (e->caller->thunk.thunk_p)
+      {
+       struct cgraph_node *thunk = e->caller;
+
+       e = e->next_caller;
+       assemble_thunks_and_aliases (thunk);
+        assemble_thunk (thunk);
+      }
+    else
+      e = e->next_caller;
+  for (i = 0; ipa_ref_list_refering_iterate (&node->ref_list, i, ref); i++)
+    if (ref->use == IPA_REF_ALIAS)
+      {
+       struct cgraph_node *alias = ipa_ref_refering_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));
+       assemble_thunks_and_aliases (alias);
+       TREE_ASM_WRITTEN (alias->thunk.alias) = saved_written;
+      }
+}
+
 /* Expand function specified by NODE.  */
 
 static void
 /* Expand function specified by NODE.  */
 
 static void
@@ -1622,7 +1831,6 @@ cgraph_expand_function (struct cgraph_node *node)
 
   announce_function (decl);
   node->process = 0;
 
   announce_function (decl);
   node->process = 0;
-
   gcc_assert (node->lowered);
 
   /* Generate RTL for the body of DECL.  */
   gcc_assert (node->lowered);
 
   /* Generate RTL for the body of DECL.  */
@@ -1631,27 +1839,15 @@ cgraph_expand_function (struct cgraph_node *node)
   /* Make sure that BE didn't give up on compiling.  */
   gcc_assert (TREE_ASM_WRITTEN (decl));
   current_function_decl = NULL;
   /* Make sure that BE didn't give up on compiling.  */
   gcc_assert (TREE_ASM_WRITTEN (decl));
   current_function_decl = NULL;
-  if (node->same_body)
-    {
-      struct cgraph_node *alias, *next;
-      bool saved_alias = node->alias;
-      for (alias = node->same_body;
-          alias && alias->next; alias = alias->next)
-        ;
-      /* Walk aliases in the order they were created; it is possible that
-         thunks reffers to the aliases made earlier.  */
-      for (; alias; alias = next)
-        {
-         next = alias->previous;
-         if (!alias->thunk.thunk_p)
-           assemble_alias (alias->decl,
-                           DECL_ASSEMBLER_NAME (alias->thunk.alias));
-         else
-           assemble_thunk (alias);
-       }
-      node->alias = saved_alias;
-    }
-  gcc_assert (!cgraph_preserve_function_body_p (decl));
+  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
+     with comdats. See PR48668.  Also aliases must come after function itself to
+     make one pass assemblers, like one on AIX happy.  See PR 50689.
+     FIXME: Perhaps thunks should be move before function IFF they are not in comdat
+     groups.  */
+  assemble_thunks_and_aliases (node);
   cgraph_release_function_body (node);
   /* Eliminate all call edges.  This is important so the GIMPLE_CALL no longer
      points to the dead function body.  */
   cgraph_release_function_body (node);
   /* Eliminate all call edges.  This is important so the GIMPLE_CALL no longer
      points to the dead function body.  */
@@ -1689,7 +1885,7 @@ cgraph_expand_all_functions (void)
   int order_pos, new_order_pos = 0;
   int i;
 
   int order_pos, new_order_pos = 0;
   int i;
 
-  order_pos = cgraph_postorder (order);
+  order_pos = ipa_reverse_postorder (order);
   gcc_assert (order_pos == cgraph_n_nodes);
 
   /* Garbage collector may remove inline clones we eliminate during
   gcc_assert (order_pos == cgraph_n_nodes);
 
   /* Garbage collector may remove inline clones we eliminate during
@@ -1758,7 +1954,7 @@ cgraph_output_in_order (void)
 
   for (pf = cgraph_nodes; pf; pf = pf->next)
     {
 
   for (pf = cgraph_nodes; pf; pf = pf->next)
     {
-      if (pf->process)
+      if (pf->process && !pf->thunk.thunk_p && !pf->alias)
        {
          i = pf->order;
          gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
        {
          i = pf->order;
          gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
@@ -1794,6 +1990,10 @@ cgraph_output_in_order (void)
   varpool_empty_needed_queue ();
 
   for (i = 0; i < max; ++i)
   varpool_empty_needed_queue ();
 
   for (i = 0; i < max; ++i)
+    if (nodes[i].kind == ORDER_VAR)
+      varpool_finalize_named_section_flags (nodes[i].u.v);
+
+  for (i = 0; i < max; ++i)
     {
       switch (nodes[i].kind)
        {
     {
       switch (nodes[i].kind)
        {
@@ -1825,13 +2025,12 @@ cgraph_output_in_order (void)
 /* Return true when function body of DECL still needs to be kept around
    for later re-use.  */
 bool
 /* Return true when function body of DECL still needs to be kept around
    for later re-use.  */
 bool
-cgraph_preserve_function_body_p (tree decl)
+cgraph_preserve_function_body_p (struct cgraph_node *node)
 {
 {
-  struct cgraph_node *node;
-
   gcc_assert (cgraph_global_info_ready);
   gcc_assert (cgraph_global_info_ready);
+  gcc_assert (!node->alias && !node->thunk.thunk_p);
+
   /* Look if there is any clone around.  */
   /* Look if there is any clone around.  */
-  node = cgraph_node (decl);
   if (node->clones)
     return true;
   return false;
   if (node->clones)
     return true;
   return false;
@@ -1848,7 +2047,17 @@ ipa_passes (void)
   invoke_plugin_callbacks (PLUGIN_ALL_IPA_PASSES_START, NULL);
 
   if (!in_lto_p)
   invoke_plugin_callbacks (PLUGIN_ALL_IPA_PASSES_START, NULL);
 
   if (!in_lto_p)
-    execute_ipa_pass_list (all_small_ipa_passes);
+    {
+      execute_ipa_pass_list (all_small_ipa_passes);
+      if (seen_error ())
+       return;
+    }
+
+  /* We never run removal of unreachable nodes after early passes.  This is
+     because TODO is run before the subpasses.  It is important to remove
+     the unreachable functions to save works at IPA level and to get LTO
+     symbol tables right.  */
+  cgraph_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.  */
 
   /* If pass_all_early_optimizations was not scheduled, the state of
      the cgraph will not be properly updated.  Update it now.  */
@@ -1881,7 +2090,7 @@ ipa_passes (void)
   if (flag_generate_lto)
     targetm.asm_out.lto_end ();
 
   if (flag_generate_lto)
     targetm.asm_out.lto_end ();
 
-  if (!flag_ltrans)
+  if (!flag_ltrans && (in_lto_p || !flag_lto || flag_fat_lto_objects))
     execute_ipa_pass_list (all_regular_ipa_passes);
   invoke_plugin_callbacks (PLUGIN_ALL_IPA_PASSES_END, NULL);
 
     execute_ipa_pass_list (all_regular_ipa_passes);
   invoke_plugin_callbacks (PLUGIN_ALL_IPA_PASSES_END, NULL);
 
@@ -1889,6 +2098,42 @@ ipa_passes (void)
 }
 
 
 }
 
 
+/* 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.  */
+
+static void
+output_weakrefs (void)
+{
+  struct cgraph_node *node;
+  struct varpool_node *vnode;
+  for (node = cgraph_nodes; node; node = node->next)
+    if (node->alias && DECL_EXTERNAL (node->decl)
+        && !TREE_ASM_WRITTEN (node->decl)
+       && lookup_attribute ("weakref", DECL_ATTRIBUTES (node->decl)))
+      assemble_alias (node->decl,
+                     node->thunk.alias ? DECL_ASSEMBLER_NAME (node->thunk.alias)
+                     : get_alias_symbol (node->decl));
+  for (vnode = varpool_nodes; vnode; vnode = vnode->next)
+    if (vnode->alias && DECL_EXTERNAL (vnode->decl)
+        && !TREE_ASM_WRITTEN (vnode->decl)
+       && lookup_attribute ("weakref", DECL_ATTRIBUTES (vnode->decl)))
+      assemble_alias (vnode->decl,
+                     vnode->alias_of ? DECL_ASSEMBLER_NAME (vnode->alias_of)
+                     : get_alias_symbol (vnode->decl));
+}
+
+
 /* Perform simple optimizations based on callgraph.  */
 
 void
 /* Perform simple optimizations based on callgraph.  */
 
 void
@@ -1919,8 +2164,9 @@ cgraph_optimize (void)
   if (!seen_error ())
     ipa_passes ();
 
   if (!seen_error ())
     ipa_passes ();
 
-  /* Do nothing else if any IPA pass found errors.  */
-  if (seen_error ())
+  /* Do nothing else if any IPA pass found errors or if we are just streaming LTO.  */
+  if (seen_error ()
+      || (!in_lto_p && flag_lto && !flag_fat_lto_objects))
     {
       timevar_pop (TV_CGRAPHOPT);
       return;
     {
       timevar_pop (TV_CGRAPHOPT);
       return;
@@ -1952,7 +2198,15 @@ cgraph_optimize (void)
 #endif
 
   cgraph_materialize_all_clones ();
 #endif
 
   cgraph_materialize_all_clones ();
+  bitmap_obstack_initialize (NULL);
+  execute_ipa_pass_list (all_late_ipa_passes);
+  cgraph_remove_unreachable_nodes (true, dump_file);
+#ifdef ENABLE_CHECKING
+  verify_cgraph ();
+#endif
+  bitmap_obstack_release (NULL);
   cgraph_mark_functions_to_output ();
   cgraph_mark_functions_to_output ();
+  output_weakrefs ();
 
   cgraph_state = CGRAPH_STATE_EXPANSION;
   if (!flag_toplevel_reorder)
 
   cgraph_state = CGRAPH_STATE_EXPANSION;
   if (!flag_toplevel_reorder)
@@ -1966,6 +2220,7 @@ cgraph_optimize (void)
 
       varpool_assemble_pending_decls ();
     }
 
       varpool_assemble_pending_decls ();
     }
+
   cgraph_process_new_functions ();
   cgraph_state = CGRAPH_STATE_FINISHED;
 
   cgraph_process_new_functions ();
   cgraph_state = CGRAPH_STATE_FINISHED;
 
@@ -1973,6 +2228,7 @@ cgraph_optimize (void)
     {
       fprintf (cgraph_dump_file, "\nFinal ");
       dump_cgraph (cgraph_dump_file);
     {
       fprintf (cgraph_dump_file, "\nFinal ");
       dump_cgraph (cgraph_dump_file);
+      dump_varpool (cgraph_dump_file);
     }
 #ifdef ENABLE_CHECKING
   verify_cgraph ();
     }
 #ifdef ENABLE_CHECKING
   verify_cgraph ();
@@ -1997,80 +2253,11 @@ cgraph_optimize (void)
 #endif
 }
 
 #endif
 }
 
-
-/* Generate and emit a static constructor or destructor.  WHICH must
-   be one of 'I' (for a constructor) or 'D' (for a destructor).  BODY
-   is a STATEMENT_LIST containing GENERIC statements.  PRIORITY is the
-   initialization priority for this constructor or destructor.  */
-
-void
-cgraph_build_static_cdtor (char which, tree body, int priority)
-{
-  static int counter = 0;
-  char which_buf[16];
-  tree decl, name, resdecl;
-
-  /* The priority is encoded in the constructor or destructor name.
-     collect2 will sort the names and arrange that they are called at
-     program startup.  */
-  sprintf (which_buf, "%c_%.5d_%d", which, priority, counter++);
-  name = get_file_function_name (which_buf);
-
-  decl = build_decl (input_location, FUNCTION_DECL, name,
-                    build_function_type (void_type_node, void_list_node));
-  current_function_decl = decl;
-
-  resdecl = build_decl (input_location,
-                       RESULT_DECL, NULL_TREE, void_type_node);
-  DECL_ARTIFICIAL (resdecl) = 1;
-  DECL_RESULT (decl) = resdecl;
-  DECL_CONTEXT (resdecl) = decl;
-
-  allocate_struct_function (decl, false);
-
-  TREE_STATIC (decl) = 1;
-  TREE_USED (decl) = 1;
-  DECL_ARTIFICIAL (decl) = 1;
-  DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;
-  DECL_SAVED_TREE (decl) = body;
-  if (!targetm.have_ctors_dtors)
-    {
-      TREE_PUBLIC (decl) = 1;
-      DECL_PRESERVE_P (decl) = 1;
-    }
-  DECL_UNINLINABLE (decl) = 1;
-
-  DECL_INITIAL (decl) = make_node (BLOCK);
-  TREE_USED (DECL_INITIAL (decl)) = 1;
-
-  DECL_SOURCE_LOCATION (decl) = input_location;
-  cfun->function_end_locus = input_location;
-
-  switch (which)
-    {
-    case 'I':
-      DECL_STATIC_CONSTRUCTOR (decl) = 1;
-      decl_init_priority_insert (decl, priority);
-      break;
-    case 'D':
-      DECL_STATIC_DESTRUCTOR (decl) = 1;
-      decl_fini_priority_insert (decl, priority);
-      break;
-    default:
-      gcc_unreachable ();
-    }
-
-  gimplify_function_tree (decl);
-
-  cgraph_add_new_function (decl, false);
-  cgraph_mark_needed_node (cgraph_node (decl));
-  set_cfun (NULL);
-}
-
 void
 init_cgraph (void)
 {
 void
 init_cgraph (void)
 {
-  cgraph_dump_file = dump_begin (TDI_cgraph, NULL);
+  if (!cgraph_dump_file)
+    cgraph_dump_file = dump_begin (TDI_cgraph, NULL);
 }
 
 /* The edges representing the callers of the NEW_VERSION node were
 }
 
 /* The edges representing the callers of the NEW_VERSION node were
@@ -2105,7 +2292,7 @@ update_call_expr (struct cgraph_node *new_version)
    was copied to prevent duplications of calls that are dead
    in the clone.  */
 
    was copied to prevent duplications of calls that are dead
    in the clone.  */
 
-static struct cgraph_node *
+struct cgraph_node *
 cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
                                 tree new_decl,
                                 VEC(cgraph_edge_p,heap) *redirect_callers,
 cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
                                 tree new_decl,
                                 VEC(cgraph_edge_p,heap) *redirect_callers,
@@ -2117,13 +2304,12 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
 
    gcc_assert (old_version);
 
 
    gcc_assert (old_version);
 
-   new_version = cgraph_node (new_decl);
+   new_version = cgraph_create_node (new_decl);
 
 
-   new_version->analyzed = true;
+   new_version->analyzed = old_version->analyzed;
    new_version->local = old_version->local;
    new_version->local.externally_visible = false;
    new_version->local.local = true;
    new_version->local = old_version->local;
    new_version->local.externally_visible = false;
    new_version->local.local = true;
-   new_version->local.vtable_method = false;
    new_version->global = old_version->global;
    new_version->rtl = old_version->rtl;
    new_version->reachable = true;
    new_version->global = old_version->global;
    new_version->rtl = old_version->rtl;
    new_version->reachable = true;
@@ -2135,21 +2321,23 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
        cgraph_clone_edge (e, new_version, e->call_stmt,
                          e->lto_stmt_uid, REG_BR_PROB_BASE,
                          CGRAPH_FREQ_BASE,
        cgraph_clone_edge (e, new_version, e->call_stmt,
                          e->lto_stmt_uid, REG_BR_PROB_BASE,
                          CGRAPH_FREQ_BASE,
-                         e->loop_nest, true);
+                         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,
    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,
-                         e->loop_nest, true);
-   for (i = 0; VEC_iterate (cgraph_edge_p, redirect_callers, i, e); i++)
+                         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);
      }
 
      {
        /* Redirect calls to the old version node to point to its new
          version.  */
        cgraph_redirect_edge_callee (e, new_version);
      }
 
+   cgraph_call_node_duplication_hooks (old_version, new_version);
+
    return new_version;
  }
 
    return new_version;
  }
 
@@ -2164,17 +2352,21 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
     TREE_MAP is a mapping of tree nodes we want to replace with
     new ones (according to results of prior analysis).
     OLD_VERSION_NODE is the node that is versioned.
     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 ARGS_TO_SKIP determine function parameters to remove
     from new version.
+    If SKIP_RETURN is true, the new version will return void.
     If non-NULL BLOCK_TO_COPY determine what basic blocks to copy.
     If non-NULL BLOCK_TO_COPY determine what basic blocks to copy.
-    If non_NULL NEW_ENTRY determine new entry BB of the clone.  */
+    If non_NULL NEW_ENTRY determine new entry BB of the clone.
+
+    Return the new version's cgraph node.  */
 
 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,
 
 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,
+                           bool skip_return,
                            bitmap bbs_to_copy,
                            basic_block new_entry_block,
                            const char *clone_name)
                            bitmap bbs_to_copy,
                            basic_block new_entry_block,
                            const char *clone_name)
@@ -2186,18 +2378,24 @@ cgraph_function_versioning (struct cgraph_node *old_version_node,
   if (!tree_versionable_function_p (old_decl))
     return NULL;
 
   if (!tree_versionable_function_p (old_decl))
     return NULL;
 
-  /* Make a new FUNCTION_DECL tree node for the
-     new version. */
-  if (!args_to_skip)
+  gcc_assert (old_version_node->local.can_change_signature || !args_to_skip);
+
+  /* Make a new FUNCTION_DECL tree node for the new version. */
+  if (!args_to_skip && !skip_return)
     new_decl = copy_node (old_decl);
   else
     new_decl = copy_node (old_decl);
   else
-    new_decl = build_function_decl_skip_args (old_decl, args_to_skip);
+    new_decl
+      = build_function_decl_skip_args (old_decl, args_to_skip, skip_return);
 
   /* Generate a new name for the new version. */
   DECL_NAME (new_decl) = clone_function_name (old_decl, clone_name);
   SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
   SET_DECL_RTL (new_decl, NULL);
 
 
   /* 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);
 
+  /* When the old decl was a con-/destructor make sure the clone isn't.  */
+  DECL_STATIC_CONSTRUCTOR(new_decl) = 0;
+  DECL_STATIC_DESTRUCTOR(new_decl) = 0;
+
   /* Create the new version's call-graph node.
      and update the edges of the new node. */
   new_version_node =
   /* Create the new version's call-graph node.
      and update the edges of the new node. */
   new_version_node =
@@ -2206,7 +2404,7 @@ cgraph_function_versioning (struct cgraph_node *old_version_node,
 
   /* Copy the OLD_VERSION_NODE function tree to the new version.  */
   tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip,
 
   /* 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);
+                           skip_return, bbs_to_copy, new_entry_block);
 
   /* Update the new version's properties.
      Make The new version visible only within this translation unit.  Make sure
 
   /* Update the new version's properties.
      Make The new version visible only within this translation unit.  Make sure
@@ -2226,88 +2424,19 @@ cgraph_function_versioning (struct cgraph_node *old_version_node,
   return new_version_node;
 }
 
   return new_version_node;
 }
 
-/* Produce separate function body for inline clones so the offline copy can be
-   modified without affecting them.  */
-struct cgraph_node *
-save_inline_function_body (struct cgraph_node *node)
-{
-  struct cgraph_node *first_clone, *n;
-
-  gcc_assert (node == cgraph_node (node->decl));
-
-  cgraph_lower_function (node);
-
-  first_clone = node->clones;
-
-  first_clone->decl = copy_node (node->decl);
-  cgraph_insert_node_to_hashtable (first_clone);
-  gcc_assert (first_clone == cgraph_node (first_clone->decl));
-  if (first_clone->next_sibling_clone)
-    {
-      for (n = first_clone->next_sibling_clone; n->next_sibling_clone; n = n->next_sibling_clone)
-        n->clone_of = first_clone;
-      n->clone_of = first_clone;
-      n->next_sibling_clone = first_clone->clones;
-      if (first_clone->clones)
-        first_clone->clones->prev_sibling_clone = n;
-      first_clone->clones = first_clone->next_sibling_clone;
-      first_clone->next_sibling_clone->prev_sibling_clone = NULL;
-      first_clone->next_sibling_clone = NULL;
-      gcc_assert (!first_clone->prev_sibling_clone);
-    }
-  first_clone->clone_of = NULL;
-  node->clones = NULL;
-
-  if (first_clone->clones)
-    for (n = first_clone->clones; n != first_clone;)
-      {
-        gcc_assert (n->decl == node->decl);
-       n->decl = first_clone->decl;
-       if (n->clones)
-         n = n->clones;
-       else if (n->next_sibling_clone)
-         n = n->next_sibling_clone;
-       else
-         {
-           while (n != first_clone && !n->next_sibling_clone)
-             n = n->clone_of;
-           if (n != first_clone)
-             n = n->next_sibling_clone;
-         }
-      }
-
-  /* Copy the OLD_VERSION_NODE function tree to the new version.  */
-  tree_function_versioning (node->decl, first_clone->decl, NULL, true, NULL,
-                           NULL, NULL);
-
-  DECL_EXTERNAL (first_clone->decl) = 0;
-  DECL_COMDAT_GROUP (first_clone->decl) = NULL_TREE;
-  TREE_PUBLIC (first_clone->decl) = 0;
-  DECL_COMDAT (first_clone->decl) = 0;
-  VEC_free (ipa_opt_pass, heap,
-            first_clone->ipa_transforms_to_apply);
-  first_clone->ipa_transforms_to_apply = NULL;
-
-#ifdef ENABLE_CHECKING
-  verify_cgraph_node (first_clone);
-#endif
-  return first_clone;
-}
-
 /* Given virtual clone, turn it into actual clone.  */
 static void
 cgraph_materialize_clone (struct cgraph_node *node)
 {
   bitmap_obstack_initialize (NULL);
 /* Given virtual clone, turn it into actual clone.  */
 static void
 cgraph_materialize_clone (struct cgraph_node *node)
 {
   bitmap_obstack_initialize (NULL);
-#ifdef ENABLE_CHECKING
   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;
   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;
-#endif
   /* 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,
   /* 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);
+                           node->clone.args_to_skip, false,
+                           NULL, NULL);
   if (cgraph_dump_file)
     {
       dump_function_to_file (node->clone_of->decl, cgraph_dump_file, dump_flags);
   if (cgraph_dump_file)
     {
       dump_function_to_file (node->clone_of->decl, cgraph_dump_file, dump_flags);
@@ -2341,25 +2470,28 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e)
 {
   tree decl = gimple_call_fndecl (e->call_stmt);
   gimple new_stmt;
 {
   tree decl = gimple_call_fndecl (e->call_stmt);
   gimple new_stmt;
+  gimple_stmt_iterator gsi;
 #ifdef ENABLE_CHECKING
   struct cgraph_node *node;
 #endif
 
 #ifdef ENABLE_CHECKING
   struct cgraph_node *node;
 #endif
 
-  if (!decl || decl == e->callee->decl
-      /* Don't update call from same body alias to the real function.  */
-      || cgraph_get_node (decl) == cgraph_get_node (e->callee->decl))
+  if (e->indirect_unknown_callee
+      || decl == e->callee->decl)
     return e->call_stmt;
 
 #ifdef ENABLE_CHECKING
     return e->call_stmt;
 
 #ifdef ENABLE_CHECKING
-  node = cgraph_get_node (decl);
-  gcc_assert (!node || !node->clone.combined_args_to_skip);
+  if (decl)
+    {
+      node = cgraph_get_node (decl);
+      gcc_assert (!node || !node->clone.combined_args_to_skip);
+    }
 #endif
 
   if (cgraph_dump_file)
     {
       fprintf (cgraph_dump_file, "updating call of %s/%i -> %s/%i: ",
 #endif
 
   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);
+              xstrdup (cgraph_node_name (e->caller)), e->caller->uid,
+              xstrdup (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)
        {
       print_gimple_stmt (cgraph_dump_file, e->call_stmt, 0, dump_flags);
       if (e->callee->clone.combined_args_to_skip)
        {
@@ -2371,24 +2503,36 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e)
 
   if (e->callee->clone.combined_args_to_skip)
     {
 
   if (e->callee->clone.combined_args_to_skip)
     {
-      gimple_stmt_iterator gsi;
+      int lp_nr;
 
       new_stmt
        = gimple_call_copy_skip_args (e->call_stmt,
                                      e->callee->clone.combined_args_to_skip);
 
       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);
 
       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, true);
+      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
     }
   else
-    new_stmt = e->call_stmt;
-
-  gimple_call_set_fndecl (new_stmt, e->callee->decl);
-  update_stmt (new_stmt);
+    {
+      new_stmt = e->call_stmt;
+      gimple_call_set_fndecl (new_stmt, e->callee->decl);
+      update_stmt (new_stmt);
+    }
 
   cgraph_set_call_stmt_including_clones (e->caller, e->call_stmt, new_stmt);
 
 
   cgraph_set_call_stmt_including_clones (e->caller, e->call_stmt, new_stmt);
 
@@ -2432,9 +2576,9 @@ cgraph_materialize_all_clones (void)
                {
                  if (cgraph_dump_file)
                    {
                {
                  if (cgraph_dump_file)
                    {
-                     fprintf (cgraph_dump_file, "clonning %s to %s\n",
-                              cgraph_node_name (node->clone_of),
-                              cgraph_node_name (node));
+                     fprintf (cgraph_dump_file, "cloning %s to %s\n",
+                              xstrdup (cgraph_node_name (node->clone_of)),
+                              xstrdup (cgraph_node_name (node)));
                      if (node->clone.tree_map)
                        {
                          unsigned int i;
                      if (node->clone.tree_map)
                        {
                          unsigned int i;
@@ -2447,9 +2591,11 @@ cgraph_materialize_all_clones (void)
                              replace_info = VEC_index (ipa_replace_map_p,
                                                        node->clone.tree_map,
                                                        i);
                              replace_info = VEC_index (ipa_replace_map_p,
                                                        node->clone.tree_map,
                                                        i);
-                             print_generic_expr (cgraph_dump_file, replace_info->old_tree, 0);
+                             print_generic_expr (cgraph_dump_file,
+                                                 replace_info->old_tree, 0);
                              fprintf (cgraph_dump_file, " -> ");
                              fprintf (cgraph_dump_file, " -> ");
-                             print_generic_expr (cgraph_dump_file, replace_info->new_tree, 0);
+                             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, "%s%s;",
                                       replace_info->replace_p ? "(replace)":"",
                                       replace_info->ref_p ? "(ref)":"");
@@ -2459,12 +2605,15 @@ cgraph_materialize_all_clones (void)
                      if (node->clone.args_to_skip)
                        {
                          fprintf (cgraph_dump_file, "   args_to_skip: ");
                      if (node->clone.args_to_skip)
                        {
                          fprintf (cgraph_dump_file, "   args_to_skip: ");
-                         dump_bitmap (cgraph_dump_file, node->clone.args_to_skip);
+                         dump_bitmap (cgraph_dump_file,
+                                      node->clone.args_to_skip);
                        }
                      if (node->clone.args_to_skip)
                        {
                        }
                      if (node->clone.args_to_skip)
                        {
-                         fprintf (cgraph_dump_file, "   combined_args_to_skip:");
-                         dump_bitmap (cgraph_dump_file, node->clone.combined_args_to_skip);
+                         fprintf (cgraph_dump_file,
+                                  "   combined_args_to_skip:");
+                         dump_bitmap (cgraph_dump_file,
+                                      node->clone.combined_args_to_skip);
                        }
                    }
                  cgraph_materialize_clone (node);
                        }
                    }
                  cgraph_materialize_clone (node);