diff mbox series

[v10,2/3] C: Implement musttail attribute for returns

Message ID 20240718043750.2480283-3-ak@linux.intel.com
State New
Headers show
Series [v10,1/3] C++: Support clang compatible [[musttail]] (PR83324) | expand

Commit Message

Andi Kleen July 18, 2024, 4:30 a.m. UTC
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(-)

Comments

Marek Polacek July 18, 2024, 6:19 p.m. UTC | #1
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
Andi Kleen July 18, 2024, 10:11 p.m. UTC | #2
On Thu, Jul 18, 2024 at 02:19:21PM -0400, Marek Polacek wrote:
> 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.

Ok.

> >  	 || 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.

It wasn't there. I will add 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.

Yes it can be removed.

Is the patchk ok with these changes?

-Andi
Marek Polacek July 18, 2024, 10:15 p.m. UTC | #3
On Thu, Jul 18, 2024 at 03:11:56PM -0700, Andi Kleen wrote:
> On Thu, Jul 18, 2024 at 02:19:21PM -0400, Marek Polacek wrote:
> > 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.
> 
> Ok.
> 
> > >  	 || 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.
> 
> It wasn't there. I will add 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.
> 
> Yes it can be removed.
> 
> Is the patchk ok with these changes?

Yes, thanks.

Marek
Andi Kleen July 18, 2024, 11:18 p.m. UTC | #4
> > > > +  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.
> > 
> > Yes it can be removed.

Actually I was wrong here, after double checking. The !retval case is
needed to diagnose a [[musttail]] set on a plain return (which is not
allowed following the clang spec)

So the call has to be outside the check.

The C frontend did it correctly, but the C++ part did not (fixed now)

-Andi
Marek Polacek July 19, 2024, 2:20 p.m. UTC | #5
On Thu, Jul 18, 2024 at 04:18:56PM -0700, Andi Kleen wrote:
> > > > > +  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.
> > > 
> > > Yes it can be removed.
> 
> Actually I was wrong here, after double checking. The !retval case is
> needed to diagnose a [[musttail]] set on a plain return (which is not
> allowed following the clang spec)
> 
> So the call has to be outside the check.
> 
> The C frontend did it correctly, but the C++ part did not (fixed now)

Ah, fair enough.  Just make sure we test it somewhere, thanks.

Marek
diff mbox series

Patch

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 = {});
 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 7e0f01ed22b9..094e41fa2021 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -11725,10 +11725,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;
@@ -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;