> 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.

OK,
thanks!
Honza
> 
> -----
> 
> 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
> 

Reply via email to