diff mbox series

c, v2: Add support for unsequenced and reproducible attributes

Message ID ZqvRzY2skChYEVk1@tucnak
State New
Headers show
Series c, v2: Add support for unsequenced and reproducible attributes | expand

Commit Message

Jakub Jelinek Aug. 1, 2024, 6:19 p.m. UTC
Hi!

On Tue, Jul 30, 2024 at 07:04:05PM +0200, Jakub Jelinek wrote:
> C23 added in N2956 ( https://open-std.org/JTC1/SC22/WG14/www/docs/n2956.htm )
> two new attributes

Here is an updated version of the patch.  Based on discussions with paper
co-author the patch no longer looks into structs/unions or assumes there
could be a pointer passed to ... through which the functions could modify
memory and only allows a single level of indirection to be inspected (for
[[unsequenced]]) or stored to (for both).
The documentation is also clarified.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2024-08-01  Jakub Jelinek  <jakub@redhat.com>

	PR c/116130
gcc/
	* doc/extend.texi (unsequenced, reproducible): Document new function
	type attributes.
	* calls.cc (flags_from_decl_or_type): Handle "unsequenced noptr" and
	"reproducible noptr" attributes.
gcc/c-family/
	* c-attribs.cc (c_common_gnu_attributes): Add entries for
	"unsequenced", "reproducible", "unsequenced noptr" and
	"reproducible noptr" attributes.
	(handle_unsequenced_attribute): New function.
	(handle_reproducible_attribute): Likewise.
	* c-common.h (handle_unsequenced_attribute): Declare.
	(handle_reproducible_attribute): Likewise.
	* c-lex.cc (c_common_has_attribute): Return 202311 for standard
	unsequenced and reproducible attributes.
gcc/c/
	* c-decl.cc (handle_std_unsequenced_attribute): New function.
	(handle_std_reproducible_attribute): Likewise.
	(std_attributes): Add entries for "unsequenced" and "reproducible"
	attributes.
	(c_warn_type_attributes): Add TYPE argument.  Allow unsequenced
	or reproducible attributes if it is FUNCTION_TYPE.
	(groktypename): Adjust c_warn_type_attributes caller.
	(grokdeclarator): Likewise.
	(finish_declspecs): Likewise.
	* c-parser.cc (c_parser_declaration_or_fndef): Likewise.
	* c-tree.h (c_warn_type_attributes): Add TYPE argument.
gcc/testsuite/
	* c-c++-common/attr-reproducible-1.c: New test.
	* c-c++-common/attr-reproducible-2.c: New test.
	* c-c++-common/attr-unsequenced-1.c: New test.
	* c-c++-common/attr-unsequenced-2.c: New test.
	* gcc.dg/c23-attr-reproducible-1.c: New test.
	* gcc.dg/c23-attr-reproducible-2.c: New test.
	* gcc.dg/c23-attr-reproducible-3.c: New test.
	* gcc.dg/c23-attr-reproducible-4.c: New test.
	* gcc.dg/c23-attr-reproducible-5.c: New test.
	* gcc.dg/c23-attr-reproducible-6.c: New test.
	* gcc.dg/c23-attr-unsequenced-1.c: New test.
	* gcc.dg/c23-attr-unsequenced-2.c: New test.
	* gcc.dg/c23-attr-unsequenced-3.c: New test.
	* gcc.dg/c23-attr-unsequenced-4.c: New test.
	* gcc.dg/c23-attr-unsequenced-5.c: New test.
	* gcc.dg/c23-attr-unsequenced-6.c: New test.
	* gcc.dg/c23-has-c-attribute-2.c: Add tests for unsequenced
	and reproducible attributes.



	Jakub

Comments

Joseph Myers Aug. 21, 2024, 4 p.m. UTC | #1
On Thu, 1 Aug 2024, Jakub Jelinek wrote:

> +Unsequenced functions without pointer or reference arguments are similar
> +to functions with the @code{const} attribute, except that @code{const}
> +attribute also requires finitness.  So, both functions with @code{const}

s/finitness/finiteness/ (in all places).

> --- gcc/testsuite/gcc.dg/c23-attr-reproducible-4.c.jj	2024-08-01 14:37:23.948824359 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-4.c	2024-08-01 14:37:23.948824359 +0200
> @@ -0,0 +1,12 @@
> +/* Test C23 reproducible attribute: duplicates (allowed after N2557).  */

The reference to N2557 seems anachronistic here, since the restrictions on 
duplicates were removed some time before the unsequenced and reproducible 
attributes were added to the working draft; there never was a time when 
C23 supported those attributes without allowing duplicates.  (The test 
itself is fine; testing duplicates is a good thing to do.)

> --- gcc/testsuite/gcc.dg/c23-attr-reproducible-6.c.jj	2024-08-01 14:37:23.948824359 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-6.c	2024-08-01 14:37:23.948824359 +0200
> @@ -0,0 +1,21 @@
> +/* Test C23 reproducible attribute: composite type on ?:.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c23 -pedantic-errors" } */
> +
> +int
> +f1 () [[reproducible]]
> +{
> +  return 42;
> +}
> +
> +int
> +f2 ()
> +{
> +  return 43;
> +}
> +
> +int
> +f3 ()
> +{
> +  return 44;
> +}

I don't see how this test relates to the "composite type on ?:" comment 
(or that it's doing anything useful).

> --- gcc/testsuite/gcc.dg/c23-attr-unsequenced-4.c.jj	2024-08-01 14:37:23.949824346 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-4.c	2024-08-01 14:37:23.949824346 +0200
> @@ -0,0 +1,12 @@
> +/* Test C23 unsequenced attribute: duplicates (allowed after N2557).  */

Likewise here.

> --- gcc/testsuite/gcc.dg/c23-attr-unsequenced-6.c.jj	2024-08-01 14:37:23.949824346 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-6.c	2024-08-01 14:37:23.949824346 +0200
> @@ -0,0 +1,21 @@
> +/* Test C23 unsequenced attribute: composite type on ?:.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c23 -pedantic-errors" } */
> +
> +int
> +f1 () [[unsequenced]]
> +{
> +  return 42;
> +}
> +
> +int
> +f2 ()
> +{
> +  return 43;
> +}
> +
> +int
> +f3 ()
> +{
> +  return 44;
> +}

And likewise here.

The patch is OK with the above fixed in the absence of any objections 
within a week.  (I'm supposing fixed here means removing 
c23-attr-reproducible-6.c and c23-attr-unsequenced-6.c; if there's some 
different test meant to be there, or some reason those tests are in fact 
useful, then the updated / new tests should be reviewed.)
Jakub Jelinek Aug. 30, 2024, 2:29 p.m. UTC | #2
On Wed, Aug 21, 2024 at 04:00:23PM +0000, Joseph Myers wrote:
> On Thu, 1 Aug 2024, Jakub Jelinek wrote:
> 
> > +Unsequenced functions without pointer or reference arguments are similar
> > +to functions with the @code{const} attribute, except that @code{const}
> > +attribute also requires finitness.  So, both functions with @code{const}
> 
> s/finitness/finiteness/ (in all places).

Changed.

> > --- gcc/testsuite/gcc.dg/c23-attr-reproducible-4.c.jj	2024-08-01 14:37:23.948824359 +0200
> > +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-4.c	2024-08-01 14:37:23.948824359 +0200
> > @@ -0,0 +1,12 @@
> > +/* Test C23 reproducible attribute: duplicates (allowed after N2557).  */
> 
> The reference to N2557 seems anachronistic here, since the restrictions on 
> duplicates were removed some time before the unsequenced and reproducible 
> attributes were added to the working draft; there never was a time when 
> C23 supported those attributes without allowing duplicates.  (The test 
> itself is fine; testing duplicates is a good thing to do.)

Removed the " (allowed after N2557)" parts.
> 
> > --- gcc/testsuite/gcc.dg/c23-attr-reproducible-6.c.jj	2024-08-01 14:37:23.948824359 +0200
> > +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-6.c	2024-08-01 14:37:23.948824359 +0200
> > @@ -0,0 +1,21 @@
> > +/* Test C23 reproducible attribute: composite type on ?:.  */
> > +/* { dg-do compile } */
> > +/* { dg-options "-std=c23 -pedantic-errors" } */
> > +
> > +int
> > +f1 () [[reproducible]]
> > +{
> > +  return 42;
> > +}
> > +
> > +int
> > +f2 ()
> > +{
> > +  return 43;
> > +}
> > +
> > +int
> > +f3 ()
> > +{
> > +  return 44;
> > +}
> 
> I don't see how this test relates to the "composite type on ?:" comment 
> (or that it's doing anything useful).

This is an auxiliary file for c23-attr-reproducible-5.c, which uses:
/* { dg-additional-sources "c23-attr-reproducible-6.c" } */

I've renamed c23-attr-*-6.c to c23-attr-*-5-aux.c and adjusted comment.

> The patch is OK with the above fixed in the absence of any objections 
> within a week.  (I'm supposing fixed here means removing 
> c23-attr-reproducible-6.c and c23-attr-unsequenced-6.c; if there's some 
> different test meant to be there, or some reason those tests are in fact 
> useful, then the updated / new tests should be reviewed.)

Here is the full updated patch.

2024-08-30  Jakub Jelinek  <jakub@redhat.com>

	PR c/116130
gcc/
	* doc/extend.texi (unsequenced, reproducible): Document new function
	type attributes.
	* calls.cc (flags_from_decl_or_type): Handle "unsequenced noptr" and
	"reproducible noptr" attributes.
gcc/c-family/
	* c-attribs.cc (c_common_gnu_attributes): Add entries for
	"unsequenced", "reproducible", "unsequenced noptr" and
	"reproducible noptr" attributes.
	(handle_unsequenced_attribute): New function.
	(handle_reproducible_attribute): Likewise.
	* c-common.h (handle_unsequenced_attribute): Declare.
	(handle_reproducible_attribute): Likewise.
	* c-lex.cc (c_common_has_attribute): Return 202311 for standard
	unsequenced and reproducible attributes.
gcc/c/
	* c-decl.cc (handle_std_unsequenced_attribute): New function.
	(handle_std_reproducible_attribute): Likewise.
	(std_attributes): Add entries for "unsequenced" and "reproducible"
	attributes.
	(c_warn_type_attributes): Add TYPE argument.  Allow unsequenced
	or reproducible attributes if it is FUNCTION_TYPE.
	(groktypename): Adjust c_warn_type_attributes caller.
	(grokdeclarator): Likewise.
	(finish_declspecs): Likewise.
	* c-parser.cc (c_parser_declaration_or_fndef): Likewise.
	* c-tree.h (c_warn_type_attributes): Add TYPE argument.
gcc/testsuite/
	* c-c++-common/attr-reproducible-1.c: New test.
	* c-c++-common/attr-reproducible-2.c: New test.
	* c-c++-common/attr-unsequenced-1.c: New test.
	* c-c++-common/attr-unsequenced-2.c: New test.
	* gcc.dg/c23-attr-reproducible-1.c: New test.
	* gcc.dg/c23-attr-reproducible-2.c: New test.
	* gcc.dg/c23-attr-reproducible-3.c: New test.
	* gcc.dg/c23-attr-reproducible-4.c: New test.
	* gcc.dg/c23-attr-reproducible-5.c: New test.
	* gcc.dg/c23-attr-reproducible-5-aux.c: New test.
	* gcc.dg/c23-attr-unsequenced-1.c: New test.
	* gcc.dg/c23-attr-unsequenced-2.c: New test.
	* gcc.dg/c23-attr-unsequenced-3.c: New test.
	* gcc.dg/c23-attr-unsequenced-4.c: New test.
	* gcc.dg/c23-attr-unsequenced-5.c: New test.
	* gcc.dg/c23-attr-unsequenced-5-aux.c: New test.
	* gcc.dg/c23-has-c-attribute-2.c: Add tests for unsequenced
	and reproducible attributes.

--- gcc/doc/extend.texi.jj	2024-08-01 10:30:35.437802437 +0200
+++ gcc/doc/extend.texi	2024-08-01 14:51:21.512248155 +0200
@@ -4024,6 +4024,69 @@ diagnosed.  Because a pure function cann
 effects it does not make sense for such a function to return @code{void}.
 Declaring such a function is diagnosed.
 
+@cindex @code{unsequenced} function type attribute
+@cindex functions that have no side effects
+@item unsequenced
+
+This attribute is a GNU counterpart of the C23 @code{[[unsequenced]]}
+attribute, used to specify function pointers to effectless, idempotent,
+stateless and independent functions according to the C23 definition.
+
+Unlike the standard C23 attribute it can be also specified in attributes
+which appertain to function declarations and applies to the their function
+type even in that case.
+
+Unsequenced functions without pointer or reference arguments are similar
+to functions with the @code{const} attribute, except that @code{const}
+attribute also requires finiteness.  So, both functions with @code{const}
+and with @code{unsequenced} attributes can be optimized by common
+subexpression elimination, but only functions with @code{const}
+attribute can be optimized by dead code elimination if their result is
+unused or is used only by dead code.  Unsequenced functions without pointer
+or reference arguments with @code{void} return type are diagnosed because
+they can't store any results and don't have other observable side-effects
+either.
+
+Unsequenced functions with pointer or reference arguments can inspect
+objects through the passed pointers or references or references to pointers
+or can store additional results through those pointers or references or
+references to pointers.
+
+The @code{unsequenced} attribute imposes greater restrictions than
+the similar @code{reproducible} attribute and fewer restrictions than
+the @code{const} attribute, so during optimization @code{const} has
+precedence over @code{unsequenced} which has precedence over
+@code{reproducible}.
+
+@cindex @code{reproducible} function type attribute
+@cindex functions that have no side effects
+@item reproducible
+
+This attribute is a GNU counterpart of the C23 @code{[[reproducible]]}
+attribute, used to specify function pointers to effectless and idempotent
+functions according to the C23 definition.
+
+Unlike the standard C23 attribute it can be also specified in attributes
+which appertain to function declarations and applies to the their function
+type even in that case.
+
+Reproducible functions without pointer or reference arguments or which do
+not modify objects referenced by those pointer/reference arguments are
+similar to functions with the @code{pure} attribute, except that
+@code{pure} attribute also requires finiteness.  So, both functions with
+@code{pure} and with @code{reproducible} attributes can be optimized by common
+subexpression elimination if the global state or anything reachable through
+the pointer/reference arguments isn't modified, but only functions with
+@code{pure} attribute can be optimized by dead code elimination if their result is
+unused or is used only by dead code.  Reproducible functions without pointer
+or reference arguments with @code{void} return type are diagnosed because
+they can't store any results and don't have other observable side-effects
+either.
+
+Reproducible functions with pointer or reference arguments can store
+additional results through those pointers or references or references to
+pointers.
+
 @cindex @code{retain} function attribute
 @item retain
 For ELF targets that support the GNU or FreeBSD OSABIs, this attribute
--- gcc/calls.cc.jj	2024-08-01 10:30:35.430802527 +0200
+++ gcc/calls.cc	2024-08-01 14:37:23.941824448 +0200
@@ -852,6 +852,23 @@ flags_from_decl_or_type (const_tree exp)
 	flags |= ECF_XTHROW;
 
       flags = special_function_p (exp, flags);
+
+      if ((flags & ECF_CONST) == 0
+	  && lookup_attribute ("unsequenced noptr",
+			       TYPE_ATTRIBUTES (TREE_TYPE (exp))))
+	{
+	  /* [[unsequenced]] with no pointers in arguments is like
+	     [[gnu::const]] without finite guarantee.  */
+	  flags |= ECF_CONST;
+	  if ((flags & ECF_PURE) == 0)
+	    flags |= ECF_LOOPING_CONST_OR_PURE;
+	}
+      if ((flags & (ECF_CONST | ECF_PURE)) == 0
+	  && lookup_attribute ("reproducible noptr",
+			       TYPE_ATTRIBUTES (TREE_TYPE (exp))))
+	/* [[reproducible]] with no pointers in arguments is like
+	   [[gnu::pure]] without finite guarantee.  */
+	flags |= ECF_PURE | ECF_LOOPING_CONST_OR_PURE;
     }
   else if (TYPE_P (exp))
     {
@@ -862,6 +879,17 @@ flags_from_decl_or_type (const_tree exp)
 	  && ((flags & ECF_CONST) != 0
 	      || lookup_attribute ("transaction_pure", TYPE_ATTRIBUTES (exp))))
 	flags |= ECF_TM_PURE;
+
+      if ((flags & ECF_CONST) == 0
+	  && lookup_attribute ("unsequenced noptr", TYPE_ATTRIBUTES (exp)))
+	/* [[unsequenced]] with no pointers in arguments is like
+	   [[gnu::const]] without finite guarantee.  */
+	flags |= ECF_CONST | ECF_LOOPING_CONST_OR_PURE;
+      if ((flags & ECF_CONST) == 0
+	  && lookup_attribute ("reproducible noptr", TYPE_ATTRIBUTES (exp)))
+	/* [[reproducible]] with no pointers in arguments is like
+	   [[gnu::pure]] without finite guarantee.  */
+	flags |= ECF_PURE | ECF_LOOPING_CONST_OR_PURE;
     }
   else
     gcc_unreachable ();
--- gcc/c-family/c-attribs.cc.jj	2024-08-01 10:30:35.420802655 +0200
+++ gcc/c-family/c-attribs.cc	2024-08-01 14:43:28.775217639 +0200
@@ -444,6 +444,14 @@ const struct attribute_spec c_common_gnu
   { "pure",                   0, 0, true,  false, false, false,
 			      handle_pure_attribute,
 	                      attr_const_pure_exclusions },
+  { "reproducible",           0, 0, false, true,  true,  false,
+			      handle_reproducible_attribute, NULL },
+  { "unsequenced",            0, 0, false, true,  true,  false,
+			      handle_unsequenced_attribute, NULL },
+  { "reproducible noptr",     0, 0, false, true,  true,  false,
+			      handle_reproducible_attribute, NULL },
+  { "unsequenced noptr",      0, 0, false, true,  true,  false,
+			      handle_unsequenced_attribute, NULL },
   { "transaction_callable",   0, 0, false, true,  false, false,
 			      handle_tm_attribute, NULL },
   { "transaction_unsafe",     0, 0, false, true,  false, true,
@@ -4280,6 +4288,53 @@ handle_pure_attribute (tree *node, tree
   return NULL_TREE;
 }
 
+/* Handle an "unsequenced" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+tree
+handle_unsequenced_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+			      int flags, bool *no_add_attrs)
+{
+  tree fntype = *node;
+  for (tree argtype = TYPE_ARG_TYPES (fntype); argtype;
+       argtype = TREE_CHAIN (argtype))
+    /* If any of the arguments have pointer or reference type, just
+       add the attribute alone.  */
+    if (POINTER_TYPE_P (TREE_VALUE (argtype)))
+      return NULL_TREE;
+
+  if (VOID_TYPE_P (TREE_TYPE (fntype)))
+    warning (OPT_Wattributes, "%qE attribute on function type "
+	     "without pointer arguments returning %<void%>", name);
+  const char *name2;
+  if (IDENTIFIER_LENGTH (name) == sizeof ("unsequenced") - 1)
+    name2 = "unsequenced noptr";
+  else
+    name2 = "reproducible noptr";
+  if (!lookup_attribute (name2, TYPE_ATTRIBUTES (fntype)))
+    {
+      *no_add_attrs = true;
+      gcc_assert ((flags & (int) ATTR_FLAG_TYPE_IN_PLACE) == 0);
+      tree attr = tree_cons (get_identifier (name2), NULL_TREE,
+			     TYPE_ATTRIBUTES (fntype));
+      if (!lookup_attribute (IDENTIFIER_POINTER (name),
+			     TYPE_ATTRIBUTES (fntype)))
+	attr = tree_cons (name, NULL_TREE, attr);
+      *node = build_type_attribute_variant (*node, attr);
+    }
+  return NULL_TREE;
+}
+
+/* Handle a "reproducible" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+tree
+handle_reproducible_attribute (tree *node, tree name, tree args, int flags,
+			       bool *no_add_attrs)
+{
+  return handle_unsequenced_attribute (node, name, args, flags, no_add_attrs);
+}
+
 /* Digest an attribute list destined for a transactional memory statement.
    ALLOWED is the set of attributes that are allowed for this statement;
    return the attribute we parsed.  Multiple attributes are never allowed.  */
--- gcc/c-family/c-common.h.jj	2024-08-01 10:30:35.421802642 +0200
+++ gcc/c-family/c-common.h	2024-08-01 14:37:23.942824435 +0200
@@ -864,6 +864,8 @@ extern void check_function_format (const
 extern bool attribute_fallthrough_p (tree);
 extern tree handle_format_attribute (tree *, tree, tree, int, bool *);
 extern tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
+extern tree handle_unsequenced_attribute (tree *, tree, tree, int, bool *);
+extern tree handle_reproducible_attribute (tree *, tree, tree, int, bool *);
 extern bool c_common_handle_option (size_t, const char *, HOST_WIDE_INT, int,
 				    location_t,
 				    const struct cl_option_handlers *);
--- gcc/c-family/c-lex.cc.jj	2024-08-01 10:30:35.421802642 +0200
+++ gcc/c-family/c-lex.cc	2024-08-01 14:37:23.942824435 +0200
@@ -445,7 +445,9 @@ c_common_has_attribute (cpp_reader *pfil
 		  || is_attribute_p ("maybe_unused", attr_name)
 		  || is_attribute_p ("nodiscard", attr_name)
 		  || is_attribute_p ("noreturn", attr_name)
-		  || is_attribute_p ("_Noreturn", attr_name))
+		  || is_attribute_p ("_Noreturn", attr_name)
+		  || is_attribute_p ("reproducible", attr_name)
+		  || is_attribute_p ("unsequenced", attr_name))
 		result = 202311;
 	    }
 	  if (result)
--- gcc/c/c-decl.cc.jj	2024-08-01 10:30:35.424802604 +0200
+++ gcc/c/c-decl.cc	2024-08-01 14:37:23.944824410 +0200
@@ -4702,6 +4702,39 @@ handle_std_noreturn_attribute (tree *nod
     }
 }
 
+/* Handle the standard [[unsequenced]] attribute.  */
+
+static tree
+handle_std_unsequenced_attribute (tree *node, tree name, tree args,
+				  int flags, bool *no_add_attrs)
+{
+  /* Unlike GNU __attribute__ ((unsequenced)), the standard [[unsequenced]]
+     should be only applied to function declarators or type specifiers which
+     have function type.  */
+  if (node[2])
+    {
+      auto_diagnostic_group d;
+      if (pedwarn (input_location, OPT_Wattributes,
+		   "standard %qE attribute can only be applied to function "
+		   "declarators or type specifiers with function type", name))
+	inform (input_location, "did you mean to specify it after %<)%> "
+				"following function parameters?");
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+  return handle_unsequenced_attribute (node, name, args, flags, no_add_attrs);
+}
+
+/* Handle the standard [[reproducible]] attribute.  */
+
+static tree
+handle_std_reproducible_attribute (tree *node, tree name, tree args,
+				   int flags, bool *no_add_attrs)
+{
+  return handle_std_unsequenced_attribute (node, name, args, flags,
+					   no_add_attrs);
+}
+
 /* Table of supported standard (C23) attributes.  */
 static const attribute_spec std_attributes[] =
 {
@@ -4718,7 +4751,11 @@ static const attribute_spec std_attribut
   { "nodiscard", 0, 1, false, false, false, false,
     handle_nodiscard_attribute, NULL },
   { "noreturn", 0, 0, false, false, false, false,
-    handle_std_noreturn_attribute, NULL }
+    handle_std_noreturn_attribute, NULL },
+  { "reproducible", 0, 0, false, true, true, false,
+    handle_std_reproducible_attribute, NULL },
+  { "unsequenced", 0, 0, false, true, true, false,
+    handle_std_unsequenced_attribute, NULL }
 };
 
 const scoped_attribute_specs std_attribute_table =
@@ -4911,12 +4948,24 @@ c_warn_unused_attributes (tree attrs)
    list of attributes with them removed.  */
 
 tree
-c_warn_type_attributes (tree attrs)
+c_warn_type_attributes (tree type, tree attrs)
 {
   tree *attr_ptr = &attrs;
   while (*attr_ptr)
     if (get_attribute_namespace (*attr_ptr) == NULL_TREE)
       {
+	if (TREE_CODE (type) == FUNCTION_TYPE)
+	  {
+	    tree name = get_attribute_name (*attr_ptr);
+	    /* [[unsequenced]] and [[reproducible]] is fine on function
+	       types that aren't being defined.  */
+	    if (is_attribute_p ("unsequenced", name)
+		|| is_attribute_p ("reproducible", name))
+	      {
+		attr_ptr = &TREE_CHAIN (*attr_ptr);
+		continue;
+	      }
+	  }
 	pedwarn (input_location, OPT_Wattributes, "%qE attribute ignored",
 		 get_attribute_name (*attr_ptr));
 	*attr_ptr = TREE_CHAIN (*attr_ptr);
@@ -5386,7 +5435,7 @@ groktypename (struct c_type_name *type_n
 			 DEPRECATED_NORMAL);
 
   /* Apply attributes.  */
-  attrs = c_warn_type_attributes (attrs);
+  attrs = c_warn_type_attributes (type, attrs);
   decl_attributes (&type, attrs, 0);
 
   return type;
@@ -7196,7 +7245,7 @@ grokdeclarator (const struct c_declarato
 		else if (inner_decl->kind == cdk_array)
 		  attr_flags |= (int) ATTR_FLAG_ARRAY_NEXT;
 	      }
-	    attrs = c_warn_type_attributes (attrs);
+	    attrs = c_warn_type_attributes (type, attrs);
 	    returned_attrs = decl_attributes (&type,
 					      chainon (returned_attrs, attrs),
 					      attr_flags);
@@ -13433,7 +13482,8 @@ finish_declspecs (struct c_declspecs *sp
  handle_postfix_attrs:
   if (specs->type != NULL)
     {
-      specs->postfix_attrs = c_warn_type_attributes (specs->postfix_attrs);
+      specs->postfix_attrs
+	= c_warn_type_attributes (specs->type, specs->postfix_attrs);
       decl_attributes (&specs->type, specs->postfix_attrs, 0);
       specs->postfix_attrs = NULL_TREE;
     }
--- gcc/c/c-parser.cc.jj	2024-08-01 10:30:35.429802539 +0200
+++ gcc/c/c-parser.cc	2024-08-01 14:37:23.947824372 +0200
@@ -2677,8 +2677,9 @@ c_parser_declaration_or_fndef (c_parser
 		      /* Postfix [[]] attributes are valid with C23
 			 auto, although not with __auto_type, and
 			 modify the type given by the initializer.  */
-		      specs->postfix_attrs =
-			c_warn_type_attributes (specs->postfix_attrs);
+		      specs->postfix_attrs
+			= c_warn_type_attributes (specs->type,
+						  specs->postfix_attrs);
 		      decl_attributes (&specs->type, specs->postfix_attrs, 0);
 		      specs->postfix_attrs = NULL_TREE;
 		    }
--- gcc/c/c-tree.h.jj	2024-08-01 10:30:35.429802539 +0200
+++ gcc/c/c-tree.h	2024-08-01 14:37:23.947824372 +0200
@@ -680,7 +680,7 @@ extern tree c_builtin_function (tree);
 extern tree c_builtin_function_ext_scope (tree);
 extern tree c_simulate_builtin_function_decl (tree);
 extern void c_warn_unused_attributes (tree);
-extern tree c_warn_type_attributes (tree);
+extern tree c_warn_type_attributes (tree, tree);
 extern void shadow_tag (const struct c_declspecs *);
 extern void shadow_tag_warned (const struct c_declspecs *, int);
 extern tree start_enum (location_t, struct c_enum_contents *, tree, tree,
--- gcc/testsuite/c-c++-common/attr-reproducible-1.c.jj	2024-08-01 14:37:23.947824372 +0200
+++ gcc/testsuite/c-c++-common/attr-reproducible-1.c	2024-08-01 14:37:23.947824372 +0200
@@ -0,0 +1,80 @@
+/* Test gnu::reproducible attribute: valid uses.  */
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-additional-options "-std=gnu23" { target c } } */
+/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
+/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 2 "optimized" } } */
+
+int f1 () [[gnu::reproducible]];
+int f2 () [[gnu::reproducible]], f3 (int) [[__gnu__::__reproducible__]];
+int f4 (int, int *) [[gnu::reproducible]];
+int f5 (int) [[gnu::reproducible]];
+int f6 (int);
+int (*fp1) (int) [[gnu::reproducible]] = f6;
+typedef int ft1 (int) [[gnu::reproducible]];
+typedef int ft2 (int);
+#ifndef __cplusplus
+extern __typeof (f6) [[gnu::reproducible]] f7;
+extern ft2 [[__gnu__::__reproducible__]] f8;
+#else
+int f7 (int) [[gnu::reproducible, gnu::reproducible]];
+int f8 (int) [[__gnu__::reproducible, gnu::__reproducible__]];
+#endif
+int f1 ();
+int f9 (int);
+int f9 (int) [[__gnu__::__reproducible__]];
+extern int x;
+
+int
+f10 (int w) [[gnu::reproducible]]
+{
+  return w + 42 + x;
+}
+
+int
+f11 (int *w, long long y[1], int z) [[__gnu__::__reproducible__]]
+{
+  w[0] = z + x;
+  w[1] = z + x + 1;
+  w[2] = z + x + 2;
+  *y = z + x + 3;
+  return z + 4 + f10 (-42);
+}
+
+int
+g ()
+{
+  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int e = fp1 (14) + fp1 (14);
+  x++;
+  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int j = fp1 (14) + fp1 (14);
+  return a + b + c + d + e + f + g + h + i + j;
+}
+
+int
+h ()
+{
+  f3 (52);
+  f3 (52);
+  f3 (52);
+  return 0;
+}
--- gcc/testsuite/c-c++-common/attr-reproducible-2.c.jj	2024-08-01 14:37:23.947824372 +0200
+++ gcc/testsuite/c-c++-common/attr-reproducible-2.c	2024-08-01 14:37:23.947824372 +0200
@@ -0,0 +1,74 @@
+/* Test reproducible attribute: valid uses.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
+/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 2 "optimized" } } */
+
+__attribute__((reproducible)) int f1 (void);
+__attribute__((__reproducible__)) int f2 (void), f3 (int);
+int f4 (int, int *) __attribute__((reproducible));
+int f5 (int) __attribute__((reproducible));
+int f6 (int);
+int (*fp1) (int) __attribute__((reproducible)) = f6;
+typedef int ft1 (int) __attribute__((reproducible));
+typedef int ft2 (int);
+extern __typeof (f6) __attribute__((reproducible)) f7;
+extern ft2 __attribute__((__reproducible__)) f8;
+int f1 (void);
+int f9 (int);
+int f9 (int) __attribute__((__reproducible__));
+extern int x;
+
+__attribute__((reproducible)) int
+f10 (int w)
+{
+  return w + 42 + x;
+}
+
+__attribute__((reproducible)) int
+f11 (int *w, long long y[1], int z)
+{
+  w[0] = z + x;
+  w[1] = z + x + 1;
+  w[2] = z + x + 2;
+  *y = z + x + 3;
+  return z + 4 + f10 (-42);
+}
+
+int
+g (void)
+{
+  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int e = fp1 (14) + fp1 (14);
+  x++;
+  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int j = fp1 (14) + fp1 (14);
+  return a + b + c + d + e + f + g + h + i + j;
+}
+
+int
+h (void)
+{
+  f3 (52);
+  f3 (52);
+  f3 (52);
+  return 0;
+}
--- gcc/testsuite/c-c++-common/attr-unsequenced-1.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/c-c++-common/attr-unsequenced-1.c	2024-08-01 14:37:23.947824372 +0200
@@ -0,0 +1,87 @@
+/* Test gnu::unsequenced attribute: valid uses.  */
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-additional-options "-std=gnu23" { target c } } */
+/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f12 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f13 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
+/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 1 "optimized" } } */
+
+[[gnu::unsequenced]] int f1 ();
+[[gnu::unsequenced]] int f2 (), f3 (int);
+int f4 (int, int *) [[gnu::unsequenced]];
+int f5 (int) [[gnu::unsequenced]];
+int f6 (int);
+int (*fp1) (int) [[gnu::unsequenced]] = f6;
+typedef int ft1 (int) [[gnu::unsequenced]];
+typedef int ft2 (int);
+#ifndef __cplusplus
+extern __typeof (f6) [[gnu::unsequenced]] f7;
+extern ft2 [[__gnu__::__unsequenced__]] f8;
+#else
+int f7 (int) [[gnu::unsequenced, gnu::unsequenced]];
+int f8 (int) [[__gnu__::unsequenced, gnu::__unsequenced__]];
+#endif
+int f1 ();
+int f9 (int);
+int f9 (int) [[__gnu__::__unsequenced__]];
+extern int x;
+
+int
+f10 (int x) [[gnu::unsequenced]]
+{
+  return x + 42;
+}
+
+int
+f11 (int *x, long long y[1], int z) [[__gnu__::__unsequenced__]]
+{
+  x[0] = z;
+  x[1] = z + 1;
+  x[2] = z + 2;
+  *y = z + 3;
+  return z + 4 + f10 (-42);
+}
+
+int f12 () [[gnu::unsequenced]];
+int f12 () [[gnu::reproducible]];
+int f13 () [[gnu::reproducible]];
+int f13 () [[gnu::unsequenced]];
+
+int
+g ()
+{
+  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int e = fp1 (14) + fp1 (14);
+  x++;
+  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int j = fp1 (14) + fp1 (14);
+  return a + b + c + d + e + f + g + h + i + j;
+}
+
+int
+h ()
+{
+  f3 (52);
+  f3 (52);
+  f3 (52);
+  return 0;
+}
--- gcc/testsuite/c-c++-common/attr-unsequenced-2.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/c-c++-common/attr-unsequenced-2.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,81 @@
+/* Test unsequenced attribute: valid uses.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f12 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f13 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
+/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 1 "optimized" } } */
+
+__attribute__((unsequenced)) int f1 (void);
+__attribute__((unsequenced)) int f2 (void), f3 (int);
+int f4 (int, int *) __attribute__((unsequenced));
+int f5 (int) __attribute__((unsequenced));
+int f6 (int);
+int (*fp1) (int) __attribute__((unsequenced)) = f6;
+typedef int ft1 (int) __attribute__((unsequenced));
+typedef int ft2 (int);
+extern __typeof (f6) __attribute__((unsequenced)) f7;
+extern ft2 __attribute__((__unsequenced__)) f8;
+int f1 (void);
+int f9 (int);
+int f9 (int) __attribute__((__unsequenced__));
+extern int x;
+
+__attribute__((unsequenced)) int
+f10 (int x)
+{
+  return x + 42;
+}
+
+__attribute__((__unsequenced__)) int
+f11 (int *x, long long y[1], int z)
+{
+  x[0] = z;
+  x[1] = z + 1;
+  x[2] = z + 2;
+  *y = z + 3;
+  return z + 4 + f10 (-42);
+}
+
+int f12 (void) __attribute__((unsequenced));
+int f12 (void) __attribute__((reproducible));
+int f13 (void) __attribute__((reproducible));
+int f13 (void) __attribute__((unsequenced));
+
+int
+g (void)
+{
+  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int e = fp1 (14) + fp1 (14);
+  x++;
+  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int j = fp1 (14) + fp1 (14);
+  return a + b + c + d + e + f + g + h + i + j;
+}
+
+int
+h (void)
+{
+  f3 (52);
+  f3 (52);
+  f3 (52);
+  return 0;
+}
--- gcc/testsuite/gcc.dg/c23-attr-reproducible-1.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-1.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,74 @@
+/* Test C23 reproducible attribute: valid uses.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors -O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
+/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 2 "optimized" } } */
+
+int f1 () [[reproducible]];
+int f2 () [[reproducible]], f3 (int) [[__reproducible__]];
+int f4 (int, int *restrict) [[reproducible]];
+int f5 (int) [[reproducible]];
+int f6 (int);
+int (*fp1) (int) [[reproducible]] = f6;
+typedef int ft1 (int) [[reproducible]];
+typedef int ft2 (int);
+extern typeof (f6) [[reproducible]] f7;
+extern ft2 [[__reproducible__]] f8;
+int f1 ();
+int f9 (int);
+int f9 (int) [[__reproducible__]];
+extern int x;
+
+int
+f10 (int w) [[reproducible]]
+{
+  return w + 42 + x;
+}
+
+int
+f11 (int *restrict w, long long y[restrict static 1], int z) [[__reproducible__]]
+{
+  w[0] = z + x;
+  w[1] = z + x + 1;
+  w[2] = z + x + 2;
+  *y = z + x + 3;
+  return z + 4 + f10 (-42);
+}
+
+int
+g ()
+{
+  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int e = fp1 (14) + fp1 (14);
+  x++;
+  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int j = fp1 (14) + fp1 (14);
+  return a + b + c + d + e + f + g + h + i + j;
+}
+
+int
+h ()
+{
+  f3 (52);
+  f3 (52);
+  f3 (52);
+  return 0;
+}
--- gcc/testsuite/gcc.dg/c23-attr-reproducible-2.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-2.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,47 @@
+/* Test C23 reproducible attribute: invalid contexts.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+/* This attribute is not valid in most cases on types other than
+   type specifiers with function type or function declarators.  */
+
+[[reproducible]]; /* { dg-error "ignored" } */
+
+int [[reproducible]] var; /* { dg-error "ignored" } */
+
+int array_with_dep_type[2] [[reproducible]]; /* { dg-error "ignored" } */
+
+[[reproducible]] int fn1 (); /* { dg-error "standard 'reproducible' attribute can only be applied to function declarators or type specifiers with function type" } */
+
+[[reproducible]] int fn2 (), fn3 (); /* { dg-error "standard 'reproducible' attribute can only be applied to function declarators or type specifiers with function type" } */
+
+int var2 [[reproducible]]; /* { dg-warning "'reproducible' attribute only applies to function types" } */
+
+int fn4 [[reproducible]] (); /* { dg-error "standard 'reproducible' attribute can only be applied to function declarators or type specifiers with function type" } */
+
+int [[reproducible]] fn5 (); /* { dg-error "ignored" } */
+
+int z = sizeof (int [[__reproducible__]]); /* { dg-error "ignored" } */
+
+/* This is valid, but not really useful, as it can't return results
+   in return type nor has any pointer arguments to store results into.  */
+void
+fn6 (int x, double y) [[reproducible]]
+{ /* { dg-warning "reproducible' attribute on function type without pointer arguments returning 'void'" } */
+  y = x;
+  (void) y;
+}
+
+void
+f (void)
+{
+  int a;
+  [[reproducible]]; /* { dg-error "ignored" } */
+  [[reproducible]] a = 1; /* { dg-error "ignored" } */
+  [[reproducible]] label: ; /* { dg-warning "'reproducible' attribute only applies to function types" } */
+  switch (var)
+    {
+    [[reproducible]] case 1: ; /* { dg-warning "'reproducible' attribute only applies to function types" } */
+    [[reproducible]] default: ; /* { dg-warning "'reproducible' attribute only applies to function types" } */
+    }
+}
--- gcc/testsuite/gcc.dg/c23-attr-reproducible-3.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-3.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,14 @@
+/* Test C23 reproducible attribute: invalid syntax.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+int a () [[reproducible()]]; /* { dg-error "'reproducible' attribute does not take any arguments" } */
+
+int b () [[reproducible(0)]]; /* { dg-error "expected" } */
+			      /* { dg-error "'reproducible' attribute does not take any arguments" "" { target *-*-* } .-1 } */
+
+int c () [[reproducible("", 123)]]; /* { dg-error "expected" } */
+				    /* { dg-error "'reproducible' attribute does not take any arguments" "" { target *-*-* } .-1 } */
+
+int d () [[reproducible((""))]]; /* { dg-error "expected" } */
+				 /* { dg-error "'reproducible' attribute does not take any arguments" "" { target *-*-* } .-1 } */
--- gcc/testsuite/gcc.dg/c23-attr-reproducible-4.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-4.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,12 @@
+/* Test C23 reproducible attribute: duplicates.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+int a () [[reproducible, __reproducible__]];
+int b () [[__reproducible__, reproducible]];
+int c () [[reproducible, reproducible]];
+int d () [[__reproducible__, __reproducible__]];
+int d () [[reproducible]];
+int d () [[__reproducible__]];
+[[reproducible, reproducible]];
+/* { dg-error "ignored" "ignored" { target *-*-* } .-1 } */
--- gcc/testsuite/gcc.dg/c23-attr-reproducible-5.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-5.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,44 @@
+/* Test C23 reproducible attribute: composite type on ?:.  */
+/* { dg-do run } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+/* { dg-additional-sources "c23-attr-reproducible-5-aux.c" } */
+
+int f1 () [[reproducible]];
+int f2 ();
+int f3 ();
+int (*fp1) () [[reproducible]] = f2;
+int (*fp2) () [[reproducible]] = f3;
+extern void abort ();
+
+int
+foo (int x)
+{
+  return __builtin_has_attribute (*(x ? f1 : f3), reproducible);
+}
+
+int
+bar (int x)
+{
+  return __builtin_has_attribute (*(x ? fp1 : fp2), reproducible);
+}
+
+int
+baz (int x)
+{
+  return __builtin_has_attribute (*(x ? f3 : f1), reproducible);
+}
+
+int
+qux (int x)
+{
+  return __builtin_has_attribute (*(x ? fp2 : fp1), reproducible);
+}
+
+int
+main ()
+{
+  if (!foo (0) || !bar (0) || !baz (0) || !qux (0))
+    abort ();
+  if (!foo (1) || !bar (1) || !baz (1) || !qux (1))
+    abort ();
+}
--- gcc/testsuite/gcc.dg/c23-attr-reproducible-5-aux.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-5-aux.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,21 @@
+/* Auxiliary source for c23-attr-reproducible-5.c test.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+int
+f1 () [[reproducible]]
+{
+  return 42;
+}
+
+int
+f2 ()
+{
+  return 43;
+}
+
+int
+f3 ()
+{
+  return 44;
+}
--- gcc/testsuite/gcc.dg/c23-attr-unsequenced-1.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-1.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,81 @@
+/* Test C23 unsequenced attribute: valid uses.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors -O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f12 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f13 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
+/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 1 "optimized" } } */
+
+int f1 () [[unsequenced]];
+int f2 () [[unsequenced]], f3 (int) [[__unsequenced__]];
+int f4 (int, int *restrict) [[unsequenced]];
+int f5 (int) [[unsequenced]];
+int f6 (int);
+int (*fp1) (int) [[unsequenced]] = f6;
+typedef int ft1 (int) [[unsequenced]];
+typedef int ft2 (int);
+extern typeof (f6) [[unsequenced]] f7;
+extern ft2 [[__unsequenced__]] f8;
+int f1 ();
+int f9 (int);
+int f9 (int) [[__unsequenced__]];
+extern int x;
+
+int
+f10 (int x) [[unsequenced]]
+{
+  return x + 42;
+}
+
+int
+f11 (int *restrict x, long long y[restrict static 1], int z) [[__unsequenced__]]
+{
+  x[0] = z;
+  x[1] = z + 1;
+  x[2] = z + 2;
+  *y = z + 3;
+  return z + 4 + f10 (-42);
+}
+
+int f12 () [[unsequenced]];
+int f12 () [[reproducible]];
+int f13 () [[reproducible]];
+int f13 () [[unsequenced]];
+
+int
+g ()
+{
+  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int e = fp1 (14) + fp1 (14);
+  x++;
+  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int j = fp1 (14) + fp1 (14);
+  return a + b + c + d + e + f + g + h + i + j;
+}
+
+int
+h ()
+{
+  f3 (52);
+  f3 (52);
+  f3 (52);
+  return 0;
+}
--- gcc/testsuite/gcc.dg/c23-attr-unsequenced-2.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-2.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,47 @@
+/* Test C23 unsequenced attribute: invalid contexts.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+/* This attribute is not valid in most cases on types other than
+   type specifiers with function type or function declarators.  */
+
+[[unsequenced]]; /* { dg-error "ignored" } */
+
+int [[unsequenced]] var; /* { dg-error "ignored" } */
+
+int array_with_dep_type[2] [[unsequenced]]; /* { dg-error "ignored" } */
+
+[[unsequenced]] int fn1 (); /* { dg-error "standard 'unsequenced' attribute can only be applied to function declarators or type specifiers with function type" } */
+
+[[unsequenced]] int fn2 (), fn3 (); /* { dg-error "standard 'unsequenced' attribute can only be applied to function declarators or type specifiers with function type" } */
+
+int var2 [[unsequenced]]; /* { dg-warning "'unsequenced' attribute only applies to function types" } */
+
+int fn4 [[unsequenced]] (); /* { dg-error "standard 'unsequenced' attribute can only be applied to function declarators or type specifiers with function type" } */
+
+int [[unsequenced]] fn5 (); /* { dg-error "ignored" } */
+
+int z = sizeof (int [[__unsequenced__]]); /* { dg-error "ignored" } */
+
+/* This is valid, but not really useful, as it can't return results
+   in return type nor has any pointer arguments to store results into.  */
+void
+fn6 (int x, double y) [[unsequenced]]
+{ /* { dg-warning "unsequenced' attribute on function type without pointer arguments returning 'void'" } */
+  y = x;
+  (void) y;
+}
+
+void
+f (void)
+{
+  int a;
+  [[unsequenced]]; /* { dg-error "ignored" } */
+  [[unsequenced]] a = 1; /* { dg-error "ignored" } */
+  [[unsequenced]] label: ; /* { dg-warning "'unsequenced' attribute only applies to function types" } */
+  switch (var)
+    {
+    [[unsequenced]] case 1: ; /* { dg-warning "'unsequenced' attribute only applies to function types" } */
+    [[unsequenced]] default: ; /* { dg-warning "'unsequenced' attribute only applies to function types" } */
+    }
+}
--- gcc/testsuite/gcc.dg/c23-attr-unsequenced-3.c.jj	2024-08-01 14:37:23.949824346 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-3.c	2024-08-01 14:37:23.949824346 +0200
@@ -0,0 +1,14 @@
+/* Test C23 unsequenced attribute: invalid syntax.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+int a () [[unsequenced()]]; /* { dg-error "'unsequenced' attribute does not take any arguments" } */
+
+int b () [[unsequenced(0)]]; /* { dg-error "expected" } */
+			     /* { dg-error "'unsequenced' attribute does not take any arguments" "" { target *-*-* } .-1 } */
+
+int c () [[unsequenced("", 123)]]; /* { dg-error "expected" } */
+				   /* { dg-error "'unsequenced' attribute does not take any arguments" "" { target *-*-* } .-1 } */
+
+int d () [[unsequenced((""))]]; /* { dg-error "expected" } */
+				/* { dg-error "'unsequenced' attribute does not take any arguments" "" { target *-*-* } .-1 } */
--- gcc/testsuite/gcc.dg/c23-attr-unsequenced-4.c.jj	2024-08-01 14:37:23.949824346 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-4.c	2024-08-01 14:37:23.949824346 +0200
@@ -0,0 +1,12 @@
+/* Test C23 unsequenced attribute: duplicates.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+int a () [[unsequenced, __unsequenced__]];
+int b () [[__unsequenced__, unsequenced]];
+int c () [[unsequenced, unsequenced]];
+int d () [[__unsequenced__, __unsequenced__]];
+int d () [[unsequenced]];
+int d () [[__unsequenced__]];
+[[unsequenced, unsequenced]];
+/* { dg-error "ignored" "ignored" { target *-*-* } .-1 } */
--- gcc/testsuite/gcc.dg/c23-attr-unsequenced-5.c.jj	2024-08-01 14:37:23.949824346 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-5.c	2024-08-01 14:37:23.949824346 +0200
@@ -0,0 +1,44 @@
+/* Test C23 unsequenced attribute: composite type on ?:.  */
+/* { dg-do run } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+/* { dg-additional-sources "c23-attr-unsequenced-5-aux.c" } */
+
+int f1 () [[unsequenced]];
+int f2 ();
+int f3 ();
+int (*fp1) () [[unsequenced]] = f2;
+int (*fp2) () [[unsequenced]] = f3;
+extern void abort ();
+
+int
+foo (int x)
+{
+  return __builtin_has_attribute (*(x ? f1 : f3), unsequenced);
+}
+
+int
+bar (int x)
+{
+  return __builtin_has_attribute (*(x ? fp1 : fp2), unsequenced);
+}
+
+int
+baz (int x)
+{
+  return __builtin_has_attribute (*(x ? f3 : f1), unsequenced);
+}
+
+int
+qux (int x)
+{
+  return __builtin_has_attribute (*(x ? fp2 : fp1), unsequenced);
+}
+
+int
+main ()
+{
+  if (!foo (0) || !bar (0) || !baz (0) || !qux (0))
+    abort ();
+  if (!foo (1) || !bar (1) || !baz (1) || !qux (1))
+    abort ();
+}
--- gcc/testsuite/gcc.dg/c23-attr-unsequenced-5-aux.c.jj	2024-08-01 14:37:23.949824346 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-5-aux.c	2024-08-01 14:37:23.949824346 +0200
@@ -0,0 +1,21 @@
+/* Auxiliary source for c23-attr-unsequenced-5.c test.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+int
+f1 () [[unsequenced]]
+{
+  return 42;
+}
+
+int
+f2 ()
+{
+  return 43;
+}
+
+int
+f3 ()
+{
+  return 44;
+}
--- gcc/testsuite/gcc.dg/c23-has-c-attribute-2.c.jj	2024-08-01 10:30:35.438802424 +0200
+++ gcc/testsuite/gcc.dg/c23-has-c-attribute-2.c	2024-08-01 14:37:24.355819220 +0200
@@ -50,6 +50,22 @@
 #error "bad result for ___Noreturn__"
 #endif
   
+#if __has_c_attribute (unsequenced) != 202311L
+#error "bad result for unsequenced"
+#endif
+
+#if __has_c_attribute (__unsequenced__) != 202311L
+#error "bad result for __unsequenced__"
+#endif
+
+#if __has_c_attribute (reproducible) != 202311L
+#error "bad result for reproducible"
+#endif
+
+#if __has_c_attribute (__reproducible__) != 202311L
+#error "bad result for __reproducible__"
+#endif
+
 /* Macros in the attribute name are expanded.  */
 #define foo deprecated
 #if __has_c_attribute (foo) != 202311L


	Jakub
Joseph Myers Aug. 30, 2024, 5:12 p.m. UTC | #3
On Fri, 30 Aug 2024, Jakub Jelinek wrote:

> Here is the full updated patch.

This patch is OK.
diff mbox series

Patch

--- gcc/doc/extend.texi.jj	2024-08-01 10:30:35.437802437 +0200
+++ gcc/doc/extend.texi	2024-08-01 14:51:21.512248155 +0200
@@ -4024,6 +4024,69 @@  diagnosed.  Because a pure function cann
 effects it does not make sense for such a function to return @code{void}.
 Declaring such a function is diagnosed.
 
+@cindex @code{unsequenced} function type attribute
+@cindex functions that have no side effects
+@item unsequenced
+
+This attribute is a GNU counterpart of the C23 @code{[[unsequenced]]}
+attribute, used to specify function pointers to effectless, idempotent,
+stateless and independent functions according to the C23 definition.
+
+Unlike the standard C23 attribute it can be also specified in attributes
+which appertain to function declarations and applies to the their function
+type even in that case.
+
+Unsequenced functions without pointer or reference arguments are similar
+to functions with the @code{const} attribute, except that @code{const}
+attribute also requires finitness.  So, both functions with @code{const}
+and with @code{unsequenced} attributes can be optimized by common
+subexpression elimination, but only functions with @code{const}
+attribute can be optimized by dead code elimination if their result is
+unused or is used only by dead code.  Unsequenced functions without pointer
+or reference arguments with @code{void} return type are diagnosed because
+they can't store any results and don't have other observable side-effects
+either.
+
+Unsequenced functions with pointer or reference arguments can inspect
+objects through the passed pointers or references or references to pointers
+or can store additional results through those pointers or references or
+references to pointers.
+
+The @code{unsequenced} attribute imposes greater restrictions than
+the similar @code{reproducible} attribute and fewer restrictions than
+the @code{const} attribute, so during optimization @code{const} has
+precedence over @code{unsequenced} which has precedence over
+@code{reproducible}.
+
+@cindex @code{reproducible} function type attribute
+@cindex functions that have no side effects
+@item reproducible
+
+This attribute is a GNU counterpart of the C23 @code{[[reproducible]]}
+attribute, used to specify function pointers to effectless and idempotent
+functions according to the C23 definition.
+
+Unlike the standard C23 attribute it can be also specified in attributes
+which appertain to function declarations and applies to the their function
+type even in that case.
+
+Reproducible functions without pointer or reference arguments or which do
+not modify objects referenced by those pointer/reference arguments are
+similar to functions with the @code{pure} attribute, except that
+@code{pure} attribute also requires finitness.  So, both functions with
+@code{pure} and with @code{reproducible} attributes can be optimized by common
+subexpression elimination if the global state or anything reachable through
+the pointer/reference arguments isn't modified, but only functions with
+@code{pure} attribute can be optimized by dead code elimination if their result is
+unused or is used only by dead code.  Reproducible functions without pointer
+or reference arguments with @code{void} return type are diagnosed because
+they can't store any results and don't have other observable side-effects
+either.
+
+Reproducible functions with pointer or reference arguments can store
+additional results through those pointers or references or references to
+pointers.
+
 @cindex @code{retain} function attribute
 @item retain
 For ELF targets that support the GNU or FreeBSD OSABIs, this attribute
--- gcc/calls.cc.jj	2024-08-01 10:30:35.430802527 +0200
+++ gcc/calls.cc	2024-08-01 14:37:23.941824448 +0200
@@ -852,6 +852,23 @@  flags_from_decl_or_type (const_tree exp)
 	flags |= ECF_XTHROW;
 
       flags = special_function_p (exp, flags);
+
+      if ((flags & ECF_CONST) == 0
+	  && lookup_attribute ("unsequenced noptr",
+			       TYPE_ATTRIBUTES (TREE_TYPE (exp))))
+	{
+	  /* [[unsequenced]] with no pointers in arguments is like
+	     [[gnu::const]] without finite guarantee.  */
+	  flags |= ECF_CONST;
+	  if ((flags & ECF_PURE) == 0)
+	    flags |= ECF_LOOPING_CONST_OR_PURE;
+	}
+      if ((flags & (ECF_CONST | ECF_PURE)) == 0
+	  && lookup_attribute ("reproducible noptr",
+			       TYPE_ATTRIBUTES (TREE_TYPE (exp))))
+	/* [[reproducible]] with no pointers in arguments is like
+	   [[gnu::pure]] without finite guarantee.  */
+	flags |= ECF_PURE | ECF_LOOPING_CONST_OR_PURE;
     }
   else if (TYPE_P (exp))
     {
@@ -862,6 +879,17 @@  flags_from_decl_or_type (const_tree exp)
 	  && ((flags & ECF_CONST) != 0
 	      || lookup_attribute ("transaction_pure", TYPE_ATTRIBUTES (exp))))
 	flags |= ECF_TM_PURE;
+
+      if ((flags & ECF_CONST) == 0
+	  && lookup_attribute ("unsequenced noptr", TYPE_ATTRIBUTES (exp)))
+	/* [[unsequenced]] with no pointers in arguments is like
+	   [[gnu::const]] without finite guarantee.  */
+	flags |= ECF_CONST | ECF_LOOPING_CONST_OR_PURE;
+      if ((flags & ECF_CONST) == 0
+	  && lookup_attribute ("reproducible noptr", TYPE_ATTRIBUTES (exp)))
+	/* [[reproducible]] with no pointers in arguments is like
+	   [[gnu::pure]] without finite guarantee.  */
+	flags |= ECF_PURE | ECF_LOOPING_CONST_OR_PURE;
     }
   else
     gcc_unreachable ();
--- gcc/c-family/c-attribs.cc.jj	2024-08-01 10:30:35.420802655 +0200
+++ gcc/c-family/c-attribs.cc	2024-08-01 14:43:28.775217639 +0200
@@ -444,6 +444,14 @@  const struct attribute_spec c_common_gnu
   { "pure",                   0, 0, true,  false, false, false,
 			      handle_pure_attribute,
 	                      attr_const_pure_exclusions },
+  { "reproducible",           0, 0, false, true,  true,  false,
+			      handle_reproducible_attribute, NULL },
+  { "unsequenced",            0, 0, false, true,  true,  false,
+			      handle_unsequenced_attribute, NULL },
+  { "reproducible noptr",     0, 0, false, true,  true,  false,
+			      handle_reproducible_attribute, NULL },
+  { "unsequenced noptr",      0, 0, false, true,  true,  false,
+			      handle_unsequenced_attribute, NULL },
   { "transaction_callable",   0, 0, false, true,  false, false,
 			      handle_tm_attribute, NULL },
   { "transaction_unsafe",     0, 0, false, true,  false, true,
@@ -4280,6 +4288,53 @@  handle_pure_attribute (tree *node, tree
   return NULL_TREE;
 }
 
+/* Handle an "unsequenced" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+tree
+handle_unsequenced_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+			      int flags, bool *no_add_attrs)
+{
+  tree fntype = *node;
+  for (tree argtype = TYPE_ARG_TYPES (fntype); argtype;
+       argtype = TREE_CHAIN (argtype))
+    /* If any of the arguments have pointer or reference type, just
+       add the attribute alone.  */
+    if (POINTER_TYPE_P (TREE_VALUE (argtype)))
+      return NULL_TREE;
+
+  if (VOID_TYPE_P (TREE_TYPE (fntype)))
+    warning (OPT_Wattributes, "%qE attribute on function type "
+	     "without pointer arguments returning %<void%>", name);
+  const char *name2;
+  if (IDENTIFIER_LENGTH (name) == sizeof ("unsequenced") - 1)
+    name2 = "unsequenced noptr";
+  else
+    name2 = "reproducible noptr";
+  if (!lookup_attribute (name2, TYPE_ATTRIBUTES (fntype)))
+    {
+      *no_add_attrs = true;
+      gcc_assert ((flags & (int) ATTR_FLAG_TYPE_IN_PLACE) == 0);
+      tree attr = tree_cons (get_identifier (name2), NULL_TREE,
+			     TYPE_ATTRIBUTES (fntype));
+      if (!lookup_attribute (IDENTIFIER_POINTER (name),
+			     TYPE_ATTRIBUTES (fntype)))
+	attr = tree_cons (name, NULL_TREE, attr);
+      *node = build_type_attribute_variant (*node, attr);
+    }
+  return NULL_TREE;
+}
+
+/* Handle a "reproducible" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+tree
+handle_reproducible_attribute (tree *node, tree name, tree args, int flags,
+			       bool *no_add_attrs)
+{
+  return handle_unsequenced_attribute (node, name, args, flags, no_add_attrs);
+}
+
 /* Digest an attribute list destined for a transactional memory statement.
    ALLOWED is the set of attributes that are allowed for this statement;
    return the attribute we parsed.  Multiple attributes are never allowed.  */
--- gcc/c-family/c-common.h.jj	2024-08-01 10:30:35.421802642 +0200
+++ gcc/c-family/c-common.h	2024-08-01 14:37:23.942824435 +0200
@@ -864,6 +864,8 @@  extern void check_function_format (const
 extern bool attribute_fallthrough_p (tree);
 extern tree handle_format_attribute (tree *, tree, tree, int, bool *);
 extern tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
+extern tree handle_unsequenced_attribute (tree *, tree, tree, int, bool *);
+extern tree handle_reproducible_attribute (tree *, tree, tree, int, bool *);
 extern bool c_common_handle_option (size_t, const char *, HOST_WIDE_INT, int,
 				    location_t,
 				    const struct cl_option_handlers *);
--- gcc/c-family/c-lex.cc.jj	2024-08-01 10:30:35.421802642 +0200
+++ gcc/c-family/c-lex.cc	2024-08-01 14:37:23.942824435 +0200
@@ -445,7 +445,9 @@  c_common_has_attribute (cpp_reader *pfil
 		  || is_attribute_p ("maybe_unused", attr_name)
 		  || is_attribute_p ("nodiscard", attr_name)
 		  || is_attribute_p ("noreturn", attr_name)
-		  || is_attribute_p ("_Noreturn", attr_name))
+		  || is_attribute_p ("_Noreturn", attr_name)
+		  || is_attribute_p ("reproducible", attr_name)
+		  || is_attribute_p ("unsequenced", attr_name))
 		result = 202311;
 	    }
 	  if (result)
--- gcc/c/c-decl.cc.jj	2024-08-01 10:30:35.424802604 +0200
+++ gcc/c/c-decl.cc	2024-08-01 14:37:23.944824410 +0200
@@ -4702,6 +4702,39 @@  handle_std_noreturn_attribute (tree *nod
     }
 }
 
+/* Handle the standard [[unsequenced]] attribute.  */
+
+static tree
+handle_std_unsequenced_attribute (tree *node, tree name, tree args,
+				  int flags, bool *no_add_attrs)
+{
+  /* Unlike GNU __attribute__ ((unsequenced)), the standard [[unsequenced]]
+     should be only applied to function declarators or type specifiers which
+     have function type.  */
+  if (node[2])
+    {
+      auto_diagnostic_group d;
+      if (pedwarn (input_location, OPT_Wattributes,
+		   "standard %qE attribute can only be applied to function "
+		   "declarators or type specifiers with function type", name))
+	inform (input_location, "did you mean to specify it after %<)%> "
+				"following function parameters?");
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+  return handle_unsequenced_attribute (node, name, args, flags, no_add_attrs);
+}
+
+/* Handle the standard [[reproducible]] attribute.  */
+
+static tree
+handle_std_reproducible_attribute (tree *node, tree name, tree args,
+				   int flags, bool *no_add_attrs)
+{
+  return handle_std_unsequenced_attribute (node, name, args, flags,
+					   no_add_attrs);
+}
+
 /* Table of supported standard (C23) attributes.  */
 static const attribute_spec std_attributes[] =
 {
@@ -4718,7 +4751,11 @@  static const attribute_spec std_attribut
   { "nodiscard", 0, 1, false, false, false, false,
     handle_nodiscard_attribute, NULL },
   { "noreturn", 0, 0, false, false, false, false,
-    handle_std_noreturn_attribute, NULL }
+    handle_std_noreturn_attribute, NULL },
+  { "reproducible", 0, 0, false, true, true, false,
+    handle_std_reproducible_attribute, NULL },
+  { "unsequenced", 0, 0, false, true, true, false,
+    handle_std_unsequenced_attribute, NULL }
 };
 
 const scoped_attribute_specs std_attribute_table =
@@ -4911,12 +4948,24 @@  c_warn_unused_attributes (tree attrs)
    list of attributes with them removed.  */
 
 tree
-c_warn_type_attributes (tree attrs)
+c_warn_type_attributes (tree type, tree attrs)
 {
   tree *attr_ptr = &attrs;
   while (*attr_ptr)
     if (get_attribute_namespace (*attr_ptr) == NULL_TREE)
       {
+	if (TREE_CODE (type) == FUNCTION_TYPE)
+	  {
+	    tree name = get_attribute_name (*attr_ptr);
+	    /* [[unsequenced]] and [[reproducible]] is fine on function
+	       types that aren't being defined.  */
+	    if (is_attribute_p ("unsequenced", name)
+		|| is_attribute_p ("reproducible", name))
+	      {
+		attr_ptr = &TREE_CHAIN (*attr_ptr);
+		continue;
+	      }
+	  }
 	pedwarn (input_location, OPT_Wattributes, "%qE attribute ignored",
 		 get_attribute_name (*attr_ptr));
 	*attr_ptr = TREE_CHAIN (*attr_ptr);
@@ -5386,7 +5435,7 @@  groktypename (struct c_type_name *type_n
 			 DEPRECATED_NORMAL);
 
   /* Apply attributes.  */
-  attrs = c_warn_type_attributes (attrs);
+  attrs = c_warn_type_attributes (type, attrs);
   decl_attributes (&type, attrs, 0);
 
   return type;
@@ -7196,7 +7245,7 @@  grokdeclarator (const struct c_declarato
 		else if (inner_decl->kind == cdk_array)
 		  attr_flags |= (int) ATTR_FLAG_ARRAY_NEXT;
 	      }
-	    attrs = c_warn_type_attributes (attrs);
+	    attrs = c_warn_type_attributes (type, attrs);
 	    returned_attrs = decl_attributes (&type,
 					      chainon (returned_attrs, attrs),
 					      attr_flags);
@@ -13433,7 +13482,8 @@  finish_declspecs (struct c_declspecs *sp
  handle_postfix_attrs:
   if (specs->type != NULL)
     {
-      specs->postfix_attrs = c_warn_type_attributes (specs->postfix_attrs);
+      specs->postfix_attrs
+	= c_warn_type_attributes (specs->type, specs->postfix_attrs);
       decl_attributes (&specs->type, specs->postfix_attrs, 0);
       specs->postfix_attrs = NULL_TREE;
     }
--- gcc/c/c-parser.cc.jj	2024-08-01 10:30:35.429802539 +0200
+++ gcc/c/c-parser.cc	2024-08-01 14:37:23.947824372 +0200
@@ -2677,8 +2677,9 @@  c_parser_declaration_or_fndef (c_parser
 		      /* Postfix [[]] attributes are valid with C23
 			 auto, although not with __auto_type, and
 			 modify the type given by the initializer.  */
-		      specs->postfix_attrs =
-			c_warn_type_attributes (specs->postfix_attrs);
+		      specs->postfix_attrs
+			= c_warn_type_attributes (specs->type,
+						  specs->postfix_attrs);
 		      decl_attributes (&specs->type, specs->postfix_attrs, 0);
 		      specs->postfix_attrs = NULL_TREE;
 		    }
--- gcc/c/c-tree.h.jj	2024-08-01 10:30:35.429802539 +0200
+++ gcc/c/c-tree.h	2024-08-01 14:37:23.947824372 +0200
@@ -680,7 +680,7 @@  extern tree c_builtin_function (tree);
 extern tree c_builtin_function_ext_scope (tree);
 extern tree c_simulate_builtin_function_decl (tree);
 extern void c_warn_unused_attributes (tree);
-extern tree c_warn_type_attributes (tree);
+extern tree c_warn_type_attributes (tree, tree);
 extern void shadow_tag (const struct c_declspecs *);
 extern void shadow_tag_warned (const struct c_declspecs *, int);
 extern tree start_enum (location_t, struct c_enum_contents *, tree, tree,
--- gcc/testsuite/c-c++-common/attr-reproducible-1.c.jj	2024-08-01 14:37:23.947824372 +0200
+++ gcc/testsuite/c-c++-common/attr-reproducible-1.c	2024-08-01 14:37:23.947824372 +0200
@@ -0,0 +1,80 @@ 
+/* Test gnu::reproducible attribute: valid uses.  */
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-additional-options "-std=gnu23" { target c } } */
+/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
+/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 2 "optimized" } } */
+
+int f1 () [[gnu::reproducible]];
+int f2 () [[gnu::reproducible]], f3 (int) [[__gnu__::__reproducible__]];
+int f4 (int, int *) [[gnu::reproducible]];
+int f5 (int) [[gnu::reproducible]];
+int f6 (int);
+int (*fp1) (int) [[gnu::reproducible]] = f6;
+typedef int ft1 (int) [[gnu::reproducible]];
+typedef int ft2 (int);
+#ifndef __cplusplus
+extern __typeof (f6) [[gnu::reproducible]] f7;
+extern ft2 [[__gnu__::__reproducible__]] f8;
+#else
+int f7 (int) [[gnu::reproducible, gnu::reproducible]];
+int f8 (int) [[__gnu__::reproducible, gnu::__reproducible__]];
+#endif
+int f1 ();
+int f9 (int);
+int f9 (int) [[__gnu__::__reproducible__]];
+extern int x;
+
+int
+f10 (int w) [[gnu::reproducible]]
+{
+  return w + 42 + x;
+}
+
+int
+f11 (int *w, long long y[1], int z) [[__gnu__::__reproducible__]]
+{
+  w[0] = z + x;
+  w[1] = z + x + 1;
+  w[2] = z + x + 2;
+  *y = z + x + 3;
+  return z + 4 + f10 (-42);
+}
+
+int
+g ()
+{
+  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int e = fp1 (14) + fp1 (14);
+  x++;
+  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int j = fp1 (14) + fp1 (14);
+  return a + b + c + d + e + f + g + h + i + j;
+}
+
+int
+h ()
+{
+  f3 (52);
+  f3 (52);
+  f3 (52);
+  return 0;
+}
--- gcc/testsuite/c-c++-common/attr-reproducible-2.c.jj	2024-08-01 14:37:23.947824372 +0200
+++ gcc/testsuite/c-c++-common/attr-reproducible-2.c	2024-08-01 14:37:23.947824372 +0200
@@ -0,0 +1,74 @@ 
+/* Test reproducible attribute: valid uses.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
+/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 2 "optimized" } } */
+
+__attribute__((reproducible)) int f1 (void);
+__attribute__((__reproducible__)) int f2 (void), f3 (int);
+int f4 (int, int *) __attribute__((reproducible));
+int f5 (int) __attribute__((reproducible));
+int f6 (int);
+int (*fp1) (int) __attribute__((reproducible)) = f6;
+typedef int ft1 (int) __attribute__((reproducible));
+typedef int ft2 (int);
+extern __typeof (f6) __attribute__((reproducible)) f7;
+extern ft2 __attribute__((__reproducible__)) f8;
+int f1 (void);
+int f9 (int);
+int f9 (int) __attribute__((__reproducible__));
+extern int x;
+
+__attribute__((reproducible)) int
+f10 (int w)
+{
+  return w + 42 + x;
+}
+
+__attribute__((reproducible)) int
+f11 (int *w, long long y[1], int z)
+{
+  w[0] = z + x;
+  w[1] = z + x + 1;
+  w[2] = z + x + 2;
+  *y = z + x + 3;
+  return z + 4 + f10 (-42);
+}
+
+int
+g (void)
+{
+  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int e = fp1 (14) + fp1 (14);
+  x++;
+  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int j = fp1 (14) + fp1 (14);
+  return a + b + c + d + e + f + g + h + i + j;
+}
+
+int
+h (void)
+{
+  f3 (52);
+  f3 (52);
+  f3 (52);
+  return 0;
+}
--- gcc/testsuite/c-c++-common/attr-unsequenced-1.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/c-c++-common/attr-unsequenced-1.c	2024-08-01 14:37:23.947824372 +0200
@@ -0,0 +1,87 @@ 
+/* Test gnu::unsequenced attribute: valid uses.  */
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-additional-options "-std=gnu23" { target c } } */
+/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f12 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f13 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
+/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 1 "optimized" } } */
+
+[[gnu::unsequenced]] int f1 ();
+[[gnu::unsequenced]] int f2 (), f3 (int);
+int f4 (int, int *) [[gnu::unsequenced]];
+int f5 (int) [[gnu::unsequenced]];
+int f6 (int);
+int (*fp1) (int) [[gnu::unsequenced]] = f6;
+typedef int ft1 (int) [[gnu::unsequenced]];
+typedef int ft2 (int);
+#ifndef __cplusplus
+extern __typeof (f6) [[gnu::unsequenced]] f7;
+extern ft2 [[__gnu__::__unsequenced__]] f8;
+#else
+int f7 (int) [[gnu::unsequenced, gnu::unsequenced]];
+int f8 (int) [[__gnu__::unsequenced, gnu::__unsequenced__]];
+#endif
+int f1 ();
+int f9 (int);
+int f9 (int) [[__gnu__::__unsequenced__]];
+extern int x;
+
+int
+f10 (int x) [[gnu::unsequenced]]
+{
+  return x + 42;
+}
+
+int
+f11 (int *x, long long y[1], int z) [[__gnu__::__unsequenced__]]
+{
+  x[0] = z;
+  x[1] = z + 1;
+  x[2] = z + 2;
+  *y = z + 3;
+  return z + 4 + f10 (-42);
+}
+
+int f12 () [[gnu::unsequenced]];
+int f12 () [[gnu::reproducible]];
+int f13 () [[gnu::reproducible]];
+int f13 () [[gnu::unsequenced]];
+
+int
+g ()
+{
+  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int e = fp1 (14) + fp1 (14);
+  x++;
+  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int j = fp1 (14) + fp1 (14);
+  return a + b + c + d + e + f + g + h + i + j;
+}
+
+int
+h ()
+{
+  f3 (52);
+  f3 (52);
+  f3 (52);
+  return 0;
+}
--- gcc/testsuite/c-c++-common/attr-unsequenced-2.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/c-c++-common/attr-unsequenced-2.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,81 @@ 
+/* Test unsequenced attribute: valid uses.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f12 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f13 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
+/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 1 "optimized" } } */
+
+__attribute__((unsequenced)) int f1 (void);
+__attribute__((unsequenced)) int f2 (void), f3 (int);
+int f4 (int, int *) __attribute__((unsequenced));
+int f5 (int) __attribute__((unsequenced));
+int f6 (int);
+int (*fp1) (int) __attribute__((unsequenced)) = f6;
+typedef int ft1 (int) __attribute__((unsequenced));
+typedef int ft2 (int);
+extern __typeof (f6) __attribute__((unsequenced)) f7;
+extern ft2 __attribute__((__unsequenced__)) f8;
+int f1 (void);
+int f9 (int);
+int f9 (int) __attribute__((__unsequenced__));
+extern int x;
+
+__attribute__((unsequenced)) int
+f10 (int x)
+{
+  return x + 42;
+}
+
+__attribute__((__unsequenced__)) int
+f11 (int *x, long long y[1], int z)
+{
+  x[0] = z;
+  x[1] = z + 1;
+  x[2] = z + 2;
+  *y = z + 3;
+  return z + 4 + f10 (-42);
+}
+
+int f12 (void) __attribute__((unsequenced));
+int f12 (void) __attribute__((reproducible));
+int f13 (void) __attribute__((reproducible));
+int f13 (void) __attribute__((unsequenced));
+
+int
+g (void)
+{
+  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int e = fp1 (14) + fp1 (14);
+  x++;
+  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int j = fp1 (14) + fp1 (14);
+  return a + b + c + d + e + f + g + h + i + j;
+}
+
+int
+h (void)
+{
+  f3 (52);
+  f3 (52);
+  f3 (52);
+  return 0;
+}
--- gcc/testsuite/gcc.dg/c23-attr-reproducible-1.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-1.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,74 @@ 
+/* Test C23 reproducible attribute: valid uses.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors -O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
+/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 2 "optimized" } } */
+
+int f1 () [[reproducible]];
+int f2 () [[reproducible]], f3 (int) [[__reproducible__]];
+int f4 (int, int *restrict) [[reproducible]];
+int f5 (int) [[reproducible]];
+int f6 (int);
+int (*fp1) (int) [[reproducible]] = f6;
+typedef int ft1 (int) [[reproducible]];
+typedef int ft2 (int);
+extern typeof (f6) [[reproducible]] f7;
+extern ft2 [[__reproducible__]] f8;
+int f1 ();
+int f9 (int);
+int f9 (int) [[__reproducible__]];
+extern int x;
+
+int
+f10 (int w) [[reproducible]]
+{
+  return w + 42 + x;
+}
+
+int
+f11 (int *restrict w, long long y[restrict static 1], int z) [[__reproducible__]]
+{
+  w[0] = z + x;
+  w[1] = z + x + 1;
+  w[2] = z + x + 2;
+  *y = z + x + 3;
+  return z + 4 + f10 (-42);
+}
+
+int
+g ()
+{
+  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int e = fp1 (14) + fp1 (14);
+  x++;
+  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
+  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int j = fp1 (14) + fp1 (14);
+  return a + b + c + d + e + f + g + h + i + j;
+}
+
+int
+h ()
+{
+  f3 (52);
+  f3 (52);
+  f3 (52);
+  return 0;
+}
--- gcc/testsuite/gcc.dg/c23-attr-reproducible-2.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-2.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,47 @@ 
+/* Test C23 reproducible attribute: invalid contexts.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+/* This attribute is not valid in most cases on types other than
+   type specifiers with function type or function declarators.  */
+
+[[reproducible]]; /* { dg-error "ignored" } */
+
+int [[reproducible]] var; /* { dg-error "ignored" } */
+
+int array_with_dep_type[2] [[reproducible]]; /* { dg-error "ignored" } */
+
+[[reproducible]] int fn1 (); /* { dg-error "standard 'reproducible' attribute can only be applied to function declarators or type specifiers with function type" } */
+
+[[reproducible]] int fn2 (), fn3 (); /* { dg-error "standard 'reproducible' attribute can only be applied to function declarators or type specifiers with function type" } */
+
+int var2 [[reproducible]]; /* { dg-warning "'reproducible' attribute only applies to function types" } */
+
+int fn4 [[reproducible]] (); /* { dg-error "standard 'reproducible' attribute can only be applied to function declarators or type specifiers with function type" } */
+
+int [[reproducible]] fn5 (); /* { dg-error "ignored" } */
+
+int z = sizeof (int [[__reproducible__]]); /* { dg-error "ignored" } */
+
+/* This is valid, but not really useful, as it can't return results
+   in return type nor has any pointer arguments to store results into.  */
+void
+fn6 (int x, double y) [[reproducible]]
+{ /* { dg-warning "reproducible' attribute on function type without pointer arguments returning 'void'" } */
+  y = x;
+  (void) y;
+}
+
+void
+f (void)
+{
+  int a;
+  [[reproducible]]; /* { dg-error "ignored" } */
+  [[reproducible]] a = 1; /* { dg-error "ignored" } */
+  [[reproducible]] label: ; /* { dg-warning "'reproducible' attribute only applies to function types" } */
+  switch (var)
+    {
+    [[reproducible]] case 1: ; /* { dg-warning "'reproducible' attribute only applies to function types" } */
+    [[reproducible]] default: ; /* { dg-warning "'reproducible' attribute only applies to function types" } */
+    }
+}
--- gcc/testsuite/gcc.dg/c23-attr-reproducible-3.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-3.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,14 @@ 
+/* Test C23 reproducible attribute: invalid syntax.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+int a () [[reproducible()]]; /* { dg-error "'reproducible' attribute does not take any arguments" } */
+
+int b () [[reproducible(0)]]; /* { dg-error "expected" } */
+			      /* { dg-error "'reproducible' attribute does not take any arguments" "" { target *-*-* } .-1 } */
+
+int c () [[reproducible("", 123)]]; /* { dg-error "expected" } */
+				    /* { dg-error "'reproducible' attribute does not take any arguments" "" { target *-*-* } .-1 } */
+
+int d () [[reproducible((""))]]; /* { dg-error "expected" } */
+				 /* { dg-error "'reproducible' attribute does not take any arguments" "" { target *-*-* } .-1 } */
--- gcc/testsuite/gcc.dg/c23-attr-reproducible-4.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-4.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,12 @@ 
+/* Test C23 reproducible attribute: duplicates (allowed after N2557).  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+int a () [[reproducible, __reproducible__]];
+int b () [[__reproducible__, reproducible]];
+int c () [[reproducible, reproducible]];
+int d () [[__reproducible__, __reproducible__]];
+int d () [[reproducible]];
+int d () [[__reproducible__]];
+[[reproducible, reproducible]];
+/* { dg-error "ignored" "ignored" { target *-*-* } .-1 } */
--- gcc/testsuite/gcc.dg/c23-attr-reproducible-5.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-5.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,44 @@ 
+/* Test C23 reproducible attribute: composite type on ?:.  */
+/* { dg-do run } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+/* { dg-additional-sources "c23-attr-reproducible-6.c" } */
+
+int f1 () [[reproducible]];
+int f2 ();
+int f3 ();
+int (*fp1) () [[reproducible]] = f2;
+int (*fp2) () [[reproducible]] = f3;
+extern void abort ();
+
+int
+foo (int x)
+{
+  return __builtin_has_attribute (*(x ? f1 : f3), reproducible);
+}
+
+int
+bar (int x)
+{
+  return __builtin_has_attribute (*(x ? fp1 : fp2), reproducible);
+}
+
+int
+baz (int x)
+{
+  return __builtin_has_attribute (*(x ? f3 : f1), reproducible);
+}
+
+int
+qux (int x)
+{
+  return __builtin_has_attribute (*(x ? fp2 : fp1), reproducible);
+}
+
+int
+main ()
+{
+  if (!foo (0) || !bar (0) || !baz (0) || !qux (0))
+    abort ();
+  if (!foo (1) || !bar (1) || !baz (1) || !qux (1))
+    abort ();
+}
--- gcc/testsuite/gcc.dg/c23-attr-reproducible-6.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-6.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,21 @@ 
+/* Test C23 reproducible attribute: composite type on ?:.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+int
+f1 () [[reproducible]]
+{
+  return 42;
+}
+
+int
+f2 ()
+{
+  return 43;
+}
+
+int
+f3 ()
+{
+  return 44;
+}
--- gcc/testsuite/gcc.dg/c23-attr-unsequenced-1.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-1.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,81 @@ 
+/* Test C23 unsequenced attribute: valid uses.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors -O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f12 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f13 \\\(\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
+/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 1 "optimized" } } */
+
+int f1 () [[unsequenced]];
+int f2 () [[unsequenced]], f3 (int) [[__unsequenced__]];
+int f4 (int, int *restrict) [[unsequenced]];
+int f5 (int) [[unsequenced]];
+int f6 (int);
+int (*fp1) (int) [[unsequenced]] = f6;
+typedef int ft1 (int) [[unsequenced]];
+typedef int ft2 (int);
+extern typeof (f6) [[unsequenced]] f7;
+extern ft2 [[__unsequenced__]] f8;
+int f1 ();
+int f9 (int);
+int f9 (int) [[__unsequenced__]];
+extern int x;
+
+int
+f10 (int x) [[unsequenced]]
+{
+  return x + 42;
+}
+
+int
+f11 (int *restrict x, long long y[restrict static 1], int z) [[__unsequenced__]]
+{
+  x[0] = z;
+  x[1] = z + 1;
+  x[2] = z + 2;
+  *y = z + 3;
+  return z + 4 + f10 (-42);
+}
+
+int f12 () [[unsequenced]];
+int f12 () [[reproducible]];
+int f13 () [[reproducible]];
+int f13 () [[unsequenced]];
+
+int
+g ()
+{
+  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int e = fp1 (14) + fp1 (14);
+  x++;
+  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + f12 () + f13 ();
+  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
+  int j = fp1 (14) + fp1 (14);
+  return a + b + c + d + e + f + g + h + i + j;
+}
+
+int
+h ()
+{
+  f3 (52);
+  f3 (52);
+  f3 (52);
+  return 0;
+}
--- gcc/testsuite/gcc.dg/c23-attr-unsequenced-2.c.jj	2024-08-01 14:37:23.948824359 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-2.c	2024-08-01 14:37:23.948824359 +0200
@@ -0,0 +1,47 @@ 
+/* Test C23 unsequenced attribute: invalid contexts.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+/* This attribute is not valid in most cases on types other than
+   type specifiers with function type or function declarators.  */
+
+[[unsequenced]]; /* { dg-error "ignored" } */
+
+int [[unsequenced]] var; /* { dg-error "ignored" } */
+
+int array_with_dep_type[2] [[unsequenced]]; /* { dg-error "ignored" } */
+
+[[unsequenced]] int fn1 (); /* { dg-error "standard 'unsequenced' attribute can only be applied to function declarators or type specifiers with function type" } */
+
+[[unsequenced]] int fn2 (), fn3 (); /* { dg-error "standard 'unsequenced' attribute can only be applied to function declarators or type specifiers with function type" } */
+
+int var2 [[unsequenced]]; /* { dg-warning "'unsequenced' attribute only applies to function types" } */
+
+int fn4 [[unsequenced]] (); /* { dg-error "standard 'unsequenced' attribute can only be applied to function declarators or type specifiers with function type" } */
+
+int [[unsequenced]] fn5 (); /* { dg-error "ignored" } */
+
+int z = sizeof (int [[__unsequenced__]]); /* { dg-error "ignored" } */
+
+/* This is valid, but not really useful, as it can't return results
+   in return type nor has any pointer arguments to store results into.  */
+void
+fn6 (int x, double y) [[unsequenced]]
+{ /* { dg-warning "unsequenced' attribute on function type without pointer arguments returning 'void'" } */
+  y = x;
+  (void) y;
+}
+
+void
+f (void)
+{
+  int a;
+  [[unsequenced]]; /* { dg-error "ignored" } */
+  [[unsequenced]] a = 1; /* { dg-error "ignored" } */
+  [[unsequenced]] label: ; /* { dg-warning "'unsequenced' attribute only applies to function types" } */
+  switch (var)
+    {
+    [[unsequenced]] case 1: ; /* { dg-warning "'unsequenced' attribute only applies to function types" } */
+    [[unsequenced]] default: ; /* { dg-warning "'unsequenced' attribute only applies to function types" } */
+    }
+}
--- gcc/testsuite/gcc.dg/c23-attr-unsequenced-3.c.jj	2024-08-01 14:37:23.949824346 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-3.c	2024-08-01 14:37:23.949824346 +0200
@@ -0,0 +1,14 @@ 
+/* Test C23 unsequenced attribute: invalid syntax.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+int a () [[unsequenced()]]; /* { dg-error "'unsequenced' attribute does not take any arguments" } */
+
+int b () [[unsequenced(0)]]; /* { dg-error "expected" } */
+			     /* { dg-error "'unsequenced' attribute does not take any arguments" "" { target *-*-* } .-1 } */
+
+int c () [[unsequenced("", 123)]]; /* { dg-error "expected" } */
+				   /* { dg-error "'unsequenced' attribute does not take any arguments" "" { target *-*-* } .-1 } */
+
+int d () [[unsequenced((""))]]; /* { dg-error "expected" } */
+				/* { dg-error "'unsequenced' attribute does not take any arguments" "" { target *-*-* } .-1 } */
--- gcc/testsuite/gcc.dg/c23-attr-unsequenced-4.c.jj	2024-08-01 14:37:23.949824346 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-4.c	2024-08-01 14:37:23.949824346 +0200
@@ -0,0 +1,12 @@ 
+/* Test C23 unsequenced attribute: duplicates (allowed after N2557).  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+int a () [[unsequenced, __unsequenced__]];
+int b () [[__unsequenced__, unsequenced]];
+int c () [[unsequenced, unsequenced]];
+int d () [[__unsequenced__, __unsequenced__]];
+int d () [[unsequenced]];
+int d () [[__unsequenced__]];
+[[unsequenced, unsequenced]];
+/* { dg-error "ignored" "ignored" { target *-*-* } .-1 } */
--- gcc/testsuite/gcc.dg/c23-attr-unsequenced-5.c.jj	2024-08-01 14:37:23.949824346 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-5.c	2024-08-01 14:37:23.949824346 +0200
@@ -0,0 +1,44 @@ 
+/* Test C23 unsequenced attribute: composite type on ?:.  */
+/* { dg-do run } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+/* { dg-additional-sources "c23-attr-unsequenced-6.c" } */
+
+int f1 () [[unsequenced]];
+int f2 ();
+int f3 ();
+int (*fp1) () [[unsequenced]] = f2;
+int (*fp2) () [[unsequenced]] = f3;
+extern void abort ();
+
+int
+foo (int x)
+{
+  return __builtin_has_attribute (*(x ? f1 : f3), unsequenced);
+}
+
+int
+bar (int x)
+{
+  return __builtin_has_attribute (*(x ? fp1 : fp2), unsequenced);
+}
+
+int
+baz (int x)
+{
+  return __builtin_has_attribute (*(x ? f3 : f1), unsequenced);
+}
+
+int
+qux (int x)
+{
+  return __builtin_has_attribute (*(x ? fp2 : fp1), unsequenced);
+}
+
+int
+main ()
+{
+  if (!foo (0) || !bar (0) || !baz (0) || !qux (0))
+    abort ();
+  if (!foo (1) || !bar (1) || !baz (1) || !qux (1))
+    abort ();
+}
--- gcc/testsuite/gcc.dg/c23-attr-unsequenced-6.c.jj	2024-08-01 14:37:23.949824346 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-6.c	2024-08-01 14:37:23.949824346 +0200
@@ -0,0 +1,21 @@ 
+/* Test C23 unsequenced attribute: composite type on ?:.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+int
+f1 () [[unsequenced]]
+{
+  return 42;
+}
+
+int
+f2 ()
+{
+  return 43;
+}
+
+int
+f3 ()
+{
+  return 44;
+}
--- gcc/testsuite/gcc.dg/c23-has-c-attribute-2.c.jj	2024-08-01 10:30:35.438802424 +0200
+++ gcc/testsuite/gcc.dg/c23-has-c-attribute-2.c	2024-08-01 14:37:24.355819220 +0200
@@ -50,6 +50,22 @@ 
 #error "bad result for ___Noreturn__"
 #endif
   
+#if __has_c_attribute (unsequenced) != 202311L
+#error "bad result for unsequenced"
+#endif
+
+#if __has_c_attribute (__unsequenced__) != 202311L
+#error "bad result for __unsequenced__"
+#endif
+
+#if __has_c_attribute (reproducible) != 202311L
+#error "bad result for reproducible"
+#endif
+
+#if __has_c_attribute (__reproducible__) != 202311L
+#error "bad result for __reproducible__"
+#endif
+
 /* Macros in the attribute name are expanded.  */
 #define foo deprecated
 #if __has_c_attribute (foo) != 202311L