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

Reply via email to