--- 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);
