]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
ipa: Add flag ref_by_asm to symtab_node
authorMichal Jires <mjires@suse.cz>
Sat, 15 Nov 2025 23:54:00 +0000 (00:54 +0100)
committerMichal Jires <mjires@suse.cz>
Sun, 11 Jan 2026 21:48:53 +0000 (22:48 +0100)
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.

gcc/cgraph.cc
gcc/cgraph.h
gcc/cgraphunit.cc
gcc/gimple-ssa-pta-constraints.cc
gcc/ipa-comdats.cc
gcc/ipa-visibility.cc
gcc/ipa.cc
gcc/lto-cgraph.cc
gcc/lto/lto-symtab.cc
gcc/symtab.cc

index 2a02d219e93c54a006dd05c0d79821eec8a042a1..b594b0acc318fd0ca3457329b5b29e135ac9a9b0 100644 (file)
@@ -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)
index 652d045b5711353b0e4777c06b5fbac9fdffe877..f0f31c1487c58960b1c5a5f84b4152a2f1c74af2 100644 (file)
@@ -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);
 }
 
index fe1abca637b0b9ec1ead1e84c5db340069619b94..8a29cafa0f959bf83a1129c0da8548fcabf36856 100644 (file)
@@ -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 ());
index 2f62f309289197aad5111a882127b37f7f2d219a..0262873ecece719b2789d130e57af7aaa025f939 100644 (file)
@@ -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)
index db64bfbec3365888644e65869ae147ad77494dcf..b89eb596ef36af6462d3f190e2f60678d3355536 100644 (file)
@@ -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 ()
index 6d68a001089ae7051f08824381853f23f15febd9..d0d6348d534c9cf6d67d4a8ed3877128e8c937bf 100644 (file)
@@ -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 ())
index ca56729ca712e55703dbbbc1c3e4e5cda8680f94..ca939c9c118994d8dd726b0495550f37fccdad22 100644 (file)
@@ -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)
     {
index 1bd232c6f91fb14e4de12a546e268bff15c251df..50635f3f8a43c30a8fc4d8babd5776e92a231a85 100644 (file)
@@ -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);
index ca227f88cd5c01d6b7cea109a9bedef8025dff47..6bbc30c2ebcaedd788be4b91535f03b86ad4c8cb 100644 (file)
@@ -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)
index 7f79dfcc0b8ee743b654bfeb10a4a2cf353fbeb2..acb1cd5b36f58d07f304bd0f00439e86c3ac07d6 100644 (file)
@@ -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++)