Hi, this patch fixes failure in lto/attr-weakref-1 with plugin disabled setup. The issue is that currently gas rejects .weakref referring to symbol defined in the same translation unit. /tmp/cc0Zk0UZ.s: Assembler messages: /tmp/cc0Zk0UZ.s:121: Error: symbol definition loop encountered at `callmealias.lto_priv.0'
The message is wrong (there is no loop) and should be also fixed at gas side https://sourceware.org/bugzilla/show_bug.cgi?id=19498 This patch makes weakref to turn into static alias when aliases are supported and the target binds to current def. When alias target is defined in current unit and thus we know it will be defined (possibly to other definition) and there is no used attribute, we turn the weakref into transparent alias. Both should result in slightly better code, but the only code that benefits is code that defines weakrefs target which in practice IMO means that you need to LTO glibc itself in. This is currently not supported (one can't LTO runtime libraries) Bootstrapepd/regtested x86_64-linux, will commit it shortly. Honza PR ipa/68881 * cgraph.h (symtab_node::copy_visibility_from): New function. * symtab.c (symtab_node::copy_visibility_from): New function. * ipa-visibility.c (optimize_weakref): New function. (function_and_variable_visibility): Use it. Index: cgraph.h =================================================================== --- cgraph.h (revision 234669) +++ cgraph.h (working copy) @@ -293,6 +293,9 @@ public: /* Make DECL local. */ void make_decl_local (void); + /* Copy visibility from N. */ + void copy_visibility_from (symtab_node *n); + /* Return desired alignment of the definition. This is NOT alignment useful to access THIS, because THIS may be interposable and DECL_ALIGN should be used instead. It however must be guaranteed when output definition Index: ipa-visibility.c =================================================================== --- ipa-visibility.c (revision 234669) +++ ipa-visibility.c (working copy) @@ -452,6 +452,84 @@ update_visibility_by_resolution_info (sy } } +/* Try to get rid of weakref. */ + +static void +optimize_weakref (symtab_node *node) +{ +#ifdef ASM_OUTPUT_DEF + bool aliases_supported = true; +#else + bool aliases_supported = false; +#endif + bool strip_weakref = false; + bool static_alias = false; + + gcc_assert (node->weakref); + + /* Weakrefs with no target defined can not be optimized. */ + if (!node->analyzed) + return; + symtab_node *target = node->get_alias_target (); + + /* Weakrefs to weakrefs can be optimized only if target can be. */ + if (target->weakref) + optimize_weakref (target); + if (target->weakref) + return; + + /* If we have definition of weakref's target and we know it binds locally, + we can turn weakref to static alias. */ + if (target->definition && decl_binds_to_current_def_p (target->decl) + && aliases_supported) + strip_weakref = static_alias = true; + /* Otherwise we can turn weakref into transparent alias. This transformation + may break asm statements which directly refers to symbol name and expect + GNU as to translate it via .weakref directive. So do not optimize when + DECL_PRESERVED is set and .weakref is supported. */ + else if ((!DECL_PRESERVE_P (target->decl) + || IDENTIFIER_TRANSPARENT_ALIAS (DECL_ASSEMBLER_NAME (node->decl))) + && !DECL_WEAK (target->decl) + && !DECL_EXTERNAL (target->decl) + && ((target->definition && !target->can_be_discarded_p ()) + || target->resolution != LDPR_UNDEF)) + strip_weakref = true; + if (!strip_weakref) + return; + node->weakref = false; + IDENTIFIER_TRANSPARENT_ALIAS (DECL_ASSEMBLER_NAME (node->decl)) = 0; + TREE_CHAIN (DECL_ASSEMBLER_NAME (node->decl)) = NULL_TREE; + DECL_ATTRIBUTES (node->decl) = remove_attribute ("weakref", + DECL_ATTRIBUTES + (node->decl)); + + if (dump_file) + fprintf (dump_file, "Optimizing weakref %s %s\n", + node->name(), + static_alias ? "as static alias" : "as transparent alias"); + + if (static_alias) + { + /* make_decl_local will shortcircuit if it doesn't see TREE_PUBLIC. + be sure it really clears the WEAK flag. */ + TREE_PUBLIC (node->decl) = true; + node->make_decl_local (); + node->forced_by_abi = false; + node->resolution = LDPR_PREVAILING_DEF_IRONLY; + node->externally_visible = false; + gcc_assert (!DECL_WEAK (node->decl)); + node->transparent_alias = false; + } + else + { + symtab->change_decl_assembler_name + (node->decl, DECL_ASSEMBLER_NAME (node->get_alias_target ()->decl)); + node->transparent_alias = true; + node->copy_visibility_from (target); + } + gcc_assert (node->alias); +} + /* Decide on visibility of all symbols. */ static unsigned int @@ -594,6 +672,8 @@ function_and_variable_visibility (bool w } update_visibility_by_resolution_info (node); + if (node->weakref) + optimize_weakref (node); } FOR_EACH_DEFINED_FUNCTION (node) { @@ -660,6 +740,8 @@ function_and_variable_visibility (bool w || ! (ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (TREE_TYPE (vnode->decl)))))) DECL_COMMON (vnode->decl) = 0; + if (vnode->weakref) + optimize_weakref (vnode); } FOR_EACH_DEFINED_VARIABLE (vnode) { Index: symtab.c =================================================================== --- symtab.c (revision 234669) +++ symtab.c (working copy) @@ -1287,6 +1287,61 @@ symtab_node::make_decl_local (void) SYMBOL_REF_WEAK (symbol) = DECL_WEAK (decl); } +/* Copy visibility from N. + This is useful when THIS becomes a transparent alias of N. */ + +void +symtab_node::copy_visibility_from (symtab_node *n) +{ + gcc_checking_assert (n->weakref == weakref); + + ipa_ref *ref; + for (unsigned i = 0; iterate_direct_aliases (i, ref); i++) + { + struct symtab_node *alias = ref->referring; + if (alias->transparent_alias) + alias->copy_visibility_from (n); + } + + if (TREE_CODE (decl) == VAR_DECL) + { + DECL_COMMON (decl) = DECL_COMMON (n->decl); + /* ADDRESSABLE flag is not defined for public symbols. */ + if (TREE_PUBLIC (decl) && !TREE_PUBLIC (n->decl)) + TREE_ADDRESSABLE (decl) = 1; + TREE_STATIC (decl) = TREE_STATIC (n->decl); + } + else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); + + DECL_COMDAT (decl) = DECL_COMDAT (n->decl); + DECL_WEAK (decl) = DECL_WEAK (n->decl); + DECL_EXTERNAL (decl) = DECL_EXTERNAL (n->decl); + DECL_VISIBILITY_SPECIFIED (decl) = DECL_VISIBILITY_SPECIFIED (n->decl); + DECL_VISIBILITY (decl) = DECL_VISIBILITY (n->decl); + TREE_PUBLIC (decl) = TREE_PUBLIC (n->decl); + DECL_DLLIMPORT_P (decl) = DECL_DLLIMPORT_P (n->decl); + resolution = n->resolution; + set_comdat_group (n->get_comdat_group ()); + call_for_symbol_and_aliases (symtab_node::set_section, + const_cast<char *>(n->get_section ()), true); + externally_visible = n->externally_visible; + if (!DECL_RTL_SET_P (decl)) + return; + + /* Update rtl flags. */ + make_decl_rtl (decl); + + rtx rtl = DECL_RTL (decl); + if (!MEM_P (rtl)) + return; + + rtx symbol = XEXP (rtl, 0); + if (GET_CODE (symbol) != SYMBOL_REF) + return; + + SYMBOL_REF_WEAK (symbol) = DECL_WEAK (decl); +} + /* Walk the alias chain to return the symbol NODE is alias of. If NODE is not an alias, return NODE. Assumes NODE is known to be alias. */