OSDN Git Service

In libobjc/:
[pf3gnuchains/gcc-fork.git] / gcc / cgraphunit.c
index e7e9e29..024a3bc 100644 (file)
@@ -145,176 +145,11 @@ 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.  */
@@ -352,24 +187,21 @@ 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
+           && !node->local.disregard_inline_limits
            && !DECL_DECLARED_INLINE_P (decl)
-           && !node->origin))
+           && !(DECL_CONTEXT (decl)
+                && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)))
        && !flag_whole_program
        && !flag_lto
        && !flag_whopr)
       && !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;
 }
 
@@ -522,7 +354,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);
@@ -530,7 +361,9 @@ 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))
     cgraph_mark_reachable_node (node);
 
   /* If we've not yet emitted decl, tell the debug info about it.  */
@@ -568,8 +401,44 @@ clone_of_p (struct cgraph_node *node, struct cgraph_node *node2)
 }
 #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.  */
-void
+DEBUG_FUNCTION void
 verify_cgraph_node (struct cgraph_node *node)
 {
   struct cgraph_edge *e;
@@ -633,33 +502,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
@@ -682,6 +526,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");
@@ -878,7 +725,7 @@ verify_cgraph_node (struct cgraph_node *node)
 }
 
 /* Verify whole cgraph structure.  */
-void
+DEBUG_FUNCTION void
 verify_cgraph (void)
 {
   struct cgraph_node *node;
@@ -1142,10 +989,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 ();
 
@@ -1203,8 +1046,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))
@@ -1362,8 +1204,7 @@ 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);
@@ -1382,9 +1223,7 @@ thunk_adjust (gimple_stmt_iterator * bsi,
       vtabletmp3 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp2)),
                                   "vcalloffset");
       stmt = gimple_build_assign (vtabletmp3,
-                                 build1 (INDIRECT_REF,
-                                         TREE_TYPE (vtabletmp3),
-                                         vtabletmp2));
+                                 build_simple_mem_ref (vtabletmp2));
       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
       mark_symbols_for_renaming (stmt);
       find_referenced_vars_in (stmt);
@@ -1521,14 +1360,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 +1377,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);
@@ -1998,80 +1837,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,13 +1870,18 @@ 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;
    unsigned i;
@@ -2121,21 +1896,25 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
    new_version->local.local = true;
    new_version->local.vtable_method = false;
    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;
 
    for (e = old_version->callees; e; e=e->next_callee)
-     cgraph_clone_edge (e, new_version, e->call_stmt,
-                       e->lto_stmt_uid, REG_BR_PROB_BASE,
-                       CGRAPH_FREQ_BASE,
-                       e->loop_nest, true);
+     if (!bbs_to_copy
+        || bitmap_bit_p (bbs_to_copy, gimple_bb (e->call_stmt)->index))
+       cgraph_clone_edge (e, new_version, e->call_stmt,
+                         e->lto_stmt_uid, REG_BR_PROB_BASE,
+                         CGRAPH_FREQ_BASE,
+                         e->loop_nest, true);
    for (e = old_version->indirect_calls; e; e=e->next_callee)
-     cgraph_clone_edge (e, new_version, e->call_stmt,
-                       e->lto_stmt_uid, REG_BR_PROB_BASE,
-                       CGRAPH_FREQ_BASE,
-                       e->loop_nest, true);
-   for (i = 0; VEC_iterate (cgraph_edge_p, redirect_callers, i, e); i++)
+     if (!bbs_to_copy
+        || bitmap_bit_p (bbs_to_copy, gimple_bb (e->call_stmt)->index))
+       cgraph_clone_edge (e, new_version, e->call_stmt,
+                         e->lto_stmt_uid, REG_BR_PROB_BASE,
+                         CGRAPH_FREQ_BASE,
+                         e->loop_nest, true);
+   FOR_EACH_VEC_ELT (cgraph_edge_p, redirect_callers, i, e)
      {
        /* Redirect calls to the old version node to point to its new
          version.  */
@@ -2157,14 +1936,18 @@ 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 bbs_to_copy,
+                           basic_block new_entry_block,
                            const char *clone_name)
 {
   tree old_decl = old_version_node->decl;
@@ -2181,14 +1964,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
@@ -2259,7 +2048,8 @@ save_inline_function_body (struct cgraph_node *node)
       }
 
   /* Copy the OLD_VERSION_NODE function tree to the new version.  */
-  tree_function_versioning (node->decl, first_clone->decl, NULL, true, NULL);
+  tree_function_versioning (node->decl, first_clone->decl, NULL, true, NULL,
+                           NULL, NULL);
 
   DECL_EXTERNAL (first_clone->decl) = 0;
   DECL_COMDAT_GROUP (first_clone->decl) = NULL_TREE;
@@ -2288,7 +2078,7 @@ cgraph_materialize_clone (struct cgraph_node *node)
   /* 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);
@@ -2305,7 +2095,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);
 }
@@ -2318,14 +2112,23 @@ 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
+  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;
 
-  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)
     {
@@ -2334,29 +2137,34 @@ 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);
+    {
+      gimple_stmt_iterator gsi;
+
+      new_stmt
+       = gimple_call_copy_skip_args (e->call_stmt,
+                                     e->callee->clone.combined_args_to_skip);
+
+      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);
+    }
   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);
+  gimple_call_set_fndecl (new_stmt, e->callee->decl);
   update_stmt (new_stmt);
 
-  /* Update EH information too, just in case.  */
-  maybe_clean_or_replace_eh_stmt (e->call_stmt, new_stmt);
-
   cgraph_set_call_stmt_including_clones (e->caller, e->call_stmt, new_stmt);
 
   if (cgraph_dump_file)