summaryrefslogtreecommitdiff
path: root/gcc/cp/optimize.c
diff options
context:
space:
mode:
authorjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>2013-12-23 17:49:47 +0000
committerjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>2013-12-23 17:49:47 +0000
commit468088aca3d51046f289052028ca22f1552a1df9 (patch)
treedeb79005e2132e0ee5c203e13985a93f5dd4714e /gcc/cp/optimize.c
parent5a1baa9d247f64349cc016895b357948d180ef7b (diff)
downloadgcc-468088aca3d51046f289052028ca22f1552a1df9.tar.gz
PR c++/41090
Add -fdeclone-ctor-dtor. gcc/cp/ * optimize.c (can_alias_cdtor, populate_clone_array): Split out from maybe_clone_body. (maybe_thunk_body): New function. (maybe_clone_body): Call it. * mangle.c (write_mangled_name): Remove code to suppress writing of mangled name for cloned constructor or destructor. (write_special_name_constructor): Handle decloned constructor. (write_special_name_destructor): Handle decloned destructor. * method.c (trivial_fn_p): Handle decloning. * semantics.c (expand_or_defer_fn_1): Clone after setting linkage. gcc/c-family/ * c.opt: Add -fdeclone-ctor-dtor. * c-opts.c (c_common_post_options): Default to on iff -Os. gcc/ * cgraph.h (struct cgraph_node): Add calls_comdat_local. (symtab_comdat_local_p, symtab_in_same_comdat_p): New. * cif-code.def: Add USES_COMDAT_LOCAL. * symtab.c (verify_symtab_base): Make sure we don't refer to a comdat-local symbol from outside its comdat. * cgraph.c (verify_cgraph_node): Likewise. * cgraphunit.c (mark_functions_to_output): Don't mark comdat-locals. * ipa.c (symtab_remove_unreachable_nodes): Likewise. (function_and_variable_visibility): Handle comdat-local fns. * ipa-cp.c (determine_versionability): Don't clone comdat-locals. * ipa-inline-analysis.c (compute_inline_parameters): Update calls_comdat_local. * ipa-inline-transform.c (inline_call): Likewise. (save_inline_function_body): Don't clear DECL_COMDAT_GROUP. * ipa-inline.c (can_inline_edge_p): Check calls_comdat_local. * lto-cgraph.c (input_overwrite_node): Read calls_comdat_local. (lto_output_node): Write it. * symtab.c (symtab_dissolve_same_comdat_group_list): Clear DECL_COMDAT_GROUP for comdat-locals. include/ * demangle.h (enum gnu_v3_ctor_kinds): Added literal gnu_v3_unified_ctor. (enum gnu_v3_ctor_kinds): Added literal gnu_v3_unified_dtor. libiberty/ * cp-demangle.c (cplus_demangle_fill_ctor,cplus_demangle_fill_dtor): Handle unified ctor/dtor. (d_ctor_dtor_name): Handle unified ctor/dtor. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@206182 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/cp/optimize.c')
-rw-r--r--gcc/cp/optimize.c293
1 files changed, 259 insertions, 34 deletions
diff --git a/gcc/cp/optimize.c b/gcc/cp/optimize.c
index f1b09bfd55e..40494f27a55 100644
--- a/gcc/cp/optimize.c
+++ b/gcc/cp/optimize.c
@@ -193,30 +193,40 @@ 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;
+#ifndef ASM_OUTPUT_DEF
+ /* If aliases aren't supported by the assembler, fail. */
+ return false;
+#endif
+ /* 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_CONSTRUCTOR_P (fn)
+ || DECL_MAYBE_IN_CHARGE_DESTRUCTOR_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;
@@ -234,6 +244,206 @@ 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);
+ DECL_ABSTRACT (fn) = false;
+ if (!DECL_WEAK (fn))
+ {
+ TREE_PUBLIC (fn) = false;
+ DECL_EXTERNAL (fn) = false;
+ DECL_INTERFACE_KNOWN (fn) = true;
+ }
+ else if (HAVE_COMDAT_GROUP)
+ {
+ tree comdat_group = cdtor_comdat_group (fns[1], fns[0]);
+ DECL_COMDAT_GROUP (fns[0]) = comdat_group;
+ symtab_add_to_same_comdat_group (cgraph_get_create_node (fns[1]),
+ cgraph_get_create_node (fns[0]));
+ symtab_add_to_same_comdat_group (symtab_get_node (fn),
+ symtab_get_node (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. */
+ symtab_add_to_same_comdat_group (cgraph_get_create_node (fns[2]),
+ symtab_get_node (fns[0]));
+ 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 = (tree *) alloca (max_parms * sizeof (tree));
+
+ /* We know that any clones immediately follow FN in TYPE_METHODS. */
+ 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);
+ add_stmt (modify);
+ BLOCK_VARS (block) = clone_result;
+ }
+ 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 (0));
+ }
+ 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_CONSTRUCTOR_P (fn)
+ && !DECL_MAYBE_IN_CHARGE_DESTRUCTOR_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. */
@@ -247,9 +457,6 @@ maybe_clone_body (tree fn)
{
tree parm;
tree clone_parm;
- int parmno;
- bool alias = false;
- struct pointer_map_t *decl_map;
clone = fns[idx];
if (!clone)
@@ -296,26 +503,44 @@ maybe_clone_body (tree fn)
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_METHODS list. */
+ for (idx = 0; idx < 3; idx++)
+ {
+ tree parm;
+ tree clone_parm;
+ int parmno;
+ struct pointer_map_t *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]),
+ && cgraph_same_body_alias (cgraph_get_create_node (fns[0]),
clone, fns[0]))
{
alias = true;