diff mbox series

c: Add support for unsequenced and reproducible attributes

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

Commit Message

Jakub Jelinek July 30, 2024, 5:04 p.m. UTC
Hi!

C23 added in N2956 ( https://open-std.org/JTC1/SC22/WG14/www/docs/n2956.htm )
two new attributes, which are described as similar to GCC const and pure
attributes, but they aren't really same and it seems that even the paper
is missing some of the differences.
The paper says unsequenced is the same as const on functions without pointer
arguments and reproducible is the same as pure on such functions (except
that they are function type attributes rather than function
declaration ones), but it seems the paper doesn't consider the finiteness GCC
relies on (aka non-DECL_LOOPING_CONST_OR_PURE_P) - the paper only talks
about using the attributes for CSE etc., not for DCE.

The following patch introduces (for now limited) support for those
attributes, both as standard C23 attributes and as GNU extensions (the
difference is that the patch is then less strict on where it allows them,
like other function type attributes they can be specified on function
declarations as well and apply to the type, while C23 standard ones must
go on the function declarators (i.e. after closing paren after function
parameters) or in type specifiers of function type.

If function doesn't have any pointer/reference arguments (I wasn't sure
whether it must be really just pure pointer arguments or whether say
struct S { int s; int *p; } passed by value, or unions, or perhaps just
transparent unions count, and whether variadic functions which can take
pointer va_arg count too, so the check punts on all of those), the patch
adds additional internal attribute with " noptr" suffix which then is used
by flags_from_decl_or_type to handle those easy cases as
ECF_CONST|ECF_LOOPING_CONST_OR_PURE or
ECF_PURE|ECF_LOOPING_CONST_OR_PURE
The harder cases aren't handled right now, I'd hope they can be handled
incrementally.

I wonder whether we shouldn't emit a warning for the
gcc.dg/c23-attr-{reproducible,unsequenced}-5.c cases, while the standard
clearly specifies that composite types should union the attributes and it
is what GCC implements for decades, for ?: that feels dangerous for the
new attributes, it would be much better to be conservative on say
(cond ? unsequenced_function : normal_function) (args)

There is no diagnostics on incorrect [[unsequenced]] or [[reproducible]]
function definitions, while I think diagnosing non-const static/TLS
declarations in the former could be easy, the rest feels hard.  E.g. the
const/pure discovery can just punt on everything it doesn't understand,
but complete diagnostics would need to understand it.

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

2024-07-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.
	(c_maybe_contains_pointers_p): New function.
	(handle_unsequenced_attribute): Likewise.
	(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

Richard Biener July 31, 2024, 7:50 a.m. UTC | #1
On Tue, Jul 30, 2024 at 7:05 PM Jakub Jelinek <jakub@redhat.com> wrote:
>
> Hi!
>
> C23 added in N2956 ( https://open-std.org/JTC1/SC22/WG14/www/docs/n2956.htm )
> two new attributes, which are described as similar to GCC const and pure
> attributes, but they aren't really same and it seems that even the paper
> is missing some of the differences.
> The paper says unsequenced is the same as const on functions without pointer
> arguments and reproducible is the same as pure on such functions (except
> that they are function type attributes rather than function
> declaration ones), but it seems the paper doesn't consider the finiteness GCC
> relies on (aka non-DECL_LOOPING_CONST_OR_PURE_P) - the paper only talks
> about using the attributes for CSE etc., not for DCE.
>
> The following patch introduces (for now limited) support for those
> attributes, both as standard C23 attributes and as GNU extensions (the
> difference is that the patch is then less strict on where it allows them,
> like other function type attributes they can be specified on function
> declarations as well and apply to the type, while C23 standard ones must
> go on the function declarators (i.e. after closing paren after function
> parameters) or in type specifiers of function type.
>
> If function doesn't have any pointer/reference arguments (I wasn't sure
> whether it must be really just pure pointer arguments or whether say
> struct S { int s; int *p; } passed by value, or unions, or perhaps just
> transparent unions count, and whether variadic functions which can take
> pointer va_arg count too, so the check punts on all of those), the patch
> adds additional internal attribute with " noptr" suffix which then is used
> by flags_from_decl_or_type to handle those easy cases as
> ECF_CONST|ECF_LOOPING_CONST_OR_PURE or
> ECF_PURE|ECF_LOOPING_CONST_OR_PURE
> The harder cases aren't handled right now, I'd hope they can be handled
> incrementally.
>
> I wonder whether we shouldn't emit a warning for the
> gcc.dg/c23-attr-{reproducible,unsequenced}-5.c cases, while the standard
> clearly specifies that composite types should union the attributes and it
> is what GCC implements for decades, for ?: that feels dangerous for the
> new attributes, it would be much better to be conservative on say
> (cond ? unsequenced_function : normal_function) (args)
>
> There is no diagnostics on incorrect [[unsequenced]] or [[reproducible]]
> function definitions, while I think diagnosing non-const static/TLS
> declarations in the former could be easy, the rest feels hard.  E.g. the
> const/pure discovery can just punt on everything it doesn't understand,
> but complete diagnostics would need to understand it.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

I wonder if

int foo (uintrptr_t x) { *(int *)x = 1; return 1; }

is considered "noptr" by the standard but then by making a pointer out of
'x' invokes UB?

One more comment below.

> 2024-07-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.
>         (c_maybe_contains_pointers_p): New function.
>         (handle_unsequenced_attribute): Likewise.
>         (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.
>
> --- gcc/doc/extend.texi.jj      2024-07-29 09:16:15.159636903 +0200
> +++ gcc/doc/extend.texi 2024-07-30 15:59:01.158715051 +0200
> @@ -4024,6 +4024,68 @@ 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 (in the
> +declaration or through @code{va_arg} on variadic functions) 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/references or can store additional
> +results through those pointers/references.
> +
> +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.
> +
>  @cindex @code{retain} function attribute
>  @item retain
>  For ELF targets that support the GNU or FreeBSD OSABIs, this attribute
> --- gcc/calls.cc.jj     2024-07-18 09:20:31.624543703 +0200
> +++ gcc/calls.cc        2024-07-30 13:18:34.350571711 +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-07-29 13:21:55.917705757 +0200
> +++ gcc/c-family/c-attribs.cc   2024-07-29 19:40:46.020519425 +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,101 @@ handle_pure_attribute (tree *node, tree
>    return NULL_TREE;
>  }
>
> +/* Return true if type TYPE contains or may contain any data pointers
> +   (may in case of C++ dependent types).  */
> +
> +static bool
> +c_maybe_contains_pointers_p (tree type)
> +{
> +  switch (TREE_CODE (type))
> +    {
> +    case POINTER_TYPE:
> +    case REFERENCE_TYPE:
> +      return true;
> +
> +    case RECORD_TYPE:
> +    case UNION_TYPE:
> +    case QUAL_UNION_TYPE:
> +      if (COMPLETE_TYPE_P (type))
> +       {
> +         for (tree fields = TYPE_FIELDS (type); fields; fields = DECL_CHAIN (fields))
> +           if (TREE_CODE (fields) == FIELD_DECL
> +               && c_maybe_contains_pointers_p (TREE_TYPE (fields)))
> +             return true;
> +         return false;
> +       }
> +      return false;
> +
> +    case ARRAY_TYPE:
> +      return c_maybe_contains_pointers_p (TREE_TYPE (type));
> +
> +    case ENUMERAL_TYPE:
> +    case BOOLEAN_TYPE:
> +    case INTEGER_TYPE:
> +    case BITINT_TYPE:
> +    case REAL_TYPE:
> +    case NULLPTR_TYPE:
> +    case FIXED_POINT_TYPE:
> +    case COMPLEX_TYPE:
> +    case VECTOR_TYPE:
> +    case FUNCTION_TYPE:
> +    case METHOD_TYPE:
> +      return false;
> +
> +    default:
> +      return true;
> +    }
> +}
> +
> +/* 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 (argtype == void_list_node)

I think this warrants a comment that the attribute on variadic functions
is treated as receiving pointers.

> +      {
> +       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;

shouldn't you set *no_add_attrs also when the noptr attribute is
already there?  Because otherwise you'll get the non-noptr attr added?

> +           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;
> +      }
> +    else if (c_maybe_contains_pointers_p (TREE_VALUE (argtype)))
> +      break;
> +  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-07-24 15:47:15.122478781 +0200
> +++ gcc/c-family/c-common.h     2024-07-29 19:03:55.569066565 +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-07-29 13:21:55.935705521 +0200
> +++ gcc/c-family/c-lex.cc       2024-07-29 17:14:49.393945048 +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-07-29 13:21:55.943705415 +0200
> +++ gcc/c/c-decl.cc     2024-07-30 12:46:40.166828455 +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-07-24 17:55:57.388097924 +0200
> +++ gcc/c/c-parser.cc   2024-07-30 11:22:32.915821271 +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-07-24 15:47:15.129478691 +0200
> +++ gcc/c/c-tree.h      2024-07-30 11:22:04.963197642 +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-07-30 13:32:10.090796441 +0200
> +++ gcc/testsuite/c-c++-common/attr-reproducible-1.c    2024-07-30 13:43:41.088667213 +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-07-30 13:58:38.433861263 +0200
> +++ gcc/testsuite/c-c++-common/attr-reproducible-2.c    2024-07-30 14:26:47.293852999 +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-07-30 13:29:39.757782214 +0200
> +++ gcc/testsuite/c-c++-common/attr-unsequenced-1.c     2024-07-30 13:44:47.992783260 +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-07-30 13:45:58.221855370 +0200
> +++ gcc/testsuite/c-c++-common/attr-unsequenced-2.c     2024-07-30 14:03:45.395843298 +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-07-30 10:39:09.790803085 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-1.c      2024-07-30 13:19:09.908102024 +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-07-30 10:39:12.632764961 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-2.c      2024-07-30 13:09:35.803687498 +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-07-30 10:39:15.313728999 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-3.c      2024-07-30 13:10:14.556175440 +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-07-30 10:39:18.168690708 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-4.c      2024-07-30 13:10:26.005024168 +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-07-30 14:44:26.750009364 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-5.c      2024-07-30 14:54:04.312467597 +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-07-30 14:50:39.070147636 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-6.c      2024-07-30 14:51:28.339504281 +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-07-30 10:39:09.790803085 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-1.c       2024-07-30 13:07:48.585104231 +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-07-30 10:39:12.632764961 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-2.c       2024-07-30 13:04:18.566879305 +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-07-30 10:39:15.313728999 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-3.c       2024-07-30 12:59:47.146465073 +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-07-30 10:39:18.168690708 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-4.c       2024-07-30 12:45:58.298379802 +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-07-30 14:44:26.750009364 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-5.c       2024-07-30 14:53:55.734579606 +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-07-30 14:50:39.070147636 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-6.c       2024-07-30 14:52:50.354433341 +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     2023-11-09 09:04:19.505530809 +0100
> +++ gcc/testsuite/gcc.dg/c23-has-c-attribute-2.c        2024-07-30 09:52:19.525586926 +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
>
Jakub Jelinek July 31, 2024, 8:19 a.m. UTC | #2
On Wed, Jul 31, 2024 at 09:50:56AM +0200, Richard Biener wrote:
> I wonder if
> 
> int foo (uintrptr_t x) { *(int *)x = 1; return 1; }
> 
> is considered "noptr" by the standard but then by making a pointer out of
> 'x' invokes UB?

I don't know.  The paper claims same behavior as const for functions without
pointer/array arguments (but arrays decay to pointers); that claim is
most likely false because of the infinite loops, but still, I'm not sure
about the
struct S { int *p; };
int bar (struct S x) [[unsequenced]] { x.p[0] = 1; return 1; }
or
int baz (...) [[unsequenced]] { va_list ap; va_start (ap); int *p = va_arg (ap, int *); va_end (ap); *p = 1; return 1; }
or
typedef union { int *p; long long *q; } U __attribute__((transparent_union));
int qux (int x, U y) [[unsequenced]] { if (x) y.p[0] = 1; else y.q[1] = 2; return 3; }
etc. cases too.

> > +/* 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 (argtype == void_list_node)
> 
> I think this warrants a comment that the attribute on variadic functions
> is treated as receiving pointers.

Ok (though, iff we actually need to handle variadic functions that way).

> > +      {
> > +       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;
> 
> shouldn't you set *no_add_attrs also when the noptr attribute is
> already there?  Because otherwise you'll get the non-noptr attr added?

I think that isn't needed.

The reason for this (ugly) dance is to avoid building 2 separate
FUNCTION_TYPEs with build_type_attribute_variant, one with just
"* noptr" attribute and another with both.
If "* noptr" is already there, then just return NULL_TREE; without
*no_add_attrs = true; will in the caller try to lookup the attribute
and if it is found (and have same args, this one doesn't have any),
will not add anything further, otherwise it will add it and
build_type_attribute_variant.
Guess I should add a comment.

> > +           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;
> > +      }
> > +    else if (c_maybe_contains_pointers_p (TREE_VALUE (argtype)))
> > +      break;
> > +  return NULL_TREE;
> > +}

	Jakub
Jakub Jelinek July 31, 2024, 8:53 a.m. UTC | #3
On Wed, Jul 31, 2024 at 10:19:06AM +0200, Jakub Jelinek wrote:
> On Wed, Jul 31, 2024 at 09:50:56AM +0200, Richard Biener wrote:
> > I wonder if
> > 
> > int foo (uintrptr_t x) { *(int *)x = 1; return 1; }
> > 
> > is considered "noptr" by the standard but then by making a pointer out of
> > 'x' invokes UB?
> 
> I don't know.  The paper claims same behavior as const for functions without
> pointer/array arguments (but arrays decay to pointers); that claim is
> most likely false because of the infinite loops, but still, I'm not sure
> about the
> struct S { int *p; };
> int bar (struct S x) [[unsequenced]] { x.p[0] = 1; return 1; }
> or
> int baz (...) [[unsequenced]] { va_list ap; va_start (ap); int *p = va_arg (ap, int *); va_end (ap); *p = 1; return 1; }
> or
> typedef union { int *p; long long *q; } U __attribute__((transparent_union));
> int qux (int x, U y) [[unsequenced]] { if (x) y.p[0] = 1; else y.q[1] = 2; return 3; }
> etc. cases too.

And neither I'm sure about pointers to pointers and the like, so
int corge (int **x) [[unsequenced]] { *x[0] = 42; return 5; }

BTW, my reading of the idempotent property is that
int garply (int *restrict x) { *x += 2; return 1; }
would be invalid, because you can't then schedule another call immediately
after an existing one without changing the observable state of execution.
But
int freddy (int *restrict x) [[unsequenced]] { x[0] = 42; return x[1]; }
would be ok.  So, for the memory reachable from the passed in pointers, each
byte can be either stored or read but not both (at least when such store or
read has then observable side-effects).  Not really sure if we could use
that info in some alias oracle decisions.

	Jakub
diff mbox series

Patch

--- gcc/doc/extend.texi.jj	2024-07-29 09:16:15.159636903 +0200
+++ gcc/doc/extend.texi	2024-07-30 15:59:01.158715051 +0200
@@ -4024,6 +4024,68 @@  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 (in the
+declaration or through @code{va_arg} on variadic functions) 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/references or can store additional
+results through those pointers/references.
+
+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.
+
 @cindex @code{retain} function attribute
 @item retain
 For ELF targets that support the GNU or FreeBSD OSABIs, this attribute
--- gcc/calls.cc.jj	2024-07-18 09:20:31.624543703 +0200
+++ gcc/calls.cc	2024-07-30 13:18:34.350571711 +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-07-29 13:21:55.917705757 +0200
+++ gcc/c-family/c-attribs.cc	2024-07-29 19:40:46.020519425 +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,101 @@  handle_pure_attribute (tree *node, tree
   return NULL_TREE;
 }
 
+/* Return true if type TYPE contains or may contain any data pointers
+   (may in case of C++ dependent types).  */
+
+static bool
+c_maybe_contains_pointers_p (tree type)
+{
+  switch (TREE_CODE (type))
+    {
+    case POINTER_TYPE:
+    case REFERENCE_TYPE:
+      return true;
+
+    case RECORD_TYPE:
+    case UNION_TYPE:
+    case QUAL_UNION_TYPE:
+      if (COMPLETE_TYPE_P (type))
+	{
+	  for (tree fields = TYPE_FIELDS (type); fields; fields = DECL_CHAIN (fields))
+	    if (TREE_CODE (fields) == FIELD_DECL
+		&& c_maybe_contains_pointers_p (TREE_TYPE (fields)))
+	      return true;
+	  return false;
+	}
+      return false;
+
+    case ARRAY_TYPE:
+      return c_maybe_contains_pointers_p (TREE_TYPE (type));
+
+    case ENUMERAL_TYPE:
+    case BOOLEAN_TYPE:
+    case INTEGER_TYPE:
+    case BITINT_TYPE:
+    case REAL_TYPE:
+    case NULLPTR_TYPE:
+    case FIXED_POINT_TYPE:
+    case COMPLEX_TYPE:
+    case VECTOR_TYPE:
+    case FUNCTION_TYPE:
+    case METHOD_TYPE:
+      return false;
+
+    default:
+      return true;
+    }
+}
+
+/* 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 (argtype == void_list_node)
+      {
+	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;
+      }
+    else if (c_maybe_contains_pointers_p (TREE_VALUE (argtype)))
+      break;
+
+  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-07-24 15:47:15.122478781 +0200
+++ gcc/c-family/c-common.h	2024-07-29 19:03:55.569066565 +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-07-29 13:21:55.935705521 +0200
+++ gcc/c-family/c-lex.cc	2024-07-29 17:14:49.393945048 +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-07-29 13:21:55.943705415 +0200
+++ gcc/c/c-decl.cc	2024-07-30 12:46:40.166828455 +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-07-24 17:55:57.388097924 +0200
+++ gcc/c/c-parser.cc	2024-07-30 11:22:32.915821271 +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-07-24 15:47:15.129478691 +0200
+++ gcc/c/c-tree.h	2024-07-30 11:22:04.963197642 +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-07-30 13:32:10.090796441 +0200
+++ gcc/testsuite/c-c++-common/attr-reproducible-1.c	2024-07-30 13:43:41.088667213 +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-07-30 13:58:38.433861263 +0200
+++ gcc/testsuite/c-c++-common/attr-reproducible-2.c	2024-07-30 14:26:47.293852999 +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-07-30 13:29:39.757782214 +0200
+++ gcc/testsuite/c-c++-common/attr-unsequenced-1.c	2024-07-30 13:44:47.992783260 +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-07-30 13:45:58.221855370 +0200
+++ gcc/testsuite/c-c++-common/attr-unsequenced-2.c	2024-07-30 14:03:45.395843298 +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-07-30 10:39:09.790803085 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-1.c	2024-07-30 13:19:09.908102024 +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-07-30 10:39:12.632764961 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-2.c	2024-07-30 13:09:35.803687498 +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-07-30 10:39:15.313728999 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-3.c	2024-07-30 13:10:14.556175440 +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-07-30 10:39:18.168690708 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-4.c	2024-07-30 13:10:26.005024168 +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-07-30 14:44:26.750009364 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-5.c	2024-07-30 14:54:04.312467597 +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-07-30 14:50:39.070147636 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-reproducible-6.c	2024-07-30 14:51:28.339504281 +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-07-30 10:39:09.790803085 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-1.c	2024-07-30 13:07:48.585104231 +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-07-30 10:39:12.632764961 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-2.c	2024-07-30 13:04:18.566879305 +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-07-30 10:39:15.313728999 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-3.c	2024-07-30 12:59:47.146465073 +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-07-30 10:39:18.168690708 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-4.c	2024-07-30 12:45:58.298379802 +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-07-30 14:44:26.750009364 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-5.c	2024-07-30 14:53:55.734579606 +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-07-30 14:50:39.070147636 +0200
+++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-6.c	2024-07-30 14:52:50.354433341 +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	2023-11-09 09:04:19.505530809 +0100
+++ gcc/testsuite/gcc.dg/c23-has-c-attribute-2.c	2024-07-30 09:52:19.525586926 +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