OSDN Git Service

Always dereference nil receiver passed to value method.
[pf3gnuchains/gcc-fork.git] / gcc / cgraphunit.c
index c778876..2dedcc5 100644 (file)
@@ -1,6 +1,6 @@
 /* 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 Free Software Foundation, Inc.
    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.
 
-      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)
@@ -138,183 +138,20 @@ along with GCC; see the file COPYING3.  If not see
 #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_analyze_function (struct cgraph_node *);
 
-static 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;
+FILE *cgraph_dump_file;
 
 /* 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.  */
@@ -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)
+      && (!node->thunk.thunk_p && !node->same_body_alias)
       && 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)
-      && !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
@@ -352,24 +190,20 @@ cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl)
 
      When not optimizing, also output the static functions. (see
      PR24561), but don't do so for always_inline functions, functions
-     declared inline and nested functions.  These was optimized out
+     declared inline and nested functions.  These were optimized out
      in the original implementation and it is unclear whether we want
      to change the behavior here.  */
   if (((TREE_PUBLIC (decl)
-       || (!optimize && !node->local.disregard_inline_limits
+       || (!optimize
+           && !DECL_DISREGARD_INLINE_LIMITS (decl)
            && !DECL_DECLARED_INLINE_P (decl)
-           && !node->origin))
+           && !(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;
 
-  /* Constructors and destructors are reachable from the runtime by
-     some mechanism.  */
-  if (DECL_STATIC_CONSTRUCTOR (decl) || DECL_STATIC_DESTRUCTOR (decl))
-    return true;
-
   return false;
 }
 
@@ -402,6 +236,7 @@ cgraph_process_new_functions (void)
          cgraph_finalize_function (fndecl, false);
          cgraph_mark_reachable_node (node);
          output = true;
+          cgraph_call_function_insertion_hooks (node);
          break;
 
        case CGRAPH_STATE_IPA:
@@ -415,23 +250,26 @@ cgraph_process_new_functions (void)
            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);
+         else
+           compute_inline_parameters (node, true);
          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;
+          cgraph_call_function_insertion_hooks (node);
          cgraph_expand_function (node);
          break;
 
@@ -439,7 +277,6 @@ cgraph_process_new_functions (void)
          gcc_unreachable ();
          break;
        }
-      cgraph_call_function_insertion_hooks (node);
       varpool_analyze_pending_decls ();
     }
   return output;
@@ -470,24 +307,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;
-  node->local.redefined_extern_inline = true;
   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
@@ -512,17 +334,17 @@ cgraph_lower_function (struct cgraph_node *node)
 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)
-    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;
-  node->finalized_by_frontend = true;
-  record_cdtor_fn (node->decl);
 
   if (cgraph_decide_is_function_needed (node, decl))
     cgraph_mark_needed_node (node);
@@ -530,7 +352,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.  */
-  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.  */
@@ -552,39 +383,115 @@ cgraph_finalize_function (tree decl, bool nested)
 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);
 }
 
-#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)
 {
+  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;
 }
-#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
+             && !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)
+      /* IPA-CP sometimes redirect edge to clone and then back to the former
+        function.  This ping-pong has to go, eventaully.  */
+      && (node != cgraph_function_or_thunk_node (e->callee, NULL))
+      && !clone_of_p (node, e->callee))
+    return true;
+  else
+    return false;
+}
 
 /* Verify cgraph nodes of given cgraph node.  */
-void
+DEBUG_FUNCTION void
 verify_cgraph_node (struct cgraph_node *node)
 {
   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;
 
-  if (errorcount || sorrycount)
+  if (seen_error ())
     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)
       {
@@ -595,22 +502,22 @@ verify_cgraph_node (struct cgraph_node *node)
       }
   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 ("Externally visible inline clone");
+      error ("externally visible inline clone");
       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 ("Inline clone is needed");
+      error ("inline clone is needed");
       error_found = true;
     }
   for (e = node->indirect_calls; e; e = e->next_callee)
@@ -627,39 +534,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)));
-         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)
     {
-      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
@@ -682,6 +564,9 @@ verify_cgraph_node (struct cgraph_node *node)
            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");
@@ -693,7 +578,7 @@ verify_cgraph_node (struct cgraph_node *node)
       error_found = true;
     }
 
-  if (!cgraph_node (node->decl))
+  if (!cgraph_get_node (node->decl))
     {
       error ("node not found in cgraph_hash");
       error_found = true;
@@ -765,10 +650,58 @@ verify_cgraph_node (struct cgraph_node *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 refernece");
+           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)
        {
@@ -792,25 +725,12 @@ verify_cgraph_node (struct cgraph_node *node)
                        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)
                          {
-                           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);
@@ -818,7 +738,6 @@ verify_cgraph_node (struct cgraph_node *node)
                                debug_tree (decl);
                                error_found = true;
                              }
-#endif
                          }
                        else if (decl)
                          {
@@ -826,14 +745,14 @@ verify_cgraph_node (struct cgraph_node *node)
                                   "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:");
-                       debug_gimple_stmt (stmt);
+                       cgraph_debug_gimple_stmt (this_cfun, stmt);
                        error_found = true;
                      }
                  }
@@ -851,7 +770,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)));
-             debug_gimple_stmt (e->call_stmt);
+             cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
              error_found = true;
            }
          e->aux = 0;
@@ -862,7 +781,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)));
-             debug_gimple_stmt (e->call_stmt);
+             cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
              error_found = true;
            }
          e->aux = 0;
@@ -873,17 +792,16 @@ verify_cgraph_node (struct cgraph_node *node)
       dump_cgraph_node (stderr, node);
       internal_error ("verify_cgraph_node failed");
     }
-  set_cfun (saved_cfun);
   timevar_pop (TV_CGRAPH_VERIFY);
 }
 
 /* Verify whole cgraph structure.  */
-void
+DEBUG_FUNCTION void
 verify_cgraph (void)
 {
   struct cgraph_node *node;
 
-  if (sorrycount || errorcount)
+  if (seen_error ())
     return;
 
   for (node = cgraph_nodes; node; node = node->next)
@@ -897,7 +815,7 @@ cgraph_output_pending_asms (void)
 {
   struct cgraph_asm_node *can;
 
-  if (errorcount || sorrycount)
+  if (seen_error ())
     return;
 
   for (can = cgraph_asm_nodes; can; can = can->next)
@@ -906,32 +824,124 @@ cgraph_output_pending_asms (void)
 }
 
 /* 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;
 
-  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);
+      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;
 
-  pop_cfun ();
   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.
 
@@ -968,7 +978,14 @@ process_function_and_variable_attributes (struct cgraph_node *first,
       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,
@@ -977,6 +994,25 @@ process_function_and_variable_attributes (struct cgraph_node *first,
          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)
     {
@@ -987,7 +1023,14 @@ process_function_and_variable_attributes (struct cgraph_node *first,
          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,
@@ -996,6 +1039,18 @@ process_function_and_variable_attributes (struct cgraph_node *first,
          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);
     }
 }
 
@@ -1014,6 +1069,7 @@ cgraph_analyze_functions (void)
   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;
@@ -1045,9 +1101,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  */
-      if (!DECL_STRUCT_FUNCTION (decl))
+      if (!DECL_STRUCT_FUNCTION (decl)
+         && (!node->alias || !node->thunk.alias)
+         && !node->thunk.thunk_p)
        {
          cgraph_reset_node (node);
+          node->local.redefined_extern_inline = true;
          continue;
        }
 
@@ -1057,6 +1116,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->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)
        {
@@ -1068,10 +1130,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
-         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))
        {
-         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;
        }
 
@@ -1094,6 +1158,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);
+      dump_varpool (cgraph_dump_file);
     }
 
   if (cgraph_dump_file)
@@ -1104,10 +1169,14 @@ cgraph_analyze_functions (void)
       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);
 
-      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));
@@ -1116,18 +1185,80 @@ cgraph_analyze_functions (void)
        }
       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);
+      dump_varpool (cgraph_dump_file);
     }
+  bitmap_obstack_release (NULL);
   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
+          && !lookup_attribute ("weakref", DECL_ATTRIBUTES (p->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) = 0;
+         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
+              && !lookup_attribute ("weakref", DECL_ATTRIBUTES (p->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) = 0;
+         varpool_create_variable_alias (p->decl, target_vnode->decl);
+         VEC_unordered_remove (alias_pair, alias_pairs, i);
+       }
+      else
+       {
+         if (dump_file)
+           fprintf (dump_file, "Unhandled alias %s->%s\n",
+                    IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (p->decl)),
+                    IDENTIFIER_POINTER (p->target));
+
+         i++;
+       }
+    }
+}
+
 
 /* Analyze the whole compilation unit once it is parsed completely.  */
 
@@ -1136,18 +1267,24 @@ cgraph_finalize_compilation_unit (void)
 {
   timevar_push (TV_CGRAPH);
 
+  /* If LTO is enabled, initialize the streamer hooks needed by GIMPLE.  */
+  if (flag_lto)
+    lto_streamer_hooks_init ();
+
+  /* If we're here there's no current function anymore.  Some frontends
+     are lazy in clearing these.  */
+  current_function_decl = NULL;
+  set_cfun (NULL);
+
   /* Do not skip analyzing the functions if there were errors, we
      miss diagnostics for following functions otherwise.  */
 
   /* Emit size functions we didn't inline.  */
   finalize_size_functions ();
 
-  /* Call functions declared with the "constructor" or "destructor"
-     attribute.  */
-  cgraph_build_cdtor_fns ();
-
   /* Mark alias targets necessary and emit diagnostics.  */
   finish_aliases_1 ();
+  handle_alias_pairs ();
 
   if (!quiet_flag)
     {
@@ -1155,12 +1292,16 @@ cgraph_finalize_compilation_unit (void)
       fflush (stderr);
     }
 
+  if (flag_dump_passes)
+    dump_passes ();
+
   /* Gimplify and lower all functions, compute reachability and
      remove unreachable nodes.  */
   cgraph_analyze_functions ();
 
   /* Mark alias targets necessary and emit diagnostics.  */
   finish_aliases_1 ();
+  handle_alias_pairs ();
 
   /* Gimplify and lower thunks.  */
   cgraph_analyze_functions ();
@@ -1202,10 +1343,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
+         && !node->thunk.thunk_p
+         && !node->alias
          && !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))
        {
@@ -1216,7 +1359,8 @@ cgraph_mark_functions_to_output (void)
              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)
@@ -1235,6 +1379,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
+             && !node->alias
              && !DECL_EXTERNAL (decl))
            {
              dump_cgraph_node (stderr, node);
@@ -1264,7 +1409,7 @@ cgraph_mark_functions_to_output (void)
              && !DECL_EXTERNAL (decl))
            {
              dump_cgraph_node (stderr, node);
-             internal_error ("failed to reclaim unneeded function");
+             internal_error ("failed to reclaim unneeded functionin same comdat group");
            }
        }
 #endif
@@ -1293,7 +1438,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 |
-     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);
@@ -1319,11 +1464,10 @@ thunk_adjust (gimple_stmt_iterator * bsi,
   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);
     }
 
@@ -1334,7 +1478,6 @@ thunk_adjust (gimple_stmt_iterator * bsi,
       tree vtabletmp;
       tree vtabletmp2;
       tree vtabletmp3;
-      tree offsettmp;
 
       if (!vtable_entry_type)
        {
@@ -1362,44 +1505,31 @@ thunk_adjust (gimple_stmt_iterator * bsi,
       vtabletmp2 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp)),
                                   "vtableaddr");
       stmt = gimple_build_assign (vtabletmp2,
-                                 build1 (INDIRECT_REF,
-                                         TREE_TYPE (vtabletmp2), vtabletmp));
+                                 build_simple_mem_ref (vtabletmp));
       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
       mark_symbols_for_renaming (stmt);
       find_referenced_vars_in (stmt);
 
       /* Find the entry with the vcall offset.  */
       stmt = gimple_build_assign (vtabletmp2,
-                                 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.  */
       vtabletmp3 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp2)),
                                   "vcalloffset");
       stmt = gimple_build_assign (vtabletmp3,
-                                 build1 (INDIRECT_REF,
-                                         TREE_TYPE (vtabletmp3),
-                                         vtabletmp2));
-      gsi_insert_after (bsi, stmt, GSI_NEW_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));
+                                 build_simple_mem_ref (vtabletmp2));
       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
       mark_symbols_for_renaming (stmt);
       find_referenced_vars_in (stmt);
 
       /* Adjust the `this' pointer.  */
-      ptr = fold_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
@@ -1418,9 +1548,8 @@ thunk_adjust (gimple_stmt_iterator * bsi,
          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.  */
@@ -1448,16 +1577,20 @@ assemble_thunk (struct cgraph_node *node)
 
   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;
+      tree restype = TREE_TYPE (TREE_TYPE (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
@@ -1477,6 +1610,8 @@ assemble_thunk (struct cgraph_node *node)
       free_after_compilation (cfun);
       set_cfun (NULL);
       TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+      node->thunk.thunk_p = false;
+      node->analyzed = false;
     }
   else
     {
@@ -1521,14 +1656,14 @@ assemble_thunk (struct cgraph_node *node)
          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");
        }
 
-      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)
@@ -1538,7 +1673,7 @@ assemble_thunk (struct cgraph_node *node)
                                      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);
@@ -1568,8 +1703,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,
-                                       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);
@@ -1586,8 +1720,8 @@ assemble_thunk (struct cgraph_node *node)
            {
              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);
            }
@@ -1602,15 +1736,54 @@ assemble_thunk (struct cgraph_node *node)
       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.  */
+      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;
 }
 
+
+
+/* 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
@@ -1623,7 +1796,7 @@ cgraph_expand_function (struct cgraph_node *node)
 
   announce_function (decl);
   node->process = 0;
-
+  assemble_thunks_and_aliases (node);
   gcc_assert (node->lowered);
 
   /* Generate RTL for the body of DECL.  */
@@ -1632,27 +1805,7 @@ 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;
-  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));
   cgraph_release_function_body (node);
   /* Eliminate all call edges.  This is important so the GIMPLE_CALL no longer
      points to the dead function body.  */
@@ -1690,7 +1843,7 @@ cgraph_expand_all_functions (void)
   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
@@ -1759,7 +1912,7 @@ cgraph_output_in_order (void)
 
   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);
@@ -1795,6 +1948,10 @@ cgraph_output_in_order (void)
   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)
        {
@@ -1826,13 +1983,12 @@ cgraph_output_in_order (void)
 /* 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 (!node->alias && !node->thunk.thunk_p);
+
   /* Look if there is any clone around.  */
-  node = cgraph_node (decl);
   if (node->clones)
     return true;
   return false;
@@ -1849,7 +2005,11 @@ ipa_passes (void)
   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;
+    }
 
   /* If pass_all_early_optimizations was not scheduled, the state of
      the cgraph will not be properly updated.  Update it now.  */
@@ -1895,7 +2055,7 @@ ipa_passes (void)
 void
 cgraph_optimize (void)
 {
-  if (errorcount || sorrycount)
+  if (seen_error ())
     return;
 
 #ifdef ENABLE_CHECKING
@@ -1917,11 +2077,11 @@ cgraph_optimize (void)
   cgraph_state = CGRAPH_STATE_IPA;
 
   /* Don't run the IPA passes if there was any error or sorry messages.  */
-  if (errorcount == 0 && sorrycount == 0)
+  if (!seen_error ())
     ipa_passes ();
 
   /* Do nothing else if any IPA pass found errors.  */
-  if (errorcount || sorrycount)
+  if (seen_error ())
     {
       timevar_pop (TV_CGRAPHOPT);
       return;
@@ -1953,6 +2113,13 @@ cgraph_optimize (void)
 #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_state = CGRAPH_STATE_EXPANSION;
@@ -1974,12 +2141,13 @@ cgraph_optimize (void)
     {
       fprintf (cgraph_dump_file, "\nFinal ");
       dump_cgraph (cgraph_dump_file);
+      dump_varpool (cgraph_dump_file);
     }
 #ifdef ENABLE_CHECKING
   verify_cgraph ();
   /* Double check that all inline clones are gone and that all
      function bodies have been released from memory.  */
-  if (!(sorrycount || errorcount))
+  if (!seen_error ())
     {
       struct cgraph_node *node;
       bool error_found = false;
@@ -1998,80 +2166,11 @@ cgraph_optimize (void)
 #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)
 {
-  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
@@ -2100,53 +2199,50 @@ update_call_expr (struct cgraph_node *new_version)
    edges which should be redirected to point to
    NEW_VERSION.  ALL the callees edges of OLD_VERSION
    are cloned to the new version node.  Return the new
-   version node.  */
+   version node. 
+
+   If non-NULL BLOCK_TO_COPY determine what basic blocks 
+   was copied to prevent duplications of calls that are dead
+   in the clone.  */
 
 static struct cgraph_node *
 cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
                                 tree new_decl,
-                                VEC(cgraph_edge_p,heap) *redirect_callers)
+                                VEC(cgraph_edge_p,heap) *redirect_callers,
+                                bitmap bbs_to_copy)
  {
    struct cgraph_node *new_version;
    struct cgraph_edge *e;
-   struct cgraph_edge *next_callee;
    unsigned i;
 
    gcc_assert (old_version);
 
-   new_version = cgraph_node (new_decl);
+   new_version = cgraph_create_node (new_decl);
 
    new_version->analyzed = true;
    new_version->local = old_version->local;
+   new_version->local.externally_visible = false;
+   new_version->local.local = true;
    new_version->global = old_version->global;
-   new_version->rtl = new_version->rtl;
+   new_version->rtl = old_version->rtl;
    new_version->reachable = true;
    new_version->count = old_version->count;
 
-   /* Clone the old node callees.  Recursive calls are
-      also cloned.  */
-   for (e = old_version->callees;e; e=e->next_callee)
-     {
+   for (e = old_version->callees; e; e=e->next_callee)
+     if (!bbs_to_copy
+        || bitmap_bit_p (bbs_to_copy, gimple_bb (e->call_stmt)->index))
        cgraph_clone_edge (e, new_version, e->call_stmt,
                          e->lto_stmt_uid, REG_BR_PROB_BASE,
                          CGRAPH_FREQ_BASE,
-                         e->loop_nest, true);
-     }
-   /* Fix recursive calls.
-      If OLD_VERSION has a recursive call after the
-      previous edge cloning, the new version will have an edge
-      pointing to the old version, which is wrong;
-      Redirect it to point to the new version. */
-   for (e = new_version->callees ; e; e = next_callee)
-     {
-       next_callee = e->next_callee;
-       if (e->callee == old_version)
-        cgraph_redirect_edge_callee (e, new_version);
-
-       if (!next_callee)
-        break;
-     }
-   for (i = 0; VEC_iterate (cgraph_edge_p, redirect_callers, i, e); i++)
+                         true);
+   for (e = old_version->indirect_calls; e; e=e->next_callee)
+     if (!bbs_to_copy
+        || bitmap_bit_p (bbs_to_copy, gimple_bb (e->call_stmt)->index))
+       cgraph_clone_edge (e, new_version, e->call_stmt,
+                         e->lto_stmt_uid, REG_BR_PROB_BASE,
+                         CGRAPH_FREQ_BASE,
+                         true);
+   FOR_EACH_VEC_ELT (cgraph_edge_p, redirect_callers, i, e)
      {
        /* Redirect calls to the old version node to point to its new
          version.  */
@@ -2168,14 +2264,19 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
     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.
-    ARGS_TO_SKIP lists arguments to be omitted from functions
-    */
+    If non-NULL ARGS_TO_SKIP determine function parameters to remove
+    from new version.
+    If non-NULL BLOCK_TO_COPY determine what basic blocks to copy.
+    If non_NULL NEW_ENTRY determine new entry BB of the clone.  */
 
 struct cgraph_node *
 cgraph_function_versioning (struct cgraph_node *old_version_node,
                            VEC(cgraph_edge_p,heap) *redirect_callers,
                            VEC (ipa_replace_map_p,gc)* tree_map,
-                           bitmap args_to_skip)
+                           bitmap args_to_skip,
+                           bitmap bbs_to_copy,
+                           basic_block new_entry_block,
+                           const char *clone_name)
 {
   tree old_decl = old_version_node->decl;
   struct cgraph_node *new_version_node = NULL;
@@ -2184,6 +2285,8 @@ cgraph_function_versioning (struct cgraph_node *old_version_node,
   if (!tree_versionable_function_p (old_decl))
     return NULL;
 
+  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)
@@ -2191,14 +2294,20 @@ cgraph_function_versioning (struct cgraph_node *old_version_node,
   else
     new_decl = build_function_decl_skip_args (old_decl, args_to_skip);
 
+  /* Generate a new name for the new version. */
+  DECL_NAME (new_decl) = clone_function_name (old_decl, clone_name);
+  SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
+  SET_DECL_RTL (new_decl, NULL);
+
   /* Create the new version's call-graph node.
      and update the edges of the new node. */
   new_version_node =
     cgraph_copy_node_for_versioning (old_version_node, new_decl,
-                                    redirect_callers);
+                                    redirect_callers, bbs_to_copy);
 
   /* Copy the OLD_VERSION_NODE function tree to the new version.  */
-  tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip);
+  tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip,
+                           bbs_to_copy, new_entry_block);
 
   /* Update the new version's properties.
      Make The new version visible only within this translation unit.  Make sure
@@ -2218,87 +2327,18 @@ cgraph_function_versioning (struct cgraph_node *old_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);
-
-  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);
-#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;
-#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,
-                           node->clone.args_to_skip);
+                           node->clone.args_to_skip, NULL, NULL);
   if (cgraph_dump_file)
     {
       dump_function_to_file (node->clone_of->decl, cgraph_dump_file, dump_flags);
@@ -2315,7 +2355,11 @@ cgraph_materialize_clone (struct cgraph_node *node)
   node->next_sibling_clone = NULL;
   node->prev_sibling_clone = NULL;
   if (!node->clone_of->analyzed && !node->clone_of->clones)
-    cgraph_remove_node (node->clone_of);
+    {
+      cgraph_release_function_body (node->clone_of);
+      cgraph_node_remove_callees (node->clone_of);
+      ipa_remove_all_references (&node->clone_of->ref_list);
+    }
   node->clone_of = NULL;
   bitmap_obstack_release (NULL);
 }
@@ -2329,13 +2373,21 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e)
   tree decl = gimple_call_fndecl (e->call_stmt);
   gimple new_stmt;
   gimple_stmt_iterator gsi;
+#ifdef ENABLE_CHECKING
+  struct cgraph_node *node;
+#endif
 
-  if (!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;
 
-  gcc_assert (!cgraph_node (decl)->clone.combined_args_to_skip);
+#ifdef ENABLE_CHECKING
+  if (decl)
+    {
+      node = cgraph_get_node (decl);
+      gcc_assert (!node || !node->clone.combined_args_to_skip);
+    }
+#endif
 
   if (cgraph_dump_file)
     {
@@ -2344,28 +2396,45 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e)
               cgraph_node_name (e->callee), e->callee->uid);
       print_gimple_stmt (cgraph_dump_file, e->call_stmt, 0, dump_flags);
       if (e->callee->clone.combined_args_to_skip)
-        {
-          fprintf (cgraph_dump_file, " combined args to skip: ");
-          dump_bitmap (cgraph_dump_file, e->callee->clone.combined_args_to_skip);
+       {
+         fprintf (cgraph_dump_file, " combined args to skip: ");
+         dump_bitmap (cgraph_dump_file,
+                      e->callee->clone.combined_args_to_skip);
        }
     }
 
   if (e->callee->clone.combined_args_to_skip)
-    new_stmt = gimple_call_copy_skip_args (e->call_stmt,
-                                      e->callee->clone.combined_args_to_skip);
+    {
+      int lp_nr;
+
+      new_stmt
+       = gimple_call_copy_skip_args (e->call_stmt,
+                                     e->callee->clone.combined_args_to_skip);
+      gimple_call_set_fndecl (new_stmt, e->callee->decl);
+
+      if (gimple_vdef (new_stmt)
+         && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
+       SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
+
+      gsi = gsi_for_stmt (e->call_stmt);
+      gsi_replace (&gsi, new_stmt, false);
+      /* We need to defer cleaning EH info on the new statement to
+         fixup-cfg.  We may not have dominator information at this point
+        and thus would end up with unreachable blocks and have no way
+        to communicate that we need to run CFG cleanup then.  */
+      lp_nr = lookup_stmt_eh_lp (e->call_stmt);
+      if (lp_nr != 0)
+       {
+         remove_stmt_from_eh_lp (e->call_stmt);
+         add_stmt_to_eh_lp (new_stmt, lp_nr);
+       }
+    }
   else
-    new_stmt = e->call_stmt;
-  if (gimple_vdef (new_stmt)
-      && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
-    SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
-  gimple_call_set_fndecl (new_stmt, e->callee->decl);
-
-  gsi = gsi_for_stmt (e->call_stmt);
-  gsi_replace (&gsi, new_stmt, true);
-  update_stmt (new_stmt);
-
-  /* Update EH information too, just in case.  */
-  maybe_clean_or_replace_eh_stmt (e->call_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);
 
@@ -2409,7 +2478,7 @@ cgraph_materialize_all_clones (void)
                {
                  if (cgraph_dump_file)
                    {
-                     fprintf (cgraph_dump_file, "clonning %s to %s\n",
+                     fprintf (cgraph_dump_file, "cloning %s to %s\n",
                               cgraph_node_name (node->clone_of),
                               cgraph_node_name (node));
                      if (node->clone.tree_map)