[PATCH] Use aliases instead of separate copies for !DECL_ONE_ONLY C[12] ctors or D[12] dtors with identical bodies (PR c++/3187, take 2)
Jakub Jelinek
jakub@redhat.com
Fri Nov 13 18:07:00 GMT 2009
Hi!
This is an updated version of the cdtor patch. In IRC discussions with
Honza we agreed it is better to basically share the same cgraph node for all
aliases, cgraph_node (alias) will transparently return cgraph_node (decl).
This patch only affects !DECL_ONE_ONLY cdtors. I'll post a follow-up patch
to handle them momentarily. libstdc++ .text sizes are:
[jakub@tyan-ft48-01 gcc]$ for i in 8 6 4; do readelf -WS
obj54$i/*-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6 | grep '\.text' |
awk '{printf "%d\n", strtonum("0x"$6)}'; done
x86_64-linux
vanilla 476614
this patch 456470
also DECL_ONE_ONLY 435382
i686-linux
vanilla 511720
this patch 490248
also DECL_ONE_ONLY 466760
Bootstrapped/regtested on x86_64-linux and i686-linux.
The patch ATM always generates aliases from the second in FOR_EACH_CLONE
to the first one, which is the function defined is *C2* resp. *D2* and
the alias for it is *C1* resp. *D1*. We could just store all 2-3 cdtors
into fns array and iterate on that array instead if we wanted to do it the
other way around (would make the testsuite changes unnecessary).
2009-11-13 Jakub Jelinek <jakub@redhat.com>
PR c++/3187
* cgraph.h (struct cgraph_node): Add same_body and same_body_alias
fields.
(cgraph_same_body_alias): New prototype.
* cgraphunit.c (cgraph_expand_function, cgraph_emit_thunks,
cgraph_materialize_all_clones): Handle same_body aliases.
* cgraph.c (cgraph_allocate_node): New function.
(cgraph_create_node): Use it.
(cgraph_node_for_decl, cgraph_node, cgraph_get_node,
cgraph_node_for_asm, cgraph_remove_node): Handle same_body aliases.
(cgraph_same_body_alias, cgraph_remove_same_body_alias): New
functions.
* cp-tree.h (expand_or_defer_fn_1): New prototype.
* decl2.c (cp_write_global_declarations): Mark as !DECL_EXTERNAL
also all same_body aliases.
* semantics.c (expand_or_defer_fn): Move most of the function
except registering with cgraph to ...
(expand_or_defer_fn_1): ... here. New function.
* optimize.c: Include cgraph.h.
(maybe_clone_body): If in charge parm is not used and both base
and complete clones are created and are not comdat, tell cgraph
they have the same body.
* Make-lang.in (cp/optimize.o): Depend on $(CGRAPH_H).
* g++.dg/abi/mangle26.C: Also match *C2* definition.
* g++.dg/abi/mangle27.C: Likewise.
* g++.dg/abi/mangle28.C: Likewise.
* g++.dg/abi/mangle29.C: Likewise.
--- gcc/cgraph.c.jj 2009-11-13 12:51:04.000000000 +0100
+++ gcc/cgraph.c 2009-11-13 12:55:47.000000000 +0100
@@ -425,7 +425,11 @@ cgraph_node_for_decl (tree decl)
key.decl = decl;
slot = htab_find_slot (cgraph_hash, &key, NO_INSERT);
if (slot && *slot)
- node = (struct cgraph_node *) *slot;
+ {
+ node = (struct cgraph_node *) *slot;
+ if (node->same_body_alias)
+ node = node->same_body;
+ }
}
return node;
@@ -442,10 +446,10 @@ eq_node (const void *p1, const void *p2)
return DECL_UID (n1->decl) == DECL_UID (n2->decl);
}
-/* Allocate new callgraph node and insert it into basic data structures. */
+/* Allocate new callgraph node. */
-static struct cgraph_node *
-cgraph_create_node (void)
+static inline struct cgraph_node *
+cgraph_allocate_node (void)
{
struct cgraph_node *node;
@@ -460,6 +464,16 @@ cgraph_create_node (void)
node->uid = cgraph_max_uid++;
}
+ return node;
+}
+
+/* Allocate new callgraph node and insert it into basic data structures. */
+
+static struct cgraph_node *
+cgraph_create_node (void)
+{
+ struct cgraph_node *node = cgraph_allocate_node ();
+
node->next = cgraph_nodes;
node->pid = -1;
node->order = cgraph_order++;
@@ -491,6 +505,8 @@ cgraph_node (tree decl)
if (*slot)
{
node = *slot;
+ if (node->same_body_alias)
+ node = node->same_body;
return node;
}
@@ -521,6 +537,52 @@ cgraph_node (tree decl)
return node;
}
+/* Attempt to mark ALIAS as an alias to DECL. Return TRUE if successful.
+ Same body aliases are output whenever the body of DECL is output,
+ and cgraph_node (ALIAS) transparently returns cgraph_node (DECL). */
+
+bool
+cgraph_same_body_alias (tree alias, tree decl)
+{
+ struct cgraph_node key, *alias_node, *decl_node, **slot;
+
+ gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
+ gcc_assert (TREE_CODE (alias) == FUNCTION_DECL);
+ gcc_assert (!assembler_name_hash);
+
+#ifndef ASM_OUTPUT_DEF
+ /* If aliases aren't supported by the assembler, fail. */
+ return false;
+#endif
+
+ /* Comdat same body aliases are only supported when comdat groups
+ are supported and the symbols are weak. */
+ if (DECL_ONE_ONLY (decl) && (!HAVE_COMDAT_GROUP || !DECL_WEAK (decl)))
+ return false;
+
+ decl_node = cgraph_node (decl);
+
+ key.decl = alias;
+
+ slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, INSERT);
+
+ /* If the cgraph_node has been already created, fail. */
+ if (*slot)
+ return false;
+
+ alias_node = cgraph_allocate_node ();
+ alias_node->decl = alias;
+ alias_node->same_body_alias = 1;
+ alias_node->same_body = decl_node;
+ alias_node->previous = NULL;
+ if (decl_node->same_body)
+ decl_node->same_body->previous = alias_node;
+ alias_node->next = decl_node->same_body;
+ decl_node->same_body = alias_node;
+ *slot = alias_node;
+ return true;
+}
+
/* Returns the cgraph node assigned to DECL or NULL if no cgraph node
is assigned. */
@@ -540,7 +602,11 @@ cgraph_get_node (tree decl)
NO_INSERT);
if (slot && *slot)
- node = *slot;
+ {
+ node = *slot;
+ if (node->same_body_alias)
+ node = node->same_body;
+ }
return node;
}
@@ -601,9 +667,23 @@ cgraph_node_for_asm (tree asmname)
it is __builtin_strlen and strlen, for instance. Do we need to
record them all? Original implementation marked just first one
so lets hope for the best. */
- if (*slot)
- continue;
- *slot = node;
+ if (!*slot)
+ *slot = node;
+ if (node->same_body)
+ {
+ struct cgraph_node *alias;
+
+ for (alias = node->same_body; alias; alias = alias->next)
+ {
+ hashval_t hash;
+ name = DECL_ASSEMBLER_NAME (alias->decl);
+ hash = decl_assembler_name_hash (name);
+ slot = htab_find_slot_with_hash (assembler_name_hash, name,
+ hash, INSERT);
+ if (!*slot)
+ *slot = alias;
+ }
+ }
}
}
@@ -612,7 +692,12 @@ cgraph_node_for_asm (tree asmname)
NO_INSERT);
if (slot)
- return (struct cgraph_node *) *slot;
+ {
+ node = (struct cgraph_node *) *slot;
+ if (node->same_body_alias)
+ node = node->same_body;
+ return node;
+ }
return NULL;
}
@@ -1146,6 +1231,44 @@ cgraph_release_function_body (struct cgr
DECL_INITIAL (node->decl) = error_mark_node;
}
+/* Remove same body alias node. */
+
+static void
+cgraph_remove_same_body_alias (struct cgraph_node *node)
+{
+ void **slot;
+ int uid = node->uid;
+
+ gcc_assert (node->same_body_alias);
+ if (node->previous)
+ node->previous->next = node->next;
+ else
+ node->same_body->same_body = node->next;
+ if (node->next)
+ node->next->previous = node->previous;
+ node->next = NULL;
+ node->previous = NULL;
+ slot = htab_find_slot (cgraph_hash, node, NO_INSERT);
+ if (*slot == node)
+ htab_clear_slot (cgraph_hash, slot);
+ if (assembler_name_hash)
+ {
+ tree name = DECL_ASSEMBLER_NAME (node->decl);
+ slot = htab_find_slot_with_hash (assembler_name_hash, name,
+ decl_assembler_name_hash (name),
+ NO_INSERT);
+ if (slot && *slot == node)
+ htab_clear_slot (assembler_name_hash, slot);
+ }
+
+ /* Clear out the node to NULL all pointers and add the node to the free
+ list. */
+ memset (node, 0, sizeof(*node));
+ node->uid = uid;
+ NEXT_FREE_NODE (node) = free_nodes;
+ free_nodes = node;
+}
+
/* Remove the node from cgraph. */
void
@@ -1281,6 +1404,9 @@ cgraph_remove_node (struct cgraph_node *
node->clone_of->clones = node->clones;
}
+ while (node->same_body)
+ cgraph_remove_same_body_alias (node->same_body);
+
/* 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.
--- gcc/cgraphunit.c.jj 2009-11-13 12:51:06.000000000 +0100
+++ gcc/cgraphunit.c 2009-11-13 12:55:47.000000000 +0100
@@ -1028,7 +1028,7 @@ cgraph_analyze_functions (void)
static void
cgraph_emit_thunks (void)
{
- struct cgraph_node *n;
+ struct cgraph_node *n, *alias;
for (n = cgraph_nodes; n; n = n->next)
{
@@ -1041,7 +1041,12 @@ cgraph_emit_thunks (void)
cgraph_mark_functions_to_output in cgraph_optimize). */
if (n->reachable
&& !DECL_EXTERNAL (n->decl))
- lang_hooks.callgraph.emit_associated_thunks (n->decl);
+ {
+ lang_hooks.callgraph.emit_associated_thunks (n->decl);
+ for (alias = n->same_body; alias; alias = alias->next)
+ if (!DECL_EXTERNAL (alias->decl))
+ lang_hooks.callgraph.emit_associated_thunks (alias->decl);
+ }
}
}
@@ -1163,6 +1168,14 @@ cgraph_expand_function (struct cgraph_no
/* Make sure that BE didn't give up on compiling. */
gcc_assert (TREE_ASM_WRITTEN (decl));
current_function_decl = NULL;
+ if (node->same_body)
+ {
+ struct cgraph_node *alias;
+ bool saved_alias = node->alias;
+ for (alias = node->same_body; alias; alias = alias->next)
+ assemble_alias (alias->decl, DECL_ASSEMBLER_NAME (decl));
+ node->alias = saved_alias;
+ }
gcc_assert (!cgraph_preserve_function_body_p (decl));
cgraph_release_function_body (node);
/* Eliminate all call edges. This is important so the GIMPLE_CALL no longer
@@ -1910,7 +1923,22 @@ cgraph_materialize_all_clones (void)
{
gimple new_stmt;
gimple_stmt_iterator gsi;
-
+
+ if (e->callee->same_body)
+ {
+ struct cgraph_node *alias;
+
+ for (alias = e->callee->same_body;
+ alias;
+ alias = alias->next)
+ if (decl == alias->decl)
+ break;
+ /* Don't update call from same body alias to the real
+ function. */
+ if (alias)
+ continue;
+ }
+
if (cgraph_dump_file)
{
fprintf (cgraph_dump_file, "updating call of %s in %s:",
--- gcc/cp/optimize.c.jj 2009-11-13 12:51:04.000000000 +0100
+++ gcc/cp/optimize.c 2009-11-13 14:51:21.000000000 +0100
@@ -42,6 +42,7 @@ along with GCC; see the file COPYING3.
#include "tree-dump.h"
#include "gimple.h"
#include "tree-iterator.h"
+#include "cgraph.h"
/* Prototypes. */
@@ -149,8 +150,10 @@ bool
maybe_clone_body (tree fn)
{
tree clone;
- tree complete_dtor = NULL_TREE;
+ tree fns[2];
bool first = true;
+ bool in_charge_parm_used;
+ int seen = -1;
/* We only clone constructors and destructors. */
if (!DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (fn)
@@ -160,14 +163,19 @@ maybe_clone_body (tree fn)
/* 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;
+
/* Look for the complete destructor which may be used to build the
delete destructor. */
FOR_EACH_CLONE (clone, fn)
- if (DECL_NAME (clone) == complete_dtor_identifier)
- {
- complete_dtor = clone;
- break;
- }
+ if (DECL_NAME (clone) == complete_dtor_identifier
+ || DECL_NAME (clone) == complete_ctor_identifier)
+ fns[0] = clone;
+ else if (DECL_NAME (clone) == base_dtor_identifier
+ || DECL_NAME (clone) == base_ctor_identifier)
+ fns[1] = clone;
/* We know that any clones immediately follow FN in the TYPE_METHODS
list. */
@@ -177,6 +185,7 @@ maybe_clone_body (tree fn)
tree parm;
tree clone_parm;
int parmno;
+ bool alias = false;
struct pointer_map_t *decl_map;
/* Update CLONE's source position information to match FN's. */
@@ -223,12 +232,30 @@ maybe_clone_body (tree fn)
/* 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
+ && fns[0]
+ && fns[1]
+ && (fns[0] == clone || fns[1] == clone)
+ && !flag_use_repository)
+ {
+ if (seen == -1)
+ seen = fns[0] == clone ? 0 : 1;
+ else if (DECL_INTERFACE_KNOWN (fns[seen])
+ && !DECL_ONE_ONLY (fns[seen])
+ && cgraph_same_body_alias (clone, fns[seen]))
+ alias = true;
+ }
+
/* Build the delete destructor by calling complete destructor
and delete function. */
if (DECL_NAME (clone) == deleting_dtor_identifier)
- build_delete_destructor_body (clone, complete_dtor);
+ build_delete_destructor_body (clone, fns[0]);
+ else if (alias)
+ /* No need to populate body. */ ;
else
- {
+ {
/* Remap the parameters. */
decl_map = pointer_map_create ();
for (parmno = 0,
@@ -291,7 +318,10 @@ maybe_clone_body (tree fn)
/* Now, expand this function into RTL, if appropriate. */
finish_function (0);
BLOCK_ABSTRACT_ORIGIN (DECL_INITIAL (clone)) = DECL_INITIAL (fn);
- expand_or_defer_fn (clone);
+ if (alias)
+ expand_or_defer_fn_1 (clone);
+ else
+ expand_or_defer_fn (clone);
first = false;
}
pop_from_top_level ();
--- gcc/cp/decl2.c.jj 2009-11-13 12:51:06.000000000 +0100
+++ gcc/cp/decl2.c 2009-11-13 12:55:47.000000000 +0100
@@ -3658,7 +3658,19 @@ cp_write_global_declarations (void)
if (DECL_NOT_REALLY_EXTERN (decl)
&& DECL_INITIAL (decl)
&& decl_needed_p (decl))
- DECL_EXTERNAL (decl) = 0;
+ {
+ struct cgraph_node *node = cgraph_get_node (decl), *alias;
+
+ DECL_EXTERNAL (decl) = 0;
+ /* If we mark !DECL_EXTERNAL one of the same body aliases,
+ we need to mark all of them that way. */
+ if (node && node->same_body)
+ {
+ DECL_EXTERNAL (node->decl) = 0;
+ for (alias = node->same_body; alias; alias = alias->next)
+ DECL_EXTERNAL (alias->decl) = 0;
+ }
+ }
/* If we're going to need to write this function out, and
there's already a body for it, create RTL for it now.
--- gcc/cp/semantics.c.jj 2009-11-13 12:51:04.000000000 +0100
+++ gcc/cp/semantics.c 2009-11-13 12:55:47.000000000 +0100
@@ -3316,8 +3316,8 @@ emit_associated_thunks (tree fn)
/* Generate RTL for FN. */
-void
-expand_or_defer_fn (tree fn)
+bool
+expand_or_defer_fn_1 (tree fn)
{
/* When the parser calls us after finishing the body of a template
function, we don't really want to expand the body. */
@@ -3331,7 +3331,7 @@ expand_or_defer_fn (tree fn)
is not a GC root. */
if (!function_depth)
ggc_collect ();
- return;
+ return false;
}
gcc_assert (DECL_SAVED_TREE (fn));
@@ -3344,7 +3344,7 @@ expand_or_defer_fn (tree fn)
it out, even though we haven't. */
TREE_ASM_WRITTEN (fn) = 1;
DECL_SAVED_TREE (fn) = NULL_TREE;
- return;
+ return false;
}
/* We make a decision about linkage for these functions at the end
@@ -3391,14 +3391,23 @@ expand_or_defer_fn (tree fn)
/* 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)
- return;
+ return false;
- function_depth++;
+ return true;
+}
+
+void
+expand_or_defer_fn (tree fn)
+{
+ if (expand_or_defer_fn_1 (fn))
+ {
+ function_depth++;
- /* Expand or defer, at the whim of the compilation unit manager. */
- cgraph_finalize_function (fn, function_depth > 1);
+ /* Expand or defer, at the whim of the compilation unit manager. */
+ cgraph_finalize_function (fn, function_depth > 1);
- function_depth--;
+ function_depth--;
+ }
}
struct nrv_data
--- gcc/cp/cp-tree.h.jj 2009-11-13 12:51:06.000000000 +0100
+++ gcc/cp/cp-tree.h 2009-11-13 12:55:47.000000000 +0100
@@ -5037,6 +5037,7 @@ extern void finish_eh_cleanup (tree);
extern void emit_associated_thunks (tree);
extern void finish_mem_initializers (tree);
extern tree check_template_template_default_arg (tree);
+extern bool expand_or_defer_fn_1 (tree);
extern void expand_or_defer_fn (tree);
extern void check_accessibility_of_qualified_id (tree, tree, tree);
extern tree finish_qualified_id_expr (tree, tree, bool, bool,
--- gcc/cp/Make-lang.in.jj 2009-11-13 12:51:04.000000000 +0100
+++ gcc/cp/Make-lang.in 2009-11-13 12:55:47.000000000 +0100
@@ -303,7 +303,7 @@ cp/semantics.o: cp/semantics.c $(CXX_TRE
cp/dump.o: cp/dump.c $(CXX_TREE_H) $(TM_H) $(TREE_DUMP_H)
cp/optimize.o: cp/optimize.c $(CXX_TREE_H) $(TM_H) rtl.h $(INTEGRATE_H) \
insn-config.h input.h $(PARAMS_H) debug.h $(TREE_INLINE_H) $(GIMPLE_H) \
- $(TARGET_H) tree-iterator.h
+ $(TARGET_H) tree-iterator.h $(CGRAPH_H)
cp/mangle.o: cp/mangle.c $(CXX_TREE_H) $(TM_H) toplev.h $(REAL_H) \
gt-cp-mangle.h $(TARGET_H) $(TM_P_H)
cp/parser.o: cp/parser.c $(CXX_TREE_H) $(TM_H) $(DIAGNOSTIC_H) gt-cp-parser.h \
--- gcc/cgraph.h.jj 2009-11-13 12:51:06.000000000 +0100
+++ gcc/cgraph.h 2009-11-13 12:55:47.000000000 +0100
@@ -184,6 +184,9 @@ struct GTY((chain_next ("%h.next"), chai
struct cgraph_node *prev_sibling_clone;
struct cgraph_node *clones;
struct cgraph_node *clone_of;
+ /* For normal nodes pointer to the list of alias nodes, in alias
+ nodes pointer to the normal node. */
+ struct cgraph_node *same_body;
/* 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;
@@ -228,6 +231,9 @@ struct GTY((chain_next ("%h.next"), chai
unsigned alias : 1;
/* Set for nodes that was constructed and finalized by frontend. */
unsigned finalized_by_frontend : 1;
+ /* Set for alias nodes, same_body points to the node they are alias of
+ and they are linked through the next/previous pointers. */
+ unsigned same_body_alias : 1;
};
typedef struct cgraph_node *cgraph_node_ptr;
@@ -403,6 +409,7 @@ struct cgraph_edge *cgraph_create_edge (
struct cgraph_node * cgraph_get_node (tree);
struct cgraph_node *cgraph_node (tree);
+bool cgraph_same_body_alias (tree, tree);
struct cgraph_node *cgraph_node_for_asm (tree);
struct cgraph_node *cgraph_node_for_decl (tree);
struct cgraph_edge *cgraph_edge (struct cgraph_node *, gimple);
--- gcc/testsuite/g++.dg/abi/mangle27.C.jj 2008-09-30 16:55:21.000000000 +0200
+++ gcc/testsuite/g++.dg/abi/mangle27.C 2009-11-13 12:55:47.000000000 +0100
@@ -11,4 +11,4 @@ namespace std {
std::basic_iostream<char,std::char_traits<char> > s1;
-// { dg-final { scan-assembler "\n_?_ZNSdC1Ev\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZNSdC\[12\]Ev\[: \t\n\]" } }
--- gcc/testsuite/g++.dg/abi/mangle29.C.jj 2008-09-30 16:55:21.000000000 +0200
+++ gcc/testsuite/g++.dg/abi/mangle29.C 2009-11-13 12:55:47.000000000 +0100
@@ -11,4 +11,4 @@ namespace std {
std::basic_ostream<char,std::char_traits<char> > s1;
-// { dg-final { scan-assembler "\n_?_ZNSoC1Ev\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZNSoC\[12\]Ev\[: \t\n\]" } }
--- gcc/testsuite/g++.dg/abi/mangle26.C.jj 2008-09-30 16:55:21.000000000 +0200
+++ gcc/testsuite/g++.dg/abi/mangle26.C 2009-11-13 12:55:47.000000000 +0100
@@ -11,4 +11,4 @@ namespace std {
std::A a;
-// { dg-final { scan-assembler "\n_?_ZNSt1AC1Ev\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZNSt1AC\[12\]Ev\[: \t\n\]" } }
--- gcc/testsuite/g++.dg/abi/mangle28.C.jj 2008-09-30 16:55:21.000000000 +0200
+++ gcc/testsuite/g++.dg/abi/mangle28.C 2009-11-13 12:55:47.000000000 +0100
@@ -11,4 +11,4 @@ namespace std {
std::basic_istream<char,std::char_traits<char> > s1;
-// { dg-final { scan-assembler "\n_?_ZNSiC1Ev\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZNSiC\[12\]Ev\[: \t\n\]" } }
Jakub
More information about the Gcc-patches
mailing list