OSDN Git Service

2010-11-25 Basile Starynkevitch <basile@starynkevitch.net>
[pf3gnuchains/gcc-fork.git] / gcc / cgraphunit.c
index 5a69afa..c7c5665 100644 (file)
@@ -147,174 +147,9 @@ static void cgraph_analyze_function (struct cgraph_node *);
 
 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;
 
-/* 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.  */
@@ -362,16 +197,10 @@ cgraph_decide_is_function_needed (struct cgraph_node *node, tree 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;
 
-  /* Constructors and destructors are reachable from the runtime by
-     some mechanism.  */
-  if (DECL_STATIC_CONSTRUCTOR (decl) || DECL_STATIC_DESTRUCTOR (decl))
-    return true;
-
   return false;
 }
 
@@ -524,7 +353,6 @@ cgraph_finalize_function (tree decl, bool nested)
   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);
@@ -532,7 +360,15 @@ 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 compilatoin 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) && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl))))
     cgraph_mark_reachable_node (node);
 
   /* If we've not yet emitted decl, tell the debug info about it.  */
@@ -559,7 +395,6 @@ cgraph_mark_if_needed (tree 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)
@@ -568,7 +403,42 @@ clone_of_p (struct cgraph_node *node, struct cgraph_node *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
+      && (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;
+    }
+  return error_found;
+}
 
 /* Verify cgraph nodes of given cgraph node.  */
 DEBUG_FUNCTION void
@@ -597,22 +467,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)
@@ -635,33 +505,8 @@ verify_cgraph_node (struct cgraph_node *node)
     }
   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
@@ -684,6 +529,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");
@@ -805,7 +653,6 @@ verify_cgraph_node (struct cgraph_node *node)
                                debug_tree (e->callee->decl);
                                error_found = true;
                              }
-#ifdef ENABLE_CHECKING
                            else if (!e->callee->global.inlined_to
                                     && decl
                                     && cgraph_get_node (decl)
@@ -820,7 +667,6 @@ verify_cgraph_node (struct cgraph_node *node)
                                debug_tree (decl);
                                error_found = true;
                              }
-#endif
                          }
                        else if (decl)
                          {
@@ -970,7 +816,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,
@@ -989,7 +842,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 (node->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,
@@ -1016,6 +876,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;
@@ -1096,6 +957,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)
@@ -1125,7 +987,9 @@ cgraph_analyze_functions (void)
     {
       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 ();
 }
@@ -1144,10 +1008,6 @@ cgraph_finalize_compilation_unit (void)
   /* 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 ();
 
@@ -1205,8 +1065,7 @@ cgraph_mark_functions_to_output (void)
         outside the current compilation unit.  */
       if (node->analyzed
          && !node->global.inlined_to
-         && (node->needed || node->reachable_from_other_partition
-             || node->address_taken
+         && (!cgraph_only_called_directly_p (node)
              || (e && node->reachable))
          && !TREE_ASM_WRITTEN (decl)
          && !DECL_EXTERNAL (decl))
@@ -1520,14 +1379,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)
@@ -1537,7 +1396,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);
@@ -1567,8 +1426,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);
@@ -1585,8 +1443,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);
            }
@@ -1622,15 +1480,6 @@ cgraph_expand_function (struct cgraph_node *node)
 
   announce_function (decl);
   node->process = 0;
-
-  gcc_assert (node->lowered);
-
-  /* Generate RTL for the body of DECL.  */
-  tree_rest_of_compilation (decl);
-
-  /* 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;
@@ -1650,7 +1499,17 @@ cgraph_expand_function (struct cgraph_node *node)
            assemble_thunk (alias);
        }
       node->alias = saved_alias;
+      cgraph_process_new_functions ();
     }
+
+  gcc_assert (node->lowered);
+
+  /* Generate RTL for the body of DECL.  */
+  tree_rest_of_compilation (decl);
+
+  /* Make sure that BE didn't give up on compiling.  */
+  gcc_assert (TREE_ASM_WRITTEN (decl));
+  current_function_decl = NULL;
   gcc_assert (!cgraph_preserve_function_body_p (decl));
   cgraph_release_function_body (node);
   /* Eliminate all call edges.  This is important so the GIMPLE_CALL no longer
@@ -1973,6 +1832,7 @@ cgraph_optimize (void)
     {
       fprintf (cgraph_dump_file, "\nFinal ");
       dump_cgraph (cgraph_dump_file);
+      dump_varpool (cgraph_dump_file);
     }
 #ifdef ENABLE_CHECKING
   verify_cgraph ();
@@ -1997,80 +1857,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
@@ -2143,7 +1934,7 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
                          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++)
+   FOR_EACH_VEC_ELT (cgraph_edge_p, redirect_callers, i, e)
      {
        /* Redirect calls to the old version node to point to its new
          version.  */
@@ -2299,11 +2090,9 @@ 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,
@@ -2345,14 +2134,18 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e)
   struct cgraph_node *node;
 #endif
 
-  if (!decl || decl == e->callee->decl
+  if (e->indirect_unknown_callee
+      || 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))
+      || (decl && cgraph_get_node (decl) == cgraph_get_node (e->callee->decl)))
     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)
@@ -2372,23 +2165,36 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e)
   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);
+      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, 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
-    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);