re PR c++/42317 (Issues with comdat virtual dtors)

PR c++/42317
	* cgraph.h (struct cgraph_node): Add same_comdat_group field.
	* cgraph.c (cgraph_remove_node): Unchain node from same_comdat_group
	circular list.
	(cgraph_node_can_be_local_p): Return false for DECL_COMDAT with
	node->same_comdat_group.
	* ipa.c (cgraph_remove_unreachable_nodes): For any reachable node
	mark all its same_comdat_group nodes as also reachable.
	(cgraph_externally_visible_p): Return true even if any of
	same_comdat_group nodes has address taken.
	* lto-cgraph.c (lto_output_node): Stream out same_comdat_group.
	(output_cgraph): Ensure other same_comdat_group nodes are also
	included.
	(input_node): Stream in same_comdat_group.
	(input_cgraph_1): Fix up same_comdat_group fields from references
	to pointers.
	* cgraphunit.c (cgraph_analyze_functions): Mark all other
	same_comdat_group nodes as reachable.
	(cgraph_mark_functions_to_output): For each node->process process
	also other same_comdat_group nodes.
	* ipa-inline.c (cgraph_clone_inlined_nodes): Don't reuse nodes
	with same_comdat_group non-NULL.
	(cgraph_mark_inline_edge): Likewise.

	* decl2.c (cp_write_global_declarations): Clear DECL_EXTERNAL
	also on all other functions in the same comdat group.
	* optimize.c (maybe_clone_body): Also optimize virtual implicit
	dtors.  For virtual comdat dtors tell cgraph that base and deleting
	dtor are in the same comdat group.

	* config/abi/pre/gnu.ver: Don't export certain base dtors that
	weren't previously exported.

	* g++.dg/opt/dtor2.C: New test.
	* g++.dg/opt/dtor2.h: New file.
	* g++.dg/opt/dtor2-aux.cc: New file.

From-SVN: r155143
This commit is contained in:
Jakub Jelinek 2009-12-10 22:58:49 +01:00 committed by Jakub Jelinek
parent 63bcb71f99
commit b66887e4d0
16 changed files with 325 additions and 29 deletions

View File

@ -1,3 +1,29 @@
2009-12-10 Jakub Jelinek <jakub@redhat.com>
PR c++/42317
* cgraph.h (struct cgraph_node): Add same_comdat_group field.
* cgraph.c (cgraph_remove_node): Unchain node from same_comdat_group
circular list.
(cgraph_node_can_be_local_p): Return false for DECL_COMDAT with
node->same_comdat_group.
* ipa.c (cgraph_remove_unreachable_nodes): For any reachable node
mark all its same_comdat_group nodes as also reachable.
(cgraph_externally_visible_p): Return true even if any of
same_comdat_group nodes has address taken.
* lto-cgraph.c (lto_output_node): Stream out same_comdat_group.
(output_cgraph): Ensure other same_comdat_group nodes are also
included.
(input_node): Stream in same_comdat_group.
(input_cgraph_1): Fix up same_comdat_group fields from references
to pointers.
* cgraphunit.c (cgraph_analyze_functions): Mark all other
same_comdat_group nodes as reachable.
(cgraph_mark_functions_to_output): For each node->process process
also other same_comdat_group nodes.
* ipa-inline.c (cgraph_clone_inlined_nodes): Don't reuse nodes
with same_comdat_group non-NULL.
(cgraph_mark_inline_edge): Likewise.
2009-12-10 Jan Hubicka <jh@suse.cz>
PR middle-end/42228

View File

@ -1443,6 +1443,20 @@ cgraph_remove_node (struct cgraph_node *node)
while (node->same_body)
cgraph_remove_same_body_alias (node->same_body);
if (node->same_comdat_group)
{
struct cgraph_node *prev;
for (prev = node->same_comdat_group;
prev->same_comdat_group != node;
prev = prev->same_comdat_group)
;
if (node->same_comdat_group == prev)
prev->same_comdat_group = NULL;
else
prev->same_comdat_group = node->same_comdat_group;
node->same_comdat_group = NULL;
}
/* While all the clones are removed after being proceeded, the function
itself is kept in the cgraph even after it is compiled. Check whether
we are done with this body and reclaim it proactively if this is the case.
@ -2172,7 +2186,8 @@ bool
cgraph_node_can_be_local_p (struct cgraph_node *node)
{
return (!node->needed
&& (DECL_COMDAT (node->decl) || !node->local.externally_visible));
&& ((DECL_COMDAT (node->decl) && !node->same_comdat_group)
|| !node->local.externally_visible));
}
/* Bring NODE local. */

View File

@ -200,6 +200,8 @@ struct GTY((chain_next ("%h.next"), chain_prev ("%h.previous"))) cgraph_node {
/* For normal nodes pointer to the list of alias and thunk nodes,
in alias/thunk nodes pointer to the normal node. */
struct cgraph_node *same_body;
/* Circular list of nodes in the same comdat group if non-NULL. */
struct cgraph_node *same_comdat_group;
/* For functions with many calls sites it holds map from call expression
to the edge to speed up cgraph_edge function. */
htab_t GTY((param_is (struct cgraph_edge))) call_site_hash;

View File

@ -982,6 +982,14 @@ cgraph_analyze_functions (void)
if (!edge->callee->reachable)
cgraph_mark_reachable_node (edge->callee);
if (node->same_comdat_group)
{
for (next = node->same_comdat_group;
next != node;
next = next->same_comdat_group)
cgraph_mark_reachable_node (next);
}
/* If decl is a clone of an abstract function, mark that abstract
function so that we don't release its body. The DECL_INITIAL() of that
abstract function declaration will be later needed to output debug info. */
@ -1094,13 +1102,21 @@ static void
cgraph_mark_functions_to_output (void)
{
struct cgraph_node *node;
#ifdef ENABLE_CHECKING
bool check_same_comdat_groups = false;
for (node = cgraph_nodes; node; node = node->next)
gcc_assert (!node->process);
#endif
for (node = cgraph_nodes; node; node = node->next)
{
tree decl = node->decl;
struct cgraph_edge *e;
gcc_assert (!node->process);
gcc_assert (!node->process || node->same_comdat_group);
if (node->process)
continue;
for (e = node->callers; e; e = e->next_caller)
if (e->inline_failed)
@ -1115,7 +1131,23 @@ cgraph_mark_functions_to_output (void)
|| (e && node->reachable))
&& !TREE_ASM_WRITTEN (decl)
&& !DECL_EXTERNAL (decl))
node->process = 1;
{
node->process = 1;
if (node->same_comdat_group)
{
struct cgraph_node *next;
for (next = node->same_comdat_group;
next != node;
next = next->same_comdat_group)
next->process = 1;
}
}
else if (node->same_comdat_group)
{
#ifdef ENABLE_CHECKING
check_same_comdat_groups = true;
#endif
}
else
{
/* We should've reclaimed all functions that are not needed. */
@ -1135,6 +1167,21 @@ cgraph_mark_functions_to_output (void)
}
}
#ifdef ENABLE_CHECKING
if (check_same_comdat_groups)
for (node = cgraph_nodes; node; node = node->next)
if (node->same_comdat_group && !node->process)
{
tree decl = node->decl;
if (!node->global.inlined_to
&& gimple_has_body_p (decl)
&& !DECL_EXTERNAL (decl))
{
dump_cgraph_node (stderr, node);
internal_error ("failed to reclaim unneeded function");
}
}
#endif
}
/* DECL is FUNCTION_DECL. Initialize datastructures so DECL is a function

View File

@ -1,3 +1,12 @@
2009-12-10 Jakub Jelinek <jakub@redhat.com>
PR c++/42317
* decl2.c (cp_write_global_declarations): Clear DECL_EXTERNAL
also on all other functions in the same comdat group.
* optimize.c (maybe_clone_body): Also optimize virtual implicit
dtors. For virtual comdat dtors tell cgraph that base and deleting
dtor are in the same comdat group.
2009-12-04 Jason Merrill <jason@redhat.com>
PR c++/42010

View File

@ -3660,7 +3660,7 @@ cp_write_global_declarations (void)
&& DECL_INITIAL (decl)
&& decl_needed_p (decl))
{
struct cgraph_node *node = cgraph_get_node (decl), *alias;
struct cgraph_node *node = cgraph_get_node (decl), *alias, *next;
DECL_EXTERNAL (decl) = 0;
/* If we mark !DECL_EXTERNAL one of the same body aliases,
@ -3671,6 +3671,23 @@ cp_write_global_declarations (void)
for (alias = node->same_body; alias; alias = alias->next)
DECL_EXTERNAL (alias->decl) = 0;
}
/* If we mark !DECL_EXTERNAL one of the symbols in some comdat
group, we need to mark all symbols in the same comdat group
that way. */
if (node->same_comdat_group)
for (next = node->same_comdat_group;
next != node;
next = next->same_comdat_group)
{
DECL_EXTERNAL (next->decl) = 0;
if (next->same_body)
{
for (alias = next->same_body;
alias;
alias = alias->next)
DECL_EXTERNAL (alias->decl) = 0;
}
}
}
/* If we're going to need to write this function out, and

View File

@ -292,12 +292,7 @@ maybe_clone_body (tree fn)
&& (SUPPORTS_ONE_ONLY || !DECL_WEAK (fns[0]))
&& (!DECL_ONE_ONLY (fns[0])
|| (HAVE_COMDAT_GROUP
&& DECL_WEAK (fns[0])
/* Don't optimize synthetized virtual dtors, because then
the deleting and other dtors are emitted when needed
and so it is not certain we would emit both
deleting and complete/base dtors in the comdat group. */
&& (fns[2] == NULL || !DECL_ARTIFICIAL (fn))))
&& DECL_WEAK (fns[0])))
&& cgraph_same_body_alias (clone, fns[0]))
{
alias = true;
@ -396,9 +391,18 @@ maybe_clone_body (tree fn)
{
DECL_COMDAT_GROUP (fns[1]) = comdat_group;
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. */
DECL_COMDAT_GROUP (fns[2]) = comdat_group;
{
struct cgraph_node *base_dtor_node, *deleting_dtor_node;
/* If *[CD][12]* dtors go into the *[CD]5* comdat group and dtor is
virtual, it goes into the same comdat group as well. */
DECL_COMDAT_GROUP (fns[2]) = comdat_group;
base_dtor_node = cgraph_node (fns[0]);
deleting_dtor_node = cgraph_node (fns[2]);
gcc_assert (base_dtor_node->same_comdat_group == NULL);
gcc_assert (deleting_dtor_node->same_comdat_group == NULL);
base_dtor_node->same_comdat_group = deleting_dtor_node;
deleting_dtor_node->same_comdat_group = base_dtor_node;
}
}
/* We don't need to process the original function any further. */

View File

@ -247,6 +247,10 @@ cgraph_clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
In that case just go ahead and re-use it. */
if (!e->callee->callers->next_caller
&& cgraph_can_remove_if_no_direct_calls_p (e->callee)
/* Don't reuse if more than one function shares a comdat group.
If the other function(s) are needed, we need to emit even
this function out of line. */
&& !e->callee->same_comdat_group
&& !cgraph_new_nodes)
{
gcc_assert (!e->callee->global.inlined_to);
@ -311,7 +315,8 @@ cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original,
e->callee->global.inlined = true;
if (e->callee->callers->next_caller
|| !cgraph_can_remove_if_no_direct_calls_p (e->callee))
|| !cgraph_can_remove_if_no_direct_calls_p (e->callee)
|| e->callee->same_comdat_group)
duplicate = true;
cgraph_clone_inlined_nodes (e, true, update_original);

View File

@ -1,5 +1,5 @@
/* Basic IPA optimizations and utilities.
Copyright (C) 2003, 2004, 2005, 2007, 2008 Free Software Foundation,
Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009 Free Software Foundation,
Inc.
This file is part of GCC.
@ -179,7 +179,25 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
first = e->callee;
}
}
/* If any function in a comdat group is reachable, force
all other functions in the same comdat group to be
also reachable. */
if (node->same_comdat_group
&& node->reachable
&& !node->global.inlined_to)
{
for (next = node->same_comdat_group;
next != node;
next = next->same_comdat_group)
if (!next->reachable)
{
next->aux = first;
first = next;
next->reachable = true;
}
}
/* We can freely remove inline clones even if they are cloned, however if
function is clone of real clone, we must keep it around in order to
make materialize_clones produce function body with the changes
@ -302,8 +320,24 @@ cgraph_externally_visible_p (struct cgraph_node *node, bool whole_program)
/* COMDAT functions must be shared only if they have address taken,
otherwise we can produce our own private implementation with
-fwhole-program. */
if (DECL_COMDAT (node->decl) && (node->address_taken || !node->analyzed))
return true;
if (DECL_COMDAT (node->decl))
{
if (node->address_taken || !node->analyzed)
return true;
if (node->same_comdat_group)
{
struct cgraph_node *next;
/* If more than one function is in the same COMDAT group, it must
be shared even if just one function in the comdat group has
address taken. */
for (next = node->same_comdat_group;
next != node;
next = next->same_comdat_group)
if (next->address_taken || !next->analyzed)
return true;
}
}
if (MAIN_NAME_P (DECL_NAME (node->decl)))
return true;
if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (node->decl)))

View File

@ -306,6 +306,15 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
lto_output_sleb128_stream (ob->main_stream,
node->global.estimated_growth);
lto_output_uleb128_stream (ob->main_stream, node->global.inlined);
if (node->same_comdat_group)
{
ref = lto_cgraph_encoder_lookup (encoder, node->same_comdat_group);
gcc_assert (ref != LCC_NOT_FOUND);
}
else
ref = LCC_NOT_FOUND;
lto_output_sleb128_stream (ob->main_stream, ref);
if (node->same_body)
{
struct cgraph_node *alias;
@ -407,8 +416,30 @@ output_cgraph (cgraph_node_set set)
/* We should have moved all the inlines. */
gcc_assert (!callee->global.inlined_to);
lto_cgraph_encoder_encode (encoder, callee);
/* Also with each included function include all other functions
in the same comdat group. */
if (callee->same_comdat_group)
{
struct cgraph_node *next;
for (next = callee->same_comdat_group;
next != callee;
next = next->same_comdat_group)
if (!cgraph_node_in_set_p (next, set))
lto_cgraph_encoder_encode (encoder, next);
}
}
}
/* Also with each included function include all other functions
in the same comdat group. */
if (node->same_comdat_group)
{
struct cgraph_node *next;
for (next = node->same_comdat_group;
next != node;
next = next->same_comdat_group)
if (!cgraph_node_in_set_p (next, set))
lto_cgraph_encoder_encode (encoder, next);
}
}
/* Write out the nodes. */
@ -519,7 +550,7 @@ input_node (struct lto_file_decl_data *file_data,
bool clone_p;
int estimated_stack_size = 0;
int stack_frame_offset = 0;
int ref = LCC_NOT_FOUND;
int ref = LCC_NOT_FOUND, ref2 = LCC_NOT_FOUND;
int estimated_growth = 0;
int time = 0;
int size = 0;
@ -561,6 +592,7 @@ input_node (struct lto_file_decl_data *file_data,
size = lto_input_sleb128 (ib);
estimated_growth = lto_input_sleb128 (ib);
inlined = lto_input_uleb128 (ib);
ref2 = lto_input_sleb128 (ib);
same_body_count = lto_input_uleb128 (ib);
/* Make sure that we have not read this node before. Nodes that
@ -587,6 +619,9 @@ input_node (struct lto_file_decl_data *file_data,
node->global.estimated_growth = estimated_growth;
node->global.inlined = inlined;
/* Store a reference for now, and fix up later to be a pointer. */
node->same_comdat_group = (cgraph_node_ptr) (intptr_t) ref2;
while (same_body_count-- > 0)
{
tree alias_decl;
@ -707,13 +742,21 @@ input_cgraph_1 (struct lto_file_decl_data *file_data,
for (i = 0; VEC_iterate (cgraph_node_ptr, nodes, i, node); i++)
{
const int ref = (int) (intptr_t) node->global.inlined_to;
int ref = (int) (intptr_t) node->global.inlined_to;
/* Fixup inlined_to from reference to pointer. */
if (ref != LCC_NOT_FOUND)
node->global.inlined_to = VEC_index (cgraph_node_ptr, nodes, ref);
else
node->global.inlined_to = NULL;
ref = (int) (intptr_t) node->same_comdat_group;
/* Fixup same_comdat_group from reference to pointer. */
if (ref != LCC_NOT_FOUND)
node->same_comdat_group = VEC_index (cgraph_node_ptr, nodes, ref);
else
node->same_comdat_group = NULL;
}
VEC_free (cgraph_node_ptr, heap, nodes);

View File

@ -1,3 +1,10 @@
2009-12-10 Jakub Jelinek <jakub@redhat.com>
PR c++/42317
* g++.dg/opt/dtor2.C: New test.
* g++.dg/opt/dtor2.h: New file.
* g++.dg/opt/dtor2-aux.cc: New file.
2009-12-10 Daniel Franke <franke.daniel@gmail.com>
PR fortran/41369

View File

@ -0,0 +1,16 @@
// { dg-do compile }
// { dg-options "" }
#include "dtor2.h"
A::A () {}
A::~A () {}
void B::mb () {}
B::B (int) {}
B::~B () {}
void C::mc () {}
C::C (int x) : B (x) {}
D::~D () {}

View File

@ -0,0 +1,13 @@
// PR c++/42317
// { dg-do link }
// { dg-options "-O0" }
// { dg-additional-sources "dtor2-aux.cc" }
#include "dtor2.h"
D::D (int x) : C (x) {}
int
main ()
{
}

View File

@ -0,0 +1,29 @@
struct A
{
A ();
~A ();
};
struct B
{
A b;
virtual void mb ();
B (int);
virtual ~B ();
};
struct C : public B
{
virtual void mc ();
C (int);
~C ();
};
inline C::~C () {}
struct D : public C
{
A d;
D (int);
~D ();
};

View File

@ -1,3 +1,9 @@
2009-12-10 Jakub Jelinek <jakub@redhat.com>
PR c++/42317
* config/abi/pre/gnu.ver: Don't export certain base dtors that
weren't previously exported.
2009-12-10 Paolo Carlini <paolo.carlini@oracle.com>
PR libstdc++/42261 (take 2)

View File

@ -72,13 +72,18 @@ GLIBCXX_3.4 {
std::c[v-z]*;
# std::[d-g]*;
std::d[a-d]*;
std::d[f-z]*;
std::d[f-n]*;
std::domain_error::d*;
# std::domain_error::~d*;
std::d[p-z]*;
std::e[a-q]*;
std::error[^_]*;
std::e[s-z]*;
std::gslice*;
std::h[^a]*;
std::i[a-n]*;
std::i[a-m]*;
std::invalid_argument::i*;
# std::invalid_argument::~i*;
# std::ios_base::[A-Ha-z]*;
std::ios_base::[A-Ha-f]*;
std::ios_base::goodbit;
@ -94,7 +99,8 @@ GLIBCXX_3.4 {
std::istrstream*;
std::i[t-z]*;
std::[A-Zj-k]*;
std::length_error*;
std::length_error::l*;
# std::length_error::~l*;
std::logic_error*;
std::locale::[A-Za-e]*;
std::locale::facet::[A-Za-z]*;
@ -122,10 +128,14 @@ GLIBCXX_3.4 {
std::nu[^m]*;
std::num[^e]*;
std::ostrstream*;
std::out_of_range*;
std::overflow_error*;
std::out_of_range::o*;
# std::out_of_range::~o*;
std::overflow_error::o*;
# std::overflow_error::~o*;
# std::[p-q]*;
std::r[^e]*;
std::r[^ae]*;
std::range_error::r*;
# std::range_error::~r*;
std::re[^t]*;
# std::rethrow_exception
std::set_new_handler*;
@ -143,7 +153,8 @@ GLIBCXX_3.4 {
std::tr1::h[^a]*;
std::t[s-z]*;
# std::[A-Zu-z]*;
std::underflow_error*;
std::underflow_error::u*;
# std::underflow_error::~u*;
std::uncaught_exception*;
std::unexpected*;
std::[A-Zv-z]*;
@ -284,7 +295,8 @@ GLIBCXX_3.4 {
_ZNSt15basic_streambufI[cw]St11char_traitsI[cw]EEaSERKS2_;
# std::basic_stringbuf
_ZNSt15basic_stringbufI[cw]St11char_traitsI[cw]ESaI[cw]EE[CD]*;
_ZNSt15basic_stringbufI[cw]St11char_traitsI[cw]ESaI[cw]EEC*;
_ZNSt15basic_stringbufI[cw]St11char_traitsI[cw]ESaI[cw]EED[^2]*;
_ZNSt15basic_stringbufI[cw]St11char_traitsI[cw]ESaI[cw]EE[0-9][a-r]*;
_ZNSt15basic_stringbufI[cw]St11char_traitsI[cw]ESaI[cw]EE[0-9]seek*;
_ZNSt15basic_stringbufI[cw]St11char_traitsI[cw]ESaI[cw]EE[0-9]set*;
@ -639,6 +651,16 @@ GLIBCXX_3.4 {
_ZGVNSt[^1]*;
_ZGVNSt1[^7]*;
# complete and deleting destructors where base destructors should not
# be exported.
_ZNSt11range_errorD[01]Ev;
_ZNSt12domain_errorD[01]Ev;
_ZNSt12length_errorD[01]Ev;
_ZNSt12out_of_rangeD[01]Ev;
_ZNSt14overflow_errorD[01]Ev;
_ZNSt15underflow_errorD[01]Ev;
_ZNSt16invalid_argumentD[01]Ev;
# virtual function thunks
_ZThn8_NS*;
_ZThn16_NS*;
@ -891,7 +913,8 @@ GLIBCXX_3.4.10 {
_ZNSt15basic_streambufI[cw]St11char_traitsI[cw]EE6stosscEv;
_ZN9__gnu_cxx18stdio_sync_filebufI[cw]St11char_traitsI[cw]EE4syncEv;
_ZN9__gnu_cxx18stdio_sync_filebufI[cw]St11char_traitsI[cw]EE[5-9CD]*;
_ZN9__gnu_cxx18stdio_sync_filebufI[cw]St11char_traitsI[cw]EE[5-9C]*;
_ZN9__gnu_cxx18stdio_sync_filebufI[cw]St11char_traitsI[cw]EED[^2]*;
} GLIBCXX_3.4.9;