re PR sanitizer/88901 (ICE when using -fsanitize=pointer-compare)
[gcc.git] / gcc / cp / optimize.c
index c4ee8484bb95ab7197c0e6c69b59300e7e2e24c3..40d0dcc39956653af395ed359752cf420adb42aa 100644 (file)
@@ -1,5 +1,5 @@
 /* Perform optimizations on tree structure.
-   Copyright (C) 1998-2013 Free Software Foundation, Inc.
+   Copyright (C) 1998-2019 Free Software Foundation, Inc.
    Written by Mark Michell (mark@codesourcery.com).
 
 This file is part of GCC.
@@ -21,22 +21,13 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tm.h"
-#include "tree.h"
-#include "cp-tree.h"
-#include "input.h"
-#include "params.h"
-#include "hashtab.h"
 #include "target.h"
+#include "cp-tree.h"
+#include "stringpool.h"
+#include "cgraph.h"
 #include "debug.h"
 #include "tree-inline.h"
-#include "flags.h"
-#include "langhooks.h"
-#include "diagnostic-core.h"
-#include "dumpfile.h"
-#include "gimple.h"
 #include "tree-iterator.h"
-#include "cgraph.h"
 
 /* Prototypes.  */
 
@@ -55,6 +46,8 @@ update_cloned_parm (tree parm, tree cloned_parm, bool first)
   /* We may have taken its address.  */
   TREE_ADDRESSABLE (cloned_parm) = TREE_ADDRESSABLE (parm);
 
+  DECL_BY_REFERENCE (cloned_parm) = DECL_BY_REFERENCE (parm);
+
   /* The definition might have different constness.  */
   TREE_READONLY (cloned_parm) = TREE_READONLY (parm);
 
@@ -85,7 +78,7 @@ clone_body (tree clone, tree fn, void *arg_map)
   id.src_fn = fn;
   id.dst_fn = clone;
   id.src_cfun = DECL_STRUCT_FUNCTION (fn);
-  id.decl_map = (struct pointer_map_t *) arg_map;
+  id.decl_map = static_cast<hash_map<tree, tree> *> (arg_map);
 
   id.copy_decl = copy_decl_no_change;
   id.transform_call_graph_edges = CB_CGE_DUPLICATE;
@@ -121,28 +114,37 @@ clone_body (tree clone, tree fn, void *arg_map)
 static void
 build_delete_destructor_body (tree delete_dtor, tree complete_dtor)
 {
-  tree call_dtor, call_delete;
   tree parm = DECL_ARGUMENTS (delete_dtor);
   tree virtual_size = cxx_sizeof (current_class_type);
 
-  /* Call the corresponding complete destructor.  */
-  gcc_assert (complete_dtor);
-  call_dtor = build_cxx_call (complete_dtor, 1, &parm,
-                             tf_warning_or_error);
-  add_stmt (call_dtor);
-
-  add_stmt (build_stmt (0, LABEL_EXPR, cdtor_label));
-
   /* Call the delete function.  */
-  call_delete = build_op_delete_call (DELETE_EXPR, current_class_ptr,
-                                      virtual_size,
-                                      /*global_p=*/false,
-                                      /*placement=*/NULL_TREE,
-                                      /*alloc_fn=*/NULL_TREE,
-                                     tf_warning_or_error);
-  add_stmt (call_delete);
-
-  /* Return the address of the object.  */
+  tree call_delete = build_op_delete_call (DELETE_EXPR, current_class_ptr,
+                                          virtual_size,
+                                          /*global_p=*/false,
+                                          /*placement=*/NULL_TREE,
+                                          /*alloc_fn=*/NULL_TREE,
+                                          tf_warning_or_error);
+
+  tree op = get_callee_fndecl (call_delete);
+  if (op && DECL_P (op) && destroying_delete_p (op))
+    {
+      /* The destroying delete will handle calling complete_dtor.  */
+      add_stmt (call_delete);
+    }
+  else
+    {
+      /* Call the corresponding complete destructor.  */
+      gcc_assert (complete_dtor);
+      tree call_dtor = build_cxx_call (complete_dtor, 1, &parm,
+                                      tf_warning_or_error);
+
+      /* Operator delete must be called, whether or not the dtor throws.  */
+      add_stmt (build2 (TRY_FINALLY_EXPR, void_type_node,
+                       call_dtor, call_delete));
+    }
+
+  /* Return the address of the object.
+     ??? How is it useful to return an invalid address?  */
   if (targetm.cxx.cdtor_returns_this ())
     {
       tree val = DECL_ARGUMENTS (delete_dtor);
@@ -159,16 +161,12 @@ build_delete_destructor_body (tree delete_dtor, tree complete_dtor)
 static tree
 cdtor_comdat_group (tree complete, tree base)
 {
-  tree complete_name = DECL_COMDAT_GROUP (complete);
-  tree base_name = DECL_COMDAT_GROUP (base);
+  tree complete_name = DECL_ASSEMBLER_NAME (complete);
+  tree base_name = DECL_ASSEMBLER_NAME (base);
   char *grp_name;
   const char *p, *q;
   bool diff_seen = false;
   size_t idx;
-  if (complete_name == NULL)
-    complete_name = cxx_comdat_group (complete);
-  if (base_name == NULL)
-    base_name = cxx_comdat_group (base);
   gcc_assert (IDENTIFIER_LENGTH (complete_name)
              == IDENTIFIER_LENGTH (base_name));
   grp_name = XALLOCAVEC (char, IDENTIFIER_LENGTH (complete_name) + 1);
@@ -181,7 +179,8 @@ cdtor_comdat_group (tree complete, tree base)
       {
        gcc_assert (!diff_seen
                    && idx > 0
-                   && (p[idx - 1] == 'C' || p[idx - 1] == 'D')
+                   && (p[idx - 1] == 'C' || p[idx - 1] == 'D'
+                       || p[idx - 1] == 'I')
                    && p[idx] == '1'
                    && q[idx] == '2');
        grp_name[idx] = '5';
@@ -192,30 +191,39 @@ cdtor_comdat_group (tree complete, tree base)
   return get_identifier (grp_name);
 }
 
-/* FN is a function that has a complete body.  Clone the body as
-   necessary.  Returns nonzero if there's no longer any need to
-   process the main body.  */
+/* Returns true iff we can make the base and complete [cd]tor aliases of
+   the same symbol rather than separate functions.  */
 
-bool
-maybe_clone_body (tree fn)
+static bool
+can_alias_cdtor (tree fn)
 {
-  tree comdat_group = NULL_TREE;
-  tree clone;
-  tree fns[3];
-  bool first = true;
-  bool in_charge_parm_used;
-  int idx;
-  bool need_alias = false;
+  /* If aliases aren't supported by the assembler, fail.  */
+  if (!TARGET_SUPPORTS_ALIASES)
+    return false;
+
+  /* We can't use an alias if there are virtual bases.  */
+  if (CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fn)))
+    return false;
+  /* ??? Why not use aliases with -frepo?  */
+  if (flag_use_repository)
+    return false;
+  gcc_assert (DECL_MAYBE_IN_CHARGE_CDTOR_P (fn));
+  /* Don't use aliases for weak/linkonce definitions unless we can put both
+     symbols in the same COMDAT group.  */
+  return (DECL_INTERFACE_KNOWN (fn)
+         && (SUPPORTS_ONE_ONLY || !DECL_WEAK (fn))
+         && (!DECL_ONE_ONLY (fn)
+             || (HAVE_COMDAT_GROUP && DECL_WEAK (fn))));
+}
 
-  /* We only clone constructors and destructors.  */
-  if (!DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (fn)
-      && !DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (fn))
-    return 0;
+/* FN is a [cd]tor, fns is a pointer to an array of length 3.  Fill fns
+   with pointers to the base, complete, and deleting variants.  */
 
-  /* Emit the DWARF1 abstract instance.  */
-  (*debug_hooks->deferred_inline_function) (fn);
+static void
+populate_clone_array (tree fn, tree *fns)
+{
+  tree clone;
 
-  in_charge_parm_used = CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fn)) != NULL;
   fns[0] = NULL_TREE;
   fns[1] = NULL_TREE;
   fns[2] = NULL_TREE;
@@ -233,22 +241,234 @@ maybe_clone_body (tree fn)
       fns[2] = clone;
     else
       gcc_unreachable ();
+}
+
+/* FN is a constructor or destructor, and there are FUNCTION_DECLs
+   cloned from it nearby.  Instead of cloning this body, leave it
+   alone and create tiny one-call bodies for the cloned
+   FUNCTION_DECLs.  These clones are sibcall candidates, and their
+   resulting code will be very thunk-esque.  */
+
+static bool
+maybe_thunk_body (tree fn, bool force)
+{
+  tree bind, block, call, clone, clone_result, fn_parm, fn_parm_typelist;
+  tree last_arg, modify, *args;
+  int parmno, vtt_parmno, max_parms;
+  tree fns[3];
+
+  if (!force && !flag_declone_ctor_dtor)
+    return 0;
+
+  /* If function accepts variable arguments, give up.  */
+  last_arg = tree_last (TYPE_ARG_TYPES (TREE_TYPE (fn)));
+  if (last_arg != void_list_node)
+    return 0;
+
+  /* If we got this far, we've decided to turn the clones into thunks.  */
+
+  /* We're going to generate code for fn, so it is no longer "abstract."
+     Also make the unified ctor/dtor private to either the translation unit
+     (for non-vague linkage ctors) or the COMDAT group (otherwise).  */
+
+  populate_clone_array (fn, fns);
+
+  /* Can happen during error recovery (c++/71464).  */
+  if (!fns[0] || !fns[1])
+    return 0;
+
+  /* Don't use thunks if the base clone omits inherited parameters.  */
+  if (ctor_omit_inherited_parms (fns[0]))
+    return 0;
+
+  DECL_ABSTRACT_P (fn) = false;
+  if (!DECL_WEAK (fn))
+    {
+      TREE_PUBLIC (fn) = false;
+      DECL_EXTERNAL (fn) = false;
+      DECL_INTERFACE_KNOWN (fn) = true;
+    }
+  else if (HAVE_COMDAT_GROUP)
+    {
+      /* At eof, defer creation of mangling aliases temporarily.  */
+      bool save_defer_mangling_aliases = defer_mangling_aliases;
+      defer_mangling_aliases = true;
+      tree comdat_group = cdtor_comdat_group (fns[1], fns[0]);
+      defer_mangling_aliases = save_defer_mangling_aliases;
+      cgraph_node::get_create (fns[0])->set_comdat_group (comdat_group);
+      cgraph_node::get_create (fns[1])->add_to_same_comdat_group
+       (cgraph_node::get_create (fns[0]));
+      symtab_node::get (fn)->add_to_same_comdat_group
+       (symtab_node::get (fns[0]));
+      if (fns[2])
+       /* If *[CD][12]* dtors go into the *[CD]5* comdat group and dtor is
+          virtual, it goes into the same comdat group as well.  */
+       cgraph_node::get_create (fns[2])->add_to_same_comdat_group
+         (symtab_node::get (fns[0]));
+      /* Emit them now that the thunks are same comdat group aliases.  */
+      if (!save_defer_mangling_aliases)
+       generate_mangling_aliases ();
+      TREE_PUBLIC (fn) = false;
+      DECL_EXTERNAL (fn) = false;
+      DECL_INTERFACE_KNOWN (fn) = true;
+      /* function_and_variable_visibility doesn't want !PUBLIC decls to
+        have these flags set.  */
+      DECL_WEAK (fn) = false;
+      DECL_COMDAT (fn) = false;
+    }
+
+  /* Find the vtt_parm, if present.  */
+  for (vtt_parmno = -1, parmno = 0, fn_parm = DECL_ARGUMENTS (fn);
+       fn_parm;
+       ++parmno, fn_parm = TREE_CHAIN (fn_parm))
+    {
+      if (DECL_ARTIFICIAL (fn_parm)
+         && DECL_NAME (fn_parm) == vtt_parm_identifier)
+       {
+         /* Compensate for removed in_charge parameter.  */
+         vtt_parmno = parmno;
+         break;
+       }
+    }
+
+  /* Allocate an argument buffer for build_cxx_call().
+     Make sure it is large enough for any of the clones.  */
+  max_parms = 0;
+  FOR_EACH_CLONE (clone, fn)
+    {
+      int length = list_length (DECL_ARGUMENTS (fn));
+      if (length > max_parms)
+        max_parms = length;
+    }
+  args = XALLOCAVEC (tree, max_parms);
+
+  /* We know that any clones immediately follow FN in TYPE_FIELDS.  */
+  FOR_EACH_CLONE (clone, fn)
+    {
+      tree clone_parm;
+
+      /* If we've already generated a body for this clone, avoid
+        duplicating it.  (Is it possible for a clone-list to grow after we
+        first see it?)  */
+      if (DECL_SAVED_TREE (clone) || TREE_ASM_WRITTEN (clone))
+       continue;
+
+      /* Start processing the function.  */
+      start_preparsed_function (clone, NULL_TREE, SF_PRE_PARSED);
+
+      if (clone == fns[2])
+       {
+         for (clone_parm = DECL_ARGUMENTS (clone); clone_parm;
+              clone_parm = TREE_CHAIN (clone_parm))
+           DECL_ABSTRACT_ORIGIN (clone_parm) = NULL_TREE;
+         /* Build the delete destructor by calling complete destructor and
+            delete function.  */
+         build_delete_destructor_body (clone, fns[1]);
+       }
+      else
+       {
+         /* Walk parameter lists together, creating parameter list for
+            call to original function.  */
+         for (parmno = 0,
+                fn_parm = DECL_ARGUMENTS (fn),
+                fn_parm_typelist = TYPE_ARG_TYPES (TREE_TYPE (fn)),
+                clone_parm = DECL_ARGUMENTS (clone);
+              fn_parm;
+              ++parmno,
+                fn_parm = TREE_CHAIN (fn_parm))
+           {
+             if (parmno == vtt_parmno && ! DECL_HAS_VTT_PARM_P (clone))
+               {
+                 gcc_assert (fn_parm_typelist);
+                 /* Clobber argument with formal parameter type.  */
+                 args[parmno]
+                   = convert (TREE_VALUE (fn_parm_typelist),
+                              null_pointer_node);
+               }
+             else if (parmno == 1 && DECL_HAS_IN_CHARGE_PARM_P (fn))
+               {
+                 tree in_charge
+                   = copy_node (in_charge_arg_for_name (DECL_NAME (clone)));
+                 args[parmno] = in_charge;
+               }
+             /* Map other parameters to their equivalents in the cloned
+                function.  */
+             else
+               {
+                 gcc_assert (clone_parm);
+                 DECL_ABSTRACT_ORIGIN (clone_parm) = NULL;
+                 args[parmno] = clone_parm;
+                 clone_parm = TREE_CHAIN (clone_parm);
+               }
+             if (fn_parm_typelist)
+               fn_parm_typelist = TREE_CHAIN (fn_parm_typelist);
+           }
+
+         /* We built this list backwards; fix now.  */
+         mark_used (fn);
+         call = build_cxx_call (fn, parmno, args, tf_warning_or_error);
+         /* Arguments passed to the thunk by invisible reference should
+            be transmitted to the callee unchanged.  Do not create a
+            temporary and invoke the copy constructor.  The thunking
+            transformation must not introduce any constructor calls.  */
+         CALL_FROM_THUNK_P (call) = 1;
+         block = make_node (BLOCK);
+         if (targetm.cxx.cdtor_returns_this ())
+           {
+             clone_result = DECL_RESULT (clone);
+             modify = build2 (MODIFY_EXPR, TREE_TYPE (clone_result),
+                              clone_result, call);
+             modify = build1 (RETURN_EXPR, void_type_node, modify);
+             add_stmt (modify);
+           }
+         else
+           {
+             add_stmt (call);
+           }
+         bind = c_build_bind_expr (DECL_SOURCE_LOCATION (clone),
+                                   block, cur_stmt_list);
+         DECL_SAVED_TREE (clone) = push_stmt_list ();
+         add_stmt (bind);
+       }
+
+      DECL_ABSTRACT_ORIGIN (clone) = NULL;
+      expand_or_defer_fn (finish_function (/*inline_p=*/false));
+    }
+  return 1;
+}
+
+/* FN is a function that has a complete body.  Clone the body as
+   necessary.  Returns nonzero if there's no longer any need to
+   process the main body.  */
+
+bool
+maybe_clone_body (tree fn)
+{
+  tree comdat_group = NULL_TREE;
+  tree clone;
+  tree fns[3];
+  bool first = true;
+  int idx;
+  bool need_alias = false;
+
+  /* We only clone constructors and destructors.  */
+  if (!DECL_MAYBE_IN_CHARGE_CDTOR_P (fn))
+    return 0;
+
+  populate_clone_array (fn, fns);
 
   /* Remember if we can't have multiple clones for some reason.  We need to
      check this before we remap local static initializers in clone_body.  */
   if (!tree_versionable_function_p (fn))
     need_alias = true;
 
-  /* We know that any clones immediately follow FN in the TYPE_METHODS
+  /* We know that any clones immediately follow FN in the TYPE_FIELDS
      list.  */
   push_to_top_level ();
   for (idx = 0; idx < 3; idx++)
     {
       tree parm;
       tree clone_parm;
-      int parmno;
-      bool alias = false;
-      struct pointer_map_t *decl_map;
 
       clone = fns[idx];
       if (!clone)
@@ -265,8 +485,7 @@ maybe_clone_body (tree fn)
         name of fn was corrupted by write_mangled_name by adding *INTERNAL*
         to it. By doing so, it also corrupted the comdat group. */
       if (DECL_ONE_ONLY (fn))
-       DECL_COMDAT_GROUP (clone) = cxx_comdat_group (clone);
-      DECL_SECTION_NAME (clone) = DECL_SECTION_NAME (fn);
+       cgraph_node::get_create (clone)->set_comdat_group (cxx_comdat_group (clone));
       DECL_USE_TEMPLATE (clone) = DECL_USE_TEMPLATE (fn);
       DECL_EXTERNAL (clone) = DECL_EXTERNAL (fn);
       DECL_INTERFACE_KNOWN (clone) = DECL_INTERFACE_KNOWN (fn);
@@ -277,6 +496,7 @@ maybe_clone_body (tree fn)
       DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
       DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (fn));
       DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS (fn);
+      set_decl_section_name (clone, DECL_SECTION_NAME (fn));
 
       /* Adjust the parameter names and locations.  */
       parm = DECL_ARGUMENTS (fn);
@@ -291,31 +511,49 @@ maybe_clone_body (tree fn)
        parm = DECL_CHAIN (parm);
       if (DECL_HAS_VTT_PARM_P (clone))
        clone_parm = DECL_CHAIN (clone_parm);
-      for (; parm;
+      for (; parm && clone_parm;
           parm = DECL_CHAIN (parm), clone_parm = DECL_CHAIN (clone_parm))
        /* Update this parameter.  */
        update_cloned_parm (parm, clone_parm, first);
+    }
+
+  bool can_alias = can_alias_cdtor (fn);
+
+  /* If we decide to turn clones into thunks, they will branch to fn.
+     Must have original function available to call.  */
+  if (!can_alias && maybe_thunk_body (fn, need_alias))
+    {
+      pop_from_top_level ();
+      /* We still need to emit the original function.  */
+      return 0;
+    }
+
+  /* Emit the DWARF1 abstract instance.  */
+  (*debug_hooks->deferred_inline_function) (fn);
+
+  /* We know that any clones immediately follow FN in the TYPE_FIELDS. */
+  for (idx = 0; idx < 3; idx++)
+    {
+      tree parm;
+      tree clone_parm;
+      int parmno;
+      hash_map<tree, tree> *decl_map;
+      bool alias = false;
+
+      clone = fns[idx];
+      if (!clone)
+       continue;
 
       /* Start processing the function.  */
       start_preparsed_function (clone, NULL_TREE, SF_PRE_PARSED);
 
       /* Tell cgraph if both ctors or both dtors are known to have
         the same body.  */
-      if (!in_charge_parm_used
+      if (can_alias
          && fns[0]
          && idx == 1
-         && !flag_use_repository
-         && DECL_INTERFACE_KNOWN (fns[0])
-         && (SUPPORTS_ONE_ONLY || !DECL_WEAK (fns[0]))
-         && (!DECL_ONE_ONLY (fns[0])
-             || (HAVE_COMDAT_GROUP
-                 && DECL_WEAK (fns[0])))
-         && !flag_syntax_only
-         /* Set linkage flags appropriately before
-            cgraph_create_function_alias looks at them.  */
-         && expand_or_defer_fn_1 (clone)
-         && cgraph_same_body_alias (cgraph_get_node (fns[0]),
-                                    clone, fns[0]))
+         && cgraph_node::get_create (fns[0])->create_same_body_alias
+              (clone, fns[0]))
        {
          alias = true;
          if (DECL_ONE_ONLY (fns[0]))
@@ -324,9 +562,11 @@ maybe_clone_body (tree fn)
                 into the same, *[CD]5* comdat group instead of
                 *[CD][12]*.  */
              comdat_group = cdtor_comdat_group (fns[1], fns[0]);
-             DECL_COMDAT_GROUP (fns[0]) = comdat_group;
-             symtab_add_to_same_comdat_group (symtab_get_node (clone),
-                                              symtab_get_node (fns[0]));
+             cgraph_node::get_create (fns[0])->set_comdat_group (comdat_group);
+             if (symtab_node::get (clone)->same_comdat_group)
+               symtab_node::get (clone)->remove_from_same_comdat_group ();
+             symtab_node::get (clone)->add_to_same_comdat_group
+               (symtab_node::get (fns[0]));
            }
        }
 
@@ -338,9 +578,8 @@ maybe_clone_body (tree fn)
          /* If *[CD][12]* dtors go into the *[CD]5* comdat group and dtor is
             virtual, it goes into the same comdat group as well.  */
          if (comdat_group)
-           symtab_add_to_same_comdat_group
-              (cgraph_get_create_node (clone),
-               symtab_get_node (fns[0]));
+           cgraph_node::get_create (clone)->add_to_same_comdat_group
+             (symtab_node::get (fns[0]));
        }
       else if (alias)
        /* No need to populate body.  */ ;
@@ -358,7 +597,7 @@ maybe_clone_body (tree fn)
            }
 
           /* Remap the parameters.  */
-          decl_map = pointer_map_create ();
+          decl_map = new hash_map<tree, tree>;
           for (parmno = 0,
                 parm = DECL_ARGUMENTS (fn),
                 clone_parm = DECL_ARGUMENTS (clone);
@@ -371,7 +610,7 @@ maybe_clone_body (tree fn)
                 {
                   tree in_charge;
                   in_charge = in_charge_arg_for_name (DECL_NAME (clone));
-                  *pointer_map_insert (decl_map, parm) = in_charge;
+                  decl_map->put (parm, in_charge);
                 }
               else if (DECL_ARTIFICIAL (parm)
                        && DECL_NAME (parm) == vtt_parm_identifier)
@@ -382,20 +621,36 @@ maybe_clone_body (tree fn)
                   if (DECL_HAS_VTT_PARM_P (clone))
                     {
                       DECL_ABSTRACT_ORIGIN (clone_parm) = parm;
-                      *pointer_map_insert (decl_map, parm) = clone_parm;
+                      decl_map->put (parm, clone_parm);
                       clone_parm = DECL_CHAIN (clone_parm);
                     }
                   /* Otherwise, map the VTT parameter to `NULL'.  */
                   else
-                    *pointer_map_insert (decl_map, parm)
-                       = fold_convert (TREE_TYPE (parm), null_pointer_node);
+                   {
+                     tree t
+                       = fold_convert (TREE_TYPE (parm), null_pointer_node);
+                     decl_map->put (parm, t);
+                   }
                 }
               /* Map other parameters to their equivalents in the cloned
                  function.  */
               else
                 {
-                  *pointer_map_insert (decl_map, parm) = clone_parm;
-                  clone_parm = DECL_CHAIN (clone_parm);
+                 tree replacement;
+                 if (clone_parm)
+                   {
+                     replacement = clone_parm;
+                     clone_parm = DECL_CHAIN (clone_parm);
+                   }
+                 else
+                   {
+                     /* Inheriting ctors can omit parameters from the base
+                        clone.  Replace them with null lvalues.  */
+                     tree reftype = build_reference_type (TREE_TYPE (parm));
+                     replacement = fold_convert (reftype, null_pointer_node);
+                     replacement = convert_from_reference (replacement);
+                   }
+                  decl_map->put (parm, replacement);
                 }
             }
 
@@ -403,26 +658,28 @@ maybe_clone_body (tree fn)
             {
               parm = DECL_RESULT (fn);
               clone_parm = DECL_RESULT (clone);
-              *pointer_map_insert (decl_map, parm) = clone_parm;
+              decl_map->put (parm, clone_parm);
             }
 
           /* Clone the body.  */
           clone_body (clone, fn, decl_map);
 
           /* Clean up.  */
-          pointer_map_destroy (decl_map);
+          delete decl_map;
         }
 
       /* The clone can throw iff the original function can throw.  */
       cp_function_chain->can_throw = !TREE_NOTHROW (fn);
 
       /* Now, expand this function into RTL, if appropriate.  */
-      finish_function (0);
+      finish_function (/*inline_p=*/false);
       BLOCK_ABSTRACT_ORIGIN (DECL_INITIAL (clone)) = DECL_INITIAL (fn);
       if (alias)
        {
          if (expand_or_defer_fn_1 (clone))
            emit_associated_thunks (clone);
+         /* We didn't generate a body, so remove the empty one.  */
+         DECL_SAVED_TREE (clone) = NULL_TREE;
        }
       else
        expand_or_defer_fn (clone);