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);
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 %<;%>");
error_at (loc, "invalid use of void expression");
output = error_mark_node;
}
+ if (allows_reg && current_function_decl == NULL_TREE)
+ {
+ error_at (loc, "constraint allows registers 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;
}
input = error_mark_node;
}
}
+ if (allows_reg && current_function_decl == NULL_TREE)
+ {
+ error_at (loc, "constraint allows registers 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;
}
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);
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)))
{
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 `::'. */
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)
{
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))
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);
/* 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;
mark it addressable. */
if (!allows_reg && !cxx_mark_addressable (*op))
operand = error_mark_node;
+ if (allows_reg && toplev_p)
+ {
+ error_at (loc, "constraint allows registers 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;
}
if (TREE_CONSTANT (constop))
operand = constop;
}
+ if (allows_reg && toplev_p)
+ {
+ error_at (loc, "constraint allows registers 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;
}
}
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);
}
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.
@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
@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
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. */
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);
}
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;
--- /dev/null
+/* 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
--- /dev/null
+/* 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 "constraint allows registers 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 "constraint allows registers 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" } */
--- /dev/null
+/* 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" } */
#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;
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. */