diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/cgraph.h | 2 | ||||
-rw-r--r-- | gcc/ipa.c | 90 | ||||
-rw-r--r-- | gcc/lto-streamer-out.c | 17 | ||||
-rw-r--r-- | gcc/varpool.c | 3 |
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); @@ -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) |