mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-01-11 07:04:33 +08:00
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:
parent
266d276c4e
commit
1f26ac8748
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
21
gcc/cgraph.h
21
gcc/cgraph.h
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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"))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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 ();
|
||||
|
@ -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)))
|
||||
{
|
||||
|
11
gcc/ipa.c
11
gcc/ipa.c
@ -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
|
||||
|
@ -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);
|
||||
|
23
gcc/symtab.c
23
gcc/symtab.c
@ -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);
|
||||
|
25
gcc/testsuite/g++.dg/ext/label13a.C
Normal file
25
gcc/testsuite/g++.dg/ext/label13a.C
Normal 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);
|
||||
}
|
21
gcc/testsuite/g++.dg/opt/declone1.C
Normal file
21
gcc/testsuite/g++.dg/opt/declone1.C
Normal 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;
|
||||
}
|
@ -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.
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user