v4: Only changes to ipa-free-lang-data.cc:
Asm strings use C/C++/.. strings to represend the asm string.
If there was no normal string, the string type was removed/unified with
type variants. This is easier to reproduce with C++'s readonly strings.
v4 just marks asm_str types to not be removed.
-----
v3:
On Fri, 2025-12-19 at 22:42:11 +0100, Michal Jires wrote:
> >
> > I see that you add toplevel asm statements to global stream. While this
> > work, why you don't write trees into section_asm itself? It seems it is
> > already constructed uwing create_output_block instead of
> > create_simple_output_block so this should be possible?
> >
>
> Trees in the global stream are automatically fixed after decl merging.
> Other streams can only be used after decl merging is completed.
> So I used global stream for simplicity.
>
> If we want to avoid overusing the global stream, we have to delay
> streaming of toplevel assembly until merging is complete, as below.
>
-----
Streaming of toplevel extended assembly was missing implementation.
Streaming must be after merging of decls, otherwise we would have to
fix the pointers to new decls.
gcc/ChangeLog:
* ipa-free-lang-data.cc (find_decls_types_in_asm): New.
(free_lang_data_in_cgraph): Use find_decls_types_in_asm.
* lto-cgraph.cc (input_cgraph_1): Move asm to..
(input_toplevel_asms): ..here.
* lto-streamer-in.cc (lto_input_toplevel_asms):
Allow extended asm.
* lto-streamer-out.cc (lto_output_toplevel_asms):
Allow extended asm.
(lto_output_toplevel_asms): Allow ASM_EXPR.
* lto-streamer.h (input_toplevel_asms): New.
gcc/lto/ChangeLog:
* lto-common.cc (read_cgraph_and_symbols): Call
input_toplevel_asms after decl merging.
gcc/testsuite/ChangeLog:
* g++.dg/lto/toplevel_asm-0_0.C: New test.
---
gcc/ipa-free-lang-data.cc | 19 +++++++++++++++
gcc/lto-cgraph.cc | 15 ++++++++++--
gcc/lto-streamer-in.cc | 5 ++--
gcc/lto-streamer-out.cc | 26 +++++++--------------
gcc/lto-streamer.h | 1 +
gcc/lto/lto-common.cc | 3 +++
gcc/testsuite/g++.dg/lto/toplevel_asm-0_0.C | 7 ++++++
7 files changed, 55 insertions(+), 21 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/lto/toplevel_asm-0_0.C
diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc
index 8196cab6079..504c21e7b3b 100644
--- a/gcc/ipa-free-lang-data.cc
+++ b/gcc/ipa-free-lang-data.cc
@@ -1036,6 +1036,19 @@ find_decls_types_in_var (varpool_node *v, class
free_lang_data_d *fld)
find_decls_types (v->decl, fld);
}
+/* Find decls and types referenced in asm node N and store them in
+ FLD->DECLS and FLD->TYPES.
+ Asm strings are represented as a C/C++/etc. strings. If there is
+ no other string, we would delete/prune/unify the type and cause
+ issues in verify_type(tree).
+ Likely we could just add the string type, but we scan the whole
+ tree to be sure. */
+static void
+find_decls_types_in_asm (asm_node *v, class free_lang_data_d *fld)
+{
+ find_decls_types (v->asm_str, fld);
+}
+
/* Free language specific information for every operand and expression
in every node of the call graph. This process operates in three stages:
@@ -1073,6 +1086,12 @@ free_lang_data_in_cgraph (class free_lang_data_d *fld)
FOR_EACH_VARIABLE (v)
find_decls_types_in_var (v, fld);
+ /* Find the decls and types in every asm node.
+ Should only be a string type. */
+ for (asm_node* anode = symtab->first_asm_symbol (); anode;
+ anode = safe_as_a<asm_node*> (anode->next))
+ find_decls_types_in_asm (anode, fld);
+
/* Set the assembler name on every decl found. We need to do this
now because free_lang_data_in_decl will invalidate data needed
for mangling. This breaks mangling on interdependent decls. */
diff --git a/gcc/lto-cgraph.cc b/gcc/lto-cgraph.cc
index 5be50cbf8e8..3a705463f89 100644
--- a/gcc/lto-cgraph.cc
+++ b/gcc/lto-cgraph.cc
@@ -1641,8 +1641,6 @@ input_cgraph_1 (struct lto_file_decl_data *file_data,
tag = streamer_read_enum (ib, LTO_symtab_tags, LTO_symtab_last_tag);
}
- lto_input_toplevel_asms (file_data, file_data->order_base);
-
/* AUX pointers should be all non-zero for function nodes read from the
stream. */
if (flag_checking)
{
@@ -1860,6 +1858,19 @@ input_symtab (void)
}
}
+/* Input toplevel asms from each of the .o files passed to lto1.
+ Must be called after merging of decls. */
+void
+input_toplevel_asms (void)
+{
+ struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
+ struct lto_file_decl_data *file_data;
+ unsigned int j = 0;
+
+ while ((file_data = file_data_vec[j++]))
+ lto_input_toplevel_asms (file_data, file_data->order_base);
+}
+
static void
omp_requires_to_name (char *buf, size_t size, HOST_WIDE_INT requires_mask)
{
diff --git a/gcc/lto-streamer-in.cc b/gcc/lto-streamer-in.cc
index 2efa1f33967..515b0015985 100644
--- a/gcc/lto-streamer-in.cc
+++ b/gcc/lto-streamer-in.cc
@@ -1987,7 +1987,6 @@ lto_input_toplevel_asms (struct lto_file_decl_data
*file_data, int order_base)
= (const struct lto_simple_header_with_strings *) data;
int string_offset;
class data_in *data_in;
- tree str;
if (! data)
return;
@@ -2000,8 +1999,10 @@ lto_input_toplevel_asms (struct lto_file_decl_data
*file_data, int order_base)
data_in = lto_data_in_create (file_data, data + string_offset,
header->string_size, vNULL);
- while ((str = streamer_read_string_cst (data_in, &ib)))
+ unsigned asm_count = streamer_read_uhwi (&ib);
+ for (unsigned i = 0; i < asm_count; i++)
{
+ tree str = stream_read_tree (&ib, data_in);
asm_node *node = symtab->finalize_toplevel_asm (str);
node->order = streamer_read_hwi (&ib) + order_base;
node->lto_file_data = file_data;
diff --git a/gcc/lto-streamer-out.cc b/gcc/lto-streamer-out.cc
index 54f6110c933..1f7b81372c7 100644
--- a/gcc/lto-streamer-out.cc
+++ b/gcc/lto-streamer-out.cc
@@ -384,6 +384,7 @@ lto_is_streamable (tree expr)
&& code != STATEMENT_LIST
&& (code == CASE_LABEL_EXPR
|| code == DECL_EXPR
+ || code == ASM_EXPR
|| TREE_CODE_CLASS (code) != tcc_statement);
}
@@ -2560,19 +2561,18 @@ lto_output_toplevel_asms (lto_symtab_encoder_t encoder)
char *section_name;
struct lto_simple_header_with_strings header;
- bool any_asm = false;
+ unsigned asm_count = 0;
for (int i = 0; i < lto_symtab_encoder_size (encoder); i++)
if (is_a <asm_node*> (lto_symtab_encoder_deref (encoder, i)))
- any_asm = true;
+ asm_count++;
- if (!any_asm)
+ if (!asm_count)
return;
ob = create_output_block (LTO_section_asm);
- /* Make string 0 be a NULL string. */
- streamer_write_char_stream (ob->string_stream, 0);
-
+ /* Stream the length. */
+ streamer_write_uhwi (ob, asm_count);
for (int i = 0; i < lto_symtab_encoder_size (encoder); i++)
{
toplevel_node *tnode = lto_symtab_encoder_deref (encoder, i);
@@ -2580,19 +2580,11 @@ lto_output_toplevel_asms (lto_symtab_encoder_t encoder)
if (!anode)
continue;
- if (TREE_CODE (anode->asm_str) != STRING_CST)
- {
- sorry_at (EXPR_LOCATION (anode->asm_str),
- "LTO streaming of toplevel extended %<asm%> "
- "unimplemented");
- continue;
- }
- streamer_write_string_cst (ob, ob->main_stream, anode->asm_str);
- streamer_write_hwi (ob, anode->order);
+ int output_order = *encoder->order_remap->get (anode->order);
+ stream_write_tree (ob, anode->asm_str, true);
+ streamer_write_hwi (ob, output_order);
}
- streamer_write_string_cst (ob, ob->main_stream, NULL_TREE);
-
section_name = lto_get_section_name (LTO_section_asm, NULL, 0, NULL);
lto_begin_section (section_name, !flag_wpa);
free (section_name);
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index c7c6c8fbf06..b58d2395282 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -933,6 +933,7 @@ bool lto_symtab_encoder_encode_initializer_p
(lto_symtab_encoder_t,
varpool_node *);
void output_symtab (void);
void input_symtab (void);
+void input_toplevel_asms (void);
void output_offload_tables (void);
void input_offload_tables (bool);
bool referenced_from_other_partition_p (struct ipa_ref_list *,
diff --git a/gcc/lto/lto-common.cc b/gcc/lto/lto-common.cc
index 6aeeae42b6c..bca354d196f 100644
--- a/gcc/lto/lto-common.cc
+++ b/gcc/lto/lto-common.cc
@@ -2948,6 +2948,9 @@ read_cgraph_and_symbols (unsigned nfiles, const char
**fnames)
if (tree_with_vars)
ggc_free (tree_with_vars);
tree_with_vars = NULL;
+
+ input_toplevel_asms ();
+
/* During WPA we want to prevent ggc collecting by default. Grow limits
until after the IPA summaries are streamed in. Basically all IPA memory
is explcitly managed by ggc_free and ggc collect is not useful.
diff --git a/gcc/testsuite/g++.dg/lto/toplevel_asm-0_0.C
b/gcc/testsuite/g++.dg/lto/toplevel_asm-0_0.C
new file mode 100644
index 00000000000..4bfe6b1852d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/toplevel_asm-0_0.C
@@ -0,0 +1,7 @@
+// { dg-lto-do link }
+
+// Test that type of asm string is not removed.
+char c;
+asm("");
+
+int main() { }
--
2.52.0