On Wed, Jul 17, 2024 at 09:30:00PM -0700, Andi Kleen wrote:
> 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 12c5ed5d92c7..a8848d01f21a 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 =
> {});
Nit: the line seems to go over 80 columns.
> 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);
> + }
Thanks, I believe this addresses the testcase I mentioned earlier:
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 });
}
but I didn't see that being tested in your testsuite patch; apologies if
I missed it.
> 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;
> @@ -11742,6 +11743,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;
Is it deliberate that set_musttail_on_return is called outside the
if (retval) block? If it can be moved into it, set_musttail_on_return
can be simplified to assume that retval is always non-null.
Marek