summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog55
-rw-r--r--gcc/ada/ChangeLog4
-rw-r--r--gcc/ada/gcc-interface/trans.c8
-rw-r--r--gcc/builtins.c7
-rw-r--r--gcc/c-parser.c190
-rw-r--r--gcc/c-tree.h2
-rw-r--r--gcc/c-typeck.c10
-rw-r--r--gcc/cfgbuild.c9
-rw-r--r--gcc/cfglayout.c9
-rw-r--r--gcc/cfgrtl.c39
-rw-r--r--gcc/cp/ChangeLog10
-rw-r--r--gcc/cp/cp-tree.h3
-rw-r--r--gcc/cp/parser.c99
-rw-r--r--gcc/cp/pt.c5
-rw-r--r--gcc/cp/semantics.c11
-rw-r--r--gcc/doc/extend.texi90
-rw-r--r--gcc/gimple-pretty-print.c194
-rw-r--r--gcc/gimple.c83
-rw-r--r--gcc/gimple.def3
-rw-r--r--gcc/gimple.h36
-rw-r--r--gcc/gimplify.c10
-rw-r--r--gcc/jump.c40
-rw-r--r--gcc/recog.c252
-rw-r--r--gcc/reg-stack.c33
-rw-r--r--gcc/rtl.def5
-rw-r--r--gcc/rtl.h6
-rw-r--r--gcc/stmt.c129
-rw-r--r--gcc/testsuite/ChangeLog6
-rw-r--r--gcc/testsuite/c-c++-common/asmgoto-1.c15
-rw-r--r--gcc/testsuite/c-c++-common/asmgoto-2.c20
-rw-r--r--gcc/testsuite/c-c++-common/asmgoto-3.c10
-rw-r--r--gcc/testsuite/gcc.c-torture/compile/asmgoto-1.c30
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/asmgoto-1.c95
-rw-r--r--gcc/tree-cfg.c60
-rw-r--r--gcc/tree.def5
-rw-r--r--gcc/tree.h3
36 files changed, 1157 insertions, 429 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index cc048311c99..304f207a963 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,4 +1,59 @@
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.
(struct pointer_map_t): Forward declare.
diff --git a/gcc/ada/ChangeLog b/gcc/ada/ChangeLog
index 110d7357c22..cb38dab12f0 100644
--- a/gcc/ada/ChangeLog
+++ b/gcc/ada/ChangeLog
@@ -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
diff --git a/gcc/ada/gcc-interface/trans.c b/gcc/ada/gcc-interface/trans.c
index 61a3aea8c68..5bce21a7063 100644
--- a/gcc/ada/gcc-interface/trans.c
+++ b/gcc/ada/gcc-interface/trans.c
@@ -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
diff --git a/gcc/builtins.c b/gcc/builtins.c
index ee6417d61d6..8ef96070c3a 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -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);
}
diff --git a/gcc/c-parser.c b/gcc/c-parser.c
index feec8a4e624..d9ea159c4e5 100644
--- a/gcc/c-parser.c
+++ b/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)
- {
- parser->lex_untranslated_string = false;
- c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
- return NULL_TREE;
- }
- 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;
+ 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)
+ {
+ 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;
}
- /* 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
diff --git a/gcc/c-tree.h b/gcc/c-tree.h
index 5c1ccb537d5..c7490e461a3 100644
--- a/gcc/c-tree.h
+++ b/gcc/c-tree.h
@@ -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);
diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c
index abd78804aec..411b9ec2aa2 100644
--- a/gcc/c-typeck.c
+++ b/gcc/c-typeck.c
@@ -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. */
diff --git a/gcc/cfgbuild.c b/gcc/cfgbuild.c
index 7d87a7a184e..b5ddadd2ac9 100644
--- a/gcc/cfgbuild.c
+++ b/gcc/cfgbuild.c
@@ -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
{
diff --git a/gcc/cfglayout.c b/gcc/cfglayout.c
index ca400a8c503..bc8ed8b7f32 100644
--- a/gcc/cfglayout.c
+++ b/gcc/cfglayout.c
@@ -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
diff --git a/gcc/cfgrtl.c b/gcc/cfgrtl.c
index a7e93dd4c67..4146b146977 100644
--- a/gcc/cfgrtl.c
+++ b/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
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 4a17a77dbec..93b90b5c0ee 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,4 +1,14 @@
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
default_init_unwind_resume_libfunc.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index c720a565e7c..e8db635fda7 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 14733b835bd..55effed939e 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -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)
@@ -12618,6 +12637,8 @@ cp_parser_asm_definition (cp_parser* parser)
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:
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 5d48b1fb6d8..9f094a3959e 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -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)
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 477140c4472..6b741b3ebe5 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -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);
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 92f26e51970..22d9f6e3cc2 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -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}.
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 8903c661956..3d3134beaa6 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -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_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, " : ");
- }
+ 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 (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, ", ");
- }
+ for (f = 0; f < fields; ++f)
+ {
+ 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, " : ");
- }
+ 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;
- 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, " : ");
- }
+ 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;
- if (gimple_asm_nclobbers (gs))
- {
- if (flags & TDF_RAW)
- {
- newline_and_indent (buffer, spc + 2);
- pp_string (buffer, "CLOBBER: ");
- }
- else
- pp_string (buffer, " : ");
- }
+ 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;
- 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, '>');
+ 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, ");");
}
- else
- pp_string (buffer, ");");
}
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 33daafc6ad0..425463c31ca 100644
--- a/gcc/gimple.c
+++ b/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;
}
diff --git a/gcc/gimple.def b/gcc/gimple.def
index 603d97eeafc..d736dd719cb 100644
--- a/gcc/gimple.def
+++ b/gcc/gimple.def
@@ -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
diff --git a/gcc/gimple.h b/gcc/gimple.h
index b5396235f6b..e1e3b655b7d 100644
--- a/gcc/gimple.h
+++ b/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. */
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 381e611bc8f..c0cab205613 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -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));
diff --git a/gcc/jump.c b/gcc/jump.c
index 28a9b0f43ea..a12d0404500 100644
--- a/gcc/jump.c
+++ b/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);
diff --git a/gcc/recog.c b/gcc/recog.c
index bfca43b4552..6874d6c5c60 100644
--- a/gcc/recog.c
+++ b/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))
{
- asmop = SET_SRC (body);
- /* Single output operand: BODY is (set OUTPUT (asm_operands ....)). */
-
- noperands = ASM_OPERANDS_INPUT_LENGTH (asmop) + 1;
+ case ASM_OPERANDS:
+ /* Zero output asm: BODY is (asm_operands ...). */
+ asmop = body;
+ break;
- 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);
- }
+ case SET:
+ /* Single output asm: BODY is (set OUTPUT (asm_operands ...)). */
+ asmop = SET_SRC (body);
/* 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));
- }
- else if (GET_CODE (body) == ASM_OPERANDS)
- {
- 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);
- }
- }
- else if (GET_CODE (body) == PARALLEL
- && GET_CODE (XVECEXP (body, 0, 0)) == SET
- && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) == ASM_OPERANDS)
- {
- 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);
+ nbase = 1;
+ break;
- /* At least one output, plus some CLOBBERs. */
+ case PARALLEL:
+ {
+ int nparallel = XVECLEN (body, 0); /* Includes 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 */
+ asmop = XVECEXP (body, 0, 0);
+ if (GET_CODE (asmop) == SET)
+ {
+ asmop = SET_SRC (asmop);
- 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++;
- }
+ /* 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;
+ }
- 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);
- }
+ default:
+ gcc_unreachable ();
}
- 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);
+ noperands = (ASM_OPERANDS_INPUT_LENGTH (asmop)
+ + ASM_OPERANDS_LABEL_LENGTH (asmop) + nbase);
- 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);
- }
+ n = ASM_OPERANDS_INPUT_LENGTH (asmop);
+ for (i = 0; i < n; 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);
+ }
+ nbase += n;
+ n = ASM_OPERANDS_LABEL_LENGTH (asmop);
+ for (i = 0; i < n; 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;
diff --git a/gcc/reg-stack.c b/gcc/reg-stack.c
index ff09ad224d9..7e4ba6cad20 100644
--- a/gcc/reg-stack.c
+++ b/gcc/reg-stack.c
@@ -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);
diff --git a/gcc/rtl.def b/gcc/rtl.def
index d1c079353d3..2aa76b1f6c8 100644
--- a/gcc/rtl.def
+++ b/gcc/rtl.def
@@ -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
diff --git a/gcc/rtl.h b/gcc/rtl.h
index d3acebe5f57..925246f787b 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -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 *);
diff --git a/gcc/stmt.c b/gcc/stmt.c
index 23fdd08dd30..42f22b5868b 100644
--- a/gcc/stmt.c
+++ b/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. */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index db86cf68740..5649e200a85 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,11 @@
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.
* g++.dg/eh/builtin2.C, g++.dg/eh/builtin3.C: Likewise.
diff --git a/gcc/testsuite/c-c++-common/asmgoto-1.c b/gcc/testsuite/c-c++-common/asmgoto-1.c
new file mode 100644
index 00000000000..9c729fdd571
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/asmgoto-1.c
@@ -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 ("");
+}
diff --git a/gcc/testsuite/c-c++-common/asmgoto-2.c b/gcc/testsuite/c-c++-common/asmgoto-2.c
new file mode 100644
index 00000000000..5bf45725d80
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/asmgoto-2.c
@@ -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:;
+}
diff --git a/gcc/testsuite/c-c++-common/asmgoto-3.c b/gcc/testsuite/c-c++-common/asmgoto-3.c
new file mode 100644
index 00000000000..5224429a3ce
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/asmgoto-3.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-options "-Wunused" } */
+
+int foo ()
+{
+ asm goto ("" : : : : label);
+ return 1;
+ label:
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/asmgoto-1.c b/gcc/testsuite/gcc.c-torture/compile/asmgoto-1.c
new file mode 100644
index 00000000000..cc34610aba7
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/asmgoto-1.c
@@ -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);
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/asmgoto-1.c b/gcc/testsuite/gcc.dg/tree-ssa/asmgoto-1.c
new file mode 100644
index 00000000000..1d08067bba2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/asmgoto-1.c
@@ -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" } } */
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
index fb24cc48a4a..f596c75fe27 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -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);
diff --git a/gcc/tree.def b/gcc/tree.def
index 71987ef1d4d..c1ba96aa966 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -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
diff --git a/gcc/tree.h b/gcc/tree.h
index b9404c7da18..70650489dd9 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -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