Message ID | 20240708170031.1621184-6-ak@linux.intel.com |
---|---|
State | New |
Headers | show |
Series | [v9,01/10] Improve must tail in RTL backend | expand |
On Mon, Jul 08, 2024 at 09:55:57AM -0700, Andi Kleen wrote: > Implement a C23 clang compatible musttail attribute similar to the earlier > C++ implementation in the C parser. Sorry that we haven't reviewed the C parts before. > > PR83324 I don't think this is going to pass the pre-commit check; better write it as PR c/83324. > gcc/c/ChangeLog: > > * c-parser.cc (struct attr_state): Define with musttail_p. > (c_parser_statement_after_labels): Handle [[musttail]] Missing a '.'. > (c_parser_std_attribute): Dito. > (c_parser_handle_musttail): Dito. > (c_parser_compound_statement_nostart): Dito. > (c_parser_all_labels): Dito. > (c_parser_statement): Dito. > * c-tree.h (c_finish_return): Add musttail_p flag. > * c-typeck.cc (c_finish_return): Handle musttail_p flag. > --- > gcc/c/c-parser.cc | 59 +++++++++++++++++++++++++++++++++++++---------- > gcc/c/c-tree.h | 2 +- > gcc/c/c-typeck.cc | 15 ++++++++++-- > 3 files changed, 61 insertions(+), 15 deletions(-) > > diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc > index 8c4e697a4e10..ce1c2c2be835 100644 > --- a/gcc/c/c-parser.cc > +++ b/gcc/c/c-parser.cc > @@ -1621,6 +1621,11 @@ struct omp_for_parse_data { > bool fail : 1; > }; > > +struct attr_state > +{ > + bool musttail_p; // parsed a musttail for return This should follow the usual comment conventions: /* True if we parsed a musttail attribute for return. */ > +}; > + > static bool c_parser_nth_token_starts_std_attributes (c_parser *, > unsigned int); > static tree c_parser_std_attribute_specifier_sequence (c_parser *); > @@ -1665,7 +1670,7 @@ static location_t c_parser_compound_statement_nostart (c_parser *); > static void c_parser_label (c_parser *, tree); > static void c_parser_statement (c_parser *, bool *, location_t * = NULL); > static void c_parser_statement_after_labels (c_parser *, bool *, > - vec<tree> * = NULL); > + vec<tree> * = NULL, attr_state = {}); > static tree c_parser_c99_block_statement (c_parser *, bool *, > location_t * = NULL); > static void c_parser_if_statement (c_parser *, bool *, vec<tree> *); > @@ -6982,6 +6987,28 @@ c_parser_handle_directive_omp_attributes (tree &attrs, > } > } > > +/* Check if STD_ATTR contains a musttail attribute and handle it Missing a '.'. Also, "handle" is pretty generic, maybe say "and remove it if it precedes a return"? > + PARSER is the parser and A is the output attr_state. */ > + > +static tree > +c_parser_handle_musttail (c_parser *parser, tree std_attrs, attr_state &a) > +{ > + if (c_parser_next_token_is_keyword (parser, RID_RETURN)) The point of this check is to keep a musttail attribute around to give a diagnostic for code like [[gnu::musttail]] 42;, correct? > + { > + if (lookup_attribute ("gnu", "musttail", std_attrs)) > + { > + std_attrs = remove_attribute ("gnu", "musttail", std_attrs); > + a.musttail_p = true; > + } > + if (lookup_attribute ("clang", "musttail", std_attrs)) > + { > + std_attrs = remove_attribute ("clang", "musttail", std_attrs); > + a.musttail_p = true; > + } > + } > + return std_attrs; > +} > + > /* Parse a compound statement except for the opening brace. This is > used for parsing both compound statements and statement expressions > (which follow different paths to handling the opening). */ > @@ -6998,6 +7025,7 @@ c_parser_compound_statement_nostart (c_parser *parser) > bool in_omp_loop_block > = omp_for_parse_state ? omp_for_parse_state->want_nested_loop : false; > tree sl = NULL_TREE; > + attr_state a = {}; > > if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) > { > @@ -7138,7 +7166,10 @@ c_parser_compound_statement_nostart (c_parser *parser) > = c_parser_nth_token_starts_std_attributes (parser, 1); > tree std_attrs = NULL_TREE; > if (have_std_attrs) > - std_attrs = c_parser_std_attribute_specifier_sequence (parser); > + { > + std_attrs = c_parser_std_attribute_specifier_sequence (parser); > + std_attrs = c_parser_handle_musttail (parser, std_attrs, a); > + } > if (c_parser_next_token_is_keyword (parser, RID_CASE) > || c_parser_next_token_is_keyword (parser, RID_DEFAULT) > || (c_parser_next_token_is (parser, CPP_NAME) > @@ -7286,7 +7317,7 @@ c_parser_compound_statement_nostart (c_parser *parser) > last_stmt = true; > mark_valid_location_for_stdc_pragma (false); > if (!omp_for_parse_state) > - c_parser_statement_after_labels (parser, NULL); > + c_parser_statement_after_labels (parser, NULL, NULL, a); > else > { > /* In canonical loop nest form, nested loops can only appear > @@ -7328,15 +7359,18 @@ c_parser_compound_statement_nostart (c_parser *parser) > /* Parse all consecutive labels, possibly preceded by standard > attributes. In this context, a statement is required, not a > declaration, so attributes must be followed by a statement that is > - not just a semicolon. */ > + not just a semicolon. Returns an attr_state. */ > > -static void > +static attr_state > c_parser_all_labels (c_parser *parser) > { > + attr_state a = {}; > bool have_std_attrs; > tree std_attrs = NULL; > if ((have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1))) > - std_attrs = c_parser_std_attribute_specifier_sequence (parser); > + std_attrs = c_parser_handle_musttail (parser, > + c_parser_std_attribute_specifier_sequence (parser), a); The formatting is a little off; let's do { std_attrs = c_parser_std_attribute_specifier_sequence (parser); std_attrs = c_parser_handle_musttail (parser, std_attrs, a); } > while (c_parser_next_token_is_keyword (parser, RID_CASE) > || c_parser_next_token_is_keyword (parser, RID_DEFAULT) > || (c_parser_next_token_is (parser, CPP_NAME) > @@ -7358,6 +7392,7 @@ c_parser_all_labels (c_parser *parser) > } > else if (have_std_attrs && c_parser_next_token_is (parser, CPP_SEMICOLON)) > c_parser_error (parser, "expected statement"); > + return a; > } I don't think this function will handle code like: struct str { int a, b; }; struct str cstruct (int x) { if (x < 10) L: // <==== [[gnu::musttail]] return cstruct (x + 1); return ((struct str){ x, 0 }); } which works in C++. I think you need another c_parser_handle_musttail after the second call to c_parser_std_attribute_specifier_sequence. Makes me wonder if c_parser_handle_musttail should be inlined into c_parser_std_attribute_specifier_sequence... > /* Parse a label (C90 6.6.1, C99 6.8.1, C11 6.8.1). > @@ -7601,11 +7636,11 @@ c_parser_label (c_parser *parser, tree std_attrs) > static void > c_parser_statement (c_parser *parser, bool *if_p, location_t *loc_after_labels) > { > - c_parser_all_labels (parser); > + attr_state a = c_parser_all_labels (parser); > if (loc_after_labels) > *loc_after_labels = c_parser_peek_token (parser)->location; > parser->omp_attrs_forbidden_p = false; > - c_parser_statement_after_labels (parser, if_p, NULL); > + c_parser_statement_after_labels (parser, if_p, NULL, a); > } > > /* Parse a statement, other than a labeled statement. CHAIN is a vector > @@ -7614,11 +7649,11 @@ c_parser_statement (c_parser *parser, bool *if_p, location_t *loc_after_labels) > > IF_P is used to track whether there's a (possibly labeled) if statement > which is not enclosed in braces and has an else clause. This is used to > - implement -Wparentheses. */ > + implement -Wparentheses. A has an earlier parsed attribute state. */ Two spaces after a period. And s/has/is/? > > static void > c_parser_statement_after_labels (c_parser *parser, bool *if_p, > - vec<tree> *chain) > + vec<tree> *chain, attr_state a) I don't love the 'a' name for a parameter. Can we rename it to 'astate'? > { > location_t loc = c_parser_peek_token (parser)->location; > tree stmt = NULL_TREE; > @@ -7686,7 +7721,7 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p, > c_parser_consume_token (parser); > if (c_parser_next_token_is (parser, CPP_SEMICOLON)) > { > - stmt = c_finish_return (loc, NULL_TREE, NULL_TREE); > + stmt = c_finish_return (loc, NULL_TREE, NULL_TREE, a.musttail_p); > c_parser_consume_token (parser); > } > else > @@ -7695,7 +7730,7 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p, > struct c_expr expr = c_parser_expression_conv (parser); > mark_exp_read (expr.value); > stmt = c_finish_return (EXPR_LOC_OR_LOC (expr.value, xloc), > - expr.value, expr.original_type); > + expr.value, expr.original_type, a.musttail_p); > goto expect_semicolon; > } > break; > diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h > index 15da875a0290..3dc6338bf061 100644 > --- a/gcc/c/c-tree.h > +++ b/gcc/c/c-tree.h > @@ -827,7 +827,7 @@ extern tree c_begin_stmt_expr (void); > extern tree c_finish_stmt_expr (location_t, tree); > extern tree c_process_expr_stmt (location_t, tree); > extern tree c_finish_expr_stmt (location_t, tree); > -extern tree c_finish_return (location_t, tree, tree); > +extern tree c_finish_return (location_t, tree, tree, bool = false); > extern tree c_finish_bc_stmt (location_t, tree, bool); > extern tree c_finish_goto_label (location_t, tree); > extern tree c_finish_goto_ptr (location_t, c_expr val); > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc > index ffcab7df4d3b..e47b10befd90 100644 > --- a/gcc/c/c-typeck.cc > +++ b/gcc/c/c-typeck.cc > @@ -11693,10 +11693,10 @@ c_finish_goto_ptr (location_t loc, c_expr val) > to return, or a null pointer for `return;' with no value. LOC is > the location of the return statement, or the location of the expression, > if the statement has any. If ORIGTYPE is not NULL_TREE, it > - is the original type of RETVAL. */ > + is the original type of RETVAL. MUSTTAIL_P indicates a musttail attribute. */ This line looks a bit too long. > > tree > -c_finish_return (location_t loc, tree retval, tree origtype) > +c_finish_return (location_t loc, tree retval, tree origtype, bool musttail_p) > { > tree valtype = TREE_TYPE (TREE_TYPE (current_function_decl)), ret_stmt; > bool no_warning = false; > @@ -11710,6 +11710,17 @@ c_finish_return (location_t loc, tree retval, tree origtype) > warning_at (xloc, 0, > "function declared %<noreturn%> has a %<return%> statement"); > > + if (retval && musttail_p) > + { > + tree t = retval; > + if (TREE_CODE (t) == TARGET_EXPR) > + t = TARGET_EXPR_INITIAL (t); > + if (TREE_CODE (t) != CALL_EXPR) > + error_at (xloc, "cannot tail-call: return value must be call"); In C++, the message is "return value must be a call", let's use that here too. Consider a c-family/ function taking a tree and a location_t that does: + if (t && TREE_CODE (t) == TARGET_EXPR) + t = TARGET_EXPR_INITIAL (t); + if (t && TREE_CODE (t) != CALL_EXPR) + error_at (token->location, "cannot tail-call: return value must be a call"); + else + CALL_EXPR_MUST_TAIL_CALL (t) = 1; and use it here and in the C++ FE as well. > + else > + CALL_EXPR_MUST_TAIL_CALL (t) = 1; > + } Why not include the above in this... > if (retval) > { ...block, so that you don't have to check retval twice? retval isn't set to null in the first block. Marek
Here's an updated patch with your feedback addressed. Is this version ok? The common code is in the C++ patch. --- Implement a C23 clang compatible musttail attribute similar to the earlier C++ implementation in the C parser. gcc/c/ChangeLog: PR c/83324 * c-parser.cc (struct attr_state): Define with musttail_p. (c_parser_statement_after_labels): Handle [[musttail]]. (c_parser_std_attribute): Dito. (c_parser_handle_musttail): Dito. (c_parser_compound_statement_nostart): Dito. (c_parser_all_labels): Dito. (c_parser_statement): Dito. * c-tree.h (c_finish_return): Add musttail_p flag. * c-typeck.cc (c_finish_return): Handle musttail_p flag. --- gcc/c/c-parser.cc | 70 ++++++++++++++++++++++++++++++++++++++--------- gcc/c/c-tree.h | 2 +- gcc/c/c-typeck.cc | 7 +++-- 3 files changed, 63 insertions(+), 16 deletions(-) diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 8c4e697a4e10..9cb4d5d932ad 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -1621,6 +1621,12 @@ struct omp_for_parse_data { bool fail : 1; }; +struct attr_state +{ + /* True if we parsed a musttail attribute for return. */ + bool musttail_p; +}; + static bool c_parser_nth_token_starts_std_attributes (c_parser *, unsigned int); static tree c_parser_std_attribute_specifier_sequence (c_parser *); @@ -1665,7 +1671,7 @@ static location_t c_parser_compound_statement_nostart (c_parser *); static void c_parser_label (c_parser *, tree); static void c_parser_statement (c_parser *, bool *, location_t * = NULL); static void c_parser_statement_after_labels (c_parser *, bool *, - vec<tree> * = NULL); + vec<tree> * = NULL, attr_state = {}); static tree c_parser_c99_block_statement (c_parser *, bool *, location_t * = NULL); static void c_parser_if_statement (c_parser *, bool *, vec<tree> *); @@ -6982,6 +6988,29 @@ c_parser_handle_directive_omp_attributes (tree &attrs, } } +/* Check if STD_ATTR contains a musttail attribute and remove if it + precedes a return. PARSER is the parser and ATTR is the output + attr_state. */ + +static tree +c_parser_handle_musttail (c_parser *parser, tree std_attrs, attr_state &attr) +{ + if (c_parser_next_token_is_keyword (parser, RID_RETURN)) + { + if (lookup_attribute ("gnu", "musttail", std_attrs)) + { + std_attrs = remove_attribute ("gnu", "musttail", std_attrs); + attr.musttail_p = true; + } + if (lookup_attribute ("clang", "musttail", std_attrs)) + { + std_attrs = remove_attribute ("clang", "musttail", std_attrs); + attr.musttail_p = true; + } + } + return std_attrs; +} + /* Parse a compound statement except for the opening brace. This is used for parsing both compound statements and statement expressions (which follow different paths to handling the opening). */ @@ -6998,6 +7027,7 @@ c_parser_compound_statement_nostart (c_parser *parser) bool in_omp_loop_block = omp_for_parse_state ? omp_for_parse_state->want_nested_loop : false; tree sl = NULL_TREE; + attr_state a = {}; if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) { @@ -7138,7 +7168,10 @@ c_parser_compound_statement_nostart (c_parser *parser) = c_parser_nth_token_starts_std_attributes (parser, 1); tree std_attrs = NULL_TREE; if (have_std_attrs) - std_attrs = c_parser_std_attribute_specifier_sequence (parser); + { + std_attrs = c_parser_std_attribute_specifier_sequence (parser); + std_attrs = c_parser_handle_musttail (parser, std_attrs, a); + } if (c_parser_next_token_is_keyword (parser, RID_CASE) || c_parser_next_token_is_keyword (parser, RID_DEFAULT) || (c_parser_next_token_is (parser, CPP_NAME) @@ -7286,7 +7319,7 @@ c_parser_compound_statement_nostart (c_parser *parser) last_stmt = true; mark_valid_location_for_stdc_pragma (false); if (!omp_for_parse_state) - c_parser_statement_after_labels (parser, NULL); + c_parser_statement_after_labels (parser, NULL, NULL, a); else { /* In canonical loop nest form, nested loops can only appear @@ -7328,15 +7361,20 @@ c_parser_compound_statement_nostart (c_parser *parser) /* Parse all consecutive labels, possibly preceded by standard attributes. In this context, a statement is required, not a declaration, so attributes must be followed by a statement that is - not just a semicolon. */ + not just a semicolon. Returns an attr_state. */ -static void +static attr_state c_parser_all_labels (c_parser *parser) { + attr_state attr = {}; bool have_std_attrs; tree std_attrs = NULL; if ((have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1))) - std_attrs = c_parser_std_attribute_specifier_sequence (parser); + { + std_attrs = c_parser_std_attribute_specifier_sequence (parser); + std_attrs = c_parser_handle_musttail (parser, std_attrs, attr); + } + while (c_parser_next_token_is_keyword (parser, RID_CASE) || c_parser_next_token_is_keyword (parser, RID_DEFAULT) || (c_parser_next_token_is (parser, CPP_NAME) @@ -7346,7 +7384,10 @@ c_parser_all_labels (c_parser *parser) std_attrs = NULL; if ((have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1))) - std_attrs = c_parser_std_attribute_specifier_sequence (parser); + { + std_attrs = c_parser_std_attribute_specifier_sequence (parser); + std_attrs = c_parser_handle_musttail (parser, std_attrs, attr); + } } if (std_attrs && (!c_parser_handle_statement_omp_attributes (parser, std_attrs, &have_std_attrs) @@ -7358,6 +7399,7 @@ c_parser_all_labels (c_parser *parser) } else if (have_std_attrs && c_parser_next_token_is (parser, CPP_SEMICOLON)) c_parser_error (parser, "expected statement"); + return attr; } /* Parse a label (C90 6.6.1, C99 6.8.1, C11 6.8.1). @@ -7601,11 +7643,11 @@ c_parser_label (c_parser *parser, tree std_attrs) static void c_parser_statement (c_parser *parser, bool *if_p, location_t *loc_after_labels) { - c_parser_all_labels (parser); + attr_state a = c_parser_all_labels (parser); if (loc_after_labels) *loc_after_labels = c_parser_peek_token (parser)->location; parser->omp_attrs_forbidden_p = false; - c_parser_statement_after_labels (parser, if_p, NULL); + c_parser_statement_after_labels (parser, if_p, NULL, a); } /* Parse a statement, other than a labeled statement. CHAIN is a vector @@ -7614,11 +7656,11 @@ c_parser_statement (c_parser *parser, bool *if_p, location_t *loc_after_labels) IF_P is used to track whether there's a (possibly labeled) if statement which is not enclosed in braces and has an else clause. This is used to - implement -Wparentheses. */ + implement -Wparentheses. ASTATE is an earlier parsed attribute state. */ static void c_parser_statement_after_labels (c_parser *parser, bool *if_p, - vec<tree> *chain) + vec<tree> *chain, attr_state astate) { location_t loc = c_parser_peek_token (parser)->location; tree stmt = NULL_TREE; @@ -7686,7 +7728,8 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p, c_parser_consume_token (parser); if (c_parser_next_token_is (parser, CPP_SEMICOLON)) { - stmt = c_finish_return (loc, NULL_TREE, NULL_TREE); + stmt = c_finish_return (loc, NULL_TREE, NULL_TREE, + astate.musttail_p); c_parser_consume_token (parser); } else @@ -7695,7 +7738,8 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p, struct c_expr expr = c_parser_expression_conv (parser); mark_exp_read (expr.value); stmt = c_finish_return (EXPR_LOC_OR_LOC (expr.value, xloc), - expr.value, expr.original_type); + expr.value, expr.original_type, + astate.musttail_p); goto expect_semicolon; } break; diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index 15da875a0290..3dc6338bf061 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -827,7 +827,7 @@ extern tree c_begin_stmt_expr (void); extern tree c_finish_stmt_expr (location_t, tree); extern tree c_process_expr_stmt (location_t, tree); extern tree c_finish_expr_stmt (location_t, tree); -extern tree c_finish_return (location_t, tree, tree); +extern tree c_finish_return (location_t, tree, tree, bool = false); extern tree c_finish_bc_stmt (location_t, tree, bool); extern tree c_finish_goto_label (location_t, tree); extern tree c_finish_goto_ptr (location_t, c_expr val); diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index ffcab7df4d3b..94b73e3cb0c2 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -11693,10 +11693,11 @@ c_finish_goto_ptr (location_t loc, c_expr val) to return, or a null pointer for `return;' with no value. LOC is the location of the return statement, or the location of the expression, if the statement has any. If ORIGTYPE is not NULL_TREE, it - is the original type of RETVAL. */ + is the original type of RETVAL. MUSTTAIL_P indicates a musttail + attribute. */ tree -c_finish_return (location_t loc, tree retval, tree origtype) +c_finish_return (location_t loc, tree retval, tree origtype, bool musttail_p) { tree valtype = TREE_TYPE (TREE_TYPE (current_function_decl)), ret_stmt; bool no_warning = false; @@ -11710,6 +11711,8 @@ c_finish_return (location_t loc, tree retval, tree origtype) warning_at (xloc, 0, "function declared %<noreturn%> has a %<return%> statement"); + set_musttail_on_return (retval, xloc, musttail_p); + if (retval) { tree semantic_type = NULL_TREE;
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 8c4e697a4e10..ce1c2c2be835 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -1621,6 +1621,11 @@ struct omp_for_parse_data { bool fail : 1; }; +struct attr_state +{ + bool musttail_p; // parsed a musttail for return +}; + static bool c_parser_nth_token_starts_std_attributes (c_parser *, unsigned int); static tree c_parser_std_attribute_specifier_sequence (c_parser *); @@ -1665,7 +1670,7 @@ static location_t c_parser_compound_statement_nostart (c_parser *); static void c_parser_label (c_parser *, tree); static void c_parser_statement (c_parser *, bool *, location_t * = NULL); static void c_parser_statement_after_labels (c_parser *, bool *, - vec<tree> * = NULL); + vec<tree> * = NULL, attr_state = {}); static tree c_parser_c99_block_statement (c_parser *, bool *, location_t * = NULL); static void c_parser_if_statement (c_parser *, bool *, vec<tree> *); @@ -6982,6 +6987,28 @@ c_parser_handle_directive_omp_attributes (tree &attrs, } } +/* Check if STD_ATTR contains a musttail attribute and handle it + PARSER is the parser and A is the output attr_state. */ + +static tree +c_parser_handle_musttail (c_parser *parser, tree std_attrs, attr_state &a) +{ + if (c_parser_next_token_is_keyword (parser, RID_RETURN)) + { + if (lookup_attribute ("gnu", "musttail", std_attrs)) + { + std_attrs = remove_attribute ("gnu", "musttail", std_attrs); + a.musttail_p = true; + } + if (lookup_attribute ("clang", "musttail", std_attrs)) + { + std_attrs = remove_attribute ("clang", "musttail", std_attrs); + a.musttail_p = true; + } + } + return std_attrs; +} + /* Parse a compound statement except for the opening brace. This is used for parsing both compound statements and statement expressions (which follow different paths to handling the opening). */ @@ -6998,6 +7025,7 @@ c_parser_compound_statement_nostart (c_parser *parser) bool in_omp_loop_block = omp_for_parse_state ? omp_for_parse_state->want_nested_loop : false; tree sl = NULL_TREE; + attr_state a = {}; if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) { @@ -7138,7 +7166,10 @@ c_parser_compound_statement_nostart (c_parser *parser) = c_parser_nth_token_starts_std_attributes (parser, 1); tree std_attrs = NULL_TREE; if (have_std_attrs) - std_attrs = c_parser_std_attribute_specifier_sequence (parser); + { + std_attrs = c_parser_std_attribute_specifier_sequence (parser); + std_attrs = c_parser_handle_musttail (parser, std_attrs, a); + } if (c_parser_next_token_is_keyword (parser, RID_CASE) || c_parser_next_token_is_keyword (parser, RID_DEFAULT) || (c_parser_next_token_is (parser, CPP_NAME) @@ -7286,7 +7317,7 @@ c_parser_compound_statement_nostart (c_parser *parser) last_stmt = true; mark_valid_location_for_stdc_pragma (false); if (!omp_for_parse_state) - c_parser_statement_after_labels (parser, NULL); + c_parser_statement_after_labels (parser, NULL, NULL, a); else { /* In canonical loop nest form, nested loops can only appear @@ -7328,15 +7359,18 @@ c_parser_compound_statement_nostart (c_parser *parser) /* Parse all consecutive labels, possibly preceded by standard attributes. In this context, a statement is required, not a declaration, so attributes must be followed by a statement that is - not just a semicolon. */ + not just a semicolon. Returns an attr_state. */ -static void +static attr_state c_parser_all_labels (c_parser *parser) { + attr_state a = {}; bool have_std_attrs; tree std_attrs = NULL; if ((have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1))) - std_attrs = c_parser_std_attribute_specifier_sequence (parser); + std_attrs = c_parser_handle_musttail (parser, + c_parser_std_attribute_specifier_sequence (parser), a); + while (c_parser_next_token_is_keyword (parser, RID_CASE) || c_parser_next_token_is_keyword (parser, RID_DEFAULT) || (c_parser_next_token_is (parser, CPP_NAME) @@ -7358,6 +7392,7 @@ c_parser_all_labels (c_parser *parser) } else if (have_std_attrs && c_parser_next_token_is (parser, CPP_SEMICOLON)) c_parser_error (parser, "expected statement"); + return a; } /* Parse a label (C90 6.6.1, C99 6.8.1, C11 6.8.1). @@ -7601,11 +7636,11 @@ c_parser_label (c_parser *parser, tree std_attrs) static void c_parser_statement (c_parser *parser, bool *if_p, location_t *loc_after_labels) { - c_parser_all_labels (parser); + attr_state a = c_parser_all_labels (parser); if (loc_after_labels) *loc_after_labels = c_parser_peek_token (parser)->location; parser->omp_attrs_forbidden_p = false; - c_parser_statement_after_labels (parser, if_p, NULL); + c_parser_statement_after_labels (parser, if_p, NULL, a); } /* Parse a statement, other than a labeled statement. CHAIN is a vector @@ -7614,11 +7649,11 @@ c_parser_statement (c_parser *parser, bool *if_p, location_t *loc_after_labels) IF_P is used to track whether there's a (possibly labeled) if statement which is not enclosed in braces and has an else clause. This is used to - implement -Wparentheses. */ + implement -Wparentheses. A has an earlier parsed attribute state. */ static void c_parser_statement_after_labels (c_parser *parser, bool *if_p, - vec<tree> *chain) + vec<tree> *chain, attr_state a) { location_t loc = c_parser_peek_token (parser)->location; tree stmt = NULL_TREE; @@ -7686,7 +7721,7 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p, c_parser_consume_token (parser); if (c_parser_next_token_is (parser, CPP_SEMICOLON)) { - stmt = c_finish_return (loc, NULL_TREE, NULL_TREE); + stmt = c_finish_return (loc, NULL_TREE, NULL_TREE, a.musttail_p); c_parser_consume_token (parser); } else @@ -7695,7 +7730,7 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p, struct c_expr expr = c_parser_expression_conv (parser); mark_exp_read (expr.value); stmt = c_finish_return (EXPR_LOC_OR_LOC (expr.value, xloc), - expr.value, expr.original_type); + expr.value, expr.original_type, a.musttail_p); goto expect_semicolon; } break; diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index 15da875a0290..3dc6338bf061 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -827,7 +827,7 @@ extern tree c_begin_stmt_expr (void); extern tree c_finish_stmt_expr (location_t, tree); extern tree c_process_expr_stmt (location_t, tree); extern tree c_finish_expr_stmt (location_t, tree); -extern tree c_finish_return (location_t, tree, tree); +extern tree c_finish_return (location_t, tree, tree, bool = false); extern tree c_finish_bc_stmt (location_t, tree, bool); extern tree c_finish_goto_label (location_t, tree); extern tree c_finish_goto_ptr (location_t, c_expr val); diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index ffcab7df4d3b..e47b10befd90 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -11693,10 +11693,10 @@ c_finish_goto_ptr (location_t loc, c_expr val) to return, or a null pointer for `return;' with no value. LOC is the location of the return statement, or the location of the expression, if the statement has any. If ORIGTYPE is not NULL_TREE, it - is the original type of RETVAL. */ + is the original type of RETVAL. MUSTTAIL_P indicates a musttail attribute. */ tree -c_finish_return (location_t loc, tree retval, tree origtype) +c_finish_return (location_t loc, tree retval, tree origtype, bool musttail_p) { tree valtype = TREE_TYPE (TREE_TYPE (current_function_decl)), ret_stmt; bool no_warning = false; @@ -11710,6 +11710,17 @@ c_finish_return (location_t loc, tree retval, tree origtype) warning_at (xloc, 0, "function declared %<noreturn%> has a %<return%> statement"); + if (retval && musttail_p) + { + tree t = retval; + if (TREE_CODE (t) == TARGET_EXPR) + t = TARGET_EXPR_INITIAL (t); + if (TREE_CODE (t) != CALL_EXPR) + error_at (xloc, "cannot tail-call: return value must be call"); + else + CALL_EXPR_MUST_TAIL_CALL (t) = 1; + } + if (retval) { tree semantic_type = NULL_TREE;