/* 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.
#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. */
/* 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);
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;
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);
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);
{
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';
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;
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)
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);
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);
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]))
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]));
}
}
/* 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. */ ;
}
/* 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);
{
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)
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);
}
}
{
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);