On Sat, 2 Nov 2024, Jakub Jelinek wrote:
> Hi!
>
> Here is a new version of the toplevel asm PR41045 patch, not yet
> with the new constraints for symbol definitions (I intend to
> deal with that in a follow-up).
> Compared to the last patch, this one has some testcases added
> and some ICEs related to "m"/"=m" fixed; for LTO streaming I've
> added a sorry for now, because I think varpool/cgraph will want to add
> at least a vector of references and definitions somewhere too and that will
> need to be streamed as well.
>
> I think for the definitions in the asm we could use say
> void foo (void);
> extern int var;
> asm ("%0: nop; ret; %1: .zero 2" : : "@" (foo), "@" (var));
> or
> asm ("%0: nop; ret; %1: .zero 2" : "=@" (foo), "=@" (var));
> (unsure if we should pretend they are the outputs or inputs).
> For uses of symbols, e.g. "i" (foo), "i" (var) works fine with
> %c0 %c1, though only for non-PIC.
> "m" or "=m" also works fine on x86-64/ia32 with non-PIC, doesn't work
> for PIC, doesn't work on aarch64 even with non-PIC.
>
> So, I think the main question to decide is if we want to keep existing
> standard constraints work as is even in toplevel asm and add new constraints
> which will allow more stuff (like "i" which will just allow SYMBOL_REFs
> even with -fPIC, or "m" which will allow a mem with any address whose
> address could be added to e.g. constant initializers and printed),
> or whether we want to change their meaning in toplevel asm and let "i"/"s"
> there really accept SYMBOL_REFs no matter if -fPIC or not, and "m" accept
> the constant addresses of MEMs no matter what the backend chooses are
> acceptable. Because at toplevel, we really can't reload the addresses into
> registers to make the constraint be satisfied.
>
> Note, even the c modifier as used in %c0 etc. isn't flaw-less, it prints
> the value without anything added to it only if it is CONSTANT_ADDRESS_P
> (so e.g. symbol in "i"/"s"), but otherwise it just attempts to print it
> as machine specific modifier, so either one unsupported, or on x86_64/ia32
> requires a comparison (which will never be satisfied in toplevel).
>
> Bootstrapped/regtested on x86_64-linux and i686-linux.
This works for me, but I do not want to approve the FE changes. I'll
note we should get this correct and future proof in case we need
future additions here.
Thanks,
Richard.
> 2024-11-02 Jakub Jelinek <[email protected]>
>
> 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-01 14:59:29.000535601 +0100
> @@ -1660,6 +1660,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);
> @@ -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;
> + 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
>
>
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)