diff options
author | jason <jason@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-12-23 17:49:47 +0000 |
---|---|---|
committer | jason <jason@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-12-23 17:49:47 +0000 |
commit | 468088aca3d51046f289052028ca22f1552a1df9 (patch) | |
tree | deb79005e2132e0ee5c203e13985a93f5dd4714e /gcc/cp/optimize.c | |
parent | 5a1baa9d247f64349cc016895b357948d180ef7b (diff) | |
download | gcc-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.c | 293 |
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; |