https://gcc.gnu.org/g:1d7f57da583782ae5d56655a3ac413bdf259838a

commit r15-8266-g1d7f57da583782ae5d56655a3ac413bdf259838a
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Tue Mar 18 18:48:14 2025 +0100

    c, c++: Support musttail attribute even using __attribute__ form [PR116545]
    
    Apparently some programs in the wild use
     #if __has_attribute(musttail)
       __attribute__((musttail)) return foo ();
     #else
       return foo ();
     #endif
    clang supports musttail both as a standard attribute ([[clang::musttail]]
    which we also support for compatibility) and the above worked just
    fine with GCC 14 which had __has_attribute(musttail) 0.  Now that it is
    0, this doesn't compile anymore.
    So, either we need to ensure that __has_attribute(musttail) is 0
    and just __has_c{,pp}_attribute({gnu,clang}::musttail) are non-zero,
    or IMHO better we just make it work in the attribute form, especially for
    C < C23 I can see why some projects would prefer that form.
    While [[gnu::musttail]] is rejected as an error in C11 etc. before GCC 15,
    rather than just handled as an unknown attribute.
    I view this as both a regression and compatibility issue.
    The patch handles it in similar spots to fallthrough/assume attributes
    inside of __attribute__ for C, and for C++ enables mixing of standard [[]]
    and GNU __attribute__(()) attributes at the start of statements in any 
order.
    
    While working on it, I've noticed we weren't diagnosing arguments to the
    clang::musttail attribute (fixed by the c-attribs.cc hunk) and newly
    on the __attribute__ form attribute (in that case the arguments aren't just
    skipped, they are always parsed and because we don't call decl_attributes
    etc., it wouldn't be diagnosed without a manual check).
    
    2025-03-18  Jakub Jelinek  <ja...@redhat.com>
    
            PR c/116545
    gcc/
            * doc/extend.texi (musttail statement attribute): Document
            that musttail GNU attribute can be used as well.
    gcc/c-family/
            * c-attribs.cc (c_common_clang_attributes): Add musttail.
    gcc/c/
            * c-parser.cc (c_parser_declaration_or_fndef): Parse
            __attribute__((musttail)) return.
            (c_parser_handle_musttail): Diagnose attribute arguments.
            (c_parser_statement_after_labels): Parse
            __attribute__((musttail)) return.
    gcc/cp/
            * parser.cc (cp_parser_statement): Call cp_parser_attributes_opt
            rather than cp_parser_std_attribute_spec_seq.
            (cp_parser_jump_statement): Diagnose gnu::musttail attributes
            with no arguments.
    gcc/testsuite/
            * c-c++-common/attr-fallthrough-2.c: Adjust expected diagnostics
            for C++.
            * c-c++-common/musttail15.c: New test.
            * c-c++-common/musttail16.c: New test.
            * c-c++-common/musttail17.c: New test.
            * c-c++-common/musttail18.c: New test.
            * c-c++-common/musttail19.c: New test.
            * c-c++-common/musttail20.c: New test.
            * c-c++-common/musttail21.c: New test.
            * c-c++-common/musttail22.c: New test.
            * c-c++-common/musttail23.c: New test.
            * c-c++-common/musttail24.c: New test.
            * g++.dg/musttail7.C: New test.
            * g++.dg/musttail8.C: New test.
            * g++.dg/musttail12.C: New test.
            * g++.dg/musttail13.C: New test.
            * g++.dg/musttail14.C: New test.
            * g++.dg/ext/pr116545.C: New test.

Diff:
---
 gcc/c-family/c-attribs.cc                       |  4 +-
 gcc/c/c-parser.cc                               | 50 +++++++++++++-
 gcc/cp/parser.cc                                |  9 ++-
 gcc/doc/extend.texi                             | 16 +++--
 gcc/testsuite/c-c++-common/attr-fallthrough-2.c |  4 +-
 gcc/testsuite/c-c++-common/musttail15.c         | 14 ++++
 gcc/testsuite/c-c++-common/musttail16.c         | 33 +++++++++
 gcc/testsuite/c-c++-common/musttail17.c         | 17 +++++
 gcc/testsuite/c-c++-common/musttail18.c         | 14 ++++
 gcc/testsuite/c-c++-common/musttail19.c         | 17 +++++
 gcc/testsuite/c-c++-common/musttail20.c         | 15 +++++
 gcc/testsuite/c-c++-common/musttail21.c         |  5 ++
 gcc/testsuite/c-c++-common/musttail22.c         | 90 +++++++++++++++++++++++++
 gcc/testsuite/c-c++-common/musttail23.c         | 45 +++++++++++++
 gcc/testsuite/c-c++-common/musttail24.c         | 21 ++++++
 gcc/testsuite/g++.dg/ext/pr116545.C             | 39 +++++++++++
 gcc/testsuite/g++.dg/musttail12.C               | 40 +++++++++++
 gcc/testsuite/g++.dg/musttail13.C               | 33 +++++++++
 gcc/testsuite/g++.dg/musttail14.C               | 65 ++++++++++++++++++
 gcc/testsuite/g++.dg/musttail7.C                | 60 +++++++++++++++++
 gcc/testsuite/g++.dg/musttail8.C                | 10 +++
 21 files changed, 588 insertions(+), 13 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 050eb6a70102..5a0e3d328ba7 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -651,7 +651,9 @@ const struct scoped_attribute_specs 
c_common_gnu_attribute_table =
 /* Attributes also recognized in the clang:: namespace.  */
 const struct attribute_spec c_common_clang_attributes[] = {
   { "flag_enum",             0, 0, false, true, false, false,
-                             handle_flag_enum_attribute, NULL }
+                             handle_flag_enum_attribute, NULL },
+  { "musttail",                      0, 0, false, false, false,
+                             false, handle_musttail_attribute, NULL }
 };
 
 const struct scoped_attribute_specs c_common_clang_attribute_table =
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 911e7edd4811..93233a87b0a4 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -1820,6 +1820,7 @@ static void c_parser_objc_at_dynamic_declaration 
(c_parser *);
 static bool c_parser_objc_diagnose_bad_element_prefix
   (c_parser *, struct c_declspecs *);
 static location_t c_parser_parse_rtl_body (c_parser *, char *);
+static tree c_parser_handle_musttail (c_parser *, tree, attr_state &);
 
 #if ENABLE_ANALYZER
 
@@ -2519,6 +2520,32 @@ c_parser_declaration_or_fndef (c_parser *parser, bool 
fndef_ok,
        c_finish_oacc_routine (oacc_routine_data, NULL_TREE, false);
       return result;
     }
+  else if (specs->typespec_kind == ctsk_none
+          && nested
+          /* Only parse __attribute__((musttail)) when called from
+             c_parser_compound_statement_nostart.  This certainly isn't
+             a declaration in that case, but we don't do tentative parsing
+             of GNU attributes right now.  */
+          && fallthru_attr_p
+          && c_parser_next_token_is_keyword (parser, RID_RETURN))
+    {
+      attr_state astate = {};
+      specs->attrs = c_parser_handle_musttail (parser, specs->attrs, astate);
+      if (astate.musttail_p)
+       {
+         if (specs->attrs)
+           {
+             auto_urlify_attributes sentinel;
+             warning_at (c_parser_peek_token (parser)->location,
+                         OPT_Wattributes,
+                         "attribute %<musttail%> mixed with other attributes "
+                         "on %<return%> statement");
+           }
+         c_parser_statement_after_labels (parser, NULL, NULL_TREE, NULL,
+                                          astate);
+         return result;
+       }
+    }
 
   /* Provide better error recovery.  Note that a type name here is usually
      better diagnosed as a redeclaration.  */
@@ -7373,8 +7400,12 @@ 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))
+      if (tree a = lookup_attribute ("gnu", "musttail", std_attrs))
        {
+         for (; a; a = lookup_attribute ("gnu", "musttail", TREE_CHAIN (a)))
+           if (TREE_VALUE (a))
+             error ("%qs attribute does not take any arguments",
+                    "musttail");
          std_attrs = remove_attribute ("gnu", "musttail", std_attrs);
          attr.musttail_p = true;
        }
@@ -8237,7 +8268,8 @@ c_parser_statement_after_labels (c_parser *parser, bool 
*if_p,
        case RID_ATTRIBUTE:
          {
            /* Allow '__attribute__((fallthrough));' or
-              '__attribute__((assume(cond)));'.  */
+              '__attribute__((assume(cond)));' or
+              '__attribute__((musttail))) return'.  */
            tree attrs = c_parser_gnu_attributes (parser);
            bool has_assume = lookup_attribute ("assume", attrs);
            if (has_assume)
@@ -8252,6 +8284,20 @@ c_parser_statement_after_labels (c_parser *parser, bool 
*if_p,
                    has_assume = false;
                  }
              }
+           gcc_assert (!astate.musttail_p);
+           attrs = c_parser_handle_musttail (parser, attrs, astate);
+           if (astate.musttail_p)
+             {
+               if (attrs)
+                 {
+                   auto_urlify_attributes sentinel;
+                   warning_at (c_parser_peek_token (parser)->location,
+                               OPT_Wattributes,
+                               "attribute %<musttail%> mixed with other "
+                               "attributes on %<return%> statement");
+                 }
+               goto restart;
+             }
            if (attribute_fallthrough_p (attrs))
              {
                if (c_parser_next_token_is (parser, CPP_SEMICOLON))
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 94e8e11e3989..2fb1dc5992d4 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -12992,7 +12992,7 @@ cp_parser_statement (cp_parser* parser, tree 
in_statement_expr,
        c++11 attributes, or a nested objc-message-expression.  So
        let's parse the c++11 attributes tentatively.  */
     cp_parser_parse_tentatively (parser);
-  std_attrs = cp_parser_std_attribute_spec_seq (parser);
+  std_attrs = cp_parser_attributes_opt (parser);
   if (std_attrs)
     attrs_loc = make_location (attrs_loc, attrs_loc, parser->lexer);
   if (c_dialect_objc ())
@@ -15332,8 +15332,13 @@ cp_parser_jump_statement (cp_parser* parser, tree 
&std_attrs)
        if (keyword == RID_RETURN)
          {
            bool musttail_p = false;
-           if (lookup_attribute ("gnu", "musttail", std_attrs))
+           if (tree a = lookup_attribute ("gnu", "musttail", std_attrs))
              {
+               for (; a; a = lookup_attribute ("gnu", "musttail",
+                                               TREE_CHAIN (a)))
+                 if (TREE_VALUE (a))
+                   error ("%qs attribute does not take any arguments",
+                          "musttail");
                musttail_p = true;
                std_attrs = remove_attribute ("gnu", "musttail", std_attrs);
              }
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 79cc7dfcff9c..b919df914648 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -10241,18 +10241,22 @@ have to optimize it to just @code{return 42 + 42;}.
 @cindex @code{musttail} statement attribute
 @item musttail
 
-The @code{gnu::musttail} or @code{clang::musttail} attribute
-can be applied to a @code{return} statement with a return-value expression
-that is a function call.  It asserts that the call must be a tail call that
-does not allocate extra stack space, so it is safe to use tail recursion
-to implement long running loops.
+The @code{gnu::musttail} or @code{clang::musttail} standard attribute
+or @code{musttail} GNU attribute can be applied to a @code{return} statement
+with a return-value expression that is a function call.  It asserts that the
+call must be a tail call that does not allocate extra stack space, so it is
+safe to use tail recursion to implement long running loops.
 
 @smallexample
 [[gnu::musttail]] return foo();
 @end smallexample
 
+@smallexample
+__attribute__((musttail)) return bar();
+@end smallexample
+
 If the compiler cannot generate a @code{musttail} tail call it will report
-an error. On some targets tail calls may never be supported.
+an error.  On some targets tail calls may never be supported.
 Tail calls cannot reference locals in memory, which may affect
 builds without optimization when passing small structures, or passing
 or returning large structures.  Enabling @option{-O1} or @option{-O2} can
diff --git a/gcc/testsuite/c-c++-common/attr-fallthrough-2.c 
b/gcc/testsuite/c-c++-common/attr-fallthrough-2.c
index 156b413db7ad..434eab7b4b57 100644
--- a/gcc/testsuite/c-c++-common/attr-fallthrough-2.c
+++ b/gcc/testsuite/c-c++-common/attr-fallthrough-2.c
@@ -21,8 +21,8 @@ fn (int i)
   case 3:
     bar (1);
     __attribute__((fallthrough)) /* { dg-warning "not followed" "" { target c 
} } */
-  case 4: /* { dg-error "expected" } */
-    bar (1);
+  case 4: /* { dg-error "expected" "" { target c } } */
+    bar (1); /* { dg-warning "'fallthrough' attribute ignored" "" { target c++ 
} .-1 } */
     __attribute__((fallthrough)) 1;
     /* { dg-error "expected" "" { target c } .-1 } */
     /* { dg-warning "not followed" "" { target *-*-* } .-2 } */
diff --git a/gcc/testsuite/c-c++-common/musttail15.c 
b/gcc/testsuite/c-c++-common/musttail15.c
new file mode 100644
index 000000000000..2addc971922c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/musttail15.c
@@ -0,0 +1,14 @@
+/* { dg-do compile { target { musttail && { c || c++11 } } } } */
+/* { dg-additional-options "-fdelayed-branch" { target sparc*-*-* } } */
+
+int __attribute__((noinline,noclone,noipa))
+callee (int i)
+{
+  return i * i;
+}
+
+int __attribute__((noinline,noclone,noipa))
+caller (int i)
+{
+  __attribute__((musttail)) return callee (i + 1);
+}
diff --git a/gcc/testsuite/c-c++-common/musttail16.c 
b/gcc/testsuite/c-c++-common/musttail16.c
new file mode 100644
index 000000000000..b1e2ff3e6dc8
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/musttail16.c
@@ -0,0 +1,33 @@
+/* { dg-do compile { target { musttail && { c || c++11 } } } } */
+
+struct box { char field[256]; int i; };
+
+int __attribute__((noinline,noclone,noipa))
+test_2_callee (int i, struct box b)
+{
+  if (b.field[0])
+    return 5;
+  return i * i;
+}
+
+int __attribute__((noinline,noclone,noipa))
+test_2_caller (int i)
+{
+  struct box b;
+  __attribute__((musttail)) return test_2_callee (i + 1, b); /* { dg-error 
"cannot tail-call: " } */
+}
+
+extern void setjmp (void);
+void
+test_3 (void)
+{
+  __attribute__((musttail)) return setjmp (); /* { dg-error "cannot tail-call: 
" } */
+}
+
+extern float f7(void);
+
+int
+test_6 (void)
+{
+  __attribute__((musttail)) return f7(); /* { dg-error "cannot tail-call: " } 
*/
+}
diff --git a/gcc/testsuite/c-c++-common/musttail17.c 
b/gcc/testsuite/c-c++-common/musttail17.c
new file mode 100644
index 000000000000..490f3c35ca23
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/musttail17.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target { musttail && { c || c++11 } } } } */
+
+struct box { char field[64]; int i; };
+
+struct box __attribute__((noinline,noclone,noipa))
+returns_struct (int i)
+{
+  struct box b;
+  b.i = i * i;
+  return b;
+}
+
+int __attribute__((noinline,noclone))
+test_1 (int i)
+{
+  __attribute__((musttail)) return returns_struct (i * 5).i; /* { dg-error 
"cannot tail-call: " } */
+}
diff --git a/gcc/testsuite/c-c++-common/musttail18.c 
b/gcc/testsuite/c-c++-common/musttail18.c
new file mode 100644
index 000000000000..4f34a8d27f36
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/musttail18.c
@@ -0,0 +1,14 @@
+/* { dg-do compile { target { musttail && { c || c++11 } } } } */
+/* { dg-additional-options "-fdelayed-branch" { target sparc*-*-* } } */
+
+void __attribute__((noipa)) f() {}
+
+void f2()
+{
+  __attribute__((__musttail__)) return f2();
+}
+
+void f3()
+{
+  __attribute__((__musttail__)) return f();
+}
diff --git a/gcc/testsuite/c-c++-common/musttail19.c 
b/gcc/testsuite/c-c++-common/musttail19.c
new file mode 100644
index 000000000000..70f9eaff139c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/musttail19.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target { musttail && { c || c++11 } } } } */
+
+float f1(void);
+
+int f2(void)
+{
+  __attribute__((musttail)) return f1 (); /* { dg-error "changed after call" } 
*/
+}
+
+
+int f3(int *);
+
+int f4(void)
+{
+  int x;
+  __attribute__((musttail)) return f3(&x); /* { dg-error "\(refers to 
locals|other reasons\)" } */
+}
diff --git a/gcc/testsuite/c-c++-common/musttail20.c 
b/gcc/testsuite/c-c++-common/musttail20.c
new file mode 100644
index 000000000000..70f14ff2f217
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/musttail20.c
@@ -0,0 +1,15 @@
+/* { dg-do compile { target { struct_musttail && { c || c++11 } } } } */
+/* { dg-additional-options "-fdelayed-branch" { target sparc*-*-* } } */
+
+struct str
+{
+  int a, b;
+};
+struct str
+cstruct (int x)
+{
+  if (x < 10)
+    L:
+    __attribute__((musttail)) return cstruct (x + 1);  /* { dg-warning 
"'musttail' attribute ignored" "" { target c } } */
+  return ((struct str){ x, 0 });
+}
diff --git a/gcc/testsuite/c-c++-common/musttail21.c 
b/gcc/testsuite/c-c++-common/musttail21.c
new file mode 100644
index 000000000000..954209ddcd51
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/musttail21.c
@@ -0,0 +1,5 @@
+/* { dg-do compile { target { c || c++11 } } } */
+void f(void)
+{
+  __attribute__((musttail)) return; /* { dg-error "cannot tail-call.*return 
value must be a call" } */
+}
diff --git a/gcc/testsuite/c-c++-common/musttail22.c 
b/gcc/testsuite/c-c++-common/musttail22.c
new file mode 100644
index 000000000000..eb812494f44d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/musttail22.c
@@ -0,0 +1,90 @@
+/* PR tree-optimization/118430 */
+/* { dg-do compile { target musttail } } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times "  \[^\n\r]* = bar \\\(\[^\n\r]*\\\); 
\\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "  \[^\n\r]* = freddy \\\(\[^\n\r]*\\\); 
\\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "  (?:bar|freddy) \\\(\[^\n\r]*\\\); 
\\\[tail call\\\]" "optimized" } } */
+
+__attribute__ ((noipa)) void
+foo (int x)
+{
+  (void) x;
+}
+
+__attribute__ ((noinline)) int
+bar (int x)
+{
+  foo (x);
+  return 1;
+}
+
+__attribute__ ((noinline)) int
+baz (int *x)
+{
+  foo (*x);
+  return 2;
+}
+
+__attribute__((noipa)) int
+qux (int x)
+{
+  {
+    int v;
+    foo (x);
+    baz (&v);
+  }
+  __attribute__((musttail))
+  return bar (x);
+}
+
+__attribute__((noipa)) int
+corge (int x)
+{
+  {
+    int v;
+    foo (x);
+    baz (&v);
+  }
+  return bar (x) + 1;
+}
+
+__attribute__ ((noinline)) float
+freddy (int x)
+{
+  foo (x);
+  return 1.75f;
+}
+
+__attribute__((noipa)) float
+garply (int x)
+{
+  {
+    int v;
+    foo (x);
+    baz (&v);
+  }
+  __attribute__((musttail))
+  return freddy (x);
+}
+
+__attribute__((noipa)) float
+quux (int x)
+{
+  {
+    int v;
+    foo (x);
+    baz (&v);
+  }
+  return freddy (x) + 0.25f;
+}
+
+int v;
+
+int
+main ()
+{
+  qux (v);
+  corge (v);
+  garply (v);
+  quux (v);
+}
diff --git a/gcc/testsuite/c-c++-common/musttail23.c 
b/gcc/testsuite/c-c++-common/musttail23.c
new file mode 100644
index 000000000000..d2ba70b03250
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/musttail23.c
@@ -0,0 +1,45 @@
+/* { dg-do compile } */
+/* { dg-options "-W -Wall" } */
+
+void bar (void);
+
+void
+foo (int x)
+{
+  __attribute__((musttail));                           /* { dg-warning "empty 
declaration" "" { target c } } */
+                                                       /* { dg-warning 
"attributes at the beginning of statement are ignored" "" { target c++ } .-1 } 
*/
+  if (x == 1)
+    __attribute__((musttail (1))) return bar ();       /* { dg-error 
"'musttail' attribute does not take any arguments" } */
+  if (x == 2)
+    __attribute__((musttail (1, "", 3))) return bar ();        /* { dg-error 
"'musttail' attribute does not take any arguments" } */
+  if (x == 3)
+    [[gnu::musttail (1)]] return bar ();               /* { dg-error 
"'musttail' attribute does not take any arguments" } */
+                                                       /* { dg-error 
"expected" "" { target c } .-1 } */
+  if (x == 4)
+    [[gnu::musttail (1, "", 3)]] return bar ();                /* { dg-error 
"'musttail' attribute does not take any arguments" } */
+                                                       /* { dg-error 
"expected" "" { target c } .-1 } */
+  if (x == 3)
+    [[clang::musttail (1)]] return bar ();             /* { dg-error 
"'musttail' attribute does not take any arguments" } */
+                                                       /* { dg-error 
"expected" "" { target c } .-1 } */
+  if (x == 4)
+    [[clang::musttail (1, "", 3)]] return bar ();      /* { dg-error 
"'musttail' attribute does not take any arguments" } */
+                                                       /* { dg-error 
"expected" "" { target c } .-1 } */
+  if (x == 5)
+    __attribute__((fallthrough, musttail)) return bar (); /* { dg-warning 
"attribute 'musttail' mixed with other attributes on 'return' statement" "" { 
target c } } */
+                                                       /* { dg-warning 
"attributes at the beginning of statement are ignored" "" { target c++ } .-1 } 
*/
+
+  if (x == 6)
+    [[fallthrough]] [[gnu::musttail]] return bar ();   /* { dg-warning 
"'fallthrough' attribute ignored" "" { target c } } */
+                                                       /* { dg-warning 
"attributes at the beginning of statement are ignored" "" { target c++ } .-1 } 
*/
+  if (x == 7)
+    [[clang::musttail, fallthrough]] return bar ();    /* { dg-warning 
"'fallthrough' attribute ignored" "" { target c } } */
+                                                       /* { dg-warning 
"attributes at the beginning of statement are ignored" "" { target c++ } .-1 } 
*/
+  if (x == 8)
+    __attribute__((musttail, musttail)) return bar ();
+  if (x == 9)
+    [[gnu::musttail, gnu::musttail]] return bar ();
+  if (x == 10)
+    [[clang::musttail]] [[clang::musttail]] return bar ();
+  if (x == 11)
+    [[clang::musttail]] [[gnu::musttail]] return bar ();
+}
diff --git a/gcc/testsuite/c-c++-common/musttail24.c 
b/gcc/testsuite/c-c++-common/musttail24.c
new file mode 100644
index 000000000000..10c2d3f188d8
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/musttail24.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+#if !__has_attribute (musttail)
+#error missing musttail attribute
+#endif
+#ifdef __cplusplus
+#if !__has_cpp_attribute (gnu::musttail)
+#error missing gnu::musttail attribute
+#endif
+#if !__has_cpp_attribute (clang::musttail)
+#error missing clang::musttail attribute
+#endif
+#else
+#if !__has_c_attribute (gnu::musttail)
+#error missing gnu::musttail attribute
+#endif
+#if !__has_c_attribute (clang::musttail)
+#error missing clang::musttail attribute
+#endif
+#endif
diff --git a/gcc/testsuite/g++.dg/ext/pr116545.C 
b/gcc/testsuite/g++.dg/ext/pr116545.C
new file mode 100644
index 000000000000..48f6d4ade2ab
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/pr116545.C
@@ -0,0 +1,39 @@
+// PR c/116545
+// { dg-do compile }
+// { dg-options "-Wall" }
+
+void
+foo (bool x)
+{
+  [[]] __attribute__(()) [[]] __attribute__((assume (true))) [[]];
+  [[]] __attribute__(()) [[assume (true)]] __attribute__(()) [[]];
+  __attribute__(()) [[]] __attribute__((assume (true))) [[]] __attribute__(());
+  __attribute__(()) [[assume (true)]] __attribute__(()) [[]] __attribute__(());
+  if (__attribute__((assume (true))); x)       // { dg-warning "init-statement 
in selection statements only available with" "" { target c++14_down } }
+    ;
+}
+
+void
+bar (int x)
+{
+  switch (x)
+    {
+    case 1:
+      ++x;
+      [[]] __attribute__(()) [[]] __attribute__((fallthrough)) [[]];
+    case 2:
+      ++x;
+      [[]] __attribute__(()) [[fallthrough]] __attribute__(()) [[]];
+    case 3:
+      ++x;
+      __attribute__(()) [[]] __attribute__((__fallthrough__)) [[]] 
__attribute__(());
+    case 4:
+      ++x;
+      __attribute__(()) [[fallthrough]] __attribute__(()) [[]] 
__attribute__(());
+    case 5:
+      ++x;
+      break;
+    default:
+      break;
+    }
+}
diff --git a/gcc/testsuite/g++.dg/musttail12.C 
b/gcc/testsuite/g++.dg/musttail12.C
new file mode 100644
index 000000000000..19e16e6df94f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/musttail12.C
@@ -0,0 +1,40 @@
+/* { dg-do compile { target { musttail } } } */
+/* { dg-options "-std=gnu++11" } */
+/* { dg-additional-options "-fdelayed-branch" { target sparc*-*-* } } */
+
+template <class T> T f();
+
+double g() { __attribute__((musttail)) return f<int>(); } /* { dg-error 
"cannot tail-call" } */
+
+template <class T>
+__attribute__((noinline, noclone, noipa))
+T g1() { __attribute__((musttail)) return f<T>(); } /* { dg-error "target is 
not able" "" { target { ! external_musttail } } } */
+
+template <class T>
+__attribute__((noinline, noclone, noipa))
+T g2() { __attribute__((musttail)) return f<T>(); } /* { dg-error "target is 
not able" "" { target { ! external_musttail } } } */
+
+template <class T>
+__attribute__((noinline, noclone, noipa))
+/* Would work with -O1.  */
+T g3() { __attribute__((musttail)) return f<T>(); } /* { dg-error "cannot 
tail-call" } */
+
+template <class T>
+__attribute__((noinline, noclone, noipa))
+T g4() { __attribute__((musttail)) return f<double>(); } /* { dg-error "cannot 
tail-call" } */
+
+class C
+{
+  double x;
+public:
+  C(double x) : x(x) {}
+  ~C() { asm("":::"memory"); }
+};
+
+int main()
+{
+  g1<int>();
+  g2<double>();
+  g3<C>();
+  g4<int>();
+}
diff --git a/gcc/testsuite/g++.dg/musttail13.C 
b/gcc/testsuite/g++.dg/musttail13.C
new file mode 100644
index 000000000000..35a86750597a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/musttail13.C
@@ -0,0 +1,33 @@
+/* { dg-do compile { target { musttail } } } */
+/* { dg-options "-std=gnu++11" } */
+/* { dg-additional-options "-fdelayed-branch" { target sparc*-*-* } } */
+
+template <class T> T f();
+
+class C
+{
+  double x;
+public:
+  C(double x) : x(x) {}
+  ~C() { asm("":::"memory"); }
+  operator int() { return x; }
+};
+
+template <class T>
+__attribute__((noinline, noclone, noipa))
+T g5() { __attribute__((musttail)) return f<C> (); } /* { dg-error "cannot 
tail-call" } */
+
+C h();
+
+__attribute__((noinline, noclone, noipa))
+int g6() { __attribute__((__musttail__)) return h (); }  /* { dg-error "cannot 
tail-call" } */
+
+__attribute__((noinline, noclone, noipa))
+C g7() { __attribute__((musttail)) return h (); } /* { dg-error "cannot 
tail-call" } */
+
+int main()
+{
+  g5<int> ();
+  g6 ();
+  g7 ();
+}
diff --git a/gcc/testsuite/g++.dg/musttail14.C 
b/gcc/testsuite/g++.dg/musttail14.C
new file mode 100644
index 000000000000..810b45546d61
--- /dev/null
+++ b/gcc/testsuite/g++.dg/musttail14.C
@@ -0,0 +1,65 @@
+// PR tree-optimization/118430
+// { dg-do compile { target musttail } }
+// { dg-options "-O2 -fdump-tree-optimized" }
+// { dg-final { scan-tree-dump-times "  \[^\n\r]* = foo \\\(\[^\n\r]*\\\); 
\\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */
+// { dg-final { scan-tree-dump-times "  \[^\n\r]* = bar \\\(\[^\n\r]*\\\); 
\\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */
+// { dg-final { scan-tree-dump-times "  \[^\n\r]* = baz \\\(\[^\n\r]*\\\); 
\\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */
+// { dg-final { scan-tree-dump-times "  \[^\n\r]* = qux \\\(\[^\n\r]*\\\); 
\\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */
+// { dg-final { scan-tree-dump-times "  \[^\n\r]* = corge \\\(\[^\n\r]*\\\); 
\\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */
+// { dg-final { scan-tree-dump-times "  \[^\n\r]* = freddy \\\(\[^\n\r]*\\\); 
\\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */
+
+__attribute__ ((noipa)) int
+foo (int x)
+{
+  return x;
+}
+
+__attribute__ ((noipa)) int
+bar (int x)
+{
+  return x;
+}
+
+__attribute__ ((noipa)) int
+baz (int x)
+{
+  return x;
+}
+
+__attribute__ ((noipa)) int
+qux (int x)
+{
+  return x;
+}
+
+__attribute__ ((noipa)) int
+corge (int x)
+{
+  return x;
+}
+
+__attribute__ ((noipa)) int
+freddy (int x)
+{
+  return x;
+}
+
+int
+garply (int x)
+{
+  switch (x)
+    {
+    case 0:
+      [[]] __attribute__(()) [[]] __attribute__((musttail)) [[]] return foo 
(42);
+    case 1:
+      [[]] __attribute__(()) [[gnu::musttail]] __attribute__(()) [[]] return 
bar (43);
+    case 2:
+      __attribute__(()) [[]] __attribute__((musttail)) [[]] __attribute__(()) 
return baz (44);
+    case 3:
+      __attribute__(()) [[gnu::musttail]] __attribute__(()) [[]] 
__attribute__(()) return qux (45);
+    case 4:
+      [[]] __attribute__(()) [[clang::musttail]] __attribute__(()) [[]] return 
corge (46);
+    default:
+      __attribute__(()) [[clang::musttail]] __attribute__(()) [[]] 
__attribute__(()) return freddy (47);
+    }
+}
diff --git a/gcc/testsuite/g++.dg/musttail7.C b/gcc/testsuite/g++.dg/musttail7.C
new file mode 100644
index 000000000000..ceda25bd8e02
--- /dev/null
+++ b/gcc/testsuite/g++.dg/musttail7.C
@@ -0,0 +1,60 @@
+/* { dg-do compile { target { struct_musttail } } } */
+/* { dg-require-effective-target external_musttail } */
+/* A lot of architectures will not build this due to PR115606 and PR115607 */
+/* { dg-options "-std=gnu++11" } */
+/* { dg-additional-options "-fdelayed-branch" { target sparc*-*-* } } */
+
+class Foo {
+public:
+  int a, b;
+  Foo(int a, int b) : a(a), b(b) {}
+};
+
+Foo __attribute__((noinline,noclone,noipa))
+callee (int i)
+{
+  return Foo(i, i+1);
+}
+
+Foo __attribute__((noinline,noclone,noipa))
+caller (int i)
+{
+  __attribute__((__musttail__)) return callee (i + 1);
+}
+
+template<typename T>
+T __attribute__((noinline,noclone,noipa)) foo (T i)
+{
+  return i + 1;
+}
+
+int
+caller2 (int k)
+{
+  __attribute__((__musttail__)) return foo<int>(1);
+}
+
+template<typename T>
+T caller3 (T v)
+{
+  __attribute__((__musttail__)) return foo<T>(v);
+}
+
+int call3(int i)
+{
+  __attribute__((__musttail__)) return caller3<int>(i + 1);
+}
+
+struct Bar {
+  int a;
+  Bar(int a) : a(a) {}
+  Bar operator+(Bar o) { return Bar(a + o.a); }
+};
+
+#if __OPTIMIZE__ >= 1
+Bar
+caller4 (Bar k)
+{
+  __attribute__((__musttail__)) return caller3<Bar>(Bar(99));
+}
+#endif
diff --git a/gcc/testsuite/g++.dg/musttail8.C b/gcc/testsuite/g++.dg/musttail8.C
new file mode 100644
index 000000000000..0f1b68bd2695
--- /dev/null
+++ b/gcc/testsuite/g++.dg/musttail8.C
@@ -0,0 +1,10 @@
+/* { dg-do compile { target { musttail } } } */
+/* { dg-options "-std=gnu++11" } */
+/* { dg-additional-options "-fdelayed-branch" { target sparc*-*-* } } */
+
+extern void foo();
+
+void f() noexcept
+{
+  __attribute__((musttail)) return foo(); /* { dg-error "call may throw 
exception that does not propagate" } */
+}

Reply via email to