re PR c++/41090 (Using static label reference in c++ class constructor produces wrong code)

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.

From-SVN: r206182
This commit is contained in:
Jason Merrill 2013-12-23 12:49:47 -05:00
parent 266d276c4e
commit 1f26ac8748
28 changed files with 525 additions and 87 deletions

View File

@ -1,3 +1,25 @@
2013-12-23 Jason Merrill <jason@redhat.com>
* 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.
2013-12-23 H.J. Lu <hongjiu.lu@intel.com>
* config/i386/i386.c (processor_target_table): Move Bonnell and

View File

@ -1,3 +1,10 @@
2013-12-23 Stuart Hastings <stuart@apple.com>
Bill Maddox <maddox@google.com>
Jason Merrill <jason@redhat.com>
* c.opt: Add -fdeclone-ctor-dtor.
* c-opts.c (c_common_post_options): Default to on iff -Os.
2013-12-18 Balaji V. Iyer <balaji.v.iyer@intel.com>
* c-common.c (c_common_attribute_table): Added "cilk simd function"

View File

@ -899,6 +899,10 @@ c_common_post_options (const char **pfilename)
if (warn_implicit_function_declaration == -1)
warn_implicit_function_declaration = flag_isoc99;
/* Declone C++ 'structors if -Os. */
if (flag_declone_ctor_dtor == -1)
flag_declone_ctor_dtor = optimize_size;
if (cxx_dialect >= cxx11)
{
/* If we're allowing C++0x constructs, don't warn about C++98

View File

@ -890,6 +890,10 @@ fdeduce-init-list
C++ ObjC++ Var(flag_deduce_init_list) Init(0)
-fdeduce-init-list enable deduction of std::initializer_list for a template type parameter from a brace-enclosed initializer-list
fdeclone-ctor-dtor
C++ ObjC++ Var(flag_declone_ctor_dtor) Init(-1)
Factor complex constructors and destructors to favor space over speed
fdefault-inline
C++ ObjC++ Ignore
Does nothing. Preserved for backward compatibility.

View File

@ -2666,10 +2666,18 @@ verify_cgraph_node (struct cgraph_node *node)
error_found = true;
}
}
bool check_comdat = symtab_comdat_local_p (node);
for (e = node->callers; e; e = e->next_caller)
{
if (verify_edge_count_and_frequency (e))
error_found = true;
if (check_comdat
&& !symtab_in_same_comdat_p (e->caller, node))
{
error ("comdat-local function called by %s outside its comdat",
identifier_to_locale (e->caller->name ()));
error_found = true;
}
if (!e->inline_failed)
{
if (node->global.inlined_to

View File

@ -428,6 +428,9 @@ public:
unsigned tm_clone : 1;
/* True if this decl is a dispatcher for function versions. */
unsigned dispatcher_function : 1;
/* True if this decl calls a COMDAT-local function. This is set up in
compute_inline_parameters and inline_call. */
unsigned calls_comdat_local : 1;
};
@ -1490,4 +1493,22 @@ symtab_can_be_discarded (symtab_node *node)
&& node->resolution != LDPR_PREVAILING_DEF_IRONLY
&& node->resolution != LDPR_PREVAILING_DEF_IRONLY_EXP));
}
/* Return true if NODE is local to a particular COMDAT group, and must not
be named from outside the COMDAT. This is used for C++ decloned
constructors. */
static inline bool
symtab_comdat_local_p (symtab_node *node)
{
return (node->same_comdat_group && !TREE_PUBLIC (node->decl));
}
/* Return true if ONE and TWO are part of the same COMDAT group. */
static inline bool
symtab_in_same_comdat_p (symtab_node *one, symtab_node *two)
{
return DECL_COMDAT_GROUP (one->decl) == DECL_COMDAT_GROUP (two->decl);
}
#endif /* GCC_CGRAPH_H */

View File

@ -1244,7 +1244,8 @@ mark_functions_to_output (void)
for (next = cgraph (node->same_comdat_group);
next != node;
next = cgraph (next->same_comdat_group))
if (!next->thunk.thunk_p && !next->alias)
if (!next->thunk.thunk_p && !next->alias
&& !symtab_comdat_local_p (next))
next->process = 1;
}
}

View File

@ -106,3 +106,6 @@ DEFCIFCODE(TARGET_OPTION_MISMATCH, N_("target specific option mismatch"))
/* We can't inline because of mismatched optimization levels. */
DEFCIFCODE(OPTIMIZATION_MISMATCH, N_("optimization level attribute mismatch"))
/* We can't inline because the callee refers to comdat-local symbols. */
DEFCIFCODE(USES_COMDAT_LOCAL, N_("callee refers to comdat-local symbols"))

View File

@ -1,3 +1,19 @@
2013-12-23 Stuart Hastings <stuart@apple.com>
Bill Maddox <maddox@google.com>
Jason Merrill <jason@redhat.com>
PR c++/41090
* 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.
2013-12-23 Marek Polacek <polacek@redhat.com>
PR c++/59111

View File

@ -10185,7 +10185,9 @@ grokdeclarator (const cp_declarator *declarator,
/* The TYPE_DECL is "abstract" because there will be
clones of this constructor/destructor, and there will
be copies of this TYPE_DECL generated in those
clones. */
clones. The decloning optimization (for space) may
revert this subsequently if it determines that
the clones should share a common implementation. */
DECL_ABSTRACT (decl) = 1;
}
else if (current_class_type

View File

@ -689,13 +689,6 @@ write_mangled_name (const tree decl, bool top_level)
mangled_name:;
write_string ("_Z");
write_encoding (decl);
if (DECL_LANG_SPECIFIC (decl)
&& (DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (decl)
|| DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (decl)))
/* We need a distinct mangled name for these entities, but
we should never actually output it. So, we append some
characters the assembler won't like. */
write_string (" *INTERNAL* ");
}
}
@ -1653,25 +1646,21 @@ write_identifier (const char *identifier)
::= C2 # base object constructor
::= C3 # complete object allocating constructor
Currently, allocating constructors are never used.
We also need to provide mangled names for the maybe-in-charge
constructor, so we treat it here too. mangle_decl_string will
append *INTERNAL* to that, to make sure we never emit it. */
Currently, allocating constructors are never used. */
static void
write_special_name_constructor (const tree ctor)
{
if (DECL_BASE_CONSTRUCTOR_P (ctor))
write_string ("C2");
/* This is the old-style "[unified]" constructor.
In some cases, we may emit this function and call
it from the clones in order to share code and save space. */
else if (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (ctor))
write_string ("C4");
else
{
gcc_assert (DECL_COMPLETE_CONSTRUCTOR_P (ctor)
/* Even though we don't ever emit a definition of
the old-style destructor, we still have to
consider entities (like static variables) nested
inside it. */
|| DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (ctor));
gcc_assert (DECL_COMPLETE_CONSTRUCTOR_P (ctor));
write_string ("C1");
}
}
@ -1681,11 +1670,7 @@ write_special_name_constructor (const tree ctor)
<special-name> ::= D0 # deleting (in-charge) destructor
::= D1 # complete object (in-charge) destructor
::= D2 # base object (not-in-charge) destructor
We also need to provide mangled names for the maybe-incharge
destructor, so we treat it here too. mangle_decl_string will
append *INTERNAL* to that, to make sure we never emit it. */
::= D2 # base object (not-in-charge) destructor */
static void
write_special_name_destructor (const tree dtor)
@ -1694,14 +1679,14 @@ write_special_name_destructor (const tree dtor)
write_string ("D0");
else if (DECL_BASE_DESTRUCTOR_P (dtor))
write_string ("D2");
else if (DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (dtor))
/* This is the old-style "[unified]" destructor.
In some cases, we may emit this function and call
it from the clones in order to share code and save space. */
write_string ("D4");
else
{
gcc_assert (DECL_COMPLETE_DESTRUCTOR_P (dtor)
/* Even though we don't ever emit a definition of
the old-style destructor, we still have to
consider entities (like static variables) nested
inside it. */
|| DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (dtor));
gcc_assert (DECL_COMPLETE_DESTRUCTOR_P (dtor));
write_string ("D1");
}
}

View File

@ -477,7 +477,8 @@ trivial_fn_p (tree fn)
return false;
/* If fn is a clone, get the primary variant. */
fn = DECL_ORIGIN (fn);
if (tree prim = DECL_CLONED_FUNCTION (fn))
fn = prim;
return type_has_trivial_fn (DECL_CONTEXT (fn), special_function_p (fn));
}

View File

@ -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)
{
#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))));
}
/* 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. */
static void
populate_clone_array (tree fn, tree *fns)
{
tree comdat_group = NULL_TREE;
tree clone;
tree fns[3];
bool first = true;
bool in_charge_parm_used;
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;
/* Emit the DWARF1 abstract instance. */
(*debug_hooks->deferred_inline_function) (fn);
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;

View File

@ -3901,20 +3901,6 @@ expand_or_defer_fn_1 (tree fn)
gcc_assert (DECL_SAVED_TREE (fn));
/* If this is a constructor or destructor body, we have to clone
it. */
if (maybe_clone_body (fn))
{
/* We don't want to process FN again, so pretend we've written
it out, even though we haven't. */
TREE_ASM_WRITTEN (fn) = 1;
/* If this is an instantiation of a constexpr function, keep
DECL_SAVED_TREE for explain_invalid_constexpr_fn. */
if (!is_instantiation_of_constexpr (fn))
DECL_SAVED_TREE (fn) = NULL_TREE;
return false;
}
/* We make a decision about linkage for these functions at the end
of the compilation. Until that point, we do not want the back
end to output them -- but we do want it to see the bodies of
@ -3962,6 +3948,20 @@ expand_or_defer_fn_1 (tree fn)
}
}
/* If this is a constructor or destructor body, we have to clone
it. */
if (maybe_clone_body (fn))
{
/* We don't want to process FN again, so pretend we've written
it out, even though we haven't. */
TREE_ASM_WRITTEN (fn) = 1;
/* If this is an instantiation of a constexpr function, keep
DECL_SAVED_TREE for explain_invalid_constexpr_fn. */
if (!is_instantiation_of_constexpr (fn))
DECL_SAVED_TREE (fn) = NULL_TREE;
return false;
}
/* There's no reason to do any of the work here if we're only doing
semantic analysis; this code just generates RTL. */
if (flag_syntax_only)

View File

@ -7339,6 +7339,18 @@ branch-less equivalents.
Enabled at levels @option{-O}, @option{-O2}, @option{-O3}, @option{-Os}.
@item -fdeclone-ctor-dtor
@opindex fdeclone-ctor-dtor
The C++ ABI requires multiple entry points for constructors and
destructors: one for a base subobject, one for a complete object, and
one for a virtual destructor that calls operator delete afterwards.
For a hierarchy with virtual bases, the base and complete variants are
clones, which means two copies of the function. With this option, the
base and complete variants are changed to be thunks that call a common
implementation.
Enabled by @option{-Os}.
@item -fdelete-null-pointer-checks
@opindex fdelete-null-pointer-checks
Assume that programs cannot safely dereference null pointers, and that

View File

@ -437,6 +437,10 @@ determine_versionability (struct cgraph_node *node)
coexist, but that may not be worth the effort. */
reason = "function has SIMD clones";
}
/* Don't clone decls local to a comdat group; it breaks and for C++
decloned constructors, inlining is always better anyway. */
else if (symtab_comdat_local_p (node))
reason = "comdat-local function";
if (reason && dump_file && !node->alias && !node->thunk.thunk_p)
fprintf (dump_file, "Function %s/%i is not versionable, reason: %s.\n",

View File

@ -2796,6 +2796,11 @@ compute_inline_parameters (struct cgraph_node *node, bool early)
}
estimate_function_body_sizes (node, early);
for (e = node->callees; e; e = e->next_callee)
if (symtab_comdat_local_p (e->callee))
break;
node->calls_comdat_local = (e != NULL);
/* Inlining characteristics are maintained by the cgraph_mark_inline. */
info->time = info->self_time;
info->size = info->self_size;

View File

@ -272,6 +272,18 @@ inline_call (struct cgraph_edge *e, bool update_original,
inline_update_overall_summary (to);
new_size = inline_summary (to)->size;
if (callee->calls_comdat_local)
to->calls_comdat_local = true;
else if (to->calls_comdat_local && symtab_comdat_local_p (callee))
{
struct cgraph_edge *se = to->callees;
for (; se; se = se->next_callee)
if (se->inline_failed && symtab_comdat_local_p (se->callee))
break;
if (se == NULL)
to->calls_comdat_local = false;
}
#ifdef ENABLE_CHECKING
/* Verify that estimated growth match real growth. Allow off-by-one
error due to INLINE_SIZE_SCALE roudoff errors. */
@ -369,7 +381,6 @@ save_inline_function_body (struct cgraph_node *node)
/* The function will be short lived and removed after we inline all the clones,
but make it internal so we won't confuse ourself. */
DECL_EXTERNAL (first_clone->decl) = 0;
DECL_COMDAT_GROUP (first_clone->decl) = NULL_TREE;
TREE_PUBLIC (first_clone->decl) = 0;
DECL_COMDAT (first_clone->decl) = 0;
first_clone->ipa_transforms_to_apply.release ();

View File

@ -241,7 +241,7 @@ report_inline_failed_reason (struct cgraph_edge *e)
if REPORT is true, output reason to the dump file.
if DISREGARD_LIMITES is true, ignore size limits.*/
if DISREGARD_LIMITS is true, ignore size limits.*/
static bool
can_inline_edge_p (struct cgraph_edge *e, bool report,
@ -271,6 +271,11 @@ can_inline_edge_p (struct cgraph_edge *e, bool report,
e->inline_failed = CIF_BODY_NOT_AVAILABLE;
inlinable = false;
}
else if (callee->calls_comdat_local)
{
e->inline_failed = CIF_USES_COMDAT_LOCAL;
inlinable = false;
}
else if (!inline_summary (callee)->inlinable
|| (caller_cfun && fn_contains_cilk_spawn_p (caller_cfun)))
{

View File

@ -362,14 +362,17 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
enqueue_node (origin_node, &first, reachable);
}
/* If any symbol in a comdat group is reachable, force
all other in the same comdat group to be also reachable. */
all externally visible symbols in the same comdat
group to be reachable as well. Comdat-local symbols
can be discarded if all uses were inlined. */
if (node->same_comdat_group)
{
symtab_node *next;
for (next = node->same_comdat_group;
next != node;
next = next->same_comdat_group)
if (!pointer_set_insert (reachable, next))
if (!symtab_comdat_local_p (next)
&& !pointer_set_insert (reachable, next))
enqueue_node (next, &first, reachable);
}
/* Mark references as reachable. */
@ -969,14 +972,14 @@ function_and_variable_visibility (bool whole_program)
node->unique_name = ((node->resolution == LDPR_PREVAILING_DEF_IRONLY
|| node->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)
&& TREE_PUBLIC (node->decl));
symtab_make_decl_local (node->decl);
node->resolution = LDPR_PREVAILING_DEF_IRONLY;
if (node->same_comdat_group)
if (node->same_comdat_group && TREE_PUBLIC (node->decl))
/* cgraph_externally_visible_p has already checked all other nodes
in the group and they will all be made local. We need to
dissolve the group at once so that the predicate does not
segfault though. */
symtab_dissolve_same_comdat_group_list (node);
symtab_make_decl_local (node->decl);
}
if (node->thunk.thunk_p

View File

@ -518,6 +518,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
bp_pack_value (&bp, node->only_called_at_startup, 1);
bp_pack_value (&bp, node->only_called_at_exit, 1);
bp_pack_value (&bp, node->tm_clone, 1);
bp_pack_value (&bp, node->calls_comdat_local, 1);
bp_pack_value (&bp, node->thunk.thunk_p && !boundary_p, 1);
bp_pack_enum (&bp, ld_plugin_symbol_resolution,
LDPR_NUM_KNOWN, node->resolution);
@ -993,6 +994,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data,
node->only_called_at_startup = bp_unpack_value (bp, 1);
node->only_called_at_exit = bp_unpack_value (bp, 1);
node->tm_clone = bp_unpack_value (bp, 1);
node->calls_comdat_local = bp_unpack_value (bp, 1);
node->thunk.thunk_p = bp_unpack_value (bp, 1);
node->resolution = bp_unpack_enum (bp, ld_plugin_symbol_resolution,
LDPR_NUM_KNOWN);

View File

@ -538,6 +538,10 @@ symtab_dissolve_same_comdat_group_list (symtab_node *node)
{
next = n->same_comdat_group;
n->same_comdat_group = NULL;
/* Clear DECL_COMDAT_GROUP for comdat locals, since
make_decl_local doesn't. */
if (!TREE_PUBLIC (n->decl))
DECL_COMDAT_GROUP (n->decl) = NULL_TREE;
n = next;
}
while (n != node);
@ -844,6 +848,21 @@ verify_symtab_base (symtab_node *node)
n = n->same_comdat_group;
}
while (n != node);
if (symtab_comdat_local_p (node))
{
struct ipa_ref_list *refs = &node->ref_list;
struct ipa_ref *ref;
for (int i = 0; ipa_ref_list_referring_iterate (refs, i, ref); ++i)
{
if (!symtab_in_same_comdat_p (ref->referring, node))
{
error ("comdat-local symbol referred to by %s outside its "
"comdat",
identifier_to_locale (ref->referring->name()));
error_found = true;
}
}
}
}
return error_found;
}
@ -911,6 +930,10 @@ symtab_make_decl_local (tree decl)
{
rtx rtl, symbol;
/* Avoid clearing DECL_COMDAT_GROUP on comdat-local decls. */
if (TREE_PUBLIC (decl) == 0)
return;
if (TREE_CODE (decl) == VAR_DECL)
DECL_COMMON (decl) = 0;
else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);

View File

@ -0,0 +1,25 @@
// PR c++/41090
// { dg-do run }
// { dg-options "-save-temps" }
// { dg-final { scan-assembler "_ZN1CC4Ev" } }
// { dg-final cleanup-saved-temps }
int i;
struct A { A() {} };
struct C: virtual A
{
C();
};
C::C()
{
static void *labelref = &&label;
goto *labelref;
label: i = 1;
}
int main()
{
C c;
return (i != 1);
}

View File

@ -0,0 +1,21 @@
// { dg-options "-fdeclone-ctor-dtor -O3" }
struct V {};
template <class T>
struct A: virtual V {
static A* p;
A();
};
template <class T>
A<T>::A()
{
if (!p)
p = new A();
}
int main()
{
A<int> a;
}

View File

@ -1,3 +1,10 @@
2013-12-23 Bill Maddox <maddox@google.com>
* demangle.h (enum gnu_v3_ctor_kinds):
Added literal gnu_v3_unified_ctor.
(enum gnu_v3_ctor_kinds):
Added literal gnu_v3_unified_dtor.
2013-12-04 Richard Sandiford <rdsandiford@googlemail.com>
* longlong.h: New file.

View File

@ -173,6 +173,10 @@ enum gnu_v3_ctor_kinds {
gnu_v3_complete_object_ctor = 1,
gnu_v3_base_object_ctor,
gnu_v3_complete_object_allocating_ctor,
/* These are not part of the V3 ABI. Unified constructors are generated
as a speed-for-space optimization when the -fdeclone-ctor-dtor option
is used, and are always internal symbols. */
gnu_v3_unified_ctor,
gnu_v3_object_ctor_group
};
@ -188,6 +192,10 @@ enum gnu_v3_dtor_kinds {
gnu_v3_deleting_dtor = 1,
gnu_v3_complete_object_dtor,
gnu_v3_base_object_dtor,
/* These are not part of the V3 ABI. Unified destructors are generated
as a speed-for-space optimization when the -fdeclone-ctor-dtor option
is used, and are always internal symbols. */
gnu_v3_unified_dtor,
gnu_v3_object_dtor_group
};

View File

@ -1,3 +1,9 @@
2013-12-23 Bill Maddox <maddox@google.com>
* cp-demangle.c (cplus_demangle_fill_ctor,cplus_demangle_fill_dtor):
Handle unified ctor/dtor.
(d_ctor_dtor_name): Handle unified ctor/dtor.
2013-11-22 Cary Coutant <ccoutant@google.com>
PR other/59195

View File

@ -2107,6 +2107,9 @@ d_ctor_dtor_name (struct d_info *di)
case '3':
kind = gnu_v3_complete_object_allocating_ctor;
break;
case '4':
kind = gnu_v3_unified_ctor;
break;
case '5':
kind = gnu_v3_object_ctor_group;
break;
@ -2132,6 +2135,10 @@ d_ctor_dtor_name (struct d_info *di)
case '2':
kind = gnu_v3_base_object_dtor;
break;
/* digit '3' is not used */
case '4':
kind = gnu_v3_unified_dtor;
break;
case '5':
kind = gnu_v3_object_dtor_group;
break;