builtins.c (expand_builtin_synchronize): Use gimple_build_asm_vec.

* builtins.c (expand_builtin_synchronize): Use gimple_build_asm_vec.
	* cfgbuild.c (make_edges): Handle asm goto.
	* cfglayout.c (fixup_reorder_chain): Likewise.
	* cfgrtl.c (patch_jump_insn): Likewise.
	* gimple-pretty-print.c (dump_gimple_asm): Likewise.
	* gimple.c (gimple_build_asm_1): Add and use nlabels parameter.
	(gimple_build_asm_vec): Add and use labels parameter.
	(gimple_build_asm): Remove.
	(walk_gimple_asm): Walk labels too.
	* gimple.def (GIMPLE_ASM): Update docs.
	* gimple.h: Update decls.
	(struct gimple_statement_asm): Change nc to use unsigned char;
	add nl member.
	(gimple_asm_nlabels): New.
	(gimple_asm_label_op, gimple_asm_set_label_op): New.
	* gimplify.c (gimplify_asm_expr): Copy labels from ASM_EXPR
	into gimple_build_asm_vec.
	* jump.c (mark_jump_label_asm): New.
	(mark_jump_label): Use it.
	(redirect_jump_1): Handle asm goto.
	(invert_jump_1): Soft fail if X is null.
	* recog.c (extract_asm_operands): New.
	(asm_noperands): Use it; handle asm labels.
	(decode_asm_operands): Use extract_asm_operands.
	(asm_operand_ok): Properly handle empty string.
	* reg-stack.c (get_asm_operands_in_out): Rename from
	get_asm_operand_n_inputs; use extract_asm_operands; return both
	inputs and outputs by reference; update all callers.
	* rtl.def (ASM_OPERANDS): Add label vector as operand 6.
	* rtl.h (ASM_OPERANDS_LABEL_VEC): New.
	(ASM_OPERANDS_LABEL_LENGTH, ASM_OPERANDS_LABEL): New.
	(ASM_OPERANDS_SOURCE_LOCATION): Renumber.
	(extract_asm_operands): Declare.
	* stmt.c (expand_asm_operands): Add and use labels parameter.
	(check_unique_operand_names): Likewise.
	(resolve_asm_operand_names, resolve_operand_name_1): Likewise.
	(expand_asm_stmt): Handle asm labels.
	* tree-cfg.c (make_gimple_asm_edges): New.
	(make_edges): Use it.
	(cleanup_dead_labels): Handle asm labels.
	(is_ctrl_altering_stmt): Likewise.
	(gimple_redirect_edge_and_branch): Likewise.
	* tree.def (ASM_EXPR): Add 5th operand.
	* tree.h (ASM_LABELS): New.
	(resolve_asm_operand_names): Update decl.

	* c-parser.c (c_parser_asm_statement): Parse asm goto.
	(c_parser_asm_goto_operands): New.
	* c-tree.h (build_asm_expr): Update decl.
	* c-typeck.c (build_asm_expr): Add and use labels parameter.
	* doc/extend.texi: Document asm goto.

gcc/ada/
	* gcc-interface/trans.c (Pragma_to_gnu): Use build5 for ASM_EXPR.

gcc/cp/
	* cp-tree.h (finish_asm_stmt): Update decl.
	* parser.c (cp_parser_asm_definition): Parse asm goto.
	(cp_parser_asm_label_list): New.
	* pt.c (tsubst_copy_asm_operands): Don't recurse on labels.
	(tsubst_expr): Handle asm labels.
	* semantics.c (finish_asm_stmt): Add and use labels parameter.

gcc/testsuite/
	* c-c++-common/asmgoto-1.c, c-c++-common/asmgoto-2.c,
	c-c++-common/asmgoto-3.c, gcc.c-torture/compile/asmgoto-1.c,
	gcc.dg/tree-ssa/asmgoto-1.c: New files.

Co-Authored-By: Jakub Jelinek <jakub@redhat.com>

From-SVN: r151701
This commit is contained in:
Richard Henderson 2009-09-14 13:17:24 -07:00 committed by Richard Henderson
parent 40c88b9403
commit 1c384bf142
36 changed files with 1167 additions and 439 deletions

View File

@ -1,3 +1,58 @@
2009-09-14 Richard Henderson <rth@redhat.com>
Jakub Jelinek <jakub@redhat.com>
* builtins.c (expand_builtin_synchronize): Use gimple_build_asm_vec.
* cfgbuild.c (make_edges): Handle asm goto.
* cfglayout.c (fixup_reorder_chain): Likewise.
* cfgrtl.c (patch_jump_insn): Likewise.
* gimple-pretty-print.c (dump_gimple_asm): Likewise.
* gimple.c (gimple_build_asm_1): Add and use nlabels parameter.
(gimple_build_asm_vec): Add and use labels parameter.
(gimple_build_asm): Remove.
(walk_gimple_asm): Walk labels too.
* gimple.def (GIMPLE_ASM): Update docs.
* gimple.h: Update decls.
(struct gimple_statement_asm): Change nc to use unsigned char;
add nl member.
(gimple_asm_nlabels): New.
(gimple_asm_label_op, gimple_asm_set_label_op): New.
* gimplify.c (gimplify_asm_expr): Copy labels from ASM_EXPR
into gimple_build_asm_vec.
* jump.c (mark_jump_label_asm): New.
(mark_jump_label): Use it.
(redirect_jump_1): Handle asm goto.
(invert_jump_1): Soft fail if X is null.
* recog.c (extract_asm_operands): New.
(asm_noperands): Use it; handle asm labels.
(decode_asm_operands): Use extract_asm_operands.
(asm_operand_ok): Properly handle empty string.
* reg-stack.c (get_asm_operands_in_out): Rename from
get_asm_operand_n_inputs; use extract_asm_operands; return both
inputs and outputs by reference; update all callers.
* rtl.def (ASM_OPERANDS): Add label vector as operand 6.
* rtl.h (ASM_OPERANDS_LABEL_VEC): New.
(ASM_OPERANDS_LABEL_LENGTH, ASM_OPERANDS_LABEL): New.
(ASM_OPERANDS_SOURCE_LOCATION): Renumber.
(extract_asm_operands): Declare.
* stmt.c (expand_asm_operands): Add and use labels parameter.
(check_unique_operand_names): Likewise.
(resolve_asm_operand_names, resolve_operand_name_1): Likewise.
(expand_asm_stmt): Handle asm labels.
* tree-cfg.c (make_gimple_asm_edges): New.
(make_edges): Use it.
(cleanup_dead_labels): Handle asm labels.
(is_ctrl_altering_stmt): Likewise.
(gimple_redirect_edge_and_branch): Likewise.
* tree.def (ASM_EXPR): Add 5th operand.
* tree.h (ASM_LABELS): New.
(resolve_asm_operand_names): Update decl.
* c-parser.c (c_parser_asm_statement): Parse asm goto.
(c_parser_asm_goto_operands): New.
* c-tree.h (build_asm_expr): Update decl.
* c-typeck.c (build_asm_expr): Add and use labels parameter.
* doc/extend.texi: Document asm goto.
2009-09-14 Richard Henderson <rth@redhat.com>
* except.h: Update declarations.

View File

@ -1,3 +1,7 @@
2009-09-14 Richard Henderson <rth@redhat.com>
* gcc-interface/trans.c (Pragma_to_gnu): Use build5 for ASM_EXPR.
2009-09-14 Eric Botcazou <ebotcazou@adacore.com>
* exp_dbug.ads (Packed Array Encoding): Document the new encoding for

View File

@ -1026,14 +1026,14 @@ Pragma_to_gnu (Node_Id gnat_node)
asm_constraint = build_string (strlen (comment), comment);
free (comment);
#endif
gnu_expr = build4 (ASM_EXPR, void_type_node,
gnu_expr = build5 (ASM_EXPR, void_type_node,
asm_constraint,
NULL_TREE,
tree_cons
(build_tree_list (NULL_TREE,
build_string (1, "g")),
gnu_expr, NULL_TREE),
NULL_TREE);
NULL_TREE, NULL_TREE);
ASM_VOLATILE_P (gnu_expr) = 1;
set_expr_location_from_node (gnu_expr, gnat_node);
append_to_statement_list (gnu_expr, &gnu_result);
@ -5088,9 +5088,9 @@ gnat_to_gnu (Node_Id gnat_node)
TREE_VALUE (tail) = input;
}
gnu_result = build4 (ASM_EXPR, void_type_node,
gnu_result = build5 (ASM_EXPR, void_type_node,
gnu_template, gnu_outputs,
gnu_inputs, gnu_clobbers);
gnu_inputs, gnu_clobbers, NULL_TREE);
ASM_VOLATILE_P (gnu_result) = Is_Asm_Volatile (gnat_node);
}
else

View File

@ -6236,6 +6236,7 @@ static void
expand_builtin_synchronize (void)
{
gimple x;
VEC (tree, gc) *v_clobbers;
#ifdef HAVE_memory_barrier
if (HAVE_memory_barrier)
@ -6253,8 +6254,10 @@ expand_builtin_synchronize (void)
/* If no explicit memory barrier instruction is available, create an
empty asm stmt with a memory clobber. */
x = gimple_build_asm ("", 0, 0, 1,
tree_cons (NULL, build_string (6, "memory"), NULL));
v_clobbers = VEC_alloc (tree, gc, 1);
VEC_quick_push (tree, v_clobbers,
tree_cons (NULL, build_string (6, "memory"), NULL));
x = gimple_build_asm_vec ("", NULL, NULL, v_clobbers, NULL);
gimple_asm_set_volatile (x, true);
expand_asm_stmt (x);
}

View File

@ -903,6 +903,7 @@ static void c_parser_do_statement (c_parser *);
static void c_parser_for_statement (c_parser *);
static tree c_parser_asm_statement (c_parser *);
static tree c_parser_asm_operands (c_parser *, bool);
static tree c_parser_asm_goto_operands (c_parser *);
static tree c_parser_asm_clobbers (c_parser *);
static struct c_expr c_parser_expr_no_commas (c_parser *, struct c_expr *);
static struct c_expr c_parser_conditional_expression (c_parser *,
@ -4226,12 +4227,17 @@ c_parser_for_statement (c_parser *parser)
asm-statement:
asm type-qualifier[opt] ( asm-argument ) ;
asm type-qualifier[opt] goto ( asm-goto-argument ) ;
asm-argument:
asm-string-literal
asm-string-literal : asm-operands[opt]
asm-string-literal : asm-operands[opt] : asm-operands[opt]
asm-string-literal : asm-operands[opt] : asm-operands[opt] : asm-clobbers
asm-string-literal : asm-operands[opt] : asm-operands[opt] : asm-clobbers[opt]
asm-goto-argument:
asm-string-literal : : asm-operands[opt] : asm-clobbers[opt] \
: asm-goto-operands
Qualifiers other than volatile are accepted in the syntax but
warned for. */
@ -4239,9 +4245,11 @@ c_parser_for_statement (c_parser *parser)
static tree
c_parser_asm_statement (c_parser *parser)
{
tree quals, str, outputs, inputs, clobbers, ret;
bool simple;
tree quals, str, outputs, inputs, clobbers, labels, ret;
bool simple, is_goto;
location_t asm_loc = c_parser_peek_token (parser)->location;
int section, nsections;
gcc_assert (c_parser_next_token_is_keyword (parser, RID_ASM));
c_parser_consume_token (parser);
if (c_parser_next_token_is_keyword (parser, RID_VOLATILE))
@ -4261,85 +4269,96 @@ c_parser_asm_statement (c_parser *parser)
}
else
quals = NULL_TREE;
is_goto = false;
if (c_parser_next_token_is_keyword (parser, RID_GOTO))
{
c_parser_consume_token (parser);
is_goto = true;
}
/* ??? Follow the C++ parser rather than using the
lex_untranslated_string kludge. */
parser->lex_untranslated_string = true;
ret = NULL;
if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
{
parser->lex_untranslated_string = false;
return NULL_TREE;
}
goto error;
str = c_parser_asm_string_literal (parser);
if (str == NULL_TREE)
goto error_close_paren;
simple = true;
outputs = NULL_TREE;
inputs = NULL_TREE;
clobbers = NULL_TREE;
labels = NULL_TREE;
if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN) && !is_goto)
goto done_asm;
/* Parse each colon-delimited section of operands. */
nsections = 3 + is_goto;
for (section = 0; section < nsections; ++section)
{
parser->lex_untranslated_string = false;
c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
return NULL_TREE;
if (!c_parser_require (parser, CPP_COLON,
is_goto
? "expected %<:%>"
: "expected %<:%> or %<)%>"))
goto error_close_paren;
/* Once past any colon, we're no longer a simple asm. */
simple = false;
if ((!c_parser_next_token_is (parser, CPP_COLON)
&& !c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
|| section == 3)
switch (section)
{
case 0:
/* For asm goto, we don't allow output operands, but reserve
the slot for a future extension that does allow them. */
if (!is_goto)
outputs = c_parser_asm_operands (parser, false);
break;
case 1:
inputs = c_parser_asm_operands (parser, true);
break;
case 2:
clobbers = c_parser_asm_clobbers (parser);
break;
case 3:
labels = c_parser_asm_goto_operands (parser);
break;
default:
gcc_unreachable ();
}
if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN) && !is_goto)
goto done_asm;
}
if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
{
simple = true;
outputs = NULL_TREE;
inputs = NULL_TREE;
clobbers = NULL_TREE;
goto done_asm;
}
if (!c_parser_require (parser, CPP_COLON, "expected %<:%> or %<)%>"))
{
parser->lex_untranslated_string = false;
c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
return NULL_TREE;
}
simple = false;
/* Parse outputs. */
if (c_parser_next_token_is (parser, CPP_COLON)
|| c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
outputs = NULL_TREE;
else
outputs = c_parser_asm_operands (parser, false);
if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
{
inputs = NULL_TREE;
clobbers = NULL_TREE;
goto done_asm;
}
if (!c_parser_require (parser, CPP_COLON, "expected %<:%> or %<)%>"))
{
parser->lex_untranslated_string = false;
c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
return NULL_TREE;
}
/* Parse inputs. */
if (c_parser_next_token_is (parser, CPP_COLON)
|| c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
inputs = NULL_TREE;
else
inputs = c_parser_asm_operands (parser, true);
if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
{
clobbers = NULL_TREE;
goto done_asm;
}
if (!c_parser_require (parser, CPP_COLON, "expected %<:%> or %<)%>"))
{
parser->lex_untranslated_string = false;
c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
return NULL_TREE;
}
/* Parse clobbers. */
clobbers = c_parser_asm_clobbers (parser);
done_asm:
parser->lex_untranslated_string = false;
if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"))
{
c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
return NULL_TREE;
goto error;
}
if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
c_parser_skip_to_end_of_block_or_statement (parser);
ret = build_asm_stmt (quals, build_asm_expr (asm_loc, str, outputs, inputs,
clobbers, simple));
clobbers, labels, simple));
error:
parser->lex_untranslated_string = false;
return ret;
error_close_paren:
c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
goto error;
}
/* Parse asm operands, a GNU extension. If CONVERT_P (for inputs but
@ -4441,6 +4460,45 @@ c_parser_asm_clobbers (c_parser *parser)
return list;
}
/* Parse asm goto labels, a GNU extension.
asm-goto-operands:
identifier
asm-goto-operands , identifier
*/
static tree
c_parser_asm_goto_operands (c_parser *parser)
{
tree list = NULL_TREE;
while (true)
{
tree name, label;
if (c_parser_next_token_is (parser, CPP_NAME))
{
c_token *tok = c_parser_peek_token (parser);
name = tok->value;
label = lookup_label_for_goto (tok->location, name);
c_parser_consume_token (parser);
TREE_USED (label) = 1;
}
else
{
c_parser_error (parser, "expected identifier");
return NULL_TREE;
}
name = build_string (IDENTIFIER_LENGTH (name),
IDENTIFIER_POINTER (name));
list = tree_cons (name, label, list);
if (c_parser_next_token_is (parser, CPP_COMMA))
c_parser_consume_token (parser);
else
return nreverse (list);
}
}
/* Parse an expression other than a compound expression; that is, an
assignment expression (C90 6.3.16, C99 6.5.16). If AFTER is not
NULL then it is an Objective-C message expression which is the

View File

@ -541,7 +541,7 @@ extern tree build_compound_literal (location_t, tree, tree, bool);
extern void check_compound_literal_type (location_t, struct c_type_name *);
extern tree c_start_case (location_t, location_t, tree);
extern void c_finish_case (tree);
extern tree build_asm_expr (location_t, tree, tree, tree, tree, bool);
extern tree build_asm_expr (location_t, tree, tree, tree, tree, tree, bool);
extern tree build_asm_stmt (tree, tree);
extern int c_types_compatible_p (tree, tree);
extern tree c_begin_compound_stmt (bool);

View File

@ -7920,7 +7920,7 @@ build_asm_stmt (tree cv_qualifier, tree args)
are subtly different. We use a ASM_EXPR node to represent this. */
tree
build_asm_expr (location_t loc, tree string, tree outputs, tree inputs,
tree clobbers, bool simple)
tree clobbers, tree labels, bool simple)
{
tree tail;
tree args;
@ -7934,7 +7934,7 @@ build_asm_expr (location_t loc, tree string, tree outputs, tree inputs,
noutputs = list_length (outputs);
oconstraints = (const char **) alloca (noutputs * sizeof (const char *));
string = resolve_asm_operand_names (string, outputs, inputs);
string = resolve_asm_operand_names (string, outputs, inputs, labels);
/* Remove output conversions that change the type but not the mode. */
for (i = 0, tail = outputs; tail; ++i, tail = TREE_CHAIN (tail))
@ -8004,7 +8004,11 @@ build_asm_expr (location_t loc, tree string, tree outputs, tree inputs,
TREE_VALUE (tail) = input;
}
args = build_stmt (loc, ASM_EXPR, string, outputs, inputs, clobbers);
/* ASMs with labels cannot have outputs. This should have been
enforced by the parser. */
gcc_assert (outputs == NULL || labels == NULL);
args = build_stmt (loc, ASM_EXPR, string, outputs, inputs, clobbers, labels);
/* asm statements without outputs, including simple ones, are treated
as volatile. */

View File

@ -303,6 +303,15 @@ make_edges (basic_block min, basic_block max, int update_p)
else if (returnjump_p (insn))
cached_make_edge (edge_cache, bb, EXIT_BLOCK_PTR, 0);
/* Recognize asm goto and do the right thing. */
else if ((tmp = extract_asm_operands (PATTERN (insn))) != NULL)
{
int i, n = ASM_OPERANDS_LABEL_LENGTH (tmp);
for (i = 0; i < n; ++i)
make_label_edge (edge_cache, bb,
XEXP (ASM_OPERANDS_LABEL (tmp, i), 0), 0);
}
/* Otherwise, we have a plain conditional or unconditional jump. */
else
{

View File

@ -848,6 +848,15 @@ fixup_reorder_chain (void)
continue;
}
}
else if (extract_asm_operands (PATTERN (bb_end_insn)) != NULL)
{
/* If the old fallthru is still next, nothing to do. */
if (bb->aux == e_fall->dest
|| e_fall->dest == EXIT_BLOCK_PTR)
continue;
/* Otherwise we'll have to use the fallthru fixup below. */
}
else
{
/* Otherwise we have some return, switch or computed

View File

@ -956,6 +956,45 @@ patch_jump_insn (rtx insn, rtx old_label, basic_block new_bb)
++LABEL_NUSES (new_label);
}
}
else if ((tmp = extract_asm_operands (PATTERN (insn))) != NULL)
{
int i, n = ASM_OPERANDS_LABEL_LENGTH (tmp);
rtx new_label, note;
if (new_bb == EXIT_BLOCK_PTR)
return false;
new_label = block_label (new_bb);
for (i = 0; i < n; ++i)
{
rtx old_ref = ASM_OPERANDS_LABEL (tmp, i);
gcc_assert (GET_CODE (old_ref) == LABEL_REF);
if (XEXP (old_ref, 0) == old_label)
{
ASM_OPERANDS_LABEL (tmp, i)
= gen_rtx_LABEL_REF (Pmode, new_label);
--LABEL_NUSES (old_label);
++LABEL_NUSES (new_label);
}
}
if (JUMP_LABEL (insn) == old_label)
{
JUMP_LABEL (insn) = new_label;
note = find_reg_note (insn, REG_LABEL_TARGET, new_label);
if (note)
remove_note (insn, note);
}
else
{
note = find_reg_note (insn, REG_LABEL_TARGET, old_label);
if (note)
remove_note (insn, note);
if (JUMP_LABEL (insn) != new_label
&& !find_reg_note (insn, REG_LABEL_TARGET, new_label))
add_reg_note (insn, REG_LABEL_TARGET, new_label);
}
}
else
{
/* ?? We may play the games with moving the named labels from

View File

@ -1,3 +1,13 @@
2009-09-14 Richard Henderson <rth@redhat.com>
Jakub Jelinek <jakub@redhat.com>
* cp-tree.h (finish_asm_stmt): Update decl.
* parser.c (cp_parser_asm_definition): Parse asm goto.
(cp_parser_asm_label_list): New.
* pt.c (tsubst_copy_asm_operands): Don't recurse on labels.
(tsubst_expr): Handle asm labels.
* semantics.c (finish_asm_stmt): Add and use labels parameter.
2009-09-14 Richard Henderson <rth@redhat.com>
* except.c (init_exception_processing): Don't call

View File

@ -4820,7 +4820,8 @@ enum {
extern tree begin_compound_stmt (unsigned int);
extern void finish_compound_stmt (tree);
extern tree finish_asm_stmt (int, tree, tree, tree, tree);
extern tree finish_asm_stmt (int, tree, tree, tree, tree,
tree);
extern tree finish_label_stmt (tree);
extern void finish_label_decl (tree);
extern tree finish_parenthesized_expr (tree);

View File

@ -1859,6 +1859,8 @@ static tree cp_parser_asm_operand_list
(cp_parser *);
static tree cp_parser_asm_clobber_list
(cp_parser *);
static tree cp_parser_asm_label_list
(cp_parser *);
static tree cp_parser_attributes_opt
(cp_parser *);
static tree cp_parser_attribute_list
@ -12531,7 +12533,10 @@ cp_parser_using_directive (cp_parser* parser)
: asm-operand-list [opt] ) ;
asm volatile [opt] ( string-literal : asm-operand-list [opt]
: asm-operand-list [opt]
: asm-operand-list [opt] ) ; */
: asm-clobber-list [opt] ) ;
asm volatile [opt] goto ( string-literal : : asm-operand-list [opt]
: asm-clobber-list [opt]
: asm-goto-list ) ; */
static void
cp_parser_asm_definition (cp_parser* parser)
@ -12540,11 +12545,14 @@ cp_parser_asm_definition (cp_parser* parser)
tree outputs = NULL_TREE;
tree inputs = NULL_TREE;
tree clobbers = NULL_TREE;
tree labels = NULL_TREE;
tree asm_stmt;
bool volatile_p = false;
bool extended_p = false;
bool invalid_inputs_p = false;
bool invalid_outputs_p = false;
bool goto_p = false;
const char *missing = NULL;
/* Look for the `asm' keyword. */
cp_parser_require_keyword (parser, RID_ASM, "%<asm%>");
@ -12557,6 +12565,15 @@ cp_parser_asm_definition (cp_parser* parser)
/* Consume the token. */
cp_lexer_consume_token (parser->lexer);
}
if (cp_parser_allow_gnu_extensions_p (parser)
&& parser->in_function_body
&& cp_lexer_next_token_is_keyword (parser->lexer, RID_GOTO))
{
/* Remember that we saw the `goto' keyword. */
goto_p = true;
/* Consume the token. */
cp_lexer_consume_token (parser->lexer);
}
/* Look for the opening `('. */
if (!cp_parser_require (parser, CPP_OPEN_PAREN, "%<(%>"))
return;
@ -12581,6 +12598,7 @@ cp_parser_asm_definition (cp_parser* parser)
{
bool inputs_p = false;
bool clobbers_p = false;
bool labels_p = false;
/* The extended syntax was used. */
extended_p = true;
@ -12596,7 +12614,8 @@ cp_parser_asm_definition (cp_parser* parser)
&& cp_lexer_next_token_is_not (parser->lexer,
CPP_SCOPE)
&& cp_lexer_next_token_is_not (parser->lexer,
CPP_CLOSE_PAREN))
CPP_CLOSE_PAREN)
&& !goto_p)
outputs = cp_parser_asm_operand_list (parser);
if (outputs == error_mark_node)
@ -12617,6 +12636,8 @@ cp_parser_asm_definition (cp_parser* parser)
/* Parse the output-operands. */
if (cp_lexer_next_token_is_not (parser->lexer,
CPP_COLON)
&& cp_lexer_next_token_is_not (parser->lexer,
CPP_SCOPE)
&& cp_lexer_next_token_is_not (parser->lexer,
CPP_CLOSE_PAREN))
inputs = cp_parser_asm_operand_list (parser);
@ -12632,16 +12653,41 @@ cp_parser_asm_definition (cp_parser* parser)
if (clobbers_p
|| cp_lexer_next_token_is (parser->lexer, CPP_COLON))
{
clobbers_p = true;
/* Consume the `:' or `::'. */
cp_lexer_consume_token (parser->lexer);
/* Parse the clobbers. */
if (cp_lexer_next_token_is_not (parser->lexer,
CPP_CLOSE_PAREN))
CPP_COLON)
&& cp_lexer_next_token_is_not (parser->lexer,
CPP_CLOSE_PAREN))
clobbers = cp_parser_asm_clobber_list (parser);
}
else if (goto_p
&& cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))
/* The labels are coming next. */
labels_p = true;
/* Look for labels. */
if (labels_p
|| (goto_p && cp_lexer_next_token_is (parser->lexer, CPP_COLON)))
{
labels_p = true;
/* Consume the `:' or `::'. */
cp_lexer_consume_token (parser->lexer);
/* Parse the labels. */
labels = cp_parser_asm_label_list (parser);
}
if (goto_p && !labels_p)
missing = clobbers_p ? "%<:%>" : "%<:%> or %<::%>";
}
else if (goto_p)
missing = "%<:%> or %<::%>";
/* Look for the closing `)'. */
if (!cp_parser_require (parser, CPP_CLOSE_PAREN, "%<)%>"))
if (!cp_parser_require (parser, missing ? CPP_COLON : CPP_CLOSE_PAREN,
missing ? missing : "%<)%>"))
cp_parser_skip_to_closing_parenthesis (parser, true, false,
/*consume_paren=*/true);
cp_parser_require (parser, CPP_SEMICOLON, "%<;%>");
@ -12652,7 +12698,7 @@ cp_parser_asm_definition (cp_parser* parser)
if (parser->in_function_body)
{
asm_stmt = finish_asm_stmt (volatile_p, string, outputs,
inputs, clobbers);
inputs, clobbers, labels);
/* If the extended syntax was not used, mark the ASM_EXPR. */
if (!extended_p)
{
@ -16866,6 +16912,49 @@ cp_parser_asm_clobber_list (cp_parser* parser)
return clobbers;
}
/* Parse an asm-label-list.
asm-label-list:
identifier
asm-label-list , identifier
Returns a TREE_LIST, indicating the labels in the order that they
appeared. The TREE_VALUE of each node is a label. */
static tree
cp_parser_asm_label_list (cp_parser* parser)
{
tree labels = NULL_TREE;
while (true)
{
tree identifier, label, name;
/* Look for the identifier. */
identifier = cp_parser_identifier (parser);
if (!error_operand_p (identifier))
{
label = lookup_label (identifier);
if (TREE_CODE (label) == LABEL_DECL)
{
TREE_USED (label) = 1;
check_goto (label);
name = build_string (IDENTIFIER_LENGTH (identifier),
IDENTIFIER_POINTER (identifier));
labels = tree_cons (name, label, labels);
}
}
/* If the next token is not a `,', then the list is
complete. */
if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA))
break;
/* Consume the `,' token. */
cp_lexer_consume_token (parser->lexer);
}
return nreverse (labels);
}
/* Parse an (optional) series of attributes.
attributes:

View File

@ -10818,7 +10818,7 @@ tsubst_copy_asm_operands (tree t, tree args, tsubst_flags_t complain,
if (purpose)
purpose = RECUR (purpose);
value = TREE_VALUE (t);
if (value)
if (value && TREE_CODE (value) != LABEL_DECL)
value = RECUR (value);
chain = TREE_CHAIN (t);
if (chain && chain != void_type_node)
@ -11210,7 +11210,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
RECUR (ASM_STRING (t)),
tsubst_copy_asm_operands (ASM_OUTPUTS (t), args, complain, in_decl),
tsubst_copy_asm_operands (ASM_INPUTS (t), args, complain, in_decl),
tsubst_copy_asm_operands (ASM_CLOBBERS (t), args, complain, in_decl));
tsubst_copy_asm_operands (ASM_CLOBBERS (t), args, complain, in_decl),
tsubst_copy_asm_operands (ASM_LABELS (t), args, complain, in_decl));
{
tree asm_expr = tmp;
if (TREE_CODE (asm_expr) == CLEANUP_POINT_EXPR)

View File

@ -1200,12 +1200,13 @@ finish_compound_stmt (tree stmt)
}
/* Finish an asm-statement, whose components are a STRING, some
OUTPUT_OPERANDS, some INPUT_OPERANDS, and some CLOBBERS. Also note
whether the asm-statement should be considered volatile. */
OUTPUT_OPERANDS, some INPUT_OPERANDS, some CLOBBERS and some
LABELS. Also note whether the asm-statement should be
considered volatile. */
tree
finish_asm_stmt (int volatile_p, tree string, tree output_operands,
tree input_operands, tree clobbers)
tree input_operands, tree clobbers, tree labels)
{
tree r;
tree t;
@ -1223,7 +1224,7 @@ finish_asm_stmt (int volatile_p, tree string, tree output_operands,
oconstraints = (const char **) alloca (noutputs * sizeof (char *));
string = resolve_asm_operand_names (string, output_operands,
input_operands);
input_operands, labels);
for (i = 0, t = output_operands; t; t = TREE_CHAIN (t), ++i)
{
@ -1309,7 +1310,7 @@ finish_asm_stmt (int volatile_p, tree string, tree output_operands,
r = build_stmt (input_location, ASM_EXPR, string,
output_operands, input_operands,
clobbers);
clobbers, labels);
ASM_VOLATILE_P (r) = volatile_p || noutputs == 0;
r = maybe_cleanup_point_expr_void (r);
return add_stmt (r);

View File

@ -5251,7 +5251,7 @@ and most Unix assemblers do.
Speaking of labels, jumps from one @code{asm} to another are not
supported. The compiler's optimizers do not know about these jumps, and
therefore they cannot take account of them when deciding how to
optimize.
optimize. @xref{Extended asm with goto}.
@cindex macros containing @code{asm}
Usually the most convenient way to use these @code{asm} instructions is to
@ -5350,6 +5350,94 @@ For reasons similar to those described above, it is not possible to give
an assembler instruction access to the condition code left by previous
instructions.
@anchor{Extended asm with goto}
As of GCC version 4.5, @code{asm goto} may be used to have the assembly
jump to one or more C labels. In this form, a fifth section after the
clobber list contains a list of all C labels to which the assembly may jump.
Each label operand is implicitly self-named. The @code{asm} is also assumed
to fall through to the next statement.
This form of @code{asm} is restricted to not have outputs. This is due
to a internal restriction in the compiler that control transfer instructions
cannot have outputs. This restriction on @code{asm goto} may be lifted
in some future version of the compiler. In the mean time, @code{asm goto}
may include a memory clobber, and so leave outputs in memory.
@smallexample
int frob(int x)
@{
int y;
asm goto ("frob %%r5, %1; jc %l[error]; mov (%2), %%r5"
: : "r"(x), "r"(&y) : "r5", "memory" : error);
return y;
error:
return -1;
@}
@end smallexample
In this (inefficient) example, the @code{frob} instruction sets the
carry bit to indicate an error. The @code{jc} instruction detects
this and branches to the @code{error} label. Finally, the output
of the @code{frob} instruction (@code{%r5}) is stored into the memory
for variable @code{y}, which is later read by the @code{return} statement.
@smallexample
void doit(void)
@{
int i = 0;
asm goto ("mfsr %%r1, 123; jmp %%r1;"
".pushsection doit_table;"
".long %l0, %l1, %l2, %l3;"
".popsection"
: : : "r1" : label1, label2, label3, label4);
__builtin_unreachable ();
label1:
f1();
return;
label2:
f2();
return;
label3:
i = 1;
label4:
f3(i);
@}
@end smallexample
In this (also inefficient) example, the @code{mfsr} instruction reads
an address from some out-of-band machine register, and the following
@code{jmp} instruction branches to that address. The address read by
the @code{mfsr} instruction is assumed to have been previously set via
some application-specific mechanism to be one of the four values stored
in the @code{doit_table} section. Finally, the @code{asm} is followed
by a call to @code{__builtin_unreachable} to indicate that the @code{asm}
does not in fact fall through.
@smallexample
#define TRACE1(NUM) \
do @{ \
asm goto ("0: nop;" \
".pushsection trace_table;" \
".long 0b, %l0;" \
".popsection" \
: : : : trace#NUM); \
if (0) @{ trace#NUM: trace(); @} \
@} while (0)
#define TRACE TRACE1(__COUNTER__)
@end smallexample
In this example (which in fact inspired the @code{asm goto} feature)
we want on rare occasions to call the @code{trace} function; on other
occasions we'd like to keep the overhead to the absolute minimum.
The normal code path consists of a single @code{nop} instruction.
However, we record the address of this @code{nop} together with the
address of a label that calls the @code{trace} function. This allows
the @code{nop} instruction to be patched at runtime to be an
unconditional branch to the stored label. It is assumed that an
optimizing compiler will move the labeled block out of line, to
optimize the fall through path from the @code{asm}.
If you are writing a header file that should be includable in ISO C
programs, write @code{__asm__} instead of @code{asm}. @xref{Alternate
Keywords}.

View File

@ -1101,89 +1101,151 @@ dump_gimple_omp_return (pretty_printer *buffer, gimple gs, int spc, int flags)
static void
dump_gimple_asm (pretty_printer *buffer, gimple gs, int spc, int flags)
{
unsigned int i;
unsigned int i, n, f, fields;
if (flags & TDF_RAW)
dump_gimple_fmt (buffer, spc, flags, "%G <%+STRING <%n%s%n>", gs,
gimple_asm_string (gs));
{
dump_gimple_fmt (buffer, spc, flags, "%G <%+STRING <%n%s%n>", gs,
gimple_asm_string (gs));
n = gimple_asm_noutputs (gs);
if (n)
{
newline_and_indent (buffer, spc + 2);
pp_string (buffer, "OUTPUT: ");
for (i = 0; i < n; i++)
{
dump_generic_node (buffer, gimple_asm_output_op (gs, i),
spc, flags, false);
if (i < n - 1)
pp_string (buffer, ", ");
}
}
n = gimple_asm_ninputs (gs);
if (n)
{
newline_and_indent (buffer, spc + 2);
pp_string (buffer, "INPUT: ");
for (i = 0; i < n; i++)
{
dump_generic_node (buffer, gimple_asm_input_op (gs, i),
spc, flags, false);
if (i < n - 1)
pp_string (buffer, ", ");
}
}
n = gimple_asm_nclobbers (gs);
if (n)
{
newline_and_indent (buffer, spc + 2);
pp_string (buffer, "CLOBBER: ");
for (i = 0; i < n; i++)
{
dump_generic_node (buffer, gimple_asm_clobber_op (gs, i),
spc, flags, false);
if (i < n - 1)
pp_string (buffer, ", ");
}
}
n = gimple_asm_nlabels (gs);
if (n)
{
newline_and_indent (buffer, spc + 2);
pp_string (buffer, "LABEL: ");
for (i = 0; i < n; i++)
{
dump_generic_node (buffer, gimple_asm_label_op (gs, i),
spc, flags, false);
if (i < n - 1)
pp_string (buffer, ", ");
}
}
newline_and_indent (buffer, spc);
pp_character (buffer, '>');
}
else
{
pp_string (buffer, "__asm__");
if (gimple_asm_volatile_p (gs))
pp_string (buffer, " __volatile__");
if (gimple_asm_nlabels (gs))
pp_string (buffer, " goto");
pp_string (buffer, "(\"");
pp_string (buffer, gimple_asm_string (gs));
pp_string (buffer, "\"");
if (gimple_asm_nlabels (gs))
fields = 4;
else if (gimple_asm_nclobbers (gs))
fields = 3;
else if (gimple_asm_ninputs (gs))
fields = 2;
else if (gimple_asm_noutputs (gs))
fields = 1;
else
fields = 0;
for (f = 0; f < fields; ++f)
{
pp_string (buffer, " : ");
switch (f)
{
case 0:
n = gimple_asm_noutputs (gs);
for (i = 0; i < n; i++)
{
dump_generic_node (buffer, gimple_asm_output_op (gs, i),
spc, flags, false);
if (i < n - 1)
pp_string (buffer, ", ");
}
break;
case 1:
n = gimple_asm_ninputs (gs);
for (i = 0; i < n; i++)
{
dump_generic_node (buffer, gimple_asm_input_op (gs, i),
spc, flags, false);
if (i < n - 1)
pp_string (buffer, ", ");
}
break;
case 2:
n = gimple_asm_nclobbers (gs);
for (i = 0; i < n; i++)
{
dump_generic_node (buffer, gimple_asm_clobber_op (gs, i),
spc, flags, false);
if (i < n - 1)
pp_string (buffer, ", ");
}
break;
case 3:
n = gimple_asm_nlabels (gs);
for (i = 0; i < n; i++)
{
dump_generic_node (buffer, gimple_asm_label_op (gs, i),
spc, flags, false);
if (i < n - 1)
pp_string (buffer, ", ");
}
break;
default:
gcc_unreachable ();
}
}
pp_string (buffer, ");");
}
if (gimple_asm_ninputs (gs)
|| gimple_asm_noutputs (gs)
|| gimple_asm_nclobbers (gs))
{
if (gimple_asm_noutputs (gs))
{
if (flags & TDF_RAW)
{
newline_and_indent (buffer, spc + 2);
pp_string (buffer, "OUTPUT: ");
}
else
pp_string (buffer, " : ");
}
for (i = 0; i < gimple_asm_noutputs (gs); i++)
{
dump_generic_node (buffer, gimple_asm_output_op (gs, i), spc, flags,
false);
if ( i < gimple_asm_noutputs (gs) -1)
pp_string (buffer, ", ");
}
if (gimple_asm_ninputs (gs))
{
if (flags & TDF_RAW)
{
newline_and_indent (buffer, spc + 2);
pp_string (buffer, "INPUT: ");
}
else
pp_string (buffer, " : ");
}
for (i = 0; i < gimple_asm_ninputs (gs); i++)
{
dump_generic_node (buffer, gimple_asm_input_op (gs, i), spc, flags,
false);
if (i < gimple_asm_ninputs (gs) -1)
pp_string (buffer, " : ");
}
if (gimple_asm_nclobbers (gs))
{
if (flags & TDF_RAW)
{
newline_and_indent (buffer, spc + 2);
pp_string (buffer, "CLOBBER: ");
}
else
pp_string (buffer, " : ");
}
for (i = 0; i < gimple_asm_nclobbers (gs); i++)
{
dump_generic_node (buffer, gimple_asm_clobber_op (gs, i), spc, flags,
false);
if ( i < gimple_asm_nclobbers (gs) -1)
pp_string (buffer, ", ");
}
}
if (flags & TDF_RAW)
{
newline_and_indent (buffer, spc);
pp_character (buffer, '>');
}
else
pp_string (buffer, ");");
}

View File

@ -507,17 +507,22 @@ gimple_build_bind (tree vars, gimple_seq body, tree block)
static inline gimple
gimple_build_asm_1 (const char *string, unsigned ninputs, unsigned noutputs,
unsigned nclobbers)
unsigned nclobbers, unsigned nlabels)
{
gimple p;
int size = strlen (string);
/* ASMs with labels cannot have outputs. This should have been
enforced by the front end. */
gcc_assert (nlabels == 0 || noutputs == 0);
p = gimple_build_with_ops (GIMPLE_ASM, ERROR_MARK,
ninputs + noutputs + nclobbers);
ninputs + noutputs + nclobbers + nlabels);
p->gimple_asm.ni = ninputs;
p->gimple_asm.no = noutputs;
p->gimple_asm.nc = nclobbers;
p->gimple_asm.nl = nlabels;
p->gimple_asm.string = ggc_alloc_string (string, size);
#ifdef GATHER_STATISTICS
@ -535,11 +540,13 @@ gimple_build_asm_1 (const char *string, unsigned ninputs, unsigned noutputs,
NCLOBBERS is the number of clobbered registers.
INPUTS is a vector of the input register parameters.
OUTPUTS is a vector of the output register parameters.
CLOBBERS is a vector of the clobbered register parameters. */
CLOBBERS is a vector of the clobbered register parameters.
LABELS is a vector of destination labels. */
gimple
gimple_build_asm_vec (const char *string, VEC(tree,gc)* inputs,
VEC(tree,gc)* outputs, VEC(tree,gc)* clobbers)
VEC(tree,gc)* outputs, VEC(tree,gc)* clobbers,
VEC(tree,gc)* labels)
{
gimple p;
unsigned i;
@ -547,7 +554,8 @@ gimple_build_asm_vec (const char *string, VEC(tree,gc)* inputs,
p = gimple_build_asm_1 (string,
VEC_length (tree, inputs),
VEC_length (tree, outputs),
VEC_length (tree, clobbers));
VEC_length (tree, clobbers),
VEC_length (tree, labels));
for (i = 0; i < VEC_length (tree, inputs); i++)
gimple_asm_set_input_op (p, i, VEC_index (tree, inputs, i));
@ -558,39 +566,8 @@ gimple_build_asm_vec (const char *string, VEC(tree,gc)* inputs,
for (i = 0; i < VEC_length (tree, clobbers); i++)
gimple_asm_set_clobber_op (p, i, VEC_index (tree, clobbers, i));
return p;
}
/* Build a GIMPLE_ASM statement.
STRING is the assembly code.
NINPUT is the number of register inputs.
NOUTPUT is the number of register outputs.
NCLOBBERS is the number of clobbered registers.
... are trees for each input, output and clobbered register. */
gimple
gimple_build_asm (const char *string, unsigned ninputs, unsigned noutputs,
unsigned nclobbers, ...)
{
gimple p;
unsigned i;
va_list ap;
p = gimple_build_asm_1 (string, ninputs, noutputs, nclobbers);
va_start (ap, nclobbers);
for (i = 0; i < ninputs; i++)
gimple_asm_set_input_op (p, i, va_arg (ap, tree));
for (i = 0; i < noutputs; i++)
gimple_asm_set_output_op (p, i, va_arg (ap, tree));
for (i = 0; i < nclobbers; i++)
gimple_asm_set_clobber_op (p, i, va_arg (ap, tree));
va_end (ap);
for (i = 0; i < VEC_length (tree, labels); i++)
gimple_asm_set_label_op (p, i, VEC_index (tree, labels, i));
return p;
}
@ -1230,10 +1207,10 @@ static tree
walk_gimple_asm (gimple stmt, walk_tree_fn callback_op,
struct walk_stmt_info *wi)
{
tree ret;
tree ret, op;
unsigned noutputs;
const char **oconstraints;
unsigned i;
unsigned i, n;
const char *constraint;
bool allows_mem, allows_reg, is_inout;
@ -1245,7 +1222,7 @@ walk_gimple_asm (gimple stmt, walk_tree_fn callback_op,
for (i = 0; i < noutputs; i++)
{
tree op = gimple_asm_output_op (stmt, i);
op = gimple_asm_output_op (stmt, i);
constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op)));
oconstraints[i] = constraint;
parse_output_constraint (&constraint, i, 0, 0, &allows_mem, &allows_reg,
@ -1257,18 +1234,19 @@ walk_gimple_asm (gimple stmt, walk_tree_fn callback_op,
return ret;
}
for (i = 0; i < gimple_asm_ninputs (stmt); i++)
n = gimple_asm_ninputs (stmt);
for (i = 0; i < n; i++)
{
tree op = gimple_asm_input_op (stmt, i);
op = gimple_asm_input_op (stmt, i);
constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op)));
parse_input_constraint (&constraint, 0, 0, noutputs, 0,
oconstraints, &allows_mem, &allows_reg);
if (wi)
wi->val_only = (allows_reg || !allows_mem);
/* Although input "m" is not really a LHS, we need a lvalue. */
if (wi)
wi->is_lhs = !wi->val_only;
{
wi->val_only = (allows_reg || !allows_mem);
/* Although input "m" is not really a LHS, we need a lvalue. */
wi->is_lhs = !wi->val_only;
}
ret = walk_tree (&TREE_VALUE (op), callback_op, wi, NULL);
if (ret)
return ret;
@ -1280,6 +1258,15 @@ walk_gimple_asm (gimple stmt, walk_tree_fn callback_op,
wi->val_only = true;
}
n = gimple_asm_nlabels (stmt);
for (i = 0; i < n; i++)
{
op = gimple_asm_label_op (stmt, i);
ret = walk_tree (&TREE_VALUE (op), callback_op, wi, NULL);
if (ret)
return ret;
}
return NULL_TREE;
}

View File

@ -106,7 +106,8 @@ DEFGSCODE(GIMPLE_ASSIGN, "gimple_assign", GSS_WITH_MEM_OPS)
STRING is the string containing the assembly statements.
I1 ... IN are the N input operands.
O1 ... OM are the M output operands.
C1 ... CP are the P clobber operands. */
C1 ... CP are the P clobber operands.
L1 ... LQ are the Q label operands. */
DEFGSCODE(GIMPLE_ASM, "gimple_asm", GSS_ASM)
/* GIMPLE_CALL <FN, LHS, ARG1, ..., ARGN[, CHAIN]> represents function

View File

@ -553,10 +553,11 @@ struct GTY(()) gimple_statement_asm
const char *string;
/* [ WORD 10 ]
Number of inputs, outputs and clobbers. */
Number of inputs, outputs, clobbers, labels. */
unsigned char ni;
unsigned char no;
unsigned short nc;
unsigned char nc;
unsigned char nl;
/* [ WORD 11 ]
Operand vector. NOTE! This must always be the last field
@ -792,9 +793,8 @@ gimple gimple_build_label (tree label);
gimple gimple_build_goto (tree dest);
gimple gimple_build_nop (void);
gimple gimple_build_bind (tree, gimple_seq, tree);
gimple gimple_build_asm (const char *, unsigned, unsigned, unsigned, ...);
gimple gimple_build_asm_vec (const char *, VEC(tree,gc) *, VEC(tree,gc) *,
VEC(tree,gc) *);
VEC(tree,gc) *, VEC(tree,gc) *);
gimple gimple_build_catch (tree, gimple_seq);
gimple gimple_build_eh_filter (tree, gimple_seq);
gimple gimple_build_eh_must_not_throw (tree);
@ -2614,6 +2614,14 @@ gimple_asm_nclobbers (const_gimple gs)
return gs->gimple_asm.nc;
}
/* Return the number of label operands for GIMPLE_ASM GS. */
static inline unsigned
gimple_asm_nlabels (const_gimple gs)
{
GIMPLE_CHECK (gs, GIMPLE_ASM);
return gs->gimple_asm.nl;
}
/* Return input operand INDEX of GIMPLE_ASM GS. */
@ -2703,6 +2711,26 @@ gimple_asm_set_clobber_op (gimple gs, unsigned index, tree clobber_op)
gimple_set_op (gs, index + gs->gimple_asm.ni + gs->gimple_asm.no, clobber_op);
}
/* Return label operand INDEX of GIMPLE_ASM GS. */
static inline tree
gimple_asm_label_op (const_gimple gs, unsigned index)
{
GIMPLE_CHECK (gs, GIMPLE_ASM);
gcc_assert (index <= gs->gimple_asm.nl);
return gimple_op (gs, index + gs->gimple_asm.ni + gs->gimple_asm.nc);
}
/* Set LABEL_OP to be label operand INDEX in GIMPLE_ASM GS. */
static inline void
gimple_asm_set_label_op (gimple gs, unsigned index, tree label_op)
{
GIMPLE_CHECK (gs, GIMPLE_ASM);
gcc_assert (index <= gs->gimple_asm.nl);
gcc_assert (TREE_CODE (label_op) == TREE_LIST);
gimple_set_op (gs, index + gs->gimple_asm.ni + gs->gimple_asm.nc, label_op);
}
/* Return the string representing the assembly instruction in
GIMPLE_ASM GS. */

View File

@ -4765,13 +4765,14 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
VEC(tree, gc) *inputs;
VEC(tree, gc) *outputs;
VEC(tree, gc) *clobbers;
VEC(tree, gc) *labels;
tree link_next;
expr = *expr_p;
noutputs = list_length (ASM_OUTPUTS (expr));
oconstraints = (const char **) alloca ((noutputs) * sizeof (const char *));
inputs = outputs = clobbers = NULL;
inputs = outputs = clobbers = labels = NULL;
ret = GS_ALL_DONE;
link_next = NULL_TREE;
@ -4953,13 +4954,16 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
}
for (link = ASM_CLOBBERS (expr); link; ++i, link = TREE_CHAIN (link))
VEC_safe_push (tree, gc, clobbers, link);
VEC_safe_push (tree, gc, clobbers, link);
for (link = ASM_LABELS (expr); link; ++i, link = TREE_CHAIN (link))
VEC_safe_push (tree, gc, labels, link);
/* Do not add ASMs with errors to the gimple IL stream. */
if (ret != GS_ERROR)
{
stmt = gimple_build_asm_vec (TREE_STRING_POINTER (ASM_STRING (expr)),
inputs, outputs, clobbers);
inputs, outputs, clobbers, labels);
gimple_asm_set_volatile (stmt, ASM_VOLATILE_P (expr));
gimple_asm_set_input (stmt, ASM_INPUT_P (expr));

View File

@ -68,6 +68,7 @@ along with GCC; see the file COPYING3. If not see
static void init_label_info (rtx);
static void mark_all_labels (rtx);
static void mark_jump_label_1 (rtx, rtx, bool, bool);
static void mark_jump_label_asm (rtx, rtx);
static void redirect_exp_1 (rtx *, rtx, rtx, rtx);
static int invert_exp_1 (rtx, rtx);
static int returnjump_p_1 (rtx *, void *);
@ -1006,8 +1007,12 @@ sets_cc0_p (const_rtx x)
void
mark_jump_label (rtx x, rtx insn, int in_mem)
{
mark_jump_label_1 (x, insn, in_mem != 0,
(insn != NULL && x == PATTERN (insn) && JUMP_P (insn)));
rtx asmop = extract_asm_operands (x);
if (asmop)
mark_jump_label_asm (asmop, insn);
else
mark_jump_label_1 (x, insn, in_mem != 0,
(insn != NULL && x == PATTERN (insn) && JUMP_P (insn)));
}
/* Worker function for mark_jump_label. IN_MEM is TRUE when X occurs
@ -1145,6 +1150,22 @@ mark_jump_label_1 (rtx x, rtx insn, bool in_mem, bool is_target)
}
}
/* Worker function for mark_jump_label. Handle asm insns specially.
In particular, output operands need not be considered so we can
avoid re-scanning the replicated asm_operand. Also, the asm_labels
need to be considered targets. */
static void
mark_jump_label_asm (rtx asmop, rtx insn)
{
int i;
for (i = ASM_OPERANDS_INPUT_LENGTH (asmop) - 1; i >= 0; --i)
mark_jump_label_1 (ASM_OPERANDS_INPUT (asmop, i), insn, false, false);
for (i = ASM_OPERANDS_LABEL_LENGTH (asmop) - 1; i >= 0; --i)
mark_jump_label_1 (ASM_OPERANDS_LABEL (asmop, i), insn, false, true);
}
/* Delete insn INSN from the chain of insns and update label ref counts
and delete insns now unreachable.
@ -1386,9 +1407,17 @@ int
redirect_jump_1 (rtx jump, rtx nlabel)
{
int ochanges = num_validated_changes ();
rtx *loc;
rtx *loc, asmop;
if (GET_CODE (PATTERN (jump)) == PARALLEL)
asmop = extract_asm_operands (PATTERN (jump));
if (asmop)
{
if (nlabel == NULL)
return 0;
gcc_assert (ASM_OPERANDS_LABEL_LENGTH (asmop) == 1);
loc = &ASM_OPERANDS_LABEL (asmop, 0);
}
else if (GET_CODE (PATTERN (jump)) == PARALLEL)
loc = &XVECEXP (PATTERN (jump), 0, 0);
else
loc = &PATTERN (jump);
@ -1514,7 +1543,8 @@ invert_jump_1 (rtx jump, rtx nlabel)
int ok;
ochanges = num_validated_changes ();
gcc_assert (x);
if (x == NULL)
return 0;
ok = invert_exp_1 (SET_SRC (x), jump);
gcc_assert (ok);

View File

@ -1373,6 +1373,42 @@ comparison_operator (rtx op, enum machine_mode mode)
&& COMPARISON_P (op));
}
/* If BODY is an insn body that uses ASM_OPERANDS, return it. */
rtx
extract_asm_operands (rtx body)
{
rtx tmp;
switch (GET_CODE (body))
{
case ASM_OPERANDS:
return body;
case SET:
/* Single output operand: BODY is (set OUTPUT (asm_operands ...)). */
tmp = SET_SRC (body);
if (GET_CODE (tmp) == ASM_OPERANDS)
return tmp;
break;
case PARALLEL:
tmp = XVECEXP (body, 0, 0);
if (GET_CODE (tmp) == ASM_OPERANDS)
return tmp;
if (GET_CODE (tmp) == SET)
{
tmp = SET_SRC (tmp);
if (GET_CODE (tmp) == ASM_OPERANDS)
return tmp;
}
break;
default:
break;
}
return NULL;
}
/* If BODY is an insn body that uses ASM_OPERANDS,
return the number of operands (both input and output) in the insn.
Otherwise return -1. */
@ -1380,26 +1416,22 @@ comparison_operator (rtx op, enum machine_mode mode)
int
asm_noperands (const_rtx body)
{
switch (GET_CODE (body))
rtx asm_op = extract_asm_operands (CONST_CAST_RTX (body));
int n_sets = 0;
if (asm_op == NULL)
return -1;
if (GET_CODE (body) == SET)
n_sets = 1;
else if (GET_CODE (body) == PARALLEL)
{
case ASM_OPERANDS:
/* No output operands: return number of input operands. */
return ASM_OPERANDS_INPUT_LENGTH (body);
case SET:
if (GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
/* Single output operand: BODY is (set OUTPUT (asm_operands ...)). */
return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body)) + 1;
else
return -1;
case PARALLEL:
if (GET_CODE (XVECEXP (body, 0, 0)) == SET
&& GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) == ASM_OPERANDS)
int i;
if (GET_CODE (XVECEXP (body, 0, 0)) == SET)
{
/* Multiple output operands, or 1 output plus some clobbers:
body is [(set OUTPUT (asm_operands ...))... (clobber (reg ...))...]. */
int i;
int n_sets;
body is
[(set OUTPUT (asm_operands ...))... (clobber (reg ...))...]. */
/* Count backwards through CLOBBERs to determine number of SETs. */
for (i = XVECLEN (body, 0); i > 0; i--)
{
@ -1425,30 +1457,23 @@ asm_noperands (const_rtx body)
/* If these ASM_OPERANDS rtx's came from different original insns
then they aren't allowed together. */
if (ASM_OPERANDS_INPUT_VEC (SET_SRC (elt))
!= ASM_OPERANDS_INPUT_VEC (SET_SRC (XVECEXP (body, 0, 0))))
!= ASM_OPERANDS_INPUT_VEC (asm_op))
return -1;
}
return (ASM_OPERANDS_INPUT_LENGTH (SET_SRC (XVECEXP (body, 0, 0)))
+ n_sets);
}
else if (GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
else
{
/* 0 outputs, but some clobbers:
body is [(asm_operands ...) (clobber (reg ...))...]. */
int i;
/* Make sure all the other parallel things really are clobbers. */
for (i = XVECLEN (body, 0) - 1; i > 0; i--)
if (GET_CODE (XVECEXP (body, 0, i)) != CLOBBER)
return -1;
return ASM_OPERANDS_INPUT_LENGTH (XVECEXP (body, 0, 0));
}
else
return -1;
default:
return -1;
}
return (ASM_OPERANDS_INPUT_LENGTH (asm_op)
+ ASM_OPERANDS_LABEL_LENGTH (asm_op) + n_sets);
}
/* Assuming BODY is an insn body that uses ASM_OPERANDS,
@ -1466,28 +1491,19 @@ decode_asm_operands (rtx body, rtx *operands, rtx **operand_locs,
const char **constraints, enum machine_mode *modes,
location_t *loc)
{
int i;
int noperands;
rtx asmop = 0;
int noperands, nbase = 0, n, i;
rtx asmop;
if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
switch (GET_CODE (body))
{
case ASM_OPERANDS:
/* Zero output asm: BODY is (asm_operands ...). */
asmop = body;
break;
case SET:
/* Single output asm: BODY is (set OUTPUT (asm_operands ...)). */
asmop = SET_SRC (body);
/* Single output operand: BODY is (set OUTPUT (asm_operands ....)). */
noperands = ASM_OPERANDS_INPUT_LENGTH (asmop) + 1;
for (i = 1; i < noperands; i++)
{
if (operand_locs)
operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i - 1);
if (operands)
operands[i] = ASM_OPERANDS_INPUT (asmop, i - 1);
if (constraints)
constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i - 1);
if (modes)
modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i - 1);
}
/* The output is in the SET.
Its constraint is in the ASM_OPERANDS itself. */
@ -1499,93 +1515,70 @@ decode_asm_operands (rtx body, rtx *operands, rtx **operand_locs,
constraints[0] = ASM_OPERANDS_OUTPUT_CONSTRAINT (asmop);
if (modes)
modes[0] = GET_MODE (SET_DEST (body));
nbase = 1;
break;
case PARALLEL:
{
int nparallel = XVECLEN (body, 0); /* Includes CLOBBERs. */
asmop = XVECEXP (body, 0, 0);
if (GET_CODE (asmop) == SET)
{
asmop = SET_SRC (asmop);
/* At least one output, plus some CLOBBERs. The outputs are in
the SETs. Their constraints are in the ASM_OPERANDS itself. */
for (i = 0; i < nparallel; i++)
{
if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
break; /* Past last SET */
if (operands)
operands[i] = SET_DEST (XVECEXP (body, 0, i));
if (operand_locs)
operand_locs[i] = &SET_DEST (XVECEXP (body, 0, i));
if (constraints)
constraints[i] = XSTR (SET_SRC (XVECEXP (body, 0, i)), 1);
if (modes)
modes[i] = GET_MODE (SET_DEST (XVECEXP (body, 0, i)));
}
nbase = i;
}
break;
}
default:
gcc_unreachable ();
}
else if (GET_CODE (body) == ASM_OPERANDS)
noperands = (ASM_OPERANDS_INPUT_LENGTH (asmop)
+ ASM_OPERANDS_LABEL_LENGTH (asmop) + nbase);
n = ASM_OPERANDS_INPUT_LENGTH (asmop);
for (i = 0; i < n; i++)
{
asmop = body;
/* No output operands: BODY is (asm_operands ....). */
noperands = ASM_OPERANDS_INPUT_LENGTH (asmop);
/* The input operands are found in the 1st element vector. */
/* Constraints for inputs are in the 2nd element vector. */
for (i = 0; i < noperands; i++)
{
if (operand_locs)
operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i);
if (operands)
operands[i] = ASM_OPERANDS_INPUT (asmop, i);
if (constraints)
constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i);
if (modes)
modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i);
}
if (operand_locs)
operand_locs[nbase + i] = &ASM_OPERANDS_INPUT (asmop, i);
if (operands)
operands[nbase + i] = ASM_OPERANDS_INPUT (asmop, i);
if (constraints)
constraints[nbase + i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i);
if (modes)
modes[nbase + i] = ASM_OPERANDS_INPUT_MODE (asmop, i);
}
else if (GET_CODE (body) == PARALLEL
&& GET_CODE (XVECEXP (body, 0, 0)) == SET
&& GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) == ASM_OPERANDS)
nbase += n;
n = ASM_OPERANDS_LABEL_LENGTH (asmop);
for (i = 0; i < n; i++)
{
int nparallel = XVECLEN (body, 0); /* Includes CLOBBERs. */
int nin;
int nout = 0; /* Does not include CLOBBERs. */
asmop = SET_SRC (XVECEXP (body, 0, 0));
nin = ASM_OPERANDS_INPUT_LENGTH (asmop);
/* At least one output, plus some CLOBBERs. */
/* The outputs are in the SETs.
Their constraints are in the ASM_OPERANDS itself. */
for (i = 0; i < nparallel; i++)
{
if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
break; /* Past last SET */
if (operands)
operands[i] = SET_DEST (XVECEXP (body, 0, i));
if (operand_locs)
operand_locs[i] = &SET_DEST (XVECEXP (body, 0, i));
if (constraints)
constraints[i] = XSTR (SET_SRC (XVECEXP (body, 0, i)), 1);
if (modes)
modes[i] = GET_MODE (SET_DEST (XVECEXP (body, 0, i)));
nout++;
}
for (i = 0; i < nin; i++)
{
if (operand_locs)
operand_locs[i + nout] = &ASM_OPERANDS_INPUT (asmop, i);
if (operands)
operands[i + nout] = ASM_OPERANDS_INPUT (asmop, i);
if (constraints)
constraints[i + nout] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i);
if (modes)
modes[i + nout] = ASM_OPERANDS_INPUT_MODE (asmop, i);
}
}
else if (GET_CODE (body) == PARALLEL
&& GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
{
/* No outputs, but some CLOBBERs. */
int nin;
asmop = XVECEXP (body, 0, 0);
nin = ASM_OPERANDS_INPUT_LENGTH (asmop);
for (i = 0; i < nin; i++)
{
if (operand_locs)
operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i);
if (operands)
operands[i] = ASM_OPERANDS_INPUT (asmop, i);
if (constraints)
constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i);
if (modes)
modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i);
}
if (operand_locs)
operand_locs[nbase + i] = &ASM_OPERANDS_LABEL (asmop, i);
if (operands)
operands[nbase + i] = ASM_OPERANDS_LABEL (asmop, i);
if (constraints)
constraints[nbase + i] = "";
if (modes)
modes[nbase + i] = Pmode;
}
if (loc)
@ -1605,6 +1598,11 @@ asm_operand_ok (rtx op, const char *constraint, const char **constraints)
/* Use constrain_operands after reload. */
gcc_assert (!reload_completed);
/* Empty constraint string is the same as "X,...,X", i.e. X for as
many alternatives as required to match the other operands. */
if (*constraint == '\0')
return 1;
while (*constraint)
{
char c = *constraint;

View File

@ -254,7 +254,7 @@ static void pop_stack (stack, int);
static rtx *get_true_reg (rtx *);
static int check_asm_stack_operands (rtx);
static int get_asm_operand_n_inputs (rtx);
static void get_asm_operands_in_out (rtx, int *, int *);
static rtx stack_result (tree);
static void replace_reg (rtx *, int);
static void remove_regno_note (rtx, enum reg_note, unsigned int);
@ -480,8 +480,7 @@ check_asm_stack_operands (rtx insn)
preprocess_constraints ();
n_inputs = get_asm_operand_n_inputs (body);
n_outputs = recog_data.n_operands - n_inputs;
get_asm_operands_in_out (body, &n_outputs, &n_inputs);
if (alt < 0)
{
@ -645,24 +644,15 @@ check_asm_stack_operands (rtx insn)
N_INPUTS and N_OUTPUTS are pointers to ints into which the results are
placed. */
static int
get_asm_operand_n_inputs (rtx body)
static void
get_asm_operands_in_out (rtx body, int *pout, int *pin)
{
switch (GET_CODE (body))
{
case SET:
gcc_assert (GET_CODE (SET_SRC (body)) == ASM_OPERANDS);
return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body));
case ASM_OPERANDS:
return ASM_OPERANDS_INPUT_LENGTH (body);
case PARALLEL:
return get_asm_operand_n_inputs (XVECEXP (body, 0, 0));
default:
gcc_unreachable ();
}
rtx asmop = extract_asm_operands (body);
*pin = ASM_OPERANDS_INPUT_LENGTH (asmop);
*pout = (recog_data.n_operands
- ASM_OPERANDS_INPUT_LENGTH (asmop)
- ASM_OPERANDS_LABEL_LENGTH (asmop));
}
/* If current function returns its result in an fp stack register,
@ -2034,8 +2024,7 @@ subst_asm_stack_regs (rtx insn, stack regstack)
preprocess_constraints ();
n_inputs = get_asm_operand_n_inputs (body);
n_outputs = recog_data.n_operands - n_inputs;
get_asm_operands_in_out (body, &n_outputs, &n_inputs);
gcc_assert (alt >= 0);

View File

@ -187,8 +187,9 @@ DEF_RTL_EXPR(ASM_INPUT, "asm_input", "si", RTX_EXTRA)
5th is a vector of modes and constraints for the input operands.
Each element is an ASM_INPUT containing a constraint string
and whose mode indicates the mode of the input operand.
6th is the source line number. */
DEF_RTL_EXPR(ASM_OPERANDS, "asm_operands", "ssiEEi", RTX_EXTRA)
6th is a vector of labels that may be branched to by the asm.
7th is the source line number. */
DEF_RTL_EXPR(ASM_OPERANDS, "asm_operands", "ssiEEEi", RTX_EXTRA)
/* A machine-specific operation.
1st operand is a vector of operands being used by the operation so that

View File

@ -1188,7 +1188,10 @@ do { \
XSTR (XCVECEXP (RTX, 4, N, ASM_OPERANDS), 0)
#define ASM_OPERANDS_INPUT_MODE(RTX, N) \
GET_MODE (XCVECEXP (RTX, 4, N, ASM_OPERANDS))
#define ASM_OPERANDS_SOURCE_LOCATION(RTX) XCUINT (RTX, 5, ASM_OPERANDS)
#define ASM_OPERANDS_LABEL_VEC(RTX) XCVEC (RTX, 5, ASM_OPERANDS)
#define ASM_OPERANDS_LABEL_LENGTH(RTX) XCVECLEN (RTX, 5, ASM_OPERANDS)
#define ASM_OPERANDS_LABEL(RTX, N) XCVECEXP (RTX, 5, N, ASM_OPERANDS)
#define ASM_OPERANDS_SOURCE_LOCATION(RTX) XCUINT (RTX, 6, ASM_OPERANDS)
#define ASM_INPUT_SOURCE_LOCATION(RTX) XCUINT (RTX, 1, ASM_INPUT)
/* 1 if RTX is a mem that is statically allocated in read-only memory. */
@ -1926,6 +1929,7 @@ extern bool resize_reg_info (void);
extern void free_reg_info (void);
/* recog.c */
extern rtx extract_asm_operands (rtx);
extern int asm_noperands (const_rtx);
extern const char *decode_asm_operands (rtx, rtx *, rtx **, const char **,
enum machine_mode *, location_t *);

View File

@ -110,8 +110,8 @@ static int n_occurrences (int, const char *);
static bool tree_conflicts_with_clobbers_p (tree, HARD_REG_SET *);
static void expand_nl_goto_receiver (void);
static bool check_operand_nalternatives (tree, tree);
static bool check_unique_operand_names (tree, tree);
static char *resolve_operand_name_1 (char *, tree, tree);
static bool check_unique_operand_names (tree, tree, tree);
static char *resolve_operand_name_1 (char *, tree, tree, tree);
static void expand_null_return_1 (void);
static void expand_value_return (rtx);
static int estimate_case_costs (case_node_ptr);
@ -633,12 +633,13 @@ tree_conflicts_with_clobbers_p (tree t, HARD_REG_SET *clobbered_regs)
static void
expand_asm_operands (tree string, tree outputs, tree inputs,
tree clobbers, int vol, location_t locus)
tree clobbers, tree labels, int vol, location_t locus)
{
rtvec argvec, constraintvec;
rtvec argvec, constraintvec, labelvec;
rtx body;
int ninputs = list_length (inputs);
int noutputs = list_length (outputs);
int nlabels = list_length (labels);
int ninout;
int nclobbers;
HARD_REG_SET clobbered_regs;
@ -661,7 +662,7 @@ expand_asm_operands (tree string, tree outputs, tree inputs,
if (! check_operand_nalternatives (outputs, inputs))
return;
string = resolve_asm_operand_names (string, outputs, inputs);
string = resolve_asm_operand_names (string, outputs, inputs, labels);
/* Collect constraints. */
i = 0;
@ -845,12 +846,13 @@ expand_asm_operands (tree string, tree outputs, tree inputs,
argvec = rtvec_alloc (ninputs);
constraintvec = rtvec_alloc (ninputs);
labelvec = rtvec_alloc (nlabels);
body = gen_rtx_ASM_OPERANDS ((noutputs == 0 ? VOIDmode
: GET_MODE (output_rtx[0])),
ggc_strdup (TREE_STRING_POINTER (string)),
empty_string, 0, argvec, constraintvec,
locus);
labelvec, locus);
MEM_VOLATILE_P (body) = vol;
@ -959,6 +961,11 @@ expand_asm_operands (tree string, tree outputs, tree inputs,
= gen_rtx_ASM_INPUT (inout_mode[i], ggc_strdup (buffer));
}
/* Copy labels to the vector. */
for (i = 0, tail = labels; i < nlabels; ++i, tail = TREE_CHAIN (tail))
ASM_OPERANDS_LABEL (body, i)
= gen_rtx_LABEL_REF (Pmode, label_rtx (TREE_VALUE (tail)));
generating_concat_p = old_generating_concat_p;
/* Now, for each output, construct an rtx
@ -966,18 +973,21 @@ expand_asm_operands (tree string, tree outputs, tree inputs,
ARGVEC CONSTRAINTS OPNAMES))
If there is more than one, put them inside a PARALLEL. */
if (noutputs == 1 && nclobbers == 0)
if (nlabels > 0 && nclobbers == 0)
{
ASM_OPERANDS_OUTPUT_CONSTRAINT (body) = ggc_strdup (constraints[0]);
emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body));
gcc_assert (noutputs == 0);
emit_jump_insn (body);
}
else if (noutputs == 0 && nclobbers == 0)
{
/* No output operands: put in a raw ASM_OPERANDS rtx. */
emit_insn (body);
}
else if (noutputs == 1 && nclobbers == 0)
{
ASM_OPERANDS_OUTPUT_CONSTRAINT (body) = ggc_strdup (constraints[0]);
emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body));
}
else
{
rtx obody = body;
@ -998,7 +1008,7 @@ expand_asm_operands (tree string, tree outputs, tree inputs,
(GET_MODE (output_rtx[i]),
ggc_strdup (TREE_STRING_POINTER (string)),
ggc_strdup (constraints[i]),
i, argvec, constraintvec, locus));
i, argvec, constraintvec, labelvec, locus));
MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i))) = vol;
}
@ -1062,7 +1072,10 @@ expand_asm_operands (tree string, tree outputs, tree inputs,
= gen_rtx_CLOBBER (VOIDmode, clobbered_reg);
}
emit_insn (body);
if (nlabels > 0)
emit_jump_insn (body);
else
emit_insn (body);
}
/* For any outputs that needed reloading into registers, spill them
@ -1083,7 +1096,7 @@ expand_asm_stmt (gimple stmt)
tree *o;
size_t i, n;
const char *s;
tree str, out, in, cl;
tree str, out, in, cl, labels;
/* Meh... convert the gimple asm operands into real tree lists.
Eventually we should make all routines work on the vectors instead
@ -1094,10 +1107,7 @@ expand_asm_stmt (gimple stmt)
{
t = out = gimple_asm_output_op (stmt, 0);
for (i = 1; i < n; i++)
{
TREE_CHAIN (t) = gimple_asm_output_op (stmt, i);
t = gimple_asm_output_op (stmt, i);
}
t = TREE_CHAIN (t) = gimple_asm_output_op (stmt, i);
}
in = NULL_TREE;
@ -1106,10 +1116,7 @@ expand_asm_stmt (gimple stmt)
{
t = in = gimple_asm_input_op (stmt, 0);
for (i = 1; i < n; i++)
{
TREE_CHAIN (t) = gimple_asm_input_op (stmt, i);
t = gimple_asm_input_op (stmt, i);
}
t = TREE_CHAIN (t) = gimple_asm_input_op (stmt, i);
}
cl = NULL_TREE;
@ -1118,10 +1125,16 @@ expand_asm_stmt (gimple stmt)
{
t = cl = gimple_asm_clobber_op (stmt, 0);
for (i = 1; i < n; i++)
{
TREE_CHAIN (t) = gimple_asm_clobber_op (stmt, i);
t = gimple_asm_clobber_op (stmt, i);
}
t = TREE_CHAIN (t) = gimple_asm_clobber_op (stmt, i);
}
labels = NULL_TREE;
n = gimple_asm_nlabels (stmt);
if (n > 0)
{
t = labels = gimple_asm_label_op (stmt, 0);
for (i = 1; i < n; i++)
t = TREE_CHAIN (t) = gimple_asm_label_op (stmt, i);
}
s = gimple_asm_string (stmt);
@ -1144,8 +1157,8 @@ expand_asm_stmt (gimple stmt)
/* Generate the ASM_OPERANDS insn; store into the TREE_VALUEs of
OUTPUTS some trees for where the values were actually stored. */
expand_asm_operands (str, outputs, in, cl, gimple_asm_volatile_p (stmt),
input_location);
expand_asm_operands (str, outputs, in, cl, labels,
gimple_asm_volatile_p (stmt), input_location);
/* Copy all the intermediate outputs into the specified outputs. */
for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
@ -1210,7 +1223,7 @@ check_operand_nalternatives (tree outputs, tree inputs)
so all we need are pointer comparisons. */
static bool
check_unique_operand_names (tree outputs, tree inputs)
check_unique_operand_names (tree outputs, tree inputs, tree labels)
{
tree i, j;
@ -1239,6 +1252,20 @@ check_unique_operand_names (tree outputs, tree inputs)
goto failure;
}
for (i = labels; i ; i = TREE_CHAIN (i))
{
tree i_name = TREE_PURPOSE (i);
if (! i_name)
continue;
for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
if (simple_cst_equal (i_name, TREE_PURPOSE (j)))
goto failure;
for (j = inputs; j ; j = TREE_CHAIN (j))
if (simple_cst_equal (i_name, TREE_PURPOSE (TREE_PURPOSE (j))))
goto failure;
}
return true;
failure:
@ -1252,14 +1279,14 @@ check_unique_operand_names (tree outputs, tree inputs)
STRING and in the constraints to those numbers. */
tree
resolve_asm_operand_names (tree string, tree outputs, tree inputs)
resolve_asm_operand_names (tree string, tree outputs, tree inputs, tree labels)
{
char *buffer;
char *p;
const char *c;
tree t;
check_unique_operand_names (outputs, inputs);
check_unique_operand_names (outputs, inputs, labels);
/* Substitute [<name>] in input constraint strings. There should be no
named operands in output constraints. */
@ -1270,7 +1297,7 @@ resolve_asm_operand_names (tree string, tree outputs, tree inputs)
{
p = buffer = xstrdup (c);
while ((p = strchr (p, '[')) != NULL)
p = resolve_operand_name_1 (p, outputs, inputs);
p = resolve_operand_name_1 (p, outputs, inputs, NULL);
TREE_VALUE (TREE_PURPOSE (t))
= build_string (strlen (buffer), buffer);
free (buffer);
@ -1313,7 +1340,7 @@ resolve_asm_operand_names (tree string, tree outputs, tree inputs)
continue;
}
p = resolve_operand_name_1 (p, outputs, inputs);
p = resolve_operand_name_1 (p, outputs, inputs, labels);
}
string = build_string (strlen (buffer), buffer);
@ -1329,53 +1356,49 @@ resolve_asm_operand_names (tree string, tree outputs, tree inputs)
balance of the string after substitution. */
static char *
resolve_operand_name_1 (char *p, tree outputs, tree inputs)
resolve_operand_name_1 (char *p, tree outputs, tree inputs, tree labels)
{
char *q;
int op;
tree t;
size_t len;
/* Collect the operand name. */
q = strchr (p, ']');
q = strchr (++p, ']');
if (!q)
{
error ("missing close brace for named operand");
return strchr (p, '\0');
}
len = q - p - 1;
*q = '\0';
/* Resolve the name to a number. */
for (op = 0, t = outputs; t ; t = TREE_CHAIN (t), op++)
{
tree name = TREE_PURPOSE (TREE_PURPOSE (t));
if (name)
{
const char *c = TREE_STRING_POINTER (name);
if (strncmp (c, p + 1, len) == 0 && c[len] == '\0')
goto found;
}
if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
goto found;
}
for (t = inputs; t ; t = TREE_CHAIN (t), op++)
{
tree name = TREE_PURPOSE (TREE_PURPOSE (t));
if (name)
{
const char *c = TREE_STRING_POINTER (name);
if (strncmp (c, p + 1, len) == 0 && c[len] == '\0')
goto found;
}
if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
goto found;
}
for (t = labels; t ; t = TREE_CHAIN (t), op++)
{
tree name = TREE_PURPOSE (t);
if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
goto found;
}
*q = '\0';
error ("undefined named operand %qs", identifier_to_locale (p + 1));
error ("undefined named operand %qs", identifier_to_locale (p));
op = 0;
found:
found:
/* Replace the name with the number. Unfortunately, not all libraries
get the return value of sprintf correct, so search for the end of the
generated string by hand. */
sprintf (p, "%d", op);
sprintf (--p, "%d", op);
p = strchr (p, '\0');
/* Verify the no extra buffer space assumption. */

View File

@ -1,3 +1,9 @@
2009-09-14 Richard Henderson <rth@redhat.com>
* c-c++-common/asmgoto-1.c, c-c++-common/asmgoto-2.c,
c-c++-common/asmgoto-3.c, gcc.c-torture/compile/asmgoto-1.c,
gcc.dg/tree-ssa/asmgoto-1.c: New files.
2009-09-14 Richard Henderson <rth@redhat.com>
* g++.dg/eh/builtin1.C: Update resx pattern match.

View File

@ -0,0 +1,15 @@
/* { dg-do compile } */
/* { dg-options "" } */
void
foo (void)
{
int i = 0;
asm ("" : : : "memory");
asm ("" : : : );
asm ("" : : "r" (i));
asm ("" : : );
asm ("" : "=r" (i));
asm ("" : );
asm ("");
}

View File

@ -0,0 +1,20 @@
/* { dg-do compile } */
/* { dg-options "" } */
void
foo (void)
{
__label__ lab;
int i = 0;
asm goto ("" : : : : lab);
asm goto ("" : "=r" (i) : : : lab); /* { dg-error "expected" } */
asm goto ("" : : : : ); /* { dg-error "expected" } */
asm goto ("" : : : "memory"); /* { dg-error "expected" } */
asm goto ("" : : : ); /* { dg-error "expected" } */
asm goto ("" : : "r" (i)); /* { dg-error "expected" } */
asm goto ("" : : ); /* { dg-error "expected" } */
asm goto ("" : "=r" (i)); /* { dg-error "expected" } */
asm goto ("" : ); /* { dg-error "expected" } */
asm goto (""); /* { dg-error "expected" } */
lab:;
}

View File

@ -0,0 +1,10 @@
/* { dg-do compile } */
/* { dg-options "-Wunused" } */
int foo ()
{
asm goto ("" : : : : label);
return 1;
label:
return 0;
}

View File

@ -0,0 +1,30 @@
void fn (void);
void
foo (void *x, unsigned long y)
{
asm goto ("": : : : lab);
lab:
fn ();
}
static void
bar (unsigned long x)
{
foo (0, x);
}
static void
baz (unsigned long x)
{
if (x > 8192)
bar (x);
else
({ __here: (unsigned long) &&__here; });
}
void
test (void)
{
baz (16384);
}

View File

@ -0,0 +1,95 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-optimized" } */
extern void XYZZY (void);
typedef unsigned long __kernel_size_t;
typedef __kernel_size_t size_t;
typedef unsigned gfp_t;
struct per_cpu_pageset { } __attribute__ ((__aligned__ ((1 << (6)))));
struct zone { struct per_cpu_pageset *pageset[64]; }
zone_flags_t; typedef struct pglist_data { struct zone node_zones[4]; } pg_data_t;
extern struct pglist_data *first_online_pgdat (void);
extern struct zone *next_zone (struct zone *zone);
extern volatile int per_cpu__x86_cpu_to_node_map[];
struct kmem_cache { int size; };
extern struct kmem_cache kmalloc_caches[(12 + 2)];
struct tracepoint { void **funcs; } __attribute__ ((aligned (32)));
extern struct tracepoint __tracepoint_kmalloc_node;
void *__kmalloc_node (size_t size, gfp_t flags, int node);
static inline int
cpu_to_node (int cpu)
{
return per_cpu__x86_cpu_to_node_map[cpu];
}
static inline void
trace_kmalloc_node (unsigned long call_site, const void *ptr,
size_t bytes_req, size_t bytes_alloc, gfp_t gfp_flags,
int node)
{
asm goto ("" : : : : trace_label);
if (0)
{
void **it_func;
trace_label:
asm ("" : "=r"(it_func) : "0"(&__tracepoint_kmalloc_node.funcs));
}
};
static inline __attribute__ ((always_inline)) int
kmalloc_index (size_t size)
{
if (size <= 64)
return 6;
return -1;
}
static inline __attribute__ ((always_inline)) struct kmem_cache *
kmalloc_slab (size_t size)
{
int index = kmalloc_index (size);
if (index == 0)
return ((void *) 0);
return &kmalloc_caches[index];
}
static inline __attribute__ ((always_inline)) void *
kmalloc_node (size_t size, gfp_t flags, int node)
{
void *ret;
if (__builtin_constant_p (size) && size <= (2 * ((1UL) << 12))
&& !(flags & ((gfp_t) 0x01u)))
{
struct kmem_cache *s = kmalloc_slab (size);
if (!s)
return ((void *) 16);
trace_kmalloc_node (({ __here:(unsigned long) &&__here;}),
ret, size, s->size, flags, node);
}
return __kmalloc_node (size, flags, node);
}
int
process_zones (int cpu)
{
struct zone *zone, *dzone;
int node = cpu_to_node (cpu);
for (zone = (first_online_pgdat ())->node_zones;
zone; zone = next_zone (zone))
{
((zone)->pageset[(cpu)]) =
kmalloc_node (sizeof (struct per_cpu_pageset),
(((gfp_t) 0x10u) | ((gfp_t) 0x40u) | ((gfp_t) 0x80u)),
node);
if (!((zone)->pageset[(cpu)]))
goto bad;
}
return 0;
bad:
XYZZY ();
return -12;
}
/* { dg-final { scan-tree-dump-times "XYZZY" 1 "optimized" } } */
/* { dg-final { cleanup-tree-dump "optimized" } } */

View File

@ -99,6 +99,7 @@ static void make_edges (void);
static void make_cond_expr_edges (basic_block);
static void make_gimple_switch_edges (basic_block);
static void make_goto_expr_edges (basic_block);
static void make_gimple_asm_edges (basic_block);
static unsigned int locus_map_hash (const void *);
static int locus_map_eq (const void *, const void *);
static void assign_discriminator (location_t, basic_block);
@ -572,6 +573,11 @@ make_edges (void)
fallthru = true;
break;
case GIMPLE_ASM:
make_gimple_asm_edges (bb);
fallthru = true;
break;
case GIMPLE_OMP_PARALLEL:
case GIMPLE_OMP_TASK:
case GIMPLE_OMP_FOR:
@ -593,13 +599,11 @@ make_edges (void)
fallthru = false;
break;
case GIMPLE_OMP_ATOMIC_LOAD:
case GIMPLE_OMP_ATOMIC_STORE:
fallthru = true;
break;
case GIMPLE_OMP_RETURN:
/* In the case of a GIMPLE_OMP_SECTION, the edge will go
somewhere other than the next block. This will be
@ -1011,6 +1015,23 @@ make_goto_expr_edges (basic_block bb)
make_abnormal_goto_edges (bb, false);
}
/* Create edges for an asm statement with labels at block BB. */
static void
make_gimple_asm_edges (basic_block bb)
{
gimple stmt = last_stmt (bb);
location_t stmt_loc = gimple_location (stmt);
int i, n = gimple_asm_nlabels (stmt);
for (i = 0; i < n; ++i)
{
tree label = TREE_VALUE (gimple_asm_label_op (stmt, i));
basic_block label_bb = label_to_block (label);
make_edge (bb, label_bb, 0);
assign_discriminator (stmt_loc, label_bb);
}
}
/*---------------------------------------------------------------------------
Flowgraph analysis
@ -1188,6 +1209,19 @@ cleanup_dead_labels (void)
break;
}
case GIMPLE_ASM:
{
int i, n = gimple_asm_nlabels (stmt);
for (i = 0; i < n; ++i)
{
tree cons = gimple_asm_label_op (stmt, i);
tree label = main_block_label (TREE_VALUE (cons));
TREE_VALUE (cons) = label;
}
break;
}
/* We have to handle gotos until they're removed, and we don't
remove them until after we've created the CFG edges. */
case GIMPLE_GOTO:
@ -1195,8 +1229,8 @@ cleanup_dead_labels (void)
{
tree new_dest = main_block_label (gimple_goto_dest (stmt));
gimple_goto_set_dest (stmt, new_dest);
break;
}
break;
default:
break;
@ -2821,6 +2855,11 @@ is_ctrl_altering_stmt (gimple t)
fallthru to the next statement as well. */
return true;
case GIMPLE_ASM:
if (gimple_asm_nlabels (t) > 0)
return true;
break;
CASE_GIMPLE_OMP:
/* OpenMP directives alter control flow. */
return true;
@ -5184,9 +5223,22 @@ gimple_redirect_edge_and_branch (edge e, basic_block dest)
CASE_LABEL (elt) = label;
}
}
break;
}
break;
case GIMPLE_ASM:
{
int i, n = gimple_asm_nlabels (stmt);
tree label = gimple_block_label (dest);
for (i = 0; i < n; ++i)
{
tree cons = gimple_asm_label_op (stmt, i);
if (label_to_block (TREE_VALUE (cons)) == e->dest)
TREE_VALUE (cons) = label;
}
}
break;
case GIMPLE_RETURN:
gsi_remove (&gsi, true);

View File

@ -875,8 +875,9 @@ DEFTREECODE (CASE_LABEL_EXPR, "case_label_expr", tcc_statement, 3)
/* Used to represent an inline assembly statement. ASM_STRING returns a
STRING_CST for the instruction (e.g., "mov x, y"). ASM_OUTPUTS,
ASM_INPUTS, and ASM_CLOBBERS represent the outputs, inputs, and clobbers
for the statement. */
DEFTREECODE (ASM_EXPR, "asm_expr", tcc_statement, 4)
for the statement. ASM_LABELS, if present, indicates various destinations
for the asm; labels cannot be combined with outputs. */
DEFTREECODE (ASM_EXPR, "asm_expr", tcc_statement, 5)
/* Variable references for SSA analysis. New SSA names are created every
time a variable is assigned a new value. The SSA builder uses SSA_NAME

View File

@ -1626,6 +1626,7 @@ extern void protected_set_expr_location (tree, location_t);
#define ASM_OUTPUTS(NODE) TREE_OPERAND (ASM_EXPR_CHECK (NODE), 1)
#define ASM_INPUTS(NODE) TREE_OPERAND (ASM_EXPR_CHECK (NODE), 2)
#define ASM_CLOBBERS(NODE) TREE_OPERAND (ASM_EXPR_CHECK (NODE), 3)
#define ASM_LABELS(NODE) TREE_OPERAND (ASM_EXPR_CHECK (NODE), 4)
/* Nonzero if we want to create an ASM_INPUT instead of an
ASM_OPERAND with no operands. */
#define ASM_INPUT_P(NODE) (ASM_EXPR_CHECK (NODE)->base.static_flag)
@ -5087,7 +5088,7 @@ extern bool parse_output_constraint (const char **, int, int, int,
extern bool parse_input_constraint (const char **, int, int, int, int,
const char * const *, bool *, bool *);
extern void expand_asm_stmt (gimple);
extern tree resolve_asm_operand_names (tree, tree, tree);
extern tree resolve_asm_operand_names (tree, tree, tree, tree);
extern void expand_case (gimple);
extern void expand_decl (tree);
#ifdef HARD_CONST