On Thu, Nov 21, 2024 at 09:32:51PM +0000, Joseph Myers wrote:
> On Sat, 2 Nov 2024, Jakub Jelinek wrote:
> 
> > +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.
> 
> Just ' in Texinfo, not %'.
> 
> > @@ -3071,7 +3072,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;
> 
> The syntax comment above this function needs updating.
> 
> The C front-end changes are OK with that fix.

Thanks.
Here is the adjusted patch.

2024-11-22  Jakub Jelinek  <ja...@redhat.com>

        PR c/41045
gcc/
        * output.h (insn_noperands): Declare.
        * final.cc (insn_noperands): No longer static.
        * varasm.cc (assemble_asm): Handle ASM_EXPR.
        * lto-streamer-out.cc (lto_output_toplevel_asms): Add sorry_at
        for non-STRING_CST toplevel asm for now.
        * 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/testsuite/
        * c-c++-common/toplevel-asm-1.c: New test.
        * c-c++-common/toplevel-asm-2.c: New test.
        * c-c++-common/toplevel-asm-3.c: New test.

--- gcc/output.h.jj     2024-10-02 13:30:11.006426823 +0200
+++ gcc/output.h        2024-11-01 14:59:28.563541848 +0100
@@ -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-24 18:53:38.780079517 +0200
+++ gcc/final.cc        2024-11-01 14:59:28.980535887 +0100
@@ -150,7 +150,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-29 13:51:43.247898084 +0100
+++ gcc/varasm.cc       2024-11-01 16:46:23.998237127 +0100
@@ -62,6 +62,8 @@ along with GCC; see the file COPYING3.
 #include "toplev.h"
 #include "opts.h"
 #include "asan.h"
+#include "recog.h"
+#include "gimple-expr.h"
 
 /* The (assembler) name of the first globally-visible object output.  */
 extern GTY(()) const char *first_global_object_name;
@@ -1667,16 +1669,167 @@ 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;
+      int save_cse_not_expected = cse_not_expected;
+      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;
+      cse_not_expected = 1;
+      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);
+         if (!is_gimple_addressable (output))
+           {
+             error ("output number %d not directly addressable", i);
+             goto done;
+           }
+         ops[i] = expand_expr (build_fold_addr_expr (output), NULL_RTX,
+                               VOIDmode, EXPAND_INITIALIZER);
+         ops[i] = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (output)), ops[i]);
+
+         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);
+         if (allows_mem && is_gimple_addressable (input))
+           {
+             ops[i + noutputs]
+               = expand_expr (build_fold_addr_expr (input), NULL_RTX,
+                              VOIDmode, EXPAND_INITIALIZER);
+             ops[i + noutputs] = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (input)),
+                                              ops[i + noutputs]);
+           }
+         else
+           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;
+      cse_not_expected = save_cse_not_expected;
+    }
 }
 
 /* Write the address of the entity given by SYMBOL to SEC.  */
--- gcc/lto-streamer-out.cc.jj  2024-10-25 10:00:29.489767556 +0200
+++ gcc/lto-streamer-out.cc     2024-11-01 17:35:28.700127715 +0100
@@ -2543,6 +2543,13 @@ lto_output_toplevel_asms (void)
 
   for (can = symtab->first_asm_symbol (); can; can = can->next)
     {
+      if (TREE_CODE (can->asm_str) != STRING_CST)
+       {
+         sorry_at (EXPR_LOCATION (can->asm_str),
+                   "LTO streaming of toplevel extended %<asm%> "
+                   "unimplemented");
+         continue;
+       }
       streamer_write_string_cst (ob, ob->main_stream, can->asm_str);
       streamer_write_hwi (ob, can->order);
     }
--- gcc/doc/extend.texi.jj      2024-11-01 11:49:46.722734014 +0100
+++ gcc/doc/extend.texi 2024-11-01 14:59:28.985535816 +0100
@@ -10839,8 +10839,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.
@@ -10878,6 +10878,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
@@ -10922,25 +10924,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-31 21:17:06.861021154 +0100
+++ gcc/c/c-parser.cc   2024-11-22 10:38:04.201298498 +0100
@@ -1662,6 +1662,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);
@@ -3103,12 +3104,74 @@ c_parser_declaration_or_fndef (c_parser
 
    asm-definition:
      simple-asm-expr ;
-*/
+     asm ( toplevel-asm-argument ) ;
+
+   toplevel-asm-argument:
+     asm-string-literal
+     asm-string-literal : asm-operands[opt]
+     asm-string-literal : asm-operands[opt] : asm-operands[opt]
+
+   The :: token is considered equivalent to two consecutive : tokens.  */
 
 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-31 21:17:06.909020481 +0100
+++ gcc/c/c-typeck.cc   2024-11-01 14:59:29.021535301 +0100
@@ -12171,10 +12171,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;
     }
 
@@ -12214,10 +12241,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-25 10:00:29.409768701 +0200
+++ gcc/cp/cp-tree.h    2024-11-01 14:59:29.023535272 +0100
@@ -7854,7 +7854,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-25 10:00:29.427768443 +0200
+++ gcc/cp/parser.cc    2024-11-01 14:59:29.036535086 +0100
@@ -23112,7 +23112,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)))
     {
@@ -23166,13 +23165,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 `::'.  */
@@ -23218,7 +23219,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)
            {
@@ -23229,8 +23231,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-29 13:51:33.257037334 +0100
+++ gcc/cp/semantics.cc 2024-11-01 14:59:29.039535043 +0100
@@ -2137,12 +2137,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;
@@ -2216,10 +2217,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;
        }
 
@@ -2284,10 +2320,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;
        }
     }
@@ -2297,6 +2378,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-11-01 11:57:23.900128987 +0100
+++ gcc/cp/pt.cc        2024-11-01 14:59:29.047534929 +0100
@@ -18982,7 +18982,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);
--- gcc/testsuite/c-c++-common/toplevel-asm-1.c.jj      2024-11-01 
15:09:46.209708353 +0100
+++ gcc/testsuite/c-c++-common/toplevel-asm-1.c 2024-11-01 19:25:47.512567789 
+0100
@@ -0,0 +1,25 @@
+/* PR c/41045 */
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-fno-pie" { target pie } } */
+
+struct S { char a; long long b; int c; };
+enum E { E0, E1 = sizeof (struct S) + 15 };
+int v[42];
+void foo (void) {}
+
+asm ("# %0 %1 %2 %c3 %c4 %5 %% %="
+     :: "i" (sizeof (struct S)),
+       "i" (__builtin_offsetof (struct S, c)),
+       "i" (E1),
+       "s" (foo),
+       "i" (v),
+/* Not all targets can satisfy "m" even in non-pic code.  */
+#if !defined(__i386__) && !defined(__x86_64__)
+       "s" (v));
+#else
+       "m" (v));
+asm ("# %0 %1"
+     : "=m" (v[16])
+     : "m" (v[41]));
+#endif
--- gcc/testsuite/c-c++-common/toplevel-asm-2.c.jj      2024-11-01 
17:33:35.173750585 +0100
+++ gcc/testsuite/c-c++-common/toplevel-asm-2.c 2024-11-01 17:52:37.657418058 
+0100
@@ -0,0 +1,21 @@
+/* PR c/41045 */
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-fno-pie" { target pie } } */
+
+int v[42], w[42], x;
+void l1 (void);
+
+asm ("# %0" : "=m" (32));              /* { dg-error "lvalue required in 'asm' 
statement" } */
+asm ("# %0" : "=m" (v) : "0" (v));     /* { dg-warning "matching constraint 
does not allow a register" } */
+asm ("# %0" : : "m" (v), "0" (v));     /* { dg-error "matching constraint 
references invalid operand number" } */
+asm ("# %0" :: "i" (0) : "cc");                /* { dg-error "expected '\\\)' 
before ':' token" } */
+asm ("# %0" : : "i" (0) :: l1);                /* { dg-error "expected '\\\)' 
before '::?' token" } */
+asm ("# %0" : "=r" (x));               /* { dg-error "invalid constraint 
outside of a function" } */
+asm ("# %0" : "=m" (x++));             /* { dg-error "lvalue required in 'asm' 
statement" } */
+asm ("# %0" : "=m" (v[x]));            /* { dg-error "output operand outside 
of a function is not constant" } */
+asm ("# %0" :: "r" (x));               /* { dg-error "invalid constraint 
outside of a function" } */
+asm ("# %0" : : "m" (x++));            /* { dg-error "side-effects in input 
operand outside of a function" } */
+asm ("# %0" : : "m" (v[x]));           /* { dg-error "input operand outside of 
a function is not constant" } */
+asm ("# %0" : : "i" (v[x]));           /* { dg-error "input operand outside of 
a function is not constant" } */
+asm ("# %0" : : "i" (x++));            /* { dg-error "side-effects in input 
operand outside of a function" } */
--- gcc/testsuite/c-c++-common/toplevel-asm-3.c.jj      2024-11-01 
17:45:33.482482325 +0100
+++ gcc/testsuite/c-c++-common/toplevel-asm-3.c 2024-11-01 17:53:46.399435004 
+0100
@@ -0,0 +1,11 @@
+/* PR c/41045 */
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-fno-pie" { target pie } } */
+
+int v[42], w[42], x;
+
+asm ("# %0" : "+m" (v));               /* { dg-error "'\\\+' in output operand 
outside of a function" } */
+asm ("# %0" : "=&m" (v));              /* { dg-error "'&' in output operand 
outside of a function" } */
+asm ("# %0, %1" : "=%m" (v), "=m" (w));        /* { dg-error "'%' in output 
operand outside of a function" } */
+asm ("# %0, %1" : : "%m" (v), "m" (w));        /* { dg-error "'%' in input 
operand outside of a function" } */


        Jakub

Reply via email to