https://gcc.gnu.org/g:4003b752f76e31fcf2d7f791d4c2dd2f2eed4318
commit r16-6688-g4003b752f76e31fcf2d7f791d4c2dd2f2eed4318 Author: Michal Jires <[email protected]> Date: Sun Nov 16 00:54:00 2025 +0100 ipa: Add flag ref_by_asm to symtab_node ref_by_asm will be used by toplevel assembly to mark symbols that cannot be removed. It largely overlaps with force_output. Main difference is that ref_by_asm is meaningful on declarations by not removing them. force_output with declaration is ignored, which cannot be easily changed, since several places depend on this behavior. Global ref_by_asm should not be localized, because they cannot benefit from it. It would only result in complications, for example by renaming the symbol. Notes on different solutions in unreachability analysis: First unreachability analysis is done in analyze_functions. Marking ref_by_asm declarations as needed from the start would require reprocessing, because some declarations may gain definition during the analysis. Since at this point declarations do not need adding any other symbol, we can check for ref_by_asm at the end, next to referred_to_p check. Second unreachability analysis is in remove_unreachable_nodes. Here declarations (or symbols in_other_partition) may require an alias. So there we need to add the declarations from the start. gcc/ChangeLog: * cgraph.cc (cgraph_node_cannot_be_local_p_1): Check ref_by_asm. (cgraph_node::verify_node): Likewise. * cgraph.h (cgraph_node::only_called_directly_or_aliased_p): Likewise. (cgraph_node::can_remove_if_no_direct_calls_and_refs_p): Likewise. (varpool_node::can_remove_if_no_refs_p): Likewise. (varpool_node::all_refs_explicit_p): Likewise. * cgraphunit.cc (symtab_node::needed_p): Likewise. (analyze_functions): Likewise. * gimple-ssa-pta-constraints.cc (refered_from_nonlocal_fn): Likewise. (refered_from_nonlocal_var): Likewise. (ipa_create_global_variable_infos): Likewise. * ipa-comdats.cc (ipa_comdats): Likewise. * ipa-visibility.cc (cgraph_externally_visible_p): Likewise. (varpool_node::externally_visible_p): Likewise. * ipa.cc (symbol_table::remove_unreachable_nodes): Likewise. * lto-cgraph.cc (lto_output_node): Output ref_by_asm. (lto_output_varpool_node): Likewise. (input_overwrite_node): Input ref_by_asm. (input_varpool_node): Likewise. * symtab.cc (address_matters_1): Check ref_by_asm. gcc/lto/ChangeLog: * lto-symtab.cc (lto_cgraph_replace_node): Propagate ref_by_asm. (lto_varpool_replace_node): Propagate ref_by_asm. Diff: --- gcc/cgraph.cc | 6 ++++++ gcc/cgraph.h | 10 +++++++++- gcc/cgraphunit.cc | 4 +++- gcc/gimple-ssa-pta-constraints.cc | 5 ++++- gcc/ipa-comdats.cc | 1 + gcc/ipa-visibility.cc | 4 ++++ gcc/ipa.cc | 7 +++++++ gcc/lto-cgraph.cc | 4 ++++ gcc/lto/lto-symtab.cc | 3 +++ gcc/symtab.cc | 2 +- 10 files changed, 42 insertions(+), 4 deletions(-) diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc index 2a02d219e93c..b594b0acc318 100644 --- a/gcc/cgraph.cc +++ b/gcc/cgraph.cc @@ -2844,6 +2844,7 @@ static bool cgraph_node_cannot_be_local_p_1 (cgraph_node *node, void *) { return !(!node->force_output + && !node->ref_by_asm && !node->ifunc_resolver /* Limitation of gas requires us to output targets of symver aliases as global symbols. This is binutils PR 25295. */ @@ -4004,6 +4005,11 @@ cgraph_node::verify_node (void) error ("inline clone is forced to output"); error_found = true; } + if (inlined_to && ref_by_asm) + { + error ("inline clone is referenced by assembly"); + error_found = true; + } if (symtab->state != LTO_STREAMING) { if (calls_comdat_local && !same_comdat_group) diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 652d045b5711..f0f31c1487c5 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -145,6 +145,7 @@ public: symver (false), analyzed (false), writeonly (false), refuse_visibility_changes (false), externally_visible (false), no_reorder (false), force_output (false), forced_by_abi (false), + ref_by_asm (false), unique_name (false), implicit_section (false), body_removed (false), semantic_interposition (flag_semantic_interposition), used_from_other_partition (false), in_other_partition (false), @@ -593,6 +594,10 @@ public: exported. Unlike FORCE_OUTPUT this flag gets cleared to symbols promoted to static and it does not inhibit optimization. */ unsigned forced_by_abi : 1; + /* Referenced from toplevel assembly. Must not be removed. + Static symbol may be renamed. Global symbol should not be renamed. + Unlike force_output, can be on declarations. */ + unsigned ref_by_asm : 1; /* True when the name is known to be unique and thus it does not need mangling. */ unsigned unique_name : 1; /* Specify whether the section was set by user or by @@ -3437,6 +3442,7 @@ cgraph_node::only_called_directly_or_aliased_p (void) { gcc_assert (!inlined_to); return (!force_output && !address_taken + && !ref_by_asm && !ifunc_resolver && !used_from_other_partition && !DECL_VIRTUAL_P (decl) @@ -3457,7 +3463,7 @@ cgraph_node::can_remove_if_no_direct_calls_and_refs_p (void) if (DECL_EXTERNAL (decl)) return true; /* When function is needed, we cannot remove it. */ - if (force_output || used_from_other_partition) + if (force_output || used_from_other_partition || ref_by_asm) return false; if (DECL_STATIC_CONSTRUCTOR (decl) || DECL_STATIC_DESTRUCTOR (decl)) @@ -3489,6 +3495,7 @@ varpool_node::can_remove_if_no_refs_p (void) if (DECL_EXTERNAL (decl)) return true; return (!force_output && !used_from_other_partition + && !ref_by_asm && ((DECL_COMDAT (decl) && !forced_by_abi && !used_from_object_file_p ()) @@ -3507,6 +3514,7 @@ varpool_node::all_refs_explicit_p () return (definition && !externally_visible && !used_from_other_partition + && !ref_by_asm && !force_output); } diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc index fe1abca637b0..8a29cafa0f95 100644 --- a/gcc/cgraphunit.cc +++ b/gcc/cgraphunit.cc @@ -255,6 +255,8 @@ symtab_node::needed_p (void) /* If the user told us it is used, then it must be so. */ if (force_output) return true; + if (ref_by_asm) + return true; /* ABI forced symbols are needed when they are external. */ if (forced_by_abi && TREE_PUBLIC (decl)) @@ -1384,7 +1386,7 @@ analyze_functions (bool first_time) && TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (node->decl)) && DECL_EXTERNAL (node->decl)) TREE_READONLY (node->decl) = 0; - if (!node->aux && !node->referred_to_p ()) + if (!node->aux && !node->referred_to_p () && !node->ref_by_asm) { if (symtab->dump_file) fprintf (symtab->dump_file, " %s", node->dump_name ()); diff --git a/gcc/gimple-ssa-pta-constraints.cc b/gcc/gimple-ssa-pta-constraints.cc index 2f62f3092891..0262873ecece 100644 --- a/gcc/gimple-ssa-pta-constraints.cc +++ b/gcc/gimple-ssa-pta-constraints.cc @@ -3866,6 +3866,7 @@ refered_from_nonlocal_fn (struct cgraph_node *node, void *data) || DECL_EXTERNAL (node->decl) || TREE_PUBLIC (node->decl) || node->force_output + || node->ref_by_asm || lookup_attribute ("noipa", DECL_ATTRIBUTES (node->decl))); return false; } @@ -3878,6 +3879,7 @@ refered_from_nonlocal_var (struct varpool_node *node, void *data) *nonlocal_p |= (node->used_from_other_partition || DECL_EXTERNAL (node->decl) || TREE_PUBLIC (node->decl) + || node->ref_by_asm || node->force_output); return false; } @@ -3962,7 +3964,8 @@ ipa_create_global_variable_infos (void) bool nonlocal_p = (DECL_EXTERNAL (var->decl) || TREE_PUBLIC (var->decl) || var->used_from_other_partition - || var->force_output); + || var->force_output + || var->ref_by_asm); var->call_for_symbol_and_aliases (refered_from_nonlocal_var, &nonlocal_p, true); if (nonlocal_p) diff --git a/gcc/ipa-comdats.cc b/gcc/ipa-comdats.cc index db64bfbec336..b89eb596ef36 100644 --- a/gcc/ipa-comdats.cc +++ b/gcc/ipa-comdats.cc @@ -262,6 +262,7 @@ ipa_comdats (void) user section names. */ else if (symbol->externally_visible || symbol->force_output + || symbol->ref_by_asm || symbol->used_from_other_partition || TREE_THIS_VOLATILE (symbol->decl) || symbol->get_section () diff --git a/gcc/ipa-visibility.cc b/gcc/ipa-visibility.cc index 6d68a001089a..d0d6348d534c 100644 --- a/gcc/ipa-visibility.cc +++ b/gcc/ipa-visibility.cc @@ -198,6 +198,8 @@ cgraph_externally_visible_p (struct cgraph_node *node, if (!TREE_PUBLIC (node->decl) || DECL_EXTERNAL (node->decl)) return false; + if (node->ref_by_asm) + return true; /* Do not try to localize built-in functions yet. One of problems is that we end up mangling their asm for WHOPR that makes it impossible to call them @@ -269,6 +271,8 @@ varpool_node::externally_visible_p (void) if (!TREE_PUBLIC (decl)) return false; + if (ref_by_asm) + return true; /* If linker counts on us, we must preserve the function. */ if (used_from_object_file_p ()) diff --git a/gcc/ipa.cc b/gcc/ipa.cc index ca56729ca712..ca939c9c1189 100644 --- a/gcc/ipa.cc +++ b/gcc/ipa.cc @@ -310,6 +310,7 @@ bool symbol_table::remove_unreachable_nodes (FILE *file) { symtab_node *first = (symtab_node *) (void *) 1; + symtab_node *snode; struct cgraph_node *node, *next; varpool_node *vnode, *vnext; bool changed = false; @@ -358,6 +359,12 @@ symbol_table::remove_unreachable_nodes (FILE *file) enqueue_node (vnode, &first, &reachable); } + /* Declarations or symbols in other partitions are also needed if referenced + from asm. */ + FOR_EACH_SYMBOL (snode) + if (snode->ref_by_asm) + enqueue_node (snode, &first, &reachable); + /* Perform reachability analysis. */ while (first != (symtab_node *) (void *) 1) { diff --git a/gcc/lto-cgraph.cc b/gcc/lto-cgraph.cc index 1bd232c6f91f..50635f3f8a43 100644 --- a/gcc/lto-cgraph.cc +++ b/gcc/lto-cgraph.cc @@ -533,6 +533,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node, bp_pack_value (&bp, node->redefined_extern_inline, 1); bp_pack_value (&bp, node->force_output, 1); bp_pack_value (&bp, node->forced_by_abi, 1); + bp_pack_value (&bp, node->ref_by_asm, 1); bp_pack_value (&bp, node->unique_name, 1); bp_pack_value (&bp, node->body_removed, 1); bp_pack_value (&bp, node->semantic_interposition, 1); @@ -620,6 +621,7 @@ lto_output_varpool_node (struct lto_simple_output_block *ob, varpool_node *node, bp_pack_value (&bp, node->no_reorder, 1); bp_pack_value (&bp, node->force_output, 1); bp_pack_value (&bp, node->forced_by_abi, 1); + bp_pack_value (&bp, node->ref_by_asm, 1); bp_pack_value (&bp, node->unique_name, 1); bp_pack_value (&bp, node->body_removed @@ -1234,6 +1236,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data, node->redefined_extern_inline = bp_unpack_value (bp, 1); node->force_output = bp_unpack_value (bp, 1); node->forced_by_abi = bp_unpack_value (bp, 1); + node->ref_by_asm = bp_unpack_value (bp, 1); node->unique_name = bp_unpack_value (bp, 1); node->body_removed = bp_unpack_value (bp, 1); node->semantic_interposition = bp_unpack_value (bp, 1); @@ -1440,6 +1443,7 @@ input_varpool_node (struct lto_file_decl_data *file_data, node->no_reorder = bp_unpack_value (&bp, 1); node->force_output = bp_unpack_value (&bp, 1); node->forced_by_abi = bp_unpack_value (&bp, 1); + node->ref_by_asm = bp_unpack_value (&bp, 1); node->unique_name = bp_unpack_value (&bp, 1); node->body_removed = bp_unpack_value (&bp, 1); node->semantic_interposition = bp_unpack_value (&bp, 1); diff --git a/gcc/lto/lto-symtab.cc b/gcc/lto/lto-symtab.cc index ca227f88cd5c..6bbc30c2ebca 100644 --- a/gcc/lto/lto-symtab.cc +++ b/gcc/lto/lto-symtab.cc @@ -61,6 +61,8 @@ lto_cgraph_replace_node (struct cgraph_node *node, prevailing_node->mark_force_output (); if (node->forced_by_abi) prevailing_node->forced_by_abi = true; + prevailing_node->ref_by_asm |= node->ref_by_asm; + if (node->address_taken) { gcc_assert (!prevailing_node->inlined_to); @@ -121,6 +123,7 @@ lto_varpool_replace_node (varpool_node *vnode, prevailing_node->force_output = true; if (vnode->forced_by_abi) prevailing_node->forced_by_abi = true; + prevailing_node->ref_by_asm |= vnode->ref_by_asm; /* Be sure we can garbage collect the initializer. */ if (DECL_INITIAL (vnode->decl) diff --git a/gcc/symtab.cc b/gcc/symtab.cc index 7f79dfcc0b8e..acb1cd5b36f5 100644 --- a/gcc/symtab.cc +++ b/gcc/symtab.cc @@ -2452,7 +2452,7 @@ address_matters_1 (symtab_node *n, void *) if (!n->address_can_be_compared_p ()) return false; - if (n->externally_visible || n->force_output) + if (n->externally_visible || n->force_output || n->ref_by_asm) return true; for (unsigned int i = 0; n->iterate_referring (i, ref); i++)
