aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cgraph.h2
-rw-r--r--gcc/ipa.c90
-rw-r--r--gcc/lto-streamer-out.c17
-rw-r--r--gcc/varpool.c3
4 files changed, 89 insertions, 23 deletions
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 0334a15..99e4ee3 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -56,6 +56,7 @@ enum availability
struct lto_file_decl_data;
extern const char * const cgraph_availability_names[];
+extern const char * const ld_plugin_symbol_resolution_names[];
/* Function inlining information. */
@@ -695,6 +696,7 @@ void varpool_node_set_remove (varpool_node_set, struct varpool_node *);
void dump_varpool_node_set (FILE *, varpool_node_set);
void debug_varpool_node_set (varpool_node_set);
void ipa_discover_readonly_nonaddressable_vars (void);
+bool cgraph_comdat_can_be_unshared_p (struct cgraph_node *);
/* In predict.c */
bool cgraph_maybe_hot_edge_p (struct cgraph_edge *e);
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 3742f84..28e6872 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -588,6 +588,56 @@ ipa_discover_readonly_nonaddressable_vars (void)
fprintf (dump_file, "\n");
}
+/* Return true when there is a reference to node and it is not vtable. */
+static bool
+cgraph_address_taken_from_non_vtable_p (struct cgraph_node *node)
+{
+ int i;
+ struct ipa_ref *ref;
+ for (i = 0; ipa_ref_list_reference_iterate (&node->ref_list, i, ref); i++)
+ {
+ struct varpool_node *node;
+ if (ref->refered_type == IPA_REF_CGRAPH)
+ return true;
+ node = ipa_ref_varpool_node (ref);
+ if (!DECL_VIRTUAL_P (node->decl))
+ return true;
+ }
+ return false;
+}
+
+/* COMDAT functions must be shared only if they have address taken,
+ otherwise we can produce our own private implementation with
+ -fwhole-program.
+ Return true when turning COMDAT functoin static can not lead to wrong
+ code when the resulting object links with a library defining same COMDAT.
+
+ Virtual functions do have their addresses taken from the vtables,
+ but in C++ there is no way to compare their addresses for equality. */
+
+bool
+cgraph_comdat_can_be_unshared_p (struct cgraph_node *node)
+{
+ if ((cgraph_address_taken_from_non_vtable_p (node)
+ && !DECL_VIRTUAL_P (node->decl))
+ || !node->analyzed)
+ return false;
+ 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 (cgraph_address_taken_from_non_vtable_p (node)
+ && !DECL_VIRTUAL_P (next->decl))
+ return false;
+ }
+ return true;
+}
+
/* Return true when function NODE should be considered externally visible. */
static bool
@@ -613,6 +663,15 @@ cgraph_externally_visible_p (struct cgraph_node *node, bool whole_program, bool
if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (node->decl)))
return true;
+ /* When doing LTO or whole program, we can bring COMDAT functoins static.
+ This improves code quality and we know we will duplicate them at most twice
+ (in the case that we are not using plugin and link with object file
+ implementing same COMDAT) */
+ if ((in_lto_p || whole_program)
+ && DECL_COMDAT (node->decl)
+ && cgraph_comdat_can_be_unshared_p (node))
+ return false;
+
/* See if we have linker information about symbol not being used or
if we need to make guess based on the declaration.
@@ -635,27 +694,6 @@ cgraph_externally_visible_p (struct cgraph_node *node, bool whole_program, bool
;
else if (!whole_program)
return true;
- /* COMDAT functions must be shared only if they have address taken,
- otherwise we can produce our own private implementation with
- -fwhole-program. */
- else 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;
@@ -701,6 +739,16 @@ varpool_externally_visible_p (struct varpool_node *vnode, bool aliased)
if (!alias && vnode->resolution == LDPR_PREVAILING_DEF_IRONLY)
return false;
+ /* As a special case, the COMDAT virutal tables can be unshared.
+ In LTO mode turn vtables into static variables. The variable is readonly,
+ so this does not enable more optimization, but referring static var
+ is faster for dynamic linking. Also this match logic hidding vtables
+ from LTO symbol tables. */
+ if ((in_lto_p || flag_whole_program)
+ && !vnode->force_output
+ && DECL_COMDAT (vnode->decl) && DECL_VIRTUAL_P (vnode->decl))
+ return false;
+
/* When doing link time optimizations, hidden symbols become local. */
if (in_lto_p
&& (DECL_VISIBILITY (vnode->decl) == VISIBILITY_HIDDEN
diff --git a/gcc/lto-streamer-out.c b/gcc/lto-streamer-out.c
index 20e3991..b6414b6 100644
--- a/gcc/lto-streamer-out.c
+++ b/gcc/lto-streamer-out.c
@@ -2492,7 +2492,7 @@ produce_symtab (struct output_block *ob,
if (DECL_EXTERNAL (node->decl))
continue;
if (DECL_COMDAT (node->decl)
- && cgraph_can_remove_if_no_direct_calls_p (node))
+ && cgraph_comdat_can_be_unshared_p (node))
continue;
if (node->alias || node->global.inlined_to)
continue;
@@ -2506,7 +2506,7 @@ produce_symtab (struct output_block *ob,
if (!DECL_EXTERNAL (node->decl))
continue;
if (DECL_COMDAT (node->decl)
- && cgraph_can_remove_if_no_direct_calls_p (node))
+ && cgraph_comdat_can_be_unshared_p (node))
continue;
if (node->alias || node->global.inlined_to)
continue;
@@ -2521,6 +2521,14 @@ produce_symtab (struct output_block *ob,
vnode = lto_varpool_encoder_deref (varpool_encoder, i);
if (DECL_EXTERNAL (vnode->decl))
continue;
+ /* COMDAT virtual tables can be unshared. Do not declare them
+ in the LTO symbol table to prevent linker from forcing them
+ into the output. */
+ if (DECL_COMDAT (vnode->decl)
+ && !vnode->force_output
+ && vnode->finalized
+ && DECL_VIRTUAL_P (vnode->decl))
+ continue;
if (vnode->alias)
continue;
write_symbol (cache, &stream, vnode->decl, seen, false);
@@ -2532,6 +2540,11 @@ produce_symtab (struct output_block *ob,
vnode = lto_varpool_encoder_deref (varpool_encoder, i);
if (!DECL_EXTERNAL (vnode->decl))
continue;
+ if (DECL_COMDAT (vnode->decl)
+ && !vnode->force_output
+ && vnode->finalized
+ && DECL_VIRTUAL_P (vnode->decl))
+ continue;
if (vnode->alias)
continue;
write_symbol (cache, &stream, vnode->decl, seen, false);
diff --git a/gcc/varpool.c b/gcc/varpool.c
index 8822660..d266ce9 100644
--- a/gcc/varpool.c
+++ b/gcc/varpool.c
@@ -241,6 +241,9 @@ dump_varpool_node (FILE *f, struct varpool_node *node)
fprintf (f, " output");
if (node->externally_visible)
fprintf (f, " externally_visible");
+ if (node->resolution != LDPR_UNKNOWN)
+ fprintf (f, " %s",
+ ld_plugin_symbol_resolution_names[(int)node->resolution]);
if (node->in_other_partition)
fprintf (f, " in_other_partition");
else if (node->used_from_other_partition)