On Wed, 2 Oct 2024, Jakub Jelinek wrote:

> Hi!
> 
> In the Cauldron IPA/LTO BoF we've discussed toplevel asms and it was
> discussed it would be nice to tell the compiler something about what
> the toplevel asm does.  Sure, I'm aware the kernel people said they
> aren't willing to use something like that, but perhaps other projects
> do.  And for kernel perhaps we should add some new option which allows
> some dumb parsing of the toplevel asms and gather something from that
> parsing.
> 
> The following patch is just a small step towards that, namely, allow
> some subset of extended inline asm outside of functions.
> The patch is unfinished, LTO streaming (out/in) of the ASM_EXPRs isn't
> implemented, nor any cgraph/varpool changes to find out references etc.
> 
> The patch allows something like:
> 
> int a[2], b;
> enum { E1, E2, E3, E4, E5 };
> struct S { int a; char b; long long c; };
> asm (".section blah; .quad %P0, %P1, %P2, %P3, %P4; .previous"
>      : : "m" (a), "m" (b), "i" (42), "i" (E4), "i" (sizeof (struct S)));
> 
> Even for non-LTO, that could be useful e.g. for getting enumerators from
> C/C++ as integers into the toplevel asm, or sizeof/offsetof etc.
> 
> The restrictions I've implemented are:
> 1) asm qualifiers aren't still allowed, so asm goto or asm inline can't be
>    specified at toplevel, asm volatile has the volatile ignored for C++ with
>    a warning and is an error in C like before
> 2) I see good use for mainly input operands, output maybe to make it clear
>    that the inline asm may write some memory, I don't see a good use for
>    clobbers, so the patch doesn't allow those (and of course labels because
>    asm goto can't be specified)
> 3) the patch allows only constraints which don't allow registers, so
>    typically "m" or "i" or other memory or immediate constraints; for
>    memory, it requires that the operand is addressable and its address
>    could be used in static var initializer (so that no code actually
>    needs to be emitted for it), for others that they are constants usable
>    in the static var initializers
> 4) the patch disallows + (there is no reload of the operands, so I don't
>    see benefits of tying some operands together), nor % (who cares if
>    something is commutative in this case), or & (again, no code is emitted
>    around the asm), nor the 0-9 constraints
> 
> Right now there is no way to tell the compiler that the inline asm defines
> some symbol, I wonder if we can find some unused constraint letter or
> sequence or other syntax to denote that.  Note, I think we want to be
> able to specify that an inline asm defines a function or variable and
> be able to provide the type etc. thereof.  So
> extern void foo (void);
> extern int var;
> asm ("%P0: ret" : : "defines" (foo));
> asm ("%P0: .quad 0" : : "defines" (var));
> where the exact "defines" part is TBD.

As you are using input constraints to mark symbol uses maybe we can
use output constraints with a magic identifier (and a constraint letter
specifying 'identifier'):

asm (".globl %0; %0: ret" : "_D" (extern int foo()) : ...);

In the BOF it was noted that LTO wants to be able to rename / localize
symbols so both use and definition should be used in a way to support
this (though changing visibility is difficult - the assembler might
tie to GOT uses, and .globl is hard to replace).

Richard.

> Another question is whether all targets have something like x86 P print
> modifier which doesn't add any stuff around the printed expressions
> (perhaps there are targets which don't do that just for %0 etc.), or
> whether we want to add something that will be usable on all targets.
> 
> Thoughts on this?
> 
> 2024-10-02  Jakub Jelinek  <ja...@redhat.com>
> 
> gcc/
>       * output.h (insn_noperands): Declare.
>       * final.cc (insn_noperands): No longer static.
>       * varasm.cc (assemble_asm): Handle ASM_EXPR.
>       * doc/extend.texi (Basic @code{asm}, Extended @code{asm}): Document
>       that extended asm is now allowed outside of functions with certain
>       restrictions.
> gcc/c/
>       * c-parser.cc (c_parser_asm_string_literal): Add forward declaration.
>       (c_parser_asm_definition): Parse also extended asm without
>       clobbers/labels.
>       * c-typeck.cc (build_asm_expr): Allow extended asm outside of
>       functions and check extra restrictions.
> gcc/cp/
>       * cp-tree.h (finish_asm_stmt): Add TOPLEV_P argument.
>       * parser.cc (cp_parser_asm_definition): Parse also extended asm
>       without clobbers/labels outside of functions.
>       * semantics.cc (finish_asm_stmt): Add TOPLEV_P argument, if set,
>       check extra restrictions for extended asm outside of functions.
>       * pt.cc (tsubst_stmt): Adjust finish_asm_stmt caller.
> 
> --- gcc/output.h.jj   2024-10-02 10:02:08.031896380 +0200
> +++ gcc/output.h      2024-10-02 11:27:13.383943702 +0200
> @@ -338,6 +338,9 @@ extern rtx_insn *current_output_insn;
>     The precise value is the insn being output, to pass to error_for_asm.  */
>  extern const rtx_insn *this_is_asm_operands;
>  
> +/* Number of operands of this insn, for an `asm' with operands.  */
> +extern unsigned int insn_noperands;
> +
>  /* Carry information from ASM_DECLARE_OBJECT_NAME
>     to ASM_FINISH_DECLARE_OBJECT.  */
>  extern int size_directive_output;
> --- gcc/final.cc.jj   2024-10-02 10:02:08.031896380 +0200
> +++ gcc/final.cc      2024-10-02 11:27:13.382943715 +0200
> @@ -149,7 +149,7 @@ extern const int length_unit_log; /* Thi
>  const rtx_insn *this_is_asm_operands;
>  
>  /* Number of operands of this insn, for an `asm' with operands.  */
> -static unsigned int insn_noperands;
> +unsigned int insn_noperands;
>  
>  /* Compare optimization flag.  */
>  
> --- gcc/varasm.cc.jj  2024-10-02 10:02:08.056896032 +0200
> +++ gcc/varasm.cc     2024-10-02 12:27:46.244456857 +0200
> @@ -62,6 +62,7 @@ along with GCC; see the file COPYING3.
>  #include "toplev.h"
>  #include "opts.h"
>  #include "asan.h"
> +#include "recog.h"
>  
>  /* The (assembler) name of the first globally-visible object output.  */
>  extern GTY(()) const char *first_global_object_name;
> @@ -1667,16 +1668,154 @@ make_decl_rtl_for_debug (tree decl)
>     for an `asm' keyword used between functions.  */
>  
>  void
> -assemble_asm (tree string)
> +assemble_asm (tree asm_str)
>  {
>    const char *p;
> -  app_enable ();
>  
> -  if (TREE_CODE (string) == ADDR_EXPR)
> -    string = TREE_OPERAND (string, 0);
> +  if (TREE_CODE (asm_str) != ASM_EXPR)
> +    {
> +      app_enable ();
> +      if (TREE_CODE (asm_str) == ADDR_EXPR)
> +     asm_str = TREE_OPERAND (asm_str, 0);
> +
> +      p = TREE_STRING_POINTER (asm_str);
> +      fprintf (asm_out_file, "%s%s\n", p[0] == '\t' ? "" : "\t", p);
> +    }
> +  else
> +    {
> +      location_t save_loc = input_location;
> +      int save_reload_completed = reload_completed;
> +      input_location = EXPR_LOCATION (asm_str);
> +      int noutputs = list_length (ASM_OUTPUTS (asm_str));
> +      int ninputs = list_length (ASM_INPUTS (asm_str));
> +      const char **constraints = NULL;
> +      int i;
> +      tree tail;
> +      bool allows_mem, allows_reg, is_inout;
> +      rtx *ops = NULL;
> +      if (noutputs + ninputs > MAX_RECOG_OPERANDS)
> +     {
> +       error ("more than %d operands in %<asm%>", MAX_RECOG_OPERANDS);
> +       goto done;
> +     }
> +      constraints = XALLOCAVEC (const char *, noutputs + ninputs);
> +      ops = XALLOCAVEC (rtx, noutputs + ninputs);
> +      memset (&recog_data, 0, sizeof (recog_data));
> +      recog_data.n_operands = ninputs + noutputs;
> +      recog_data.is_asm = true;
> +      reload_completed = 0;
> +      for (i = 0, tail = ASM_OUTPUTS (asm_str); tail;
> +        ++i, tail = TREE_CHAIN (tail))
> +     {
> +       tree output = TREE_VALUE (tail);
> +       if (output == error_mark_node)
> +         goto done;
> +       constraints[i]
> +         = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (tail)));
> +       if (!parse_output_constraint (&constraints[i], i, ninputs, noutputs,
> +                                     &allows_mem, &allows_reg, &is_inout))
> +         goto done;
> +       if (is_inout)
> +         {
> +           error ("%qc in output operand outside of a function", '+');
> +           goto done;
> +         }
> +       if (strchr (constraints[i], '&'))
> +         {
> +           error ("%qc in output operand outside of a function", '&');
> +           goto done;
> +         }
> +       if (strchr (constraints[i], '%'))
> +         {
> +           error ("%qc in output operand outside of a function", '%');
> +           goto done;
> +         }
> +       output_addressed_constants (output, 0);
> +       ops[i] = expand_expr (output, NULL_RTX, VOIDmode,
> +                             EXPAND_INITIALIZER);
> +       if (!MEM_P (ops[i]))
> +         {
> +           error ("output number %d not directly addressable", i);
> +           goto done;
> +         }
> +
> +       recog_data.operand[i] = ops[i];
> +       recog_data.operand_loc[i] = &ops[i];
> +       recog_data.constraints[i] = constraints[i];
> +       recog_data.operand_mode[i] = TYPE_MODE (TREE_TYPE (output));
> +     }
> +      for (i = 0, tail = ASM_INPUTS (asm_str); tail;
> +        ++i, tail = TREE_CHAIN (tail))
> +     {
> +       tree input = TREE_VALUE (tail);
> +       if (input == error_mark_node)
> +         goto done;
> +       constraints[i + noutputs]
> +         = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (tail)));
> +       if (!parse_input_constraint (&constraints[i + noutputs], i,
> +                                    ninputs, noutputs, 0, constraints,
> +                                    &allows_mem, &allows_reg))
> +         goto done;
> +       if (strchr (constraints[i], '%'))
> +         {
> +           error ("%qc in input operand outside of a function", '%');
> +           goto done;
> +         }
> +       const char *constraint = constraints[i + noutputs];
> +       size_t c_len = strlen (constraint);
> +       for (size_t j = 0; j < c_len;
> +            j += CONSTRAINT_LEN (constraint[j], constraint + j))
> +         if (constraint[j] >= '0' && constraint[j] <= '9')
> +           {
> +             error ("matching constraint outside of a function");
> +             goto done;
> +           }
> +       output_addressed_constants (input, 0);
> +       ops[i + noutputs] = expand_expr (input, NULL_RTX, VOIDmode,
> +                                        EXPAND_INITIALIZER);
> +       if (asm_operand_ok (ops[i + noutputs], constraint, NULL) <= 0)
> +         {
> +           if (!allows_mem)
> +             warning (0, "%<asm%> operand %d probably does not "
> +                         "match constraints", i + noutputs);
> +         }
> +       recog_data.operand[i + noutputs] = ops[i + noutputs];
> +       recog_data.operand_loc[i + noutputs] = &ops[i + noutputs];
> +       recog_data.constraints[i + noutputs] = constraints[i + noutputs];
> +       recog_data.operand_mode[i + noutputs]
> +         = TYPE_MODE (TREE_TYPE (input));
> +     }
> +      if (recog_data.n_operands > 0)
> +     {
> +       const char *p = recog_data.constraints[0];
> +       recog_data.n_alternatives = 1;
> +       while (*p)
> +         recog_data.n_alternatives += (*p++ == ',');
> +     }
> +      for (i = 0; i < recog_data.n_operands; i++)
> +     recog_data.operand_type[i]
> +       = recog_data.constraints[i][0] == '=' ? OP_OUT : OP_IN;
> +      reload_completed = 1;
> +      constrain_operands (1, ALL_ALTERNATIVES);
> +      if (which_alternative < 0)
> +     {
> +       error ("impossible constraint in %<asm%>");
> +       goto done;
> +     }
> +      this_is_asm_operands = make_insn_raw (gen_nop ());
> +      insn_noperands = recog_data.n_operands;
> +      if (TREE_STRING_POINTER (ASM_STRING (asm_str))[0])
> +     {
> +       app_enable ();
> +       output_asm_insn (TREE_STRING_POINTER (ASM_STRING (asm_str)), ops);
> +     }
> +      insn_noperands = 0;
> +      this_is_asm_operands = NULL;
>  
> -  p = TREE_STRING_POINTER (string);
> -  fprintf (asm_out_file, "%s%s\n", p[0] == '\t' ? "" : "\t", p);
> +    done:
> +      input_location = save_loc;
> +      reload_completed = save_reload_completed;
> +    }
>  }
>  
>  /* Write the address of the entity given by SYMBOL to SEC.  */
> --- gcc/doc/extend.texi.jj    2024-10-02 10:02:14.549805644 +0200
> +++ gcc/doc/extend.texi       2024-10-02 11:27:13.392943576 +0200
> @@ -10809,8 +10809,8 @@ statements.  A @dfn{basic @code{asm}} st
>  operands (@pxref{Basic Asm}), while an @dfn{extended @code{asm}}
>  statement (@pxref{Extended Asm}) includes one or more operands.  
>  The extended form is preferred for mixing C and assembly language
> -within a function, but to include assembly language at
> -top level you must use basic @code{asm}.
> +within a function and can be used at top level as well with certain
> +restrictions.
>  
>  You can also use the @code{asm} keyword to override the assembler name
>  for a C symbol, or to place a C variable in a specific register.
> @@ -10848,6 +10848,8 @@ can be used for code compiled with @opti
>  @item volatile
>  The optional @code{volatile} qualifier has no effect. 
>  All basic @code{asm} blocks are implicitly volatile.
> +Basic @code{asm} statements outside of functions may not use any
> +qualifiers.
>  
>  @item inline
>  If you use the @code{inline} qualifier, then for inlining purposes the size
> @@ -10892,25 +10894,19 @@ void function()
>  @subsubheading Remarks
>  Using extended @code{asm} (@pxref{Extended Asm}) typically produces
>  smaller, safer, and more efficient code, and in most cases it is a
> -better solution than basic @code{asm}.  However, there are two
> -situations where only basic @code{asm} can be used:
> +better solution than basic @code{asm}.  However, functions declared
> +with the @code{naked} attribute require only basic @code{asm}
> +(@pxref{Function Attributes}).
>  
> -@itemize @bullet
> -@item
> -Extended @code{asm} statements have to be inside a C
> -function, so to write inline assembly language at file scope (``top-level''),
> -outside of C functions, you must use basic @code{asm}.
> -You can use this technique to emit assembler directives,
> +Extended @code{asm} statements may be used both inside a C
> +function or at file scope (``top-level''), where
> +you can use this technique to emit assembler directives,
>  define assembly language macros that can be invoked elsewhere in the file,
>  or write entire functions in assembly language.
> -Basic @code{asm} statements outside of functions may not use any
> -qualifiers.
> -
> -@item
> -Functions declared
> -with the @code{naked} attribute also require basic @code{asm}
> -(@pxref{Function Attributes}).
> -@end itemize
> +Extended @code{asm} statements outside of functions may not use any
> +qualifiers, may not specify clobbers, may not use @code{%}, @code{+} or
> +@code{&} modifiers in constraints and can only use constraints which don%'t
> +allow using any register.
>  
>  Safely accessing C data and calling functions from basic @code{asm} is more 
>  complex than it may appear. To access C data, it is better to use extended 
> --- gcc/c/c-parser.cc.jj      2024-10-02 10:02:07.970897229 +0200
> +++ gcc/c/c-parser.cc 2024-10-02 11:27:13.388943632 +0200
> @@ -1658,6 +1658,7 @@ static struct c_arg_info *c_parser_parms
>  static struct c_arg_info *c_parser_parms_list_declarator (c_parser *, tree,
>                                                         tree, bool);
>  static struct c_parm *c_parser_parameter_declaration (c_parser *, tree, 
> bool);
> +static tree c_parser_asm_string_literal (c_parser *);
>  static tree c_parser_simple_asm_expr (c_parser *);
>  static tree c_parser_gnu_attributes (c_parser *);
>  static struct c_expr c_parser_initializer (c_parser *, tree);
> @@ -3068,7 +3069,62 @@ c_parser_declaration_or_fndef (c_parser
>  static void
>  c_parser_asm_definition (c_parser *parser)
>  {
> -  tree asm_str = c_parser_simple_asm_expr (parser);
> +  location_t asm_loc = c_parser_peek_token (parser)->location;
> +  gcc_assert (c_parser_next_token_is_keyword (parser, RID_ASM));
> +  c_parser_consume_token (parser);
> +  matching_parens parens;
> +  tree asm_str = NULL_TREE;
> +  tree outputs = NULL_TREE, inputs = NULL_TREE;
> +  if (!parens.require_open (parser))
> +    goto done;
> +  asm_str = c_parser_asm_string_literal (parser);
> +  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
> +    {
> +      parens.require_close (parser);
> +      goto done;
> +    }
> +  for (int section = 0; section < 2; ++section)
> +    {
> +      if (c_parser_next_token_is (parser, CPP_SCOPE))
> +     {
> +       ++section;
> +       if (section == 2)
> +         {
> +           c_parser_error (parser, "expected %<)%>");
> +         error_close_paren:
> +           c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
> +           asm_str = NULL_TREE;
> +           goto done;
> +         }
> +       c_parser_consume_token (parser);
> +     }
> +      else if (!c_parser_require (parser, CPP_COLON,
> +                               "expected %<:%> or %<)%>",
> +                               UNKNOWN_LOCATION, false))
> +     goto error_close_paren;
> +      if (!c_parser_next_token_is (parser, CPP_COLON)
> +       && !c_parser_next_token_is (parser, CPP_SCOPE)
> +       && !c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
> +     {
> +       if (section)
> +         inputs = c_parser_asm_operands (parser);
> +       else
> +         outputs = c_parser_asm_operands (parser);
> +     }
> +      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
> +     break;
> +    }
> +
> +  if (!parens.require_close (parser))
> +    {
> +      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
> +      asm_str = NULL_TREE;
> +    }
> +
> +  if (asm_str)
> +    asm_str = build_asm_expr (asm_loc, asm_str, outputs, inputs,
> +                           NULL_TREE, NULL_TREE, false, false);
> +done:
>    if (asm_str)
>      symtab->finalize_toplevel_asm (asm_str);
>    c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
> --- gcc/c/c-typeck.cc.jj      2024-10-02 10:02:07.975897160 +0200
> +++ gcc/c/c-typeck.cc 2024-10-02 11:27:13.385943674 +0200
> @@ -11621,10 +11621,37 @@ build_asm_expr (location_t loc, tree str
>             error_at (loc, "invalid use of void expression");
>             output = error_mark_node;
>           }
> +       if (allows_reg && current_function_decl == NULL_TREE)
> +         {
> +           error_at (loc, "invalid constraint outside of a function");
> +           output = error_mark_node;
> +         }
>       }
>        else
>       output = error_mark_node;
>  
> +      if (current_function_decl == NULL_TREE && output != error_mark_node)
> +     {
> +       if (TREE_SIDE_EFFECTS (output))
> +         {
> +           error_at (loc, "side-effects in output operand outside "
> +                          "of a function");
> +           output = error_mark_node;
> +         }
> +       else
> +         {
> +           tree addr = build_unary_op (loc, ADDR_EXPR, output, false);
> +           if (addr == error_mark_node)
> +             output = error_mark_node;
> +           else if (!initializer_constant_valid_p (addr, TREE_TYPE (addr)))
> +             {
> +               error_at (loc, "output operand outside of a function is not "
> +                              "constant");
> +               output = error_mark_node;
> +             }
> +         }
> +     }
> +
>        TREE_VALUE (tail) = output;
>      }
>  
> @@ -11664,10 +11691,37 @@ build_asm_expr (location_t loc, tree str
>                 input = error_mark_node;
>               }
>           }
> +       if (allows_reg && current_function_decl == NULL_TREE)
> +         {
> +           error_at (loc, "invalid constraint outside of a function");
> +           input = error_mark_node;
> +         }
>       }
>        else
>       input = error_mark_node;
>  
> +      if (current_function_decl == NULL_TREE && input != error_mark_node)
> +     {
> +       if (TREE_SIDE_EFFECTS (input))
> +         {
> +           error_at (loc, "side-effects in input operand outside "
> +                          "of a function");
> +           input = error_mark_node;
> +         }
> +       else
> +         {
> +           tree tem = input;
> +           if (allows_mem && lvalue_p (input))
> +             tem = build_unary_op (loc, ADDR_EXPR, input, false);
> +           if (!initializer_constant_valid_p (tem, TREE_TYPE (tem)))
> +             {
> +               error_at (loc, "input operand outside of a function is not "
> +                              "constant");
> +               input = error_mark_node;
> +             }
> +         }
> +     }
> +
>        TREE_VALUE (tail) = input;
>      }
>  
> --- gcc/cp/cp-tree.h.jj       2024-10-02 10:02:14.487806507 +0200
> +++ gcc/cp/cp-tree.h  2024-10-02 11:46:56.031506125 +0200
> @@ -7848,7 +7848,7 @@ extern tree begin_compound_stmt                 (unsig
>  
>  extern void finish_compound_stmt             (tree);
>  extern tree finish_asm_stmt                  (location_t, int, tree, tree,
> -                                              tree, tree, tree, bool);
> +                                              tree, tree, tree, bool, bool);
>  extern tree finish_label_stmt                        (tree);
>  extern void finish_label_decl                        (tree);
>  extern cp_expr finish_parenthesized_expr     (cp_expr);
> --- gcc/cp/parser.cc.jj       2024-10-01 09:38:57.885963653 +0200
> +++ gcc/cp/parser.cc  2024-10-02 11:48:01.417597184 +0200
> @@ -23080,7 +23080,6 @@ cp_parser_asm_definition (cp_parser* par
>       too.  Doing that means that we have to treat the `::' operator as
>       two `:' tokens.  */
>    if (cp_parser_allow_gnu_extensions_p (parser)
> -      && parser->in_function_body
>        && (cp_lexer_next_token_is (parser->lexer, CPP_COLON)
>         || cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)))
>      {
> @@ -23134,13 +23133,15 @@ cp_parser_asm_definition (cp_parser* par
>                  invalid_inputs_p = true;
>              }
>       }
> -      else if (cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))
> +      else if (parser->in_function_body
> +            && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))
>       /* The clobbers are coming next.  */
>       clobbers_p = true;
>  
>        /* Look for clobbers.  */
>        if (clobbers_p
> -       || cp_lexer_next_token_is (parser->lexer, CPP_COLON))
> +       || (parser->in_function_body
> +           && cp_lexer_next_token_is (parser->lexer, CPP_COLON)))
>       {
>         clobbers_p = true;
>         /* Consume the `:' or `::'.  */
> @@ -23186,7 +23187,8 @@ cp_parser_asm_definition (cp_parser* par
>        if (parser->in_function_body)
>       {
>         asm_stmt = finish_asm_stmt (asm_loc, volatile_p, string, outputs,
> -                                   inputs, clobbers, labels, inline_p);
> +                                   inputs, clobbers, labels, inline_p,
> +                                   false);
>         /* If the extended syntax was not used, mark the ASM_EXPR.  */
>         if (!extended_p)
>           {
> @@ -23197,8 +23199,11 @@ cp_parser_asm_definition (cp_parser* par
>             ASM_BASIC_P (temp) = 1;
>           }
>       }
> -      else
> +      else if (!extended_p)
>       symtab->finalize_toplevel_asm (string);
> +      else
> +     finish_asm_stmt (asm_loc, false, string, outputs, inputs,
> +                      NULL_TREE, NULL_TREE, false, true);
>      }
>  
>    if (std_attrs && any_nonignored_attribute_p (std_attrs))
> --- gcc/cp/semantics.cc.jj    2024-10-02 10:02:14.536805825 +0200
> +++ gcc/cp/semantics.cc       2024-10-02 12:20:43.384331967 +0200
> @@ -2134,12 +2134,13 @@ finish_compound_stmt (tree stmt)
>  /* Finish an asm-statement, whose components are a STRING, some
>     OUTPUT_OPERANDS, some INPUT_OPERANDS, some CLOBBERS and some
>     LABELS.  Also note whether the asm-statement should be
> -   considered volatile, and whether it is asm inline.  */
> +   considered volatile, and whether it is asm inline.  TOPLEV_P
> +   is true if finishing namespace scope extended asm.  */
>  
>  tree
>  finish_asm_stmt (location_t loc, int volatile_p, tree string,
>                tree output_operands, tree input_operands, tree clobbers,
> -              tree labels, bool inline_p)
> +              tree labels, bool inline_p, bool toplev_p)
>  {
>    tree r;
>    tree t;
> @@ -2213,10 +2214,45 @@ finish_asm_stmt (location_t loc, int vol
>                mark it addressable.  */
>             if (!allows_reg && !cxx_mark_addressable (*op))
>               operand = error_mark_node;
> +           if (allows_reg && toplev_p)
> +             {
> +               error_at (loc, "invalid constraint outside of a function");
> +               operand = error_mark_node;
> +             }
>           }
>         else
>           operand = error_mark_node;
>  
> +       if (toplev_p && operand != error_mark_node)
> +         {
> +           if (TREE_SIDE_EFFECTS (operand))
> +             {
> +               error_at (loc, "side-effects in output operand outside "
> +                              "of a function");
> +               operand = error_mark_node;
> +             }
> +           else
> +             {
> +               tree addr
> +                 = cp_build_addr_expr (operand, tf_warning_or_error);
> +               if (addr == error_mark_node)
> +                 operand = error_mark_node;
> +               else
> +                 {
> +                   addr = maybe_constant_value (addr);
> +                   if (!initializer_constant_valid_p (addr,
> +                                                      TREE_TYPE (addr)))
> +                     {
> +                       error_at (loc, "output operand outside of a "
> +                                      "function is not constant");
> +                       operand = error_mark_node;
> +                     }
> +                   else
> +                     operand = build_fold_indirect_ref (addr);
> +                 }
> +             }
> +         }
> +
>         TREE_VALUE (t) = operand;
>       }
>  
> @@ -2281,10 +2317,55 @@ finish_asm_stmt (location_t loc, int vol
>                 if (TREE_CONSTANT (constop))
>                   operand = constop;
>               }
> +           if (allows_reg && toplev_p)
> +             {
> +               error_at (loc, "invalid constraint outside of a function");
> +               operand = error_mark_node;
> +             }
>           }
>         else
>           operand = error_mark_node;
>  
> +       if (toplev_p && operand != error_mark_node)
> +         {
> +           if (TREE_SIDE_EFFECTS (operand))
> +             {
> +               error_at (loc, "side-effects in input operand outside "
> +                              "of a function");
> +               operand = error_mark_node;
> +             }
> +           else if (allows_mem && lvalue_or_else (operand, lv_asm, tf_none))
> +             {
> +               tree addr = cp_build_addr_expr (operand, tf_warning_or_error);
> +               if (addr == error_mark_node)
> +                 operand = error_mark_node;
> +               else
> +                 {
> +                   addr = maybe_constant_value (addr);
> +                   if (!initializer_constant_valid_p (addr,
> +                                                      TREE_TYPE (addr)))
> +                     {
> +                       error_at (loc, "input operand outside of a "
> +                                      "function is not constant");
> +                       operand = error_mark_node;
> +                     }
> +                   else
> +                     operand = build_fold_indirect_ref (addr);
> +                 }
> +             }
> +           else
> +             {
> +               operand = maybe_constant_value (operand);
> +               if (!initializer_constant_valid_p (operand,
> +                                                  TREE_TYPE (operand)))
> +                 {
> +                   error_at (loc, "input operand outside of a "
> +                                  "function is not constant");
> +                   operand = error_mark_node;
> +                 }
> +             }
> +         }
> +
>         TREE_VALUE (t) = operand;
>       }
>      }
> @@ -2294,6 +2375,11 @@ finish_asm_stmt (location_t loc, int vol
>                 clobbers, labels);
>    ASM_VOLATILE_P (r) = volatile_p || noutputs == 0;
>    ASM_INLINE_P (r) = inline_p;
> +  if (toplev_p)
> +    {
> +      symtab->finalize_toplevel_asm (r);
> +      return r;
> +    }
>    r = maybe_cleanup_point_expr_void (r);
>    return add_stmt (r);
>  }
> --- gcc/cp/pt.cc.jj   2024-10-01 09:38:57.915963234 +0200
> +++ gcc/cp/pt.cc      2024-10-02 11:50:03.662897824 +0200
> @@ -18966,7 +18966,7 @@ tsubst_stmt (tree t, tree args, tsubst_f
>                                               complain, in_decl);
>       tmp = finish_asm_stmt (EXPR_LOCATION (t), ASM_VOLATILE_P (t), string,
>                              outputs, inputs, clobbers, labels,
> -                            ASM_INLINE_P (t));
> +                            ASM_INLINE_P (t), false);
>       tree asm_expr = tmp;
>       if (TREE_CODE (asm_expr) == CLEANUP_POINT_EXPR)
>         asm_expr = TREE_OPERAND (asm_expr, 0);
> 
>       Jakub
> 
> 

-- 
Richard Biener <rguent...@suse.de>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)

Reply via email to