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)