--- ada/gcc-interface/trans.c (revision 150074) +++ ada/gcc-interface/trans.c (local) @@ -1032,14 +1032,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); @@ -5081,9 +5081,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 --- c-parser.c (revision 150074) +++ c-parser.c (local) @@ -902,6 +902,7 @@ static void c_parser_do_statement (c_par 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 *, @@ -4225,6 +4226,7 @@ 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 @@ -4232,15 +4234,21 @@ c_parser_for_statement (c_parser *parser asm-string-literal : asm-operands[opt] : asm-operands[opt] asm-string-literal : asm-operands[opt] : asm-operands[opt] : asm-clobbers + asm-goto-argument: + asm-string-literal : : asm-operands[opt] : asm-clobbers \ + : asm-goto-operands + Qualifiers other than volatile are accepted in the syntax but warned for. */ 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)) @@ -4260,85 +4268,91 @@ 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); + 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)) + goto done_asm; + + /* Parse each colon-delimited section of operands. */ + nsections = 3 + is_goto; + for (section = 0; section < nsections; ++section) { - 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; + if (!c_parser_require (parser, CPP_COLON, "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)) + 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)) + 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 @@ -4440,6 +4454,44 @@ 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); + } + 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 --- c-tree.h (revision 150074) +++ c-tree.h (local) @@ -577,7 +577,7 @@ extern tree build_compound_literal (loca 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); --- c-typeck.c (revision 150074) +++ c-typeck.c (local) @@ -7910,7 +7910,7 @@ build_asm_stmt (tree cv_qualifier, tree 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; @@ -7924,7 +7924,7 @@ build_asm_expr (location_t loc, tree str 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)) @@ -7994,7 +7994,11 @@ build_asm_expr (location_t loc, tree str 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. */ --- cfgbuild.c (revision 150074) +++ cfgbuild.c (local) @@ -298,6 +298,15 @@ make_edges (basic_block min, basic_block 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 { --- cfgexpand.c (revision 150074) +++ cfgexpand.c (local) @@ -181,7 +181,7 @@ gimple_to_tree (gimple stmt) case GIMPLE_ASM: { size_t i, n; - tree out, in, cl; + tree out, in, cl, lab; const char *s; out = NULL_TREE; @@ -220,9 +220,21 @@ gimple_to_tree (gimple stmt) } } + lab = NULL_TREE; + n = gimple_asm_nlabels (stmt); + if (n > 0) + { + t = lab = gimple_asm_label_op (stmt, 0); + for (i = 1; i < n; i++) + { + TREE_CHAIN (t) = gimple_asm_label_op (stmt, i); + t = TREE_CHAIN (t); + } + } + s = gimple_asm_string (stmt); - t = build4 (ASM_EXPR, void_type_node, build_string (strlen (s), s), - out, in, cl); + t = build5 (ASM_EXPR, void_type_node, build_string (strlen (s), s), + out, in, cl, lab); ASM_VOLATILE_P (t) = gimple_asm_volatile_p (stmt); ASM_INPUT_P (t) = gimple_asm_input_p (stmt); } --- cfglayout.c (revision 150074) +++ cfglayout.c (local) @@ -836,6 +836,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 --- cfgrtl.c (revision 150074) +++ cfgrtl.c (local) @@ -921,6 +921,23 @@ patch_jump_insn (rtx insn, rtx old_label ++LABEL_NUSES (new_label); } } + else if ((tmp = extract_asm_operands (PATTERN (insn))) != NULL) + { + int i, n = ASM_OPERANDS_LABEL_LENGTH (tmp); + rtx new_label; + + if (new_bb == EXIT_BLOCK_PTR) + return false; + new_label = block_label (new_bb); + + for (i = 0; i < n; ++i) + if (ASM_OPERANDS_LABEL (tmp, i) == old_label) + { + ASM_OPERANDS_LABEL (tmp, i) = new_label; + --LABEL_NUSES (old_label); + ++LABEL_NUSES (new_label); + } + } else { /* ?? We may play the games with moving the named labels from --- cp/semantics.c (revision 150074) +++ cp/semantics.c (local) @@ -1223,7 +1223,7 @@ finish_asm_stmt (int volatile_p, tree st oconstraints = (const char **) alloca (noutputs * sizeof (char *)); string = resolve_asm_operand_names (string, output_operands, - input_operands); + input_operands, NULL_TREE); for (i = 0, t = output_operands; t; t = TREE_CHAIN (t), ++i) { @@ -1309,7 +1309,7 @@ finish_asm_stmt (int volatile_p, tree st r = build_stmt (input_location, ASM_EXPR, string, output_operands, input_operands, - clobbers); + clobbers, NULL_TREE); ASM_VOLATILE_P (r) = volatile_p || noutputs == 0; r = maybe_cleanup_point_expr_void (r); return add_stmt (r); --- doc/extend.texi (revision 150074) +++ doc/extend.texi (local) @@ -5196,7 +5196,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 @@ -5295,6 +5295,94 @@ For reasons similar to those described a 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}. --- gimple-pretty-print.c (revision 150074) +++ gimple-pretty-print.c (local) @@ -1056,14 +1056,17 @@ dump_gimple_asm (pretty_printer *buffer, 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)) + || gimple_asm_noutputs (gs) + || gimple_asm_nclobbers (gs) + || gimple_asm_nlabels (gs)) { if (gimple_asm_noutputs (gs)) { @@ -1084,6 +1087,25 @@ dump_gimple_asm (pretty_printer *buffer, pp_string (buffer, ", "); } + if (gimple_asm_nlabels (gs)) + { + if (flags & TDF_RAW) + { + newline_and_indent (buffer, spc + 2); + pp_string (buffer, "LABEL: "); + } + else + pp_string (buffer, " : "); + } + + for (i = 0; i < gimple_asm_nlabels (gs); i++) + { + dump_generic_node (buffer, gimple_asm_label_op (gs, i), spc, flags, + false); + if (i < gimple_asm_nlabels (gs) - 1) + pp_string (buffer, ", "); + } + if (gimple_asm_ninputs (gs)) { if (flags & TDF_RAW) --- gimple.c (revision 150074) +++ gimple.c (local) @@ -594,17 +594,22 @@ gimple_build_bind (tree vars, gimple_seq 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 @@ -622,11 +627,13 @@ gimple_build_asm_1 (const char *string, 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; @@ -634,7 +641,8 @@ gimple_build_asm_vec (const char *string 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)); @@ -645,39 +653,8 @@ gimple_build_asm_vec (const char *string 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; } --- gimple.def (revision 150074) +++ gimple.def (local) @@ -112,7 +112,8 @@ DEFGSCODE(GIMPLE_ASSIGN, "gimple_assign" 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", struct gimple_statement_asm) /* GIMPLE_CALL represents function --- gimple.h (revision 150074) +++ gimple.h (local) @@ -537,10 +537,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 @@ -763,9 +764,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_try (gimple_seq, gimple_seq, enum gimple_try_flags); @@ -2563,6 +2563,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. */ @@ -2652,6 +2660,26 @@ gimple_asm_set_clobber_op (gimple gs, un 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. */ --- gimplify.c (revision 150074) +++ gimplify.c (local) @@ -4733,13 +4733,14 @@ gimplify_asm_expr (tree *expr_p, gimple_ 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; @@ -4921,13 +4922,16 @@ gimplify_asm_expr (tree *expr_p, gimple_ } 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)); --- jump.c (revision 150074) +++ jump.c (local) @@ -1386,9 +1386,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 +1522,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); --- recog.c (revision 150074) +++ recog.c (local) @@ -1370,6 +1370,42 @@ comparison_operator (rtx op, enum machin && 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. */ @@ -1377,26 +1413,22 @@ comparison_operator (rtx op, enum machin 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--) { @@ -1422,30 +1454,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, @@ -1463,28 +1488,19 @@ decode_asm_operands (rtx body, rtx *oper 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. */ @@ -1496,93 +1512,70 @@ decode_asm_operands (rtx body, rtx *oper 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 ....). */ + nbase = 1; + break; - 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); - - /* 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 */ + case PARALLEL: + { + int nparallel = XVECLEN (body, 0); /* Includes CLOBBERs. */ - 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++; - } + 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; + } - 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) @@ -1602,6 +1595,11 @@ asm_operand_ok (rtx op, const char *cons /* 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; --- reg-stack.c (revision 150074) +++ reg-stack.c (local) @@ -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, @@ -2007,8 +1997,7 @@ subst_asm_stack_regs (rtx insn, stack re 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); --- rtl.def (revision 150074) +++ rtl.def (local) @@ -177,8 +177,9 @@ DEF_RTL_EXPR(ASM_INPUT, "asm_input", "si 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 --- rtl.h (revision 150074) +++ rtl.h (local) @@ -1137,7 +1137,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. */ @@ -1852,6 +1855,7 @@ extern void 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 *); --- stmt.c (revision 150074) +++ stmt.c (local) @@ -109,8 +109,8 @@ static int n_occurrences (int, const cha 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); @@ -632,12 +632,13 @@ tree_conflicts_with_clobbers_p (tree t, 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; @@ -660,7 +661,7 @@ expand_asm_operands (tree string, tree o 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; @@ -844,12 +845,13 @@ expand_asm_operands (tree string, tree o 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; @@ -958,6 +960,11 @@ expand_asm_operands (tree string, tree o = 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 @@ -965,18 +972,21 @@ expand_asm_operands (tree string, tree o 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; @@ -997,7 +1007,7 @@ expand_asm_operands (tree string, tree o (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; } @@ -1061,7 +1071,10 @@ expand_asm_operands (tree string, tree o = 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 @@ -1099,8 +1112,8 @@ expand_asm_expr (tree exp) /* Generate the ASM_OPERANDS insn; store into the TREE_VALUEs of OUTPUTS some trees for where the values were actually stored. */ expand_asm_operands (ASM_STRING (exp), outputs, ASM_INPUTS (exp), - ASM_CLOBBERS (exp), ASM_VOLATILE_P (exp), - input_location); + ASM_CLOBBERS (exp), ASM_LABELS (exp), + ASM_VOLATILE_P (exp), input_location); /* Copy all the intermediate outputs into the specified outputs. */ for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) @@ -1165,7 +1178,7 @@ check_operand_nalternatives (tree output 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; @@ -1194,6 +1207,20 @@ check_unique_operand_names (tree outputs 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: @@ -1207,14 +1234,14 @@ check_unique_operand_names (tree outputs 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 [] in input constraint strings. There should be no named operands in output constraints. */ @@ -1225,7 +1252,7 @@ resolve_asm_operand_names (tree string, { 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); @@ -1268,7 +1295,7 @@ resolve_asm_operand_names (tree string, continue; } - p = resolve_operand_name_1 (p, outputs, inputs); + p = resolve_operand_name_1 (p, outputs, inputs, labels); } string = build_string (strlen (buffer), buffer); @@ -1284,53 +1311,49 @@ resolve_asm_operand_names (tree string, 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. */ --- tree-cfg.c (revision 150074) +++ tree-cfg.c (local) @@ -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); @@ -571,6 +572,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: @@ -592,13 +598,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 @@ -1008,6 +1012,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 @@ -1156,6 +1177,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: @@ -1165,6 +1199,7 @@ cleanup_dead_labels (void) gimple_goto_set_dest (stmt, new_dest); break; } + break; default: break; @@ -2695,25 +2730,35 @@ is_ctrl_altering_stmt (gimple t) { gcc_assert (t); - if (is_gimple_call (t)) + switch (gimple_code (t)) { - int flags = gimple_call_flags (t); + case GIMPLE_CALL: + { + int flags = gimple_call_flags (t); + + /* A non-pure/const call alters flow control if the current + function has nonlocal labels. */ + if (!(flags & (ECF_CONST | ECF_PURE)) && cfun->has_nonlocal_label) + return true; + + /* A call also alters control flow if it does not return. */ + if (gimple_call_flags (t) & ECF_NORETURN) + return true; + } + break; - /* A non-pure/const call alters flow control if the current - function has nonlocal labels. */ - if (!(flags & (ECF_CONST | ECF_PURE)) - && cfun->has_nonlocal_label) + case GIMPLE_ASM: + if (gimple_asm_nlabels (t) > 0) return true; + break; - /* A call also alters control flow if it does not return. */ - if (gimple_call_flags (t) & ECF_NORETURN) + default: + /* OpenMP directives alter control flow. */ + if (is_gimple_omp (t)) return true; + break; } - /* OpenMP directives alter control flow. */ - if (is_gimple_omp (t)) - return true; - /* If a statement can throw, it alters control flow. */ return stmt_can_throw_internal (t); } @@ -5006,6 +5051,20 @@ gimple_redirect_edge_and_branch (edge e, 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); e->flags |= EDGE_FALLTHRU; --- tree.def (revision 150074) +++ tree.def (local) @@ -895,8 +895,9 @@ DEFTREECODE (RESX_EXPR, "resx_expr", tcc /* 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 --- tree.h (revision 150074) +++ tree.h (local) @@ -1604,6 +1604,7 @@ extern void protected_set_expr_location #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) @@ -5044,7 +5045,7 @@ extern bool parse_output_constraint (con extern bool parse_input_constraint (const char **, int, int, int, int, const char * const *, bool *, bool *); extern void expand_asm_expr (tree); -extern tree resolve_asm_operand_names (tree, tree, tree); +extern tree resolve_asm_operand_names (tree, tree, tree, tree); extern void expand_case (tree); extern void expand_decl (tree); #ifdef HARD_CONST