https://gcc.gnu.org/g:666d330268a46cd7a6f56f3b8f2021740b380105
commit r16-6693-g666d330268a46cd7a6f56f3b8f2021740b380105 Author: Michal Jires <[email protected]> Date: Sun Nov 16 15:45:21 2025 +0100 lto: Handle .local symbols in toplevel extended assembly .local symbols cannot become global, so we have to use must_remain_in_tu. There is no way to mark declaration as both external and static/.local in C. So we have to disable the implicit definition of static variables. Also .local asm function still produces "used but never defined" warning. gcc/ChangeLog: * asm-toplevel.cc (mark_fragile_ref_by_asm): New. (struct constraint_data): New. (walk_through_constraints): Handle .local definitions. (analyze_toplevel_extended_asm): Propagate constraint_data. gcc/testsuite/ChangeLog: * gcc.dg/lto/toplevel-extended-asm-2_0.c: New test. * gcc.dg/lto/toplevel-extended-asm-2_1.c: New test. * gcc.dg/lto/toplevel-extended-asm-3_0.c: New test. * gcc.dg/lto/toplevel-extended-asm-3_1.c: New test. Diff: --- gcc/asm-toplevel.cc | 58 ++++++++++++++++++++-- .../gcc.dg/lto/toplevel-extended-asm-2_0.c | 10 ++++ .../gcc.dg/lto/toplevel-extended-asm-2_1.c | 12 +++++ .../gcc.dg/lto/toplevel-extended-asm-3_0.c | 18 +++++++ .../gcc.dg/lto/toplevel-extended-asm-3_1.c | 14 ++++++ 5 files changed, 108 insertions(+), 4 deletions(-) diff --git a/gcc/asm-toplevel.cc b/gcc/asm-toplevel.cc index 336564bb95e0..576e4b3ba73c 100644 --- a/gcc/asm-toplevel.cc +++ b/gcc/asm-toplevel.cc @@ -26,11 +26,40 @@ along with GCC; see the file COPYING3. If not see #include "tree-pass.h" #include "cgraph.h" +/* This symbol must be available and cannot be renamed. + Marks the symbol and symbols that reference it. */ +static void +mark_fragile_ref_by_asm (symtab_node* node) +{ + node->ref_by_asm = true; + /* Local symbols must remain in the same partition with their callers. */ + if (!TREE_PUBLIC (node->decl)) + { + unsigned j; + ipa_ref *ref; + node->must_remain_in_tu_name = true; + for (j = 0; node->iterate_referring (j, ref); j++) + ref->referring->must_remain_in_tu_body = true; + + if (cgraph_node* cnode = dyn_cast <cgraph_node *> (node)) + for (cgraph_edge *e = cnode->callers; e ; e = e->next_caller) + e->caller->must_remain_in_tu_body = true; + } +} + +/* Helper struct for walk_through_constraints. */ +struct constraint_data { + asm_node *node; + unsigned asm_definition : 1; +}; + /* Mark symbols in constraints. */ static tree -walk_through_constraints (tree* t, int*, void* data) +walk_through_constraints (tree* t, int*, void* dat) { - asm_node* anode = (asm_node*) data; + constraint_data* data = (constraint_data*) dat; + asm_node* anode = data->node; + if (VAR_OR_FUNCTION_DECL_P (*t)) { symtab_node* node; @@ -38,11 +67,25 @@ walk_through_constraints (tree* t, int*, void* data) { node = symtab_node::get_create (*t); node->ref_by_asm = true; + + /* Disable implicit definition on static variables defined in asm. */ + if (data->asm_definition && is_a<varpool_node*> (node) + && !TREE_PUBLIC (node->decl)) + DECL_EXTERNAL (node->decl) = true; + + if (data->asm_definition && !TREE_PUBLIC (node->decl) && flag_lto) + node->must_remain_in_tu_name = true; } else { node = symtab_node::get (*t); gcc_assert (node); + + /* Local symbols defined in asm cannot be renamed. + LGEN pass is too early to use node->callers. + So we do it in WPA. */ + if (data->asm_definition && flag_wpa) + mark_fragile_ref_by_asm (node); } anode->symbols_referenced.safe_push (node); } @@ -59,10 +102,17 @@ analyze_toplevel_extended_asm () { if (TREE_CODE (anode->asm_str) != ASM_EXPR) continue; + struct constraint_data data {anode, false}; for (tree l = ASM_INPUTS (anode->asm_str); l; l = TREE_CHAIN (l)) - walk_tree (&l, walk_through_constraints, (void*) anode, NULL); + { + tree constraint = TREE_VALUE (TREE_PURPOSE (l)); + const char* c = TREE_STRING_POINTER (constraint); + data.asm_definition = c[0] == ':' && c[1] == 0; + walk_tree (&l, walk_through_constraints, (void*) &data, NULL); + } + data.asm_definition = false; for (tree l = ASM_OUTPUTS (anode->asm_str); l; l = TREE_CHAIN (l)) - walk_tree (&l, walk_through_constraints, (void*) anode, NULL); + walk_tree (&l, walk_through_constraints, (void*) &data, NULL); } } diff --git a/gcc/testsuite/gcc.dg/lto/toplevel-extended-asm-2_0.c b/gcc/testsuite/gcc.dg/lto/toplevel-extended-asm-2_0.c new file mode 100644 index 000000000000..19ac7c21b2d1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/lto/toplevel-extended-asm-2_0.c @@ -0,0 +1,10 @@ +/* { dg-lto-do link } */ +/* { dg-lto-options {{-O2 -flto -flto-partition=1to1} } } */ + +extern int use_statics (); + +extern int asm_var; + +int main() { + return use_statics (); +} diff --git a/gcc/testsuite/gcc.dg/lto/toplevel-extended-asm-2_1.c b/gcc/testsuite/gcc.dg/lto/toplevel-extended-asm-2_1.c new file mode 100644 index 000000000000..a115f139eaa9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/lto/toplevel-extended-asm-2_1.c @@ -0,0 +1,12 @@ +static void static_asm_fn (); +static int static_asm_var; +asm("%cc0:" :: ":" (&static_asm_fn)); +asm("%cc0:" :: ":" (&static_asm_var)); + +extern int asm_var; +asm("%cc0:" :: ":" (&asm_var)); + +int use_statics () { + static_asm_fn (); + return static_asm_var; +} diff --git a/gcc/testsuite/gcc.dg/lto/toplevel-extended-asm-3_0.c b/gcc/testsuite/gcc.dg/lto/toplevel-extended-asm-3_0.c new file mode 100644 index 000000000000..c20f12b9dfd1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/lto/toplevel-extended-asm-3_0.c @@ -0,0 +1,18 @@ +/* { dg-lto-do link } */ +/* { dg-lto-options {{-O2 -flto -flto-partition=1to1} } } */ + +static void asm_fn(); +asm("%cc0:" :: ":"(&asm_fn)); + +static int asm_var; +asm("%cc0:" :: ":"(&asm_var)); + +int use_asm_var1 (int* var); +int use_asm_var2 (int** var); +void use_asm_fn (void (*fn) ()); + +int main () { + use_asm_fn (asm_fn); + int *asm_var_ptr = &asm_var; + return use_asm_var2(&asm_var_ptr) + use_asm_var1(&asm_var); +} diff --git a/gcc/testsuite/gcc.dg/lto/toplevel-extended-asm-3_1.c b/gcc/testsuite/gcc.dg/lto/toplevel-extended-asm-3_1.c new file mode 100644 index 000000000000..4bd287bce459 --- /dev/null +++ b/gcc/testsuite/gcc.dg/lto/toplevel-extended-asm-3_1.c @@ -0,0 +1,14 @@ +__attribute__((noinline)) +int use_asm_var1 (int* var) { + return *var; +} + +__attribute__((noinline)) +int use_asm_var2 (int** var) { + return **var; +} + +__attribute__((noinline)) +void use_asm_fn (void (*fn) ()) { + fn (); +}
