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:
parent
40c88b9403
commit
1c384bf142
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
188
gcc/c-parser.c
188
gcc/c-parser.c
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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. */
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
39
gcc/cfgrtl.c
39
gcc/cfgrtl.c
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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}.
|
||||
|
@ -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, ");");
|
||||
}
|
||||
|
||||
|
||||
|
83
gcc/gimple.c
83
gcc/gimple.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
36
gcc/gimple.h
36
gcc/gimple.h
@ -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. */
|
||||
|
@ -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));
|
||||
|
40
gcc/jump.c
40
gcc/jump.c
@ -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);
|
||||
|
||||
|
260
gcc/recog.c
260
gcc/recog.c
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 *);
|
||||
|
129
gcc/stmt.c
129
gcc/stmt.c
@ -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. */
|
||||
|
@ -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.
|
||||
|
15
gcc/testsuite/c-c++-common/asmgoto-1.c
Normal file
15
gcc/testsuite/c-c++-common/asmgoto-1.c
Normal 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 ("");
|
||||
}
|
20
gcc/testsuite/c-c++-common/asmgoto-2.c
Normal file
20
gcc/testsuite/c-c++-common/asmgoto-2.c
Normal 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:;
|
||||
}
|
10
gcc/testsuite/c-c++-common/asmgoto-3.c
Normal file
10
gcc/testsuite/c-c++-common/asmgoto-3.c
Normal file
@ -0,0 +1,10 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-Wunused" } */
|
||||
|
||||
int foo ()
|
||||
{
|
||||
asm goto ("" : : : : label);
|
||||
return 1;
|
||||
label:
|
||||
return 0;
|
||||
}
|
30
gcc/testsuite/gcc.c-torture/compile/asmgoto-1.c
Normal file
30
gcc/testsuite/gcc.c-torture/compile/asmgoto-1.c
Normal 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);
|
||||
}
|
95
gcc/testsuite/gcc.dg/tree-ssa/asmgoto-1.c
Normal file
95
gcc/testsuite/gcc.dg/tree-ssa/asmgoto-1.c
Normal 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" } } */
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user