diff options
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 |