Ping: [PATCH] Allow target attributes in non-gnu namespaces

Message ID mptwmtzcous.fsf@arm.com
State New
Headers
Series Ping: [PATCH] Allow target attributes in non-gnu namespaces |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Patch failed to apply

Commit Message

Richard Sandiford Nov. 30, 2023, 2:11 p.m. UTC
  Ping

Richard Sandiford <richard.sandiford@arm.com> writes:
> This is a ping+rebase of the patch below.  I've also optimised the
> handling of ignored attributes so that we don't register empty tables.
> There was also a typo in the jit changes (which I had tested, but the
> typo didn't seem to cause a failure).
>
> Retested on aarch64-linux-gnu & x86_64-linux-gnu.  The original was
> also tested on the full target list in config-list.mk.
>
> Iain has already approved the D parts (thanks!).  OK for the rest?
>
> And sorry to be pinging something when I'm behind on reviews myself...
>
> ---
>
> Currently there are four static sources of attributes:
>
> - LANG_HOOKS_ATTRIBUTE_TABLE
> - LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
> - LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE
> - TARGET_ATTRIBUTE_TABLE
>
> All of the attributes in these tables go in the "gnu" namespace.
> This means that they can use the traditional GNU __attribute__((...))
> syntax and the standard [[gnu::...]] syntax.
>
> Standard attributes are registered dynamically with a null namespace.
> There are no supported attributes in other namespaces (clang, vendor
> namespaces, etc.).
>
> This patch tries to generalise things by making the namespace
> part of the attribute specification.
>
> It's usual for multiple attributes to be defined in the same namespace,
> so rather than adding the namespace to each individual definition,
> it seemed better to group attributes in the same namespace together.
> This would also allow us to reuse the same table for clang attributes
> that are written with the GNU syntax, or other similar situations
> where the attribute can be accessed via multiple "spellings".
>
> The patch therefore adds a scoped_attribute_specs that contains
> a namespace and a list of attributes in that namespace.
>
> It's still possible to have multiple scoped_attribute_specs
> for the same namespace.  E.g. it makes sense to keep the
> C++-specific, C/C++-common, and format-related attributes in
> separate tables, even though they're all GNU attributes.
>
> Current lists of attributes are terminated by a null name.
> Rather than keep that for the new structure, it seemed neater
> to use an array_slice.  This also makes the tables slighly more
> compact.
>
> In general, a target might want to support attributes in multiple
> namespaces.  Rather than have a separate hook for each possibility
> (like the three langhooks above), it seemed better to make
> TARGET_ATTRIBUTE_TABLE a table of tables.  Specifically, it's
> an array_slice of scoped_attribute_specs.
>
> We can do the same thing for langhooks, which allows the three hooks
> above to be merged into a single LANG_HOOKS_ATTRIBUTE_TABLE.
> It also allows the standard attributes to be registered statically
> and checked by the usual attribs.cc checks.
>
> The patch adds a TARGET_GNU_ATTRIBUTES helper for the common case
> in which a target wants a single table of gnu attributes.  It can
> only be used if the table is free of preprocessor directives.
>
> There are probably other things we need to do to make vendor namespaces
> work smoothly.  E.g. in principle it would be good to make exclusion
> sets namespace-aware.  But to some extent we have that with standard
> vs. gnu attributes too.  This patch is just supposed to be a first step.

gcc/
	* attribs.h (scoped_attribute_specs): New structure.
	(register_scoped_attributes): Take a reference to a
	scoped_attribute_specs instead of separate namespace and array
	parameters.
	* plugin.h (register_scoped_attributes): Likewise.
	* attribs.cc (register_scoped_attributes): Likewise.
	(attribute_tables): Change into an array of scoped_attribute_specs
	pointers.  Reduce to 1 element for frontends and 1 element for targets.
	(empty_attribute_table): Delete.
	(check_attribute_tables): Update for changes to attribute_tables.
	Use a hash_set to identify duplicates.
	(handle_ignored_attributes_option): Update for above changes.
	(init_attributes): Likewise.
	(excl_pair): Delete.
	(test_attribute_exclusions): Update for above changes.  Don't
	enforce symmetry for standard attributes in the top-level namespace.
	* langhooks-def.h (LANG_HOOKS_COMMON_ATTRIBUTE_TABLE): Delete.
	(LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE): Likewise.
	(LANG_HOOKS_INITIALIZER): Update accordingly.
	(LANG_HOOKS_ATTRIBUTE_TABLE): Define to an empty constructor.
	* langhooks.h (lang_hooks::common_attribute_table): Delete.
	(lang_hooks::format_attribute_table): Likewise.
	(lang_hooks::attribute_table): Redefine to an array of
	scoped_attribute_specs pointers.
	* target-def.h (TARGET_GNU_ATTRIBUTES): New macro.
	* target.def (attribute_spec): Redefine to return an array of
	scoped_attribute_specs pointers.
	* tree-inline.cc (function_attribute_inlinable_p): Update accordingly.
	* doc/tm.texi: Regenerate.
	* config/aarch64/aarch64.cc (aarch64_attribute_table): Define using
	TARGET_GNU_ATTRIBUTES.
	* config/alpha/alpha.cc (vms_attribute_table): Likewise.
	* config/avr/avr.cc (avr_attribute_table): Likewise.
	* config/bfin/bfin.cc (bfin_attribute_table): Likewise.
	* config/bpf/bpf.cc (bpf_attribute_table): Likewise.
	* config/csky/csky.cc (csky_attribute_table): Likewise.
	* config/epiphany/epiphany.cc (epiphany_attribute_table): Likewise.
	* config/gcn/gcn.cc (gcn_attribute_table): Likewise.
	* config/h8300/h8300.cc (h8300_attribute_table): Likewise.
	* config/loongarch/loongarch.cc (loongarch_attribute_table): Likewise.
	* config/m32c/m32c.cc (m32c_attribute_table): Likewise.
	* config/m32r/m32r.cc (m32r_attribute_table): Likewise.
	* config/m68k/m68k.cc (m68k_attribute_table): Likewise.
	* config/mcore/mcore.cc (mcore_attribute_table): Likewise.
	* config/microblaze/microblaze.cc (microblaze_attribute_table):
	Likewise.
	* config/mips/mips.cc (mips_attribute_table): Likewise.
	* config/msp430/msp430.cc (msp430_attribute_table): Likewise.
	* config/nds32/nds32.cc (nds32_attribute_table): Likewise.
	* config/nvptx/nvptx.cc (nvptx_attribute_table): Likewise.
	* config/riscv/riscv.cc (riscv_attribute_table): Likewise.
	* config/rl78/rl78.cc (rl78_attribute_table): Likewise.
	* config/rx/rx.cc (rx_attribute_table): Likewise.
	* config/s390/s390.cc (s390_attribute_table): Likewise.
	* config/sh/sh.cc (sh_attribute_table): Likewise.
	* config/sparc/sparc.cc (sparc_attribute_table): Likewise.
	* config/stormy16/stormy16.cc (xstormy16_attribute_table): Likewise.
	* config/v850/v850.cc (v850_attribute_table): Likewise.
	* config/visium/visium.cc (visium_attribute_table): Likewise.
	* config/arc/arc.cc (arc_attribute_table): Likewise.  Move further
	down file.
	* config/arm/arm.cc (arm_attribute_table): Update for above changes,
	using...
	(arm_gnu_attributes, arm_gnu_attribute_table): ...these new globals.
	* config/i386/i386-options.h (ix86_attribute_table): Delete.
	(ix86_gnu_attribute_table): Declare.
	* config/i386/i386-options.cc (ix86_attribute_table): Replace with...
	(ix86_gnu_attributes, ix86_gnu_attribute_table): ...these two globals.
	* config/i386/i386.cc (ix86_attribute_table): Define as an array of
	scoped_attribute_specs pointers.
	* config/ia64/ia64.cc (ia64_attribute_table): Update for above changes,
	using...
	(ia64_gnu_attributes, ia64_gnu_attribute_table): ...these new globals.
	* config/rs6000/rs6000.cc (rs6000_attribute_table): Update for above
	changes, using...
	(rs6000_gnu_attributes, rs6000_gnu_attribute_table): ...these new
	globals.

gcc/ada/
	* gcc-interface/gigi.h (gnat_internal_attribute_table): Change
	type to scoped_attribute_specs.
	* gcc-interface/utils.cc (gnat_internal_attribute_table): Likewise,
	using...
	(gnat_internal_attributes): ...this as the underlying array.
	* gcc-interface/misc.cc (gnat_attribute_table): New global.
	(LANG_HOOKS_ATTRIBUTE_TABLE): Use it.

gcc/c-family/
	* c-common.h (c_common_attribute_table): Replace with...
	(c_common_gnu_attribute_table): ...this.
	(c_common_format_attribute_table): Change type to
	scoped_attribute_specs.
	* c-attribs.cc (c_common_attribute_table): Replace with...
	(c_common_gnu_attributes, c_common_gnu_attribute_table): ...these
	new globals.
	(c_common_format_attribute_table): Change type to
	scoped_attribute_specs, using...
	(c_common_format_attributes): ...this as the underlying array.

gcc/c/
	* c-tree.h (std_attribute_table): Declare.
	* c-decl.cc (std_attribute_table): Change type to
	scoped_attribute_specs, using...
	(std_attributes): ...this as the underlying array.
	(c_init_decl_processing): Remove call to register_scoped_attributes.
	* c-objc-common.h (c_objc_attribute_table): New global.
	(LANG_HOOKS_ATTRIBUTE_TABLE): Use it.
	(LANG_HOOKS_COMMON_ATTRIBUTE_TABLE): Delete.
	(LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE): Delete.

gcc/cp/
	* cp-tree.h (cxx_attribute_table): Delete.
	(cxx_gnu_attribute_table, std_attribute_table): Declare.
	* cp-objcp-common.h (LANG_HOOKS_COMMON_ATTRIBUTE_TABLE): Delete.
	(LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE): Delete.
	(cp_objcp_attribute_table): New table.
	(LANG_HOOKS_ATTRIBUTE_TABLE): Redefine.
	* tree.cc (cxx_attribute_table): Replace with...
	(cxx_gnu_attributes, cxx_gnu_attribute_table): ...these globals.
	(std_attribute_table): Change type to scoped_attribute_specs, using...
	(std_attributes): ...this as the underlying array.
	(init_tree): Remove call to register_scoped_attributes.

gcc/d/
	* d-tree.h (d_langhook_attribute_table): Replace with...
	(d_langhook_gnu_attribute_table): ...this.
	(d_langhook_common_attribute_table): Change type to
	scoped_attribute_specs.
	* d-attribs.cc (d_langhook_common_attribute_table): Change type to
	scoped_attribute_specs, using...
	(d_langhook_common_attributes): ...this as the underlying array.
	(d_langhook_attribute_table): Replace with...
	(d_langhook_gnu_attributes, d_langhook_gnu_attribute_table): ...these
	new globals.
	(uda_attribute_p): Update accordingly, and update for new
	targetm.attribute_table type.
	* d-lang.cc (d_langhook_attribute_table): New global.
	(LANG_HOOKS_COMMON_ATTRIBUTE_TABLE): Delete.

gcc/fortran/
	* f95-lang.cc: Include attribs.h.
	(gfc_attribute_table): Change to an array of scoped_attribute_specs
	pointers, using...
	(gfc_gnu_attributes, gfc_gnu_attribute_table): ...these new globals.

gcc/jit/
	* dummy-frontend.cc (jit_format_attribute_table): Change type to
	scoped_attribute_specs, using...
	(jit_format_attributes): ...this as the underlying array.
	(jit_attribute_table): Change to an array of scoped_attribute_specs
	pointers, using...
	(jit_gnu_attributes, jit_gnu_attribute_table): ...these new globals
	for the original array.  Include the format attributes.
	(LANG_HOOKS_COMMON_ATTRIBUTE_TABLE): Delete.
	(LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE): Delete.
	(LANG_HOOKS_ATTRIBUTE_TABLE): Define.

gcc/lto/
	* lto-lang.cc (lto_format_attribute_table): Change type to
	scoped_attribute_specs, using...
	(lto_format_attributes): ...this as the underlying array.
	(lto_attribute_table): Change to an array of scoped_attribute_specs
	pointers, using...
	(lto_gnu_attributes, lto_gnu_attribute_table): ...these new globals
	for the original array.  Include the format attributes.
	(LANG_HOOKS_COMMON_ATTRIBUTE_TABLE): Delete.
	(LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE): Delete.
	(LANG_HOOKS_ATTRIBUTE_TABLE): Define.
---
 gcc/ada/gcc-interface/gigi.h        |   2 +-
 gcc/ada/gcc-interface/misc.cc       |   7 +-
 gcc/ada/gcc-interface/utils.cc      |   8 +-
 gcc/attribs.cc                      | 221 ++++++++++++----------------
 gcc/attribs.h                       |  12 +-
 gcc/c-family/c-attribs.cc           |  20 ++-
 gcc/c-family/c-common.h             |   4 +-
 gcc/c/c-decl.cc                     |  12 +-
 gcc/c/c-objc-common.h               |  14 +-
 gcc/c/c-tree.h                      |   2 +
 gcc/config/aarch64/aarch64.cc       |   7 +-
 gcc/config/alpha/alpha.cc           |   7 +-
 gcc/config/arc/arc.cc               |  74 +++++-----
 gcc/config/arm/arm.cc               |  15 +-
 gcc/config/avr/avr.cc               |   7 +-
 gcc/config/bfin/bfin.cc             |   7 +-
 gcc/config/bpf/bpf.cc               |   9 +-
 gcc/config/csky/csky.cc             |   7 +-
 gcc/config/epiphany/epiphany.cc     |   7 +-
 gcc/config/gcn/gcn.cc               |   8 +-
 gcc/config/h8300/h8300.cc           |   7 +-
 gcc/config/i386/i386-options.cc     |  10 +-
 gcc/config/i386/i386-options.h      |   2 +-
 gcc/config/i386/i386.cc             |   5 +
 gcc/config/ia64/ia64.cc             |  15 +-
 gcc/config/loongarch/loongarch.cc   |   8 +-
 gcc/config/m32c/m32c.cc             |   7 +-
 gcc/config/m32r/m32r.cc             |   7 +-
 gcc/config/m68k/m68k.cc             |   7 +-
 gcc/config/mcore/mcore.cc           |   7 +-
 gcc/config/microblaze/microblaze.cc |   7 +-
 gcc/config/mips/mips.cc             |   7 +-
 gcc/config/msp430/msp430.cc         |   8 +-
 gcc/config/nds32/nds32.cc           |   9 +-
 gcc/config/nvptx/nvptx.cc           |   7 +-
 gcc/config/riscv/riscv.cc           |   9 +-
 gcc/config/rl78/rl78.cc             |   7 +-
 gcc/config/rs6000/rs6000.cc         |  13 +-
 gcc/config/rx/rx.cc                 |   7 +-
 gcc/config/s390/s390.cc             |   9 +-
 gcc/config/sh/sh.cc                 |   7 +-
 gcc/config/sparc/sparc.cc           |   7 +-
 gcc/config/stormy16/stormy16.cc     |   7 +-
 gcc/config/v850/v850.cc             |   7 +-
 gcc/config/visium/visium.cc         |   7 +-
 gcc/cp/cp-objcp-common.h            |  15 +-
 gcc/cp/cp-tree.h                    |   3 +-
 gcc/cp/tree.cc                      |  16 +-
 gcc/d/d-attribs.cc                  |  35 ++---
 gcc/d/d-lang.cc                     |   8 +-
 gcc/d/d-tree.h                      |   4 +-
 gcc/doc/tm.texi                     |  33 ++++-
 gcc/fortran/f95-lang.cc             |  14 +-
 gcc/jit/dummy-frontend.cc           |  32 ++--
 gcc/langhooks-def.h                 |   6 +-
 gcc/langhooks.h                     |   4 +-
 gcc/lto/lto-lang.cc                 |  30 ++--
 gcc/plugin.h                        |   3 +-
 gcc/target-def.h                    |  14 ++
 gcc/target.def                      |  35 ++++-
 gcc/tree-inline.cc                  |   7 +-
 61 files changed, 494 insertions(+), 408 deletions(-)
  

Comments

Richard Biener Dec. 1, 2023, 7:13 a.m. UTC | #1
On Thu, Nov 30, 2023 at 3:12 PM Richard Sandiford
<richard.sandiford@arm.com> wrote:
>
> Ping

OK.

Thanks,
Richard.

> Richard Sandiford <richard.sandiford@arm.com> writes:
> > This is a ping+rebase of the patch below.  I've also optimised the
> > handling of ignored attributes so that we don't register empty tables.
> > There was also a typo in the jit changes (which I had tested, but the
> > typo didn't seem to cause a failure).
> >
> > Retested on aarch64-linux-gnu & x86_64-linux-gnu.  The original was
> > also tested on the full target list in config-list.mk.
> >
> > Iain has already approved the D parts (thanks!).  OK for the rest?
> >
> > And sorry to be pinging something when I'm behind on reviews myself...
> >
> > ---
> >
> > Currently there are four static sources of attributes:
> >
> > - LANG_HOOKS_ATTRIBUTE_TABLE
> > - LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
> > - LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE
> > - TARGET_ATTRIBUTE_TABLE
> >
> > All of the attributes in these tables go in the "gnu" namespace.
> > This means that they can use the traditional GNU __attribute__((...))
> > syntax and the standard [[gnu::...]] syntax.
> >
> > Standard attributes are registered dynamically with a null namespace.
> > There are no supported attributes in other namespaces (clang, vendor
> > namespaces, etc.).
> >
> > This patch tries to generalise things by making the namespace
> > part of the attribute specification.
> >
> > It's usual for multiple attributes to be defined in the same namespace,
> > so rather than adding the namespace to each individual definition,
> > it seemed better to group attributes in the same namespace together.
> > This would also allow us to reuse the same table for clang attributes
> > that are written with the GNU syntax, or other similar situations
> > where the attribute can be accessed via multiple "spellings".
> >
> > The patch therefore adds a scoped_attribute_specs that contains
> > a namespace and a list of attributes in that namespace.
> >
> > It's still possible to have multiple scoped_attribute_specs
> > for the same namespace.  E.g. it makes sense to keep the
> > C++-specific, C/C++-common, and format-related attributes in
> > separate tables, even though they're all GNU attributes.
> >
> > Current lists of attributes are terminated by a null name.
> > Rather than keep that for the new structure, it seemed neater
> > to use an array_slice.  This also makes the tables slighly more
> > compact.
> >
> > In general, a target might want to support attributes in multiple
> > namespaces.  Rather than have a separate hook for each possibility
> > (like the three langhooks above), it seemed better to make
> > TARGET_ATTRIBUTE_TABLE a table of tables.  Specifically, it's
> > an array_slice of scoped_attribute_specs.
> >
> > We can do the same thing for langhooks, which allows the three hooks
> > above to be merged into a single LANG_HOOKS_ATTRIBUTE_TABLE.
> > It also allows the standard attributes to be registered statically
> > and checked by the usual attribs.cc checks.
> >
> > The patch adds a TARGET_GNU_ATTRIBUTES helper for the common case
> > in which a target wants a single table of gnu attributes.  It can
> > only be used if the table is free of preprocessor directives.
> >
> > There are probably other things we need to do to make vendor namespaces
> > work smoothly.  E.g. in principle it would be good to make exclusion
> > sets namespace-aware.  But to some extent we have that with standard
> > vs. gnu attributes too.  This patch is just supposed to be a first step.
>
> gcc/
>         * attribs.h (scoped_attribute_specs): New structure.
>         (register_scoped_attributes): Take a reference to a
>         scoped_attribute_specs instead of separate namespace and array
>         parameters.
>         * plugin.h (register_scoped_attributes): Likewise.
>         * attribs.cc (register_scoped_attributes): Likewise.
>         (attribute_tables): Change into an array of scoped_attribute_specs
>         pointers.  Reduce to 1 element for frontends and 1 element for targets.
>         (empty_attribute_table): Delete.
>         (check_attribute_tables): Update for changes to attribute_tables.
>         Use a hash_set to identify duplicates.
>         (handle_ignored_attributes_option): Update for above changes.
>         (init_attributes): Likewise.
>         (excl_pair): Delete.
>         (test_attribute_exclusions): Update for above changes.  Don't
>         enforce symmetry for standard attributes in the top-level namespace.
>         * langhooks-def.h (LANG_HOOKS_COMMON_ATTRIBUTE_TABLE): Delete.
>         (LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE): Likewise.
>         (LANG_HOOKS_INITIALIZER): Update accordingly.
>         (LANG_HOOKS_ATTRIBUTE_TABLE): Define to an empty constructor.
>         * langhooks.h (lang_hooks::common_attribute_table): Delete.
>         (lang_hooks::format_attribute_table): Likewise.
>         (lang_hooks::attribute_table): Redefine to an array of
>         scoped_attribute_specs pointers.
>         * target-def.h (TARGET_GNU_ATTRIBUTES): New macro.
>         * target.def (attribute_spec): Redefine to return an array of
>         scoped_attribute_specs pointers.
>         * tree-inline.cc (function_attribute_inlinable_p): Update accordingly.
>         * doc/tm.texi: Regenerate.
>         * config/aarch64/aarch64.cc (aarch64_attribute_table): Define using
>         TARGET_GNU_ATTRIBUTES.
>         * config/alpha/alpha.cc (vms_attribute_table): Likewise.
>         * config/avr/avr.cc (avr_attribute_table): Likewise.
>         * config/bfin/bfin.cc (bfin_attribute_table): Likewise.
>         * config/bpf/bpf.cc (bpf_attribute_table): Likewise.
>         * config/csky/csky.cc (csky_attribute_table): Likewise.
>         * config/epiphany/epiphany.cc (epiphany_attribute_table): Likewise.
>         * config/gcn/gcn.cc (gcn_attribute_table): Likewise.
>         * config/h8300/h8300.cc (h8300_attribute_table): Likewise.
>         * config/loongarch/loongarch.cc (loongarch_attribute_table): Likewise.
>         * config/m32c/m32c.cc (m32c_attribute_table): Likewise.
>         * config/m32r/m32r.cc (m32r_attribute_table): Likewise.
>         * config/m68k/m68k.cc (m68k_attribute_table): Likewise.
>         * config/mcore/mcore.cc (mcore_attribute_table): Likewise.
>         * config/microblaze/microblaze.cc (microblaze_attribute_table):
>         Likewise.
>         * config/mips/mips.cc (mips_attribute_table): Likewise.
>         * config/msp430/msp430.cc (msp430_attribute_table): Likewise.
>         * config/nds32/nds32.cc (nds32_attribute_table): Likewise.
>         * config/nvptx/nvptx.cc (nvptx_attribute_table): Likewise.
>         * config/riscv/riscv.cc (riscv_attribute_table): Likewise.
>         * config/rl78/rl78.cc (rl78_attribute_table): Likewise.
>         * config/rx/rx.cc (rx_attribute_table): Likewise.
>         * config/s390/s390.cc (s390_attribute_table): Likewise.
>         * config/sh/sh.cc (sh_attribute_table): Likewise.
>         * config/sparc/sparc.cc (sparc_attribute_table): Likewise.
>         * config/stormy16/stormy16.cc (xstormy16_attribute_table): Likewise.
>         * config/v850/v850.cc (v850_attribute_table): Likewise.
>         * config/visium/visium.cc (visium_attribute_table): Likewise.
>         * config/arc/arc.cc (arc_attribute_table): Likewise.  Move further
>         down file.
>         * config/arm/arm.cc (arm_attribute_table): Update for above changes,
>         using...
>         (arm_gnu_attributes, arm_gnu_attribute_table): ...these new globals.
>         * config/i386/i386-options.h (ix86_attribute_table): Delete.
>         (ix86_gnu_attribute_table): Declare.
>         * config/i386/i386-options.cc (ix86_attribute_table): Replace with...
>         (ix86_gnu_attributes, ix86_gnu_attribute_table): ...these two globals.
>         * config/i386/i386.cc (ix86_attribute_table): Define as an array of
>         scoped_attribute_specs pointers.
>         * config/ia64/ia64.cc (ia64_attribute_table): Update for above changes,
>         using...
>         (ia64_gnu_attributes, ia64_gnu_attribute_table): ...these new globals.
>         * config/rs6000/rs6000.cc (rs6000_attribute_table): Update for above
>         changes, using...
>         (rs6000_gnu_attributes, rs6000_gnu_attribute_table): ...these new
>         globals.
>
> gcc/ada/
>         * gcc-interface/gigi.h (gnat_internal_attribute_table): Change
>         type to scoped_attribute_specs.
>         * gcc-interface/utils.cc (gnat_internal_attribute_table): Likewise,
>         using...
>         (gnat_internal_attributes): ...this as the underlying array.
>         * gcc-interface/misc.cc (gnat_attribute_table): New global.
>         (LANG_HOOKS_ATTRIBUTE_TABLE): Use it.
>
> gcc/c-family/
>         * c-common.h (c_common_attribute_table): Replace with...
>         (c_common_gnu_attribute_table): ...this.
>         (c_common_format_attribute_table): Change type to
>         scoped_attribute_specs.
>         * c-attribs.cc (c_common_attribute_table): Replace with...
>         (c_common_gnu_attributes, c_common_gnu_attribute_table): ...these
>         new globals.
>         (c_common_format_attribute_table): Change type to
>         scoped_attribute_specs, using...
>         (c_common_format_attributes): ...this as the underlying array.
>
> gcc/c/
>         * c-tree.h (std_attribute_table): Declare.
>         * c-decl.cc (std_attribute_table): Change type to
>         scoped_attribute_specs, using...
>         (std_attributes): ...this as the underlying array.
>         (c_init_decl_processing): Remove call to register_scoped_attributes.
>         * c-objc-common.h (c_objc_attribute_table): New global.
>         (LANG_HOOKS_ATTRIBUTE_TABLE): Use it.
>         (LANG_HOOKS_COMMON_ATTRIBUTE_TABLE): Delete.
>         (LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE): Delete.
>
> gcc/cp/
>         * cp-tree.h (cxx_attribute_table): Delete.
>         (cxx_gnu_attribute_table, std_attribute_table): Declare.
>         * cp-objcp-common.h (LANG_HOOKS_COMMON_ATTRIBUTE_TABLE): Delete.
>         (LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE): Delete.
>         (cp_objcp_attribute_table): New table.
>         (LANG_HOOKS_ATTRIBUTE_TABLE): Redefine.
>         * tree.cc (cxx_attribute_table): Replace with...
>         (cxx_gnu_attributes, cxx_gnu_attribute_table): ...these globals.
>         (std_attribute_table): Change type to scoped_attribute_specs, using...
>         (std_attributes): ...this as the underlying array.
>         (init_tree): Remove call to register_scoped_attributes.
>
> gcc/d/
>         * d-tree.h (d_langhook_attribute_table): Replace with...
>         (d_langhook_gnu_attribute_table): ...this.
>         (d_langhook_common_attribute_table): Change type to
>         scoped_attribute_specs.
>         * d-attribs.cc (d_langhook_common_attribute_table): Change type to
>         scoped_attribute_specs, using...
>         (d_langhook_common_attributes): ...this as the underlying array.
>         (d_langhook_attribute_table): Replace with...
>         (d_langhook_gnu_attributes, d_langhook_gnu_attribute_table): ...these
>         new globals.
>         (uda_attribute_p): Update accordingly, and update for new
>         targetm.attribute_table type.
>         * d-lang.cc (d_langhook_attribute_table): New global.
>         (LANG_HOOKS_COMMON_ATTRIBUTE_TABLE): Delete.
>
> gcc/fortran/
>         * f95-lang.cc: Include attribs.h.
>         (gfc_attribute_table): Change to an array of scoped_attribute_specs
>         pointers, using...
>         (gfc_gnu_attributes, gfc_gnu_attribute_table): ...these new globals.
>
> gcc/jit/
>         * dummy-frontend.cc (jit_format_attribute_table): Change type to
>         scoped_attribute_specs, using...
>         (jit_format_attributes): ...this as the underlying array.
>         (jit_attribute_table): Change to an array of scoped_attribute_specs
>         pointers, using...
>         (jit_gnu_attributes, jit_gnu_attribute_table): ...these new globals
>         for the original array.  Include the format attributes.
>         (LANG_HOOKS_COMMON_ATTRIBUTE_TABLE): Delete.
>         (LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE): Delete.
>         (LANG_HOOKS_ATTRIBUTE_TABLE): Define.
>
> gcc/lto/
>         * lto-lang.cc (lto_format_attribute_table): Change type to
>         scoped_attribute_specs, using...
>         (lto_format_attributes): ...this as the underlying array.
>         (lto_attribute_table): Change to an array of scoped_attribute_specs
>         pointers, using...
>         (lto_gnu_attributes, lto_gnu_attribute_table): ...these new globals
>         for the original array.  Include the format attributes.
>         (LANG_HOOKS_COMMON_ATTRIBUTE_TABLE): Delete.
>         (LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE): Delete.
>         (LANG_HOOKS_ATTRIBUTE_TABLE): Define.
> ---
>  gcc/ada/gcc-interface/gigi.h        |   2 +-
>  gcc/ada/gcc-interface/misc.cc       |   7 +-
>  gcc/ada/gcc-interface/utils.cc      |   8 +-
>  gcc/attribs.cc                      | 221 ++++++++++++----------------
>  gcc/attribs.h                       |  12 +-
>  gcc/c-family/c-attribs.cc           |  20 ++-
>  gcc/c-family/c-common.h             |   4 +-
>  gcc/c/c-decl.cc                     |  12 +-
>  gcc/c/c-objc-common.h               |  14 +-
>  gcc/c/c-tree.h                      |   2 +
>  gcc/config/aarch64/aarch64.cc       |   7 +-
>  gcc/config/alpha/alpha.cc           |   7 +-
>  gcc/config/arc/arc.cc               |  74 +++++-----
>  gcc/config/arm/arm.cc               |  15 +-
>  gcc/config/avr/avr.cc               |   7 +-
>  gcc/config/bfin/bfin.cc             |   7 +-
>  gcc/config/bpf/bpf.cc               |   9 +-
>  gcc/config/csky/csky.cc             |   7 +-
>  gcc/config/epiphany/epiphany.cc     |   7 +-
>  gcc/config/gcn/gcn.cc               |   8 +-
>  gcc/config/h8300/h8300.cc           |   7 +-
>  gcc/config/i386/i386-options.cc     |  10 +-
>  gcc/config/i386/i386-options.h      |   2 +-
>  gcc/config/i386/i386.cc             |   5 +
>  gcc/config/ia64/ia64.cc             |  15 +-
>  gcc/config/loongarch/loongarch.cc   |   8 +-
>  gcc/config/m32c/m32c.cc             |   7 +-
>  gcc/config/m32r/m32r.cc             |   7 +-
>  gcc/config/m68k/m68k.cc             |   7 +-
>  gcc/config/mcore/mcore.cc           |   7 +-
>  gcc/config/microblaze/microblaze.cc |   7 +-
>  gcc/config/mips/mips.cc             |   7 +-
>  gcc/config/msp430/msp430.cc         |   8 +-
>  gcc/config/nds32/nds32.cc           |   9 +-
>  gcc/config/nvptx/nvptx.cc           |   7 +-
>  gcc/config/riscv/riscv.cc           |   9 +-
>  gcc/config/rl78/rl78.cc             |   7 +-
>  gcc/config/rs6000/rs6000.cc         |  13 +-
>  gcc/config/rx/rx.cc                 |   7 +-
>  gcc/config/s390/s390.cc             |   9 +-
>  gcc/config/sh/sh.cc                 |   7 +-
>  gcc/config/sparc/sparc.cc           |   7 +-
>  gcc/config/stormy16/stormy16.cc     |   7 +-
>  gcc/config/v850/v850.cc             |   7 +-
>  gcc/config/visium/visium.cc         |   7 +-
>  gcc/cp/cp-objcp-common.h            |  15 +-
>  gcc/cp/cp-tree.h                    |   3 +-
>  gcc/cp/tree.cc                      |  16 +-
>  gcc/d/d-attribs.cc                  |  35 ++---
>  gcc/d/d-lang.cc                     |   8 +-
>  gcc/d/d-tree.h                      |   4 +-
>  gcc/doc/tm.texi                     |  33 ++++-
>  gcc/fortran/f95-lang.cc             |  14 +-
>  gcc/jit/dummy-frontend.cc           |  32 ++--
>  gcc/langhooks-def.h                 |   6 +-
>  gcc/langhooks.h                     |   4 +-
>  gcc/lto/lto-lang.cc                 |  30 ++--
>  gcc/plugin.h                        |   3 +-
>  gcc/target-def.h                    |  14 ++
>  gcc/target.def                      |  35 ++++-
>  gcc/tree-inline.cc                  |   7 +-
>  61 files changed, 494 insertions(+), 408 deletions(-)
>
> diff --git a/gcc/ada/gcc-interface/gigi.h b/gcc/ada/gcc-interface/gigi.h
> index eb5496f50db..63ccf311c23 100644
> --- a/gcc/ada/gcc-interface/gigi.h
> +++ b/gcc/ada/gcc-interface/gigi.h
> @@ -350,7 +350,7 @@ struct attrib
>  };
>
>  /* Table of machine-independent internal attributes.  */
> -extern const struct attribute_spec gnat_internal_attribute_table[];
> +extern const struct scoped_attribute_specs gnat_internal_attribute_table;
>
>  /* Define the entries in the standard data array.  */
>  enum standard_datatypes
> diff --git a/gcc/ada/gcc-interface/misc.cc b/gcc/ada/gcc-interface/misc.cc
> index 7d6d4466d56..01e8267f884 100644
> --- a/gcc/ada/gcc-interface/misc.cc
> +++ b/gcc/ada/gcc-interface/misc.cc
> @@ -1352,6 +1352,11 @@ get_lang_specific (tree node)
>    return TYPE_LANG_SPECIFIC (node);
>  }
>
> +const struct scoped_attribute_specs *const gnat_attribute_table[] =
> +{
> +  &gnat_internal_attribute_table
> +};
> +
>  /* Definitions for our language-specific hooks.  */
>
>  #undef  LANG_HOOKS_NAME
> @@ -1417,7 +1422,7 @@ get_lang_specific (tree node)
>  #undef  LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO
>  #define LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO gnat_get_fixed_point_type_info
>  #undef  LANG_HOOKS_ATTRIBUTE_TABLE
> -#define LANG_HOOKS_ATTRIBUTE_TABLE     gnat_internal_attribute_table
> +#define LANG_HOOKS_ATTRIBUTE_TABLE     gnat_attribute_table
>  #undef  LANG_HOOKS_BUILTIN_FUNCTION
>  #define LANG_HOOKS_BUILTIN_FUNCTION    gnat_builtin_function
>  #undef  LANG_HOOKS_INIT_TS
> diff --git a/gcc/ada/gcc-interface/utils.cc b/gcc/ada/gcc-interface/utils.cc
> index 4e2ed173fbe..c38e0e6f57d 100644
> --- a/gcc/ada/gcc-interface/utils.cc
> +++ b/gcc/ada/gcc-interface/utils.cc
> @@ -136,7 +136,7 @@ static tree fake_attribute_handler (tree *, tree, tree, int, bool *);
>
>  /* Table of machine-independent internal attributes for Ada.  We support
>     this minimal set of attributes to accommodate the needs of builtins.  */
> -const struct attribute_spec gnat_internal_attribute_table[] =
> +static const attribute_spec gnat_internal_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -217,9 +217,11 @@ const struct attribute_spec gnat_internal_attribute_table[] =
>    /* This is handled entirely in the front end.  */
>    { "hardbool",     0, 0,  false, true, false, true,
>      fake_attribute_handler, NULL },
> +};
>
> -  { NULL,           0, 0,  false, false, false, false,
> -    NULL, NULL }
> +const scoped_attribute_specs gnat_internal_attribute_table =
> +{
> +  "gnu", gnat_internal_attributes
>  };
>
>  /* Associates a GNAT tree node to a GCC tree node. It is used in
> diff --git a/gcc/attribs.cc b/gcc/attribs.cc
> index d1d9e5a28c1..6725fe78f2c 100644
> --- a/gcc/attribs.cc
> +++ b/gcc/attribs.cc
> @@ -39,7 +39,7 @@ along with GCC; see the file COPYING3.  If not see
>
>  /* Table of the tables of attributes (common, language, format, machine)
>     searched.  */
> -static const struct attribute_spec *attribute_tables[4];
> +static array_slice<const scoped_attribute_specs *const> attribute_tables[2];
>
>  /* Substring representation.  */
>
> @@ -102,13 +102,6 @@ static const struct attribute_spec *lookup_scoped_attribute_spec (const_tree,
>
>  static bool attributes_initialized = false;
>
> -/* Default empty table of attributes.  */
> -
> -static const struct attribute_spec empty_attribute_table[] =
> -{
> -  { NULL, 0, 0, false, false, false, false, NULL, NULL }
> -};
> -
>  /* Return base name of the attribute.  Ie '__attr__' is turned into 'attr'.
>     To avoid need for copying, we simply return length of the string.  */
>
> @@ -118,21 +111,19 @@ extract_attribute_substring (struct substring *str)
>    canonicalize_attr_name (str->str, str->length);
>  }
>
> -/* Insert an array of attributes ATTRIBUTES into a namespace.  This
> -   array must be NULL terminated.  NS is the name of attribute
> -   namespace.  IGNORED_P is true iff all unknown attributes in this
> -   namespace should be ignored for the purposes of -Wattributes.  The
> -   function returns the namespace into which the attributes have been
> -   registered.  */
> +/* Insert SPECS into its namespace.  IGNORED_P is true iff all unknown
> +   attributes in this namespace should be ignored for the purposes of
> +   -Wattributes.  The function returns the namespace into which the
> +   attributes have been registered.  */
>
>  scoped_attributes *
> -register_scoped_attributes (const struct attribute_spec *attributes,
> -                           const char *ns, bool ignored_p /*=false*/)
> +register_scoped_attributes (const scoped_attribute_specs &specs,
> +                           bool ignored_p /*=false*/)
>  {
>    scoped_attributes *result = NULL;
>
>    /* See if we already have attributes in the namespace NS.  */
> -  result = find_attribute_namespace (ns);
> +  result = find_attribute_namespace (specs.ns);
>
>    if (result == NULL)
>      {
> @@ -143,7 +134,7 @@ register_scoped_attributes (const struct attribute_spec *attributes,
>         attributes_table.create (64);
>
>        memset (&sa, 0, sizeof (sa));
> -      sa.ns = ns;
> +      sa.ns = specs.ns;
>        sa.attributes.create (64);
>        sa.ignored_p = ignored_p;
>        result = attributes_table.safe_push (sa);
> @@ -153,10 +144,10 @@ register_scoped_attributes (const struct attribute_spec *attributes,
>      result->ignored_p |= ignored_p;
>
>    /* Really add the attributes to their namespace now.  */
> -  for (unsigned i = 0; attributes[i].name != NULL; ++i)
> +  for (const attribute_spec &attribute : specs.attributes)
>      {
> -      result->attributes.safe_push (attributes[i]);
> -      register_scoped_attribute (&attributes[i], result);
> +      result->attributes.safe_push (attribute);
> +      register_scoped_attribute (&attribute, result);
>      }
>
>    gcc_assert (result != NULL);
> @@ -183,49 +174,40 @@ find_attribute_namespace (const char* ns)
>  static void
>  check_attribute_tables (void)
>  {
> -  for (size_t i = 0; i < ARRAY_SIZE (attribute_tables); i++)
> -    for (size_t j = 0; attribute_tables[i][j].name != NULL; j++)
> -      {
> -       /* The name must not begin and end with __.  */
> -       const char *name = attribute_tables[i][j].name;
> -       int len = strlen (name);
> +  hash_set<pair_hash<nofree_string_hash, nofree_string_hash>> names;
>
> -       gcc_assert (!(name[0] == '_' && name[1] == '_'
> -                     && name[len - 1] == '_' && name[len - 2] == '_'));
> +  for (auto scoped_array : attribute_tables)
> +    for (auto scoped_attributes : scoped_array)
> +      for (const attribute_spec &attribute : scoped_attributes->attributes)
> +       {
> +         /* The name must not begin and end with __.  */
> +         const char *name = attribute.name;
> +         int len = strlen (name);
> +
> +         gcc_assert (!(name[0] == '_' && name[1] == '_'
> +                       && name[len - 1] == '_' && name[len - 2] == '_'));
>
> -       /* The minimum and maximum lengths must be consistent.  */
> -       gcc_assert (attribute_tables[i][j].min_length >= 0);
> +         /* The minimum and maximum lengths must be consistent.  */
> +         gcc_assert (attribute.min_length >= 0);
>
> -       gcc_assert (attribute_tables[i][j].max_length == -1
> -                   || (attribute_tables[i][j].max_length
> -                       >= attribute_tables[i][j].min_length));
> +         gcc_assert (attribute.max_length == -1
> +                     || attribute.max_length >= attribute.min_length);
>
> -       /* An attribute cannot require both a DECL and a TYPE.  */
> -       gcc_assert (!attribute_tables[i][j].decl_required
> -                   || !attribute_tables[i][j].type_required);
> +         /* An attribute cannot require both a DECL and a TYPE.  */
> +         gcc_assert (!attribute.decl_required
> +                     || !attribute.type_required);
>
>           /* If an attribute requires a function type, in particular
>              it requires a type.  */
> -       gcc_assert (!attribute_tables[i][j].function_type_required
> -                   || attribute_tables[i][j].type_required);
> -      }
> -
> -  /* Check that each name occurs just once in each table.  */
> -  for (size_t i = 0; i < ARRAY_SIZE (attribute_tables); i++)
> -    for (size_t j = 0; attribute_tables[i][j].name != NULL; j++)
> -      for (size_t k = j + 1; attribute_tables[i][k].name != NULL; k++)
> -       gcc_assert (strcmp (attribute_tables[i][j].name,
> -                           attribute_tables[i][k].name));
> -
> -  /* Check that no name occurs in more than one table.  Names that
> -     begin with '*' are exempt, and may be overridden.  */
> -  for (size_t i = 0; i < ARRAY_SIZE (attribute_tables); i++)
> -    for (size_t j = i + 1; j < ARRAY_SIZE (attribute_tables); j++)
> -      for (size_t k = 0; attribute_tables[i][k].name != NULL; k++)
> -       for (size_t l = 0; attribute_tables[j][l].name != NULL; l++)
> -         gcc_assert (attribute_tables[i][k].name[0] == '*'
> -                     || strcmp (attribute_tables[i][k].name,
> -                                attribute_tables[j][l].name));
> +         gcc_assert (!attribute.function_type_required
> +                     || attribute.type_required);
> +
> +         /* Check that no name occurs more than once.  Names that
> +            begin with '*' are exempt, and may be overridden.  */
> +         const char *ns = scoped_attributes->ns;
> +         if (name[0] != '*' && names.add ({ ns ? ns : "", name }))
> +           gcc_unreachable ();
> +       }
>  }
>
>  /* Used to stash pointers to allocated memory so that we can free them at
> @@ -281,7 +263,7 @@ handle_ignored_attributes_option (vec<char *> *v)
>        canonicalize_attr_name (vendor_start, vendor_len);
>        /* We perform all this hijinks so that we don't have to copy OPT.  */
>        tree vendor_id = get_identifier_with_length (vendor_start, vendor_len);
> -      const char *attr;
> +      array_slice<const attribute_spec> attrs;
>        /* In the "vendor::" case, we should ignore *any* attribute coming
>          from this attribute namespace.  */
>        if (attr_len > 0)
> @@ -293,22 +275,23 @@ handle_ignored_attributes_option (vec<char *> *v)
>             }
>           canonicalize_attr_name (attr_start, attr_len);
>           tree attr_id = get_identifier_with_length (attr_start, attr_len);
> -         attr = IDENTIFIER_POINTER (attr_id);
> +         const char *attr = IDENTIFIER_POINTER (attr_id);
>           /* If we've already seen this vendor::attr, ignore it.  Attempting to
>              register it twice would lead to a crash.  */
>           if (lookup_scoped_attribute_spec (vendor_id, attr_id))
>             continue;
> +         /* Create a table with extra attributes which we will register.
> +            We can't free it here, so squirrel away the pointers.  */
> +         attribute_spec *table = new attribute_spec {
> +           attr, 0, -2, false, false, false, false, nullptr, nullptr
> +         };
> +         ignored_attributes_table.safe_push (table);
> +         attrs = { table, 1 };
>         }
> -      else
> -       attr = nullptr;
> -      /* Create a table with extra attributes which we will register.
> -        We can't free it here, so squirrel away the pointers.  */
> -      attribute_spec *table = new attribute_spec[2];
> -      ignored_attributes_table.safe_push (table);
> -      table[0] = { attr, 0, -2, false, false, false, false, nullptr, nullptr };
> -      table[1] = { nullptr, 0, 0, false, false, false, false, nullptr,
> -                  nullptr };
> -      register_scoped_attributes (table, IDENTIFIER_POINTER (vendor_id), !attr);
> +      const scoped_attribute_specs scoped_specs = {
> +       IDENTIFIER_POINTER (vendor_id), attrs
> +      };
> +      register_scoped_attributes (scoped_specs, attrs.empty ());
>      }
>  }
>
> @@ -328,27 +311,18 @@ free_attr_data ()
>  void
>  init_attributes (void)
>  {
> -  size_t i;
> -
>    if (attributes_initialized)
>      return;
>
> -  attribute_tables[0] = lang_hooks.common_attribute_table;
> -  attribute_tables[1] = lang_hooks.attribute_table;
> -  attribute_tables[2] = lang_hooks.format_attribute_table;
> -  attribute_tables[3] = targetm.attribute_table;
> -
> -  /* Translate NULL pointers to pointers to the empty table.  */
> -  for (i = 0; i < ARRAY_SIZE (attribute_tables); i++)
> -    if (attribute_tables[i] == NULL)
> -      attribute_tables[i] = empty_attribute_table;
> +  attribute_tables[0] = lang_hooks.attribute_table;
> +  attribute_tables[1] = targetm.attribute_table;
>
>    if (flag_checking)
>      check_attribute_tables ();
>
> -  for (i = 0; i < ARRAY_SIZE (attribute_tables); ++i)
> -    /* Put all the GNU attributes into the "gnu" namespace.  */
> -    register_scoped_attributes (attribute_tables[i], "gnu");
> +  for (auto scoped_array : attribute_tables)
> +    for (auto scoped_attributes : scoped_array)
> +      register_scoped_attributes (*scoped_attributes);
>
>    vec<char *> *ignored = (vec<char *> *) flag_ignored_attributes;
>    handle_ignored_attributes_option (ignored);
> @@ -2642,10 +2616,6 @@ attr_access::array_as_string (tree type) const
>  namespace selftest
>  {
>
> -/* Helper types to verify the consistency attribute exclusions.  */
> -
> -typedef std::pair<const char *, const char *> excl_pair;
> -
>  /* Self-test to verify that each attribute exclusion is symmetric,
>     meaning that if attribute A is encoded as incompatible with
>     attribute B then the opposite relationship is also encoded.
> @@ -2660,55 +2630,54 @@ test_attribute_exclusions ()
>    /* Iterate over the array of attribute tables first (with TI0 as
>       the index) and over the array of attribute_spec in each table
>       (with SI0 as the index).  */
> -  const size_t ntables = ARRAY_SIZE (attribute_tables);
> +  hash_set<excl_hash_traits> excl_set;
>
> -  /* Set of pairs of mutually exclusive attributes.  */
> -  typedef hash_set<excl_hash_traits> exclusion_set;
> -  exclusion_set excl_set;
> +  for (auto scoped_array : attribute_tables)
> +    for (auto scoped_attributes : scoped_array)
> +      for (const attribute_spec &attribute : scoped_attributes->attributes)
> +       {
> +         const attribute_spec::exclusions *excl = attribute.exclude;
>
> -  for (size_t ti0 = 0; ti0 != ntables; ++ti0)
> -    for (size_t s0 = 0; attribute_tables[ti0][s0].name; ++s0)
> -      {
> -       const attribute_spec::exclusions *excl
> -         = attribute_tables[ti0][s0].exclude;
> +         /* Skip each attribute that doesn't define exclusions.  */
> +         if (!excl)
> +           continue;
>
> -       /* Skip each attribute that doesn't define exclusions.  */
> -       if (!excl)
> -         continue;
> +         /* Skip standard (non-GNU) attributes, since currently the
> +            exclusions are implicitly for GNU attributes only.
> +            Also, C++ likely and unlikely get rewritten to gnu::hot
> +            and gnu::cold, so symmetry isn't necessary there.  */
> +         if (!scoped_attributes->ns)
> +           continue;
>
> -       const char *attr_name = attribute_tables[ti0][s0].name;
> +         const char *attr_name = attribute.name;
>
> -       /* Iterate over the set of exclusions for every attribute
> -          (with EI0 as the index) adding the exclusions defined
> -          for each to the set.  */
> -       for (size_t ei0 = 0; excl[ei0].name; ++ei0)
> -         {
> -           const char *excl_name = excl[ei0].name;
> +         /* Iterate over the set of exclusions for every attribute
> +            (with EI0 as the index) adding the exclusions defined
> +            for each to the set.  */
> +         for (size_t ei0 = 0; excl[ei0].name; ++ei0)
> +           {
> +             const char *excl_name = excl[ei0].name;
>
> -           if (!strcmp (attr_name, excl_name))
> -             continue;
> +             if (!strcmp (attr_name, excl_name))
> +               continue;
>
> -           excl_set.add (excl_pair (attr_name, excl_name));
> -         }
> -      }
> +             excl_set.add ({ attr_name, excl_name });
> +           }
> +       }
>
>    /* Traverse the set of mutually exclusive pairs of attributes
>       and verify that they are symmetric.  */
> -  for (exclusion_set::iterator it = excl_set.begin ();
> -       it != excl_set.end ();
> -       ++it)
> -    {
> -      if (!excl_set.contains (excl_pair ((*it).second, (*it).first)))
> -       {
> -         /* An exclusion for an attribute has been found that
> -            doesn't have a corresponding exclusion in the opposite
> -            direction.  */
> -         char desc[120];
> -         sprintf (desc, "'%s' attribute exclusion '%s' must be symmetric",
> -                  (*it).first, (*it).second);
> -         fail (SELFTEST_LOCATION, desc);
> -       }
> -    }
> +  for (auto excl_pair : excl_set)
> +    if (!excl_set.contains ({ excl_pair.second, excl_pair.first }))
> +      {
> +       /* An exclusion for an attribute has been found that
> +          doesn't have a corresponding exclusion in the opposite
> +          direction.  */
> +       char desc[120];
> +       sprintf (desc, "'%s' attribute exclusion '%s' must be symmetric",
> +                excl_pair.first, excl_pair.second);
> +       fail (SELFTEST_LOCATION, desc);
> +      }
>  }
>
>  void
> diff --git a/gcc/attribs.h b/gcc/attribs.h
> index 84a43658a70..fdeebff1cd9 100644
> --- a/gcc/attribs.h
> +++ b/gcc/attribs.h
> @@ -20,6 +20,13 @@ along with GCC; see the file COPYING3.  If not see
>  #ifndef GCC_ATTRIBS_H
>  #define GCC_ATTRIBS_H
>
> +/* A set of attributes that belong to the same namespace, given by NS.  */
> +struct scoped_attribute_specs
> +{
> +  const char *ns;
> +  array_slice<const attribute_spec> attributes;
> +};
> +
>  extern const struct attribute_spec *lookup_attribute_spec (const_tree);
>  extern void free_attr_data ();
>  extern void init_attributes (void);
> @@ -42,9 +49,8 @@ extern tree make_attribute (const char *, const char *, tree);
>  extern bool attribute_ignored_p (tree);
>  extern bool attribute_ignored_p (const attribute_spec *const);
>
> -extern struct scoped_attributes* register_scoped_attributes (const struct attribute_spec *,
> -                                                            const char *,
> -                                                            bool = false);
> +extern struct scoped_attributes *
> +  register_scoped_attributes (const scoped_attribute_specs &, bool = false);
>
>  extern char *sorted_attr_string (tree);
>  extern bool common_function_versions (tree, tree);
> diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
> index a041c3b91eb..cf54ef68f20 100644
> --- a/gcc/c-family/c-attribs.cc
> +++ b/gcc/c-family/c-attribs.cc
> @@ -288,7 +288,7 @@ static const struct attribute_spec::exclusions attr_stack_protect_exclusions[] =
>  /* Table of machine-independent attributes common to all C-like languages.
>
>     Current list of processed common attributes: nonnull.  */
> -const struct attribute_spec c_common_attribute_table[] =
> +const struct attribute_spec c_common_gnu_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -574,23 +574,31 @@ const struct attribute_spec c_common_attribute_table[] =
>    { "fd_arg_write",       1, 1, false, true, true, false,
>              handle_fd_arg_attribute, NULL},
>    { "null_terminated_string_arg", 1, 1, false, true, true, false,
> -                             handle_null_terminated_string_arg_attribute, NULL},
> -  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
> +                             handle_null_terminated_string_arg_attribute, NULL}
> +};
> +
> +const struct scoped_attribute_specs c_common_gnu_attribute_table =
> +{
> +  "gnu", c_common_gnu_attributes
>  };
>
>  /* Give the specifications for the format attributes, used by C and all
>     descendants.
>
>     Current list of processed format attributes: format, format_arg.  */
> -const struct attribute_spec c_common_format_attribute_table[] =
> +const struct attribute_spec c_common_format_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
>    { "format",                 3, 3, false, true,  true, false,
>                               handle_format_attribute, NULL },
>    { "format_arg",             1, 1, false, true,  true, false,
> -                             handle_format_arg_attribute, NULL },
> -  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
> +                             handle_format_arg_attribute, NULL }
> +};
> +
> +const struct scoped_attribute_specs c_common_format_attribute_table =
> +{
> +  "gnu", c_common_format_attributes
>  };
>
>  /* Returns TRUE iff the attribute indicated by ATTR_ID takes a plain
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index 1fdba7ef3ea..92ddad27806 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -821,8 +821,8 @@ enum conversion_safety {
>  extern struct visibility_flags visibility_options;
>
>  /* Attribute table common to the C front ends.  */
> -extern const struct attribute_spec c_common_attribute_table[];
> -extern const struct attribute_spec c_common_format_attribute_table[];
> +extern const struct scoped_attribute_specs c_common_gnu_attribute_table;
> +extern const struct scoped_attribute_specs c_common_format_attribute_table;
>
>  /* Pointer to function to lazily generate the VAR_DECL for __FUNCTION__ etc.
>     ID is the identifier to use, NAME is the string.
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index 0076725a61d..547bebe03c9 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -4635,7 +4635,7 @@ handle_std_noreturn_attribute (tree *node, tree name, tree args,
>  }
>
>  /* Table of supported standard (C2x) attributes.  */
> -const struct attribute_spec std_attribute_table[] =
> +static const attribute_spec std_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -4650,8 +4650,12 @@ const struct attribute_spec std_attribute_table[] =
>    { "nodiscard", 0, 1, false, false, false, false,
>      handle_nodiscard_attribute, NULL },
>    { "noreturn", 0, 0, false, false, false, false,
> -    handle_std_noreturn_attribute, NULL },
> -  { NULL, 0, 0, false, false, false, false, NULL, NULL }
> +    handle_std_noreturn_attribute, NULL }
> +};
> +
> +const scoped_attribute_specs std_attribute_table =
> +{
> +  nullptr, std_attributes
>  };
>
>  /* Create the predefined scalar types of C,
> @@ -4667,8 +4671,6 @@ c_init_decl_processing (void)
>    /* Initialize reserved words for parser.  */
>    c_parse_init ();
>
> -  register_scoped_attributes (std_attribute_table, NULL);
> -
>    current_function_decl = NULL_TREE;
>
>    gcc_obstack_init (&parser_obstack);
> diff --git a/gcc/c/c-objc-common.h b/gcc/c/c-objc-common.h
> index ede451cef6b..4f1925c8b07 100644
> --- a/gcc/c/c-objc-common.h
> +++ b/gcc/c/c-objc-common.h
> @@ -72,11 +72,15 @@ along with GCC; see the file COPYING3.  If not see
>  #undef LANG_HOOKS_FINALIZE_EARLY_DEBUG
>  #define LANG_HOOKS_FINALIZE_EARLY_DEBUG c_common_finalize_early_debug
>
> -/* Attribute hooks.  */
> -#undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
> -#define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE c_common_attribute_table
> -#undef LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE
> -#define LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE c_common_format_attribute_table
> +static const scoped_attribute_specs *const c_objc_attribute_table[] =
> +{
> +  &std_attribute_table,
> +  &c_common_gnu_attribute_table,
> +  &c_common_format_attribute_table
> +};
> +
> +#undef LANG_HOOKS_ATTRIBUTE_TABLE
> +#define LANG_HOOKS_ATTRIBUTE_TABLE c_objc_attribute_table
>
>  #undef LANG_HOOKS_TREE_DUMP_DUMP_TREE_FN
>  #define LANG_HOOKS_TREE_DUMP_DUMP_TREE_FN c_dump_tree
> diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
> index df6f1cefd02..69e154abdbd 100644
> --- a/gcc/c/c-tree.h
> +++ b/gcc/c/c-tree.h
> @@ -910,6 +910,8 @@ extern vec<tree> incomplete_record_decls;
>
>  extern const char *c_get_sarif_source_language (const char *filename);
>
> +extern const struct scoped_attribute_specs std_attribute_table;
> +
>  #if CHECKING_P
>  namespace selftest {
>    extern void run_c_tests (void);
> diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
> index cb65ccc8465..920e17d3a95 100644
> --- a/gcc/config/aarch64/aarch64.cc
> +++ b/gcc/config/aarch64/aarch64.cc
> @@ -2839,7 +2839,7 @@ handle_aarch64_vector_pcs_attribute (tree *node, tree name, tree,
>  }
>
>  /* Table of machine attributes.  */
> -static const struct attribute_spec aarch64_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (aarch64_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -2850,9 +2850,8 @@ static const struct attribute_spec aarch64_attribute_table[] =
>                           NULL },
>    { "Advanced SIMD type", 1, 1, false, true,  false, true,  NULL, NULL },
>    { "SVE type",                  3, 3, false, true,  false, true,  NULL, NULL },
> -  { "SVE sizeless type",  0, 0, false, true,  false, true,  NULL, NULL },
> -  { NULL,                 0, 0, false, false, false, false, NULL, NULL }
> -};
> +  { "SVE sizeless type",  0, 0, false, true,  false, true,  NULL, NULL }
> +});
>
>  typedef enum aarch64_cond_code
>  {
> diff --git a/gcc/config/alpha/alpha.cc b/gcc/config/alpha/alpha.cc
> index db6b34be9cb..6aa93783226 100644
> --- a/gcc/config/alpha/alpha.cc
> +++ b/gcc/config/alpha/alpha.cc
> @@ -7482,14 +7482,13 @@ common_object_handler (tree *node, tree name ATTRIBUTE_UNUSED,
>    return NULL_TREE;
>  }
>
> -static const struct attribute_spec vms_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (vms_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
>    { COMMON_OBJECT,   0, 1, true,  false, false, false, common_object_handler,
> -    NULL },
> -  { NULL,            0, 0, false, false, false, false, NULL, NULL }
> -};
> +    NULL }
> +});
>
>  void
>  vms_output_aligned_decl_common(FILE *file, tree decl, const char *name,
> diff --git a/gcc/config/arc/arc.cc b/gcc/config/arc/arc.cc
> index e209ad2d327..e87d7614feb 100644
> --- a/gcc/config/arc/arc.cc
> +++ b/gcc/config/arc/arc.cc
> @@ -187,44 +187,6 @@ static tree arc_handle_secure_attribute (tree *, tree, tree, int, bool *);
>  static tree arc_handle_uncached_attribute (tree *, tree, tree, int, bool *);
>  static tree arc_handle_aux_attribute (tree *, tree, tree, int, bool *);
>
> -/* Initialized arc_attribute_table to NULL since arc doesnot have any
> -   machine specific supported attributes.  */
> -const struct attribute_spec arc_attribute_table[] =
> -{
> - /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
> -      affects_type_identity, handler, exclude } */
> -  { "interrupt", 1, 1, true, false, false, true,
> -    arc_handle_interrupt_attribute, NULL },
> -  /* Function calls made to this symbol must be done indirectly, because
> -     it may lie outside of the 21/25 bit addressing range of a normal function
> -     call.  */
> -  { "long_call",    0, 0, false, true,  true,  false, NULL, NULL },
> -  /* Whereas these functions are always known to reside within the 25 bit
> -     addressing range of unconditionalized bl.  */
> -  { "medium_call",   0, 0, false, true,  true, false, NULL, NULL },
> -  /* And these functions are always known to reside within the 21 bit
> -     addressing range of blcc.  */
> -  { "short_call",   0, 0, false, true,  true,  false, NULL, NULL },
> -  /* Function which are not having the prologue and epilogue generated
> -     by the compiler.  */
> -  { "naked", 0, 0, true, false, false,  false, arc_handle_fndecl_attribute,
> -    NULL },
> -  /* Functions calls made using jli instruction.  The pointer in JLI
> -     table is found latter.  */
> -  { "jli_always",    0, 0, false, true,  true, false,  NULL, NULL },
> -  /* Functions calls made using jli instruction.  The pointer in JLI
> -     table is given as input parameter.  */
> -  { "jli_fixed",    1, 1, false, true,  true, false, arc_handle_jli_attribute,
> -    NULL },
> -  /* Call a function using secure-mode.  */
> -  { "secure_call",  1, 1, false, true, true, false, arc_handle_secure_attribute,
> -    NULL },
> -   /* Bypass caches using .di flag.  */
> -  { "uncached", 0, 0, false, true, false, false, arc_handle_uncached_attribute,
> -    NULL },
> -  { "aux", 0, 1, true, false, false, false, arc_handle_aux_attribute, NULL },
> -  { NULL, 0, 0, false, false, false, false, NULL, NULL }
> -};
>  static int arc_comp_type_attributes (const_tree, const_tree);
>  static void arc_file_start (void);
>  static void arc_internal_label (FILE *, const char *, unsigned long);
> @@ -770,6 +732,42 @@ static rtx arc_legitimize_address_0 (rtx, rtx, machine_mode mode);
>
>  #include "target-def.h"
>
> +TARGET_GNU_ATTRIBUTES (arc_attribute_table,
> +{
> + /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
> +      affects_type_identity, handler, exclude } */
> +  { "interrupt", 1, 1, true, false, false, true,
> +    arc_handle_interrupt_attribute, NULL },
> +  /* Function calls made to this symbol must be done indirectly, because
> +     it may lie outside of the 21/25 bit addressing range of a normal function
> +     call.  */
> +  { "long_call",    0, 0, false, true,  true,  false, NULL, NULL },
> +  /* Whereas these functions are always known to reside within the 25 bit
> +     addressing range of unconditionalized bl.  */
> +  { "medium_call",   0, 0, false, true,  true, false, NULL, NULL },
> +  /* And these functions are always known to reside within the 21 bit
> +     addressing range of blcc.  */
> +  { "short_call",   0, 0, false, true,  true,  false, NULL, NULL },
> +  /* Function which are not having the prologue and epilogue generated
> +     by the compiler.  */
> +  { "naked", 0, 0, true, false, false,  false, arc_handle_fndecl_attribute,
> +    NULL },
> +  /* Functions calls made using jli instruction.  The pointer in JLI
> +     table is found latter.  */
> +  { "jli_always",    0, 0, false, true,  true, false,  NULL, NULL },
> +  /* Functions calls made using jli instruction.  The pointer in JLI
> +     table is given as input parameter.  */
> +  { "jli_fixed",    1, 1, false, true,  true, false, arc_handle_jli_attribute,
> +    NULL },
> +  /* Call a function using secure-mode.  */
> +  { "secure_call",  1, 1, false, true, true, false, arc_handle_secure_attribute,
> +    NULL },
> +   /* Bypass caches using .di flag.  */
> +  { "uncached", 0, 0, false, true, false, false, arc_handle_uncached_attribute,
> +    NULL },
> +  { "aux", 0, 1, true, false, false, false, arc_handle_aux_attribute, NULL }
> +});
> +
>  #undef TARGET_ASM_ALIGNED_HI_OP
>  #define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
>  #undef TARGET_ASM_ALIGNED_SI_OP
> diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
> index 620ef7bfb2f..ed2459128ff 100644
> --- a/gcc/config/arm/arm.cc
> +++ b/gcc/config/arm/arm.cc
> @@ -332,7 +332,7 @@ static rtx_insn *thumb1_md_asm_adjust (vec<rtx> &, vec<rtx> &,
>  static const char *arm_identify_fpu_from_isa (sbitmap);
>
>  /* Table of machine attributes.  */
> -static const struct attribute_spec arm_attribute_table[] =
> +static const attribute_spec arm_gnu_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -380,8 +380,17 @@ static const struct attribute_spec arm_attribute_table[] =
>      arm_handle_cmse_nonsecure_entry, NULL },
>    { "cmse_nonsecure_call", 0, 0, false, false, false, true,
>      arm_handle_cmse_nonsecure_call, NULL },
> -  { "Advanced SIMD type", 1, 1, false, true, false, true, NULL, NULL },
> -  { NULL, 0, 0, false, false, false, false, NULL, NULL }
> +  { "Advanced SIMD type", 1, 1, false, true, false, true, NULL, NULL }
> +};
> +
> +static const scoped_attribute_specs arm_gnu_attribute_table =
> +{
> +  "gnu", arm_gnu_attributes
> +};
> +
> +static const scoped_attribute_specs *const arm_attribute_table[] =
> +{
> +  &arm_gnu_attribute_table
>  };
>
>  /* Initialize the GCC target structure.  */
> diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc
> index 5e0217de36f..59d217efc45 100644
> --- a/gcc/config/avr/avr.cc
> +++ b/gcc/config/avr/avr.cc
> @@ -10442,7 +10442,7 @@ avr_eval_addr_attrib (rtx x)
>
>
>  /* AVR attributes.  */
> -static const struct attribute_spec avr_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (avr_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -10467,9 +10467,8 @@ static const struct attribute_spec avr_attribute_table[] =
>    { "address",   1, 1, true, false, false,  false,
>      avr_handle_addr_attribute, NULL },
>    { "absdata",   0, 0, true, false, false,  false,
> -    avr_handle_absdata_attribute, NULL },
> -  { NULL,        0, 0, false, false, false, false, NULL, NULL }
> -};
> +    avr_handle_absdata_attribute, NULL }
> +});
>
>
>  /* Return true if we support address space AS for the architecture in effect
> diff --git a/gcc/config/bfin/bfin.cc b/gcc/config/bfin/bfin.cc
> index 5718babb6b2..c02136f5e0c 100644
> --- a/gcc/config/bfin/bfin.cc
> +++ b/gcc/config/bfin/bfin.cc
> @@ -4896,7 +4896,7 @@ bfin_handle_l2_attribute (tree *node, tree ARG_UNUSED (name),
>  }
>
>  /* Table of valid machine attributes.  */
> -static const struct attribute_spec bfin_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (bfin_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -4921,9 +4921,8 @@ static const struct attribute_spec bfin_attribute_table[] =
>      bfin_handle_l1_data_attribute, NULL },
>    { "l1_data_B", 0, 0, true, false, false, false,
>      bfin_handle_l1_data_attribute, NULL },
> -  { "l2", 0, 0, true, false, false, false, bfin_handle_l2_attribute, NULL },
> -  { NULL, 0, 0, false, false, false, false, NULL, NULL }
> -};
> +  { "l2", 0, 0, true, false, false, false, bfin_handle_l2_attribute, NULL }
> +});
>
>  /* Implementation of TARGET_ASM_INTEGER.  When using FD-PIC, we need to
>     tell the assembler to generate pointers to function descriptors in
> diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc
> index 63637ece78e..c619865f1fc 100644
> --- a/gcc/config/bpf/bpf.cc
> +++ b/gcc/config/bpf/bpf.cc
> @@ -139,7 +139,7 @@ bpf_handle_preserve_access_index_attribute (tree *node, tree name,
>
>  /* Target-specific attributes.  */
>
> -static const struct attribute_spec bpf_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (bpf_attribute_table,
>  {
>    /* Syntax: { name, min_len, max_len, decl_required, type_required,
>                function_type_required, affects_type_identity, handler,
> @@ -156,11 +156,8 @@ static const struct attribute_spec bpf_attribute_table[] =
>
>   /* Support for `naked' function attribute.  */
>   { "naked", 0, 1, false, false, false, false,
> -   bpf_handle_fndecl_attribute, NULL },
> -
> - /* The last attribute spec is set to be NULL.  */
> - { NULL,       0,  0, false, false, false, false, NULL, NULL }
> -};
> +   bpf_handle_fndecl_attribute, NULL }
> +});
>
>  #undef TARGET_ATTRIBUTE_TABLE
>  #define TARGET_ATTRIBUTE_TABLE bpf_attribute_table
> diff --git a/gcc/config/csky/csky.cc b/gcc/config/csky/csky.cc
> index 731f47cb2c0..ac089feea62 100644
> --- a/gcc/config/csky/csky.cc
> +++ b/gcc/config/csky/csky.cc
> @@ -211,16 +211,15 @@ const int csky_debugger_regno[FIRST_PSEUDO_REGISTER] =
>  /* Table of machine attributes.  */
>  static tree csky_handle_fndecl_attribute (tree *, tree, tree, int, bool *);
>  static tree csky_handle_isr_attribute (tree *, tree, tree, int, bool *);
> -static const struct attribute_spec csky_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (csky_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
>    { "naked",    0, 0, true,  false, false, false, csky_handle_fndecl_attribute, NULL },
>    /* Interrupt Service Routines have special prologue and epilogue requirements.  */
>    { "interrupt", 0, 1, false, false, false, false, csky_handle_isr_attribute,   NULL },
> -  { "isr",      0, 1, false, false, false, false, csky_handle_isr_attribute,    NULL },
> -  { NULL,       0, 0, false, false, false, false, NULL,                         NULL }
> -};
> +  { "isr",      0, 1, false, false, false, false, csky_handle_isr_attribute,    NULL }
> +});
>
>  /* A C structure for machine-specific, per-function data.
>     This is added to the cfun structure.  */
> diff --git a/gcc/config/epiphany/epiphany.cc b/gcc/config/epiphany/epiphany.cc
> index 68e748c688e..e10e64de823 100644
> --- a/gcc/config/epiphany/epiphany.cc
> +++ b/gcc/config/epiphany/epiphany.cc
> @@ -458,7 +458,7 @@ epiphany_init_reg_tables (void)
>                       They unmask them while calling an interruptible
>                      function, though.  */
>
> -static const struct attribute_spec epiphany_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (epiphany_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -468,9 +468,8 @@ static const struct attribute_spec epiphany_attribute_table[] =
>      epiphany_handle_forwarder_attribute, NULL },
>    { "long_call",  0, 0, false, true, true, false, NULL, NULL },
>    { "short_call", 0, 0, false, true, true, false, NULL, NULL },
> -  { "disinterrupt", 0, 0, false, true, true, true, NULL, NULL },
> -  { NULL,         0, 0, false, false, false, false, NULL, NULL }
> -};
> +  { "disinterrupt", 0, 0, false, true, true, true, NULL, NULL }
> +});
>
>  /* Handle an "interrupt" attribute; arguments as in
>     struct attribute_spec.handler.  */
> diff --git a/gcc/config/gcn/gcn.cc b/gcc/config/gcn/gcn.cc
> index 6a2aaefceca..795a852bb53 100644
> --- a/gcc/config/gcn/gcn.cc
> +++ b/gcc/config/gcn/gcn.cc
> @@ -357,14 +357,12 @@ gcn_handle_amdgpu_hsa_kernel_attribute (tree *node, tree name,
>
>     Create target-specific __attribute__ types.  */
>
> -static const struct attribute_spec gcn_attribute_table[] = {
> +TARGET_GNU_ATTRIBUTES (gcn_attribute_table, {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
>       affects_type_identity } */
>    {"amdgpu_hsa_kernel", 0, GCN_KERNEL_ARG_TYPES, false, true,
> -   true, true, gcn_handle_amdgpu_hsa_kernel_attribute, NULL},
> -  /* End element.  */
> -  {NULL, 0, 0, false, false, false, false, NULL, NULL}
> -};
> +   true, true, gcn_handle_amdgpu_hsa_kernel_attribute, NULL}
> +});
>
>  /* }}}  */
>  /* {{{ Registers and modes.  */
> diff --git a/gcc/config/h8300/h8300.cc b/gcc/config/h8300/h8300.cc
> index 4bbb1b711e8..5936cdca177 100644
> --- a/gcc/config/h8300/h8300.cc
> +++ b/gcc/config/h8300/h8300.cc
> @@ -4909,7 +4909,7 @@ h8300_insert_attributes (tree node, tree *attributes)
>     tiny_data: This variable lives in the tiny data area and can be
>     referenced with 16-bit absolute memory references.  */
>
> -static const struct attribute_spec h8300_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (h8300_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -4926,9 +4926,8 @@ static const struct attribute_spec h8300_attribute_table[] =
>    { "eightbit_data",     0, 0, true,  false, false, false,
>      h8300_handle_eightbit_data_attribute, NULL },
>    { "tiny_data",         0, 0, true,  false, false, false,
> -    h8300_handle_tiny_data_attribute, NULL },
> -  { NULL,                0, 0, false, false, false, false, NULL, NULL }
> -};
> +    h8300_handle_tiny_data_attribute, NULL }
> +});
>
>
>  /* Handle an attribute requiring a FUNCTION_DECL; arguments as in
> diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc
> index df7d24352d1..351c777c666 100644
> --- a/gcc/config/i386/i386-options.cc
> +++ b/gcc/config/i386/i386-options.cc
> @@ -3951,7 +3951,7 @@ handle_nodirect_extern_access_attribute (tree *pnode, tree name,
>  }
>
>  /* Table of valid machine attributes.  */
> -const struct attribute_spec ix86_attribute_table[] =
> +static const attribute_spec ix86_gnu_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -4031,10 +4031,12 @@ const struct attribute_spec ix86_attribute_table[] =
>    { "cf_check", 0, 0, true, false, false, false,
>      ix86_handle_fndecl_attribute, NULL },
>    { "nodirect_extern_access", 0, 0, true, false, false, false,
> -    handle_nodirect_extern_access_attribute, NULL },
> +    handle_nodirect_extern_access_attribute, NULL }
> +};
>
> -  /* End element.  */
> -  { NULL, 0, 0, false, false, false, false, NULL, NULL }
> +const scoped_attribute_specs ix86_gnu_attribute_table =
> +{
> +  "gnu", ix86_gnu_attributes
>  };
>
>  #include "gt-i386-options.h"
> diff --git a/gcc/config/i386/i386-options.h b/gcc/config/i386/i386-options.h
> index 68666067fea..6274c594647 100644
> --- a/gcc/config/i386/i386-options.h
> +++ b/gcc/config/i386/i386-options.h
> @@ -82,7 +82,7 @@ void ix86_function_specific_print (FILE *, int,
>                                    struct cl_target_option *);
>  bool ix86_valid_target_attribute_p (tree, tree, tree, int);
>
> -extern const struct attribute_spec ix86_attribute_table[];
> +extern const struct scoped_attribute_specs ix86_gnu_attribute_table;
>
>
>  #endif  /* GCC_I386_OPTIONS_H */
> diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
> index 7b72aabf0da..e0eff9f1430 100644
> --- a/gcc/config/i386/i386.cc
> +++ b/gcc/config/i386/i386.cc
> @@ -25832,6 +25832,11 @@ ix86_run_selftests (void)
>
>  #endif /* CHECKING_P */
>
> +static const scoped_attribute_specs *const ix86_attribute_table[] =
> +{
> +  &ix86_gnu_attribute_table
> +};
> +
>  /* Initialize the GCC target structure.  */
>  #undef TARGET_RETURN_IN_MEMORY
>  #define TARGET_RETURN_IN_MEMORY ix86_return_in_memory
> diff --git a/gcc/config/ia64/ia64.cc b/gcc/config/ia64/ia64.cc
> index c241e1a50fc..f7766c25622 100644
> --- a/gcc/config/ia64/ia64.cc
> +++ b/gcc/config/ia64/ia64.cc
> @@ -358,7 +358,7 @@ static bool ia64_expand_vec_perm_const_1 (struct expand_vec_perm_d *d);
>
>
>  /* Table of valid machine attributes.  */
> -static const struct attribute_spec ia64_attribute_table[] =
> +static const attribute_spec ia64_gnu_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -370,8 +370,17 @@ static const struct attribute_spec ia64_attribute_table[] =
>      ia64_vms_common_object_attribute, NULL },
>  #endif
>    { "version_id",      1, 1, true, false, false, false,
> -    ia64_handle_version_id_attribute, NULL },
> -  { NULL,             0, 0, false, false, false, false, NULL, NULL }
> +    ia64_handle_version_id_attribute, NULL }
> +};
> +
> +static const scoped_attribute_specs ia64_gnu_attribute_table =
> +{
> +  "gnu", ia64_gnu_attributes
> +};
> +
> +static const scoped_attribute_specs *const ia64_attribute_table[] =
> +{
> +  &ia64_gnu_attribute_table
>  };
>
>  /* Initialize the GCC target structure.  */
> diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc
> index c782f571abc..bf73ab60072 100644
> --- a/gcc/config/loongarch/loongarch.cc
> +++ b/gcc/config/loongarch/loongarch.cc
> @@ -7812,15 +7812,13 @@ loongarch_handle_model_attribute (tree *node, tree name, tree arg, int,
>    return NULL_TREE;
>  }
>
> -static const struct attribute_spec loongarch_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (loongarch_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
>    { "model", 1, 1, true, false, false, false,
> -    loongarch_handle_model_attribute, NULL },
> -  /* The last attribute spec is set to be NULL.  */
> -  {}
> -};
> +    loongarch_handle_model_attribute, NULL }
> +});
>
>  bool
>  loongarch_use_anchors_for_symbol_p (const_rtx symbol)
> diff --git a/gcc/config/m32c/m32c.cc b/gcc/config/m32c/m32c.cc
> index e18efc3c7f2..c63c75a6709 100644
> --- a/gcc/config/m32c/m32c.cc
> +++ b/gcc/config/m32c/m32c.cc
> @@ -2999,7 +2999,7 @@ current_function_special_page_vector (rtx x)
>
>  #undef TARGET_ATTRIBUTE_TABLE
>  #define TARGET_ATTRIBUTE_TABLE m32c_attribute_table
> -static const struct attribute_spec m32c_attribute_table[] = {
> +TARGET_GNU_ATTRIBUTES (m32c_attribute_table, {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
>    { "interrupt", 0, 0, false, false, false, false, interrupt_handler, NULL },
> @@ -3007,9 +3007,8 @@ static const struct attribute_spec m32c_attribute_table[] = {
>    { "fast_interrupt", 0, 0, false, false, false, false,
>      interrupt_handler, NULL },
>    { "function_vector", 1, 1, true,  false, false, false,
> -    function_vector_handler, NULL },
> -  { NULL, 0, 0, false, false, false, false, NULL, NULL }
> -};
> +    function_vector_handler, NULL }
> +});
>
>  #undef TARGET_COMP_TYPE_ATTRIBUTES
>  #define TARGET_COMP_TYPE_ATTRIBUTES m32c_comp_type_attributes
> diff --git a/gcc/config/m32r/m32r.cc b/gcc/config/m32r/m32r.cc
> index 63a1798da3d..1a9c8ef1391 100644
> --- a/gcc/config/m32r/m32r.cc
> +++ b/gcc/config/m32r/m32r.cc
> @@ -112,15 +112,14 @@ static HOST_WIDE_INT m32r_starting_frame_offset (void);
>
>  /* M32R specific attributes.  */
>
> -static const struct attribute_spec m32r_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (m32r_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
>    { "interrupt", 0, 0, true,  false, false, false, NULL, NULL },
>    { "model",     1, 1, true,  false, false, false, m32r_handle_model_attribute,
> -    NULL },
> -  { NULL,        0, 0, false, false, false, false, NULL, NULL }
> -};
> +    NULL }
> +});
>
>  /* Initialize the GCC target structure.  */
>  #undef  TARGET_ATTRIBUTE_TABLE
> diff --git a/gcc/config/m68k/m68k.cc b/gcc/config/m68k/m68k.cc
> index 145a92d8710..001cf5bd997 100644
> --- a/gcc/config/m68k/m68k.cc
> +++ b/gcc/config/m68k/m68k.cc
> @@ -361,7 +361,7 @@ static void m68k_asm_final_postscan_insn (FILE *, rtx_insn *insn, rtx [], int);
>  #undef TARGET_ASM_FINAL_POSTSCAN_INSN
>  #define TARGET_ASM_FINAL_POSTSCAN_INSN m68k_asm_final_postscan_insn
>
> -static const struct attribute_spec m68k_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (m68k_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -370,9 +370,8 @@ static const struct attribute_spec m68k_attribute_table[] =
>    { "interrupt_handler", 0, 0, true,  false, false, false,
>      m68k_handle_fndecl_attribute, NULL },
>    { "interrupt_thread", 0, 0, true,  false, false, false,
> -    m68k_handle_fndecl_attribute, NULL },
> -  { NULL, 0, 0, false, false, false, false, NULL, NULL }
> -};
> +    m68k_handle_fndecl_attribute, NULL }
> +});
>
>  struct gcc_target targetm = TARGET_INITIALIZER;
>
> diff --git a/gcc/config/mcore/mcore.cc b/gcc/config/mcore/mcore.cc
> index 6f1d7af7937..ca672547494 100644
> --- a/gcc/config/mcore/mcore.cc
> +++ b/gcc/config/mcore/mcore.cc
> @@ -151,16 +151,15 @@ static bool         mcore_modes_tieable_p         (machine_mode, machine_mode);
>
>  /* MCore specific attributes.  */
>
> -static const struct attribute_spec mcore_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (mcore_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
>    { "dllexport", 0, 0, true,  false, false, false, NULL, NULL },
>    { "dllimport", 0, 0, true,  false, false, false, NULL, NULL },
>    { "naked",     0, 0, true,  false, false, false,
> -    mcore_handle_naked_attribute, NULL },
> -  { NULL,        0, 0, false, false, false, false, NULL, NULL }
> -};
> +    mcore_handle_naked_attribute, NULL }
> +});
>
>  /* Initialize the GCC target structure.  */
>  #undef  TARGET_ASM_EXTERNAL_LIBCALL
> diff --git a/gcc/config/microblaze/microblaze.cc b/gcc/config/microblaze/microblaze.cc
> index 60ad55120d2..3ea177b835e 100644
> --- a/gcc/config/microblaze/microblaze.cc
> +++ b/gcc/config/microblaze/microblaze.cc
> @@ -218,15 +218,14 @@ int break_handler;
>  int fast_interrupt;
>  int save_volatiles;
>
> -const struct attribute_spec microblaze_attribute_table[] = {
> +TARGET_GNU_ATTRIBUTES (microblaze_attribute_table, {
>    /* name         min_len, max_len, decl_req, type_req, fn_type_req,
>       affects_type_identity, handler, exclude */
>    {"interrupt_handler",        0,       0,    true, false, false, false, NULL, NULL },
>    {"break_handler",    0,       0,    true, false, false, false, NULL, NULL },
>    {"fast_interrupt",   0,       0,    true, false, false, false, NULL, NULL },
> -  {"save_volatiles",   0,       0,    true, false, false, false, NULL, NULL },
> -  { NULL,              0,       0,   false, false, false, false, NULL, NULL }
> -};
> +  {"save_volatiles",   0,       0,    true, false, false, false, NULL, NULL }
> +});
>
>  static int microblaze_interrupt_function_p (tree);
>
> diff --git a/gcc/config/mips/mips.cc b/gcc/config/mips/mips.cc
> index a304e1c5637..0cf49c13bf4 100644
> --- a/gcc/config/mips/mips.cc
> +++ b/gcc/config/mips/mips.cc
> @@ -611,7 +611,7 @@ static tree mips_handle_use_shadow_register_set_attr (tree *, tree, tree, int,
>                                                       bool *);
>
>  /* The value of TARGET_ATTRIBUTE_TABLE.  */
> -static const struct attribute_spec mips_attribute_table[] = {
> +TARGET_GNU_ATTRIBUTES (mips_attribute_table, {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
>    { "long_call",   0, 0, false, true,  true,  false, NULL, NULL },
> @@ -636,9 +636,8 @@ static const struct attribute_spec mips_attribute_table[] = {
>      mips_handle_use_shadow_register_set_attr, NULL },
>    { "keep_interrupts_masked",  0, 0, false, true,  true, false, NULL, NULL },
>    { "use_debug_exception_return", 0, 0, false, true, true, false, NULL, NULL },
> -  { "use_hazard_barrier_return", 0, 0, true, false, false, false, NULL, NULL },
> -  { NULL,         0, 0, false, false, false, false, NULL, NULL }
> -};
> +  { "use_hazard_barrier_return", 0, 0, true, false, false, false, NULL, NULL }
> +});
>
>  /* A table describing all the processors GCC knows about; see
>     mips-cpus.def for details.  */
> diff --git a/gcc/config/msp430/msp430.cc b/gcc/config/msp430/msp430.cc
> index 061a9c77961..85f499f175d 100644
> --- a/gcc/config/msp430/msp430.cc
> +++ b/gcc/config/msp430/msp430.cc
> @@ -2057,7 +2057,7 @@ static const struct attribute_spec::exclusions attr_either_exclusions[] =
>  #define TARGET_ATTRIBUTE_TABLE         msp430_attribute_table
>
>  /* Table of MSP430-specific attributes.  */
> -const struct attribute_spec msp430_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (msp430_attribute_table,
>    {
>      /* { name, min_num_args, max_num_args, decl_req, type_req, fn_type_req,
>          affects_type_identity, handler, exclude } */
> @@ -2075,10 +2075,8 @@ const struct attribute_spec msp430_attribute_table[] =
>      { ATTR_UPPER,       0, 0, true,  false, false, false, msp430_section_attr,
>        attr_upper_exclusions },
>      { ATTR_EITHER,      0, 0, true,  false, false, false, msp430_section_attr,
> -      attr_either_exclusions },
> -
> -    { NULL,            0, 0, false, false, false, false, NULL,  NULL }
> -  };
> +      attr_either_exclusions }
> +  });
>
>  #undef TARGET_HANDLE_GENERIC_ATTRIBUTE
>  #define TARGET_HANDLE_GENERIC_ATTRIBUTE msp430_handle_generic_attribute
> diff --git a/gcc/config/nds32/nds32.cc b/gcc/config/nds32/nds32.cc
> index 1f8de2a514a..e0a73985b66 100644
> --- a/gcc/config/nds32/nds32.cc
> +++ b/gcc/config/nds32/nds32.cc
> @@ -288,7 +288,7 @@ static const int nds32_reg_alloc_order_for_speed[] =
>  };
>
>  /* Defining target-specific uses of __attribute__.  */
> -static const struct attribute_spec nds32_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (nds32_attribute_table,
>  {
>    /* Syntax: { name, min_len, max_len, decl_required, type_required,
>                function_type_required, affects_type_identity, handler,
> @@ -326,11 +326,8 @@ static const struct attribute_spec nds32_attribute_table[] =
>
>    /* FOR BACKWARD COMPATIBILITY,
>       this attribute also tells no prologue/epilogue.  */
> -  { "no_prologue",  0,  0, false, false, false, false, NULL, NULL },
> -
> -  /* The last attribute spec is set to be NULL.  */
> -  { NULL,           0,  0, false, false, false, false, NULL, NULL }
> -};
> +  { "no_prologue",  0,  0, false, false, false, false, NULL, NULL }
> +});
>
>
>  /* ------------------------------------------------------------------------ */
> diff --git a/gcc/config/nvptx/nvptx.cc b/gcc/config/nvptx/nvptx.cc
> index 634c31673be..d5497631429 100644
> --- a/gcc/config/nvptx/nvptx.cc
> +++ b/gcc/config/nvptx/nvptx.cc
> @@ -5834,16 +5834,15 @@ nvptx_handle_shared_attribute (tree *node, tree name, tree ARG_UNUSED (args),
>  }
>
>  /* Table of valid machine attributes.  */
> -static const struct attribute_spec nvptx_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (nvptx_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
>    { "kernel", 0, 0, true, false,  false, false, nvptx_handle_kernel_attribute,
>      NULL },
>    { "shared", 0, 0, true, false,  false, false, nvptx_handle_shared_attribute,
> -    NULL },
> -  { NULL, 0, 0, false, false, false, false, NULL, NULL }
> -};
> +    NULL }
> +});
>
>  /* Limit vector alignments to BIGGEST_ALIGNMENT.  */
>
> diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
> index e36b5fb9bd0..154d80b48e7 100644
> --- a/gcc/config/riscv/riscv.cc
> +++ b/gcc/config/riscv/riscv.cc
> @@ -421,7 +421,7 @@ static tree riscv_handle_type_attribute (tree *, tree, tree, int, bool *);
>  static void riscv_legitimize_poly_move (machine_mode, rtx, rtx, rtx);
>
>  /* Defining target-specific uses of __attribute__.  */
> -static const struct attribute_spec riscv_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (riscv_attribute_table,
>  {
>    /* Syntax: { name, min_len, max_len, decl_required, type_required,
>                function_type_required, affects_type_identity, handler,
> @@ -437,11 +437,8 @@ static const struct attribute_spec riscv_attribute_table[] =
>    /* The following two are used for the built-in properties of the Vector type
>       and are not used externally */
>    {"RVV sizeless type", 4, 4, false, true, false, true, NULL, NULL},
> -  {"RVV type", 0, 0, false, true, false, true, NULL, NULL},
> -
> -  /* The last attribute spec is set to be NULL.  */
> -  { NULL,      0,  0, false, false, false, false, NULL, NULL }
> -};
> +  {"RVV type", 0, 0, false, true, false, true, NULL, NULL}
> +});
>
>  /* Order for the CLOBBERs/USEs of gpr_save.  */
>  static const unsigned gpr_save_reg_order[] = {
> diff --git a/gcc/config/rl78/rl78.cc b/gcc/config/rl78/rl78.cc
> index 0cbd6bf780a..b2e9b04b691 100644
> --- a/gcc/config/rl78/rl78.cc
> +++ b/gcc/config/rl78/rl78.cc
> @@ -898,7 +898,7 @@ rl78_handle_vector_attribute (tree * node,
>  #define TARGET_ATTRIBUTE_TABLE         rl78_attribute_table
>
>  /* Table of RL78-specific attributes.  */
> -const struct attribute_spec rl78_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (rl78_attribute_table,
>  {
>    /* Name, min_len, max_len, decl_req, type_req, fn_type_req,
>       affects_type_identity, handler, exclude.  */
> @@ -911,9 +911,8 @@ const struct attribute_spec rl78_attribute_table[] =
>    { "saddr",          0, 0, true, false, false, false,
>      rl78_handle_saddr_attribute, NULL },
>    { "vector",         1, -1, true, false, false, false,
> -       rl78_handle_vector_attribute, NULL },
> -  { NULL,             0, 0, false, false, false, false, NULL, NULL }
> -};
> +       rl78_handle_vector_attribute, NULL }
> +});
>
>
>
> diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc
> index 5f56c3ed85b..cc3f97ae4c5 100644
> --- a/gcc/config/rs6000/rs6000.cc
> +++ b/gcc/config/rs6000/rs6000.cc
> @@ -1255,7 +1255,7 @@ static const char alt_reg_names[][8] =
>
>  /* Table of valid machine attributes.  */
>
> -static const struct attribute_spec rs6000_attribute_table[] =
> +static const attribute_spec rs6000_gnu_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -1272,7 +1272,16 @@ static const struct attribute_spec rs6000_attribute_table[] =
>  #ifdef SUBTARGET_ATTRIBUTE_TABLE
>    SUBTARGET_ATTRIBUTE_TABLE,
>  #endif
> -  { NULL,        0, 0, false, false, false, false, NULL, NULL }
> +};
> +
> +static const scoped_attribute_specs rs6000_gnu_attribute_table =
> +{
> +  "gnu", rs6000_gnu_attributes
> +};
> +
> +static const scoped_attribute_specs *const rs6000_attribute_table[] =
> +{
> +  &rs6000_gnu_attribute_table
>  };
>
>  #ifndef TARGET_PROFILE_KERNEL
> diff --git a/gcc/config/rx/rx.cc b/gcc/config/rx/rx.cc
> index 245c6a4413d..0754e286552 100644
> --- a/gcc/config/rx/rx.cc
> +++ b/gcc/config/rx/rx.cc
> @@ -2760,7 +2760,7 @@ rx_handle_vector_attribute (tree * node,
>  }
>
>  /* Table of RX specific attributes.  */
> -const struct attribute_spec rx_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (rx_attribute_table,
>  {
>    /* Name, min_len, max_len, decl_req, type_req, fn_type_req,
>       affects_type_identity, handler, exclude.  */
> @@ -2771,9 +2771,8 @@ const struct attribute_spec rx_attribute_table[] =
>    { "naked",          0, 0, true, false, false, false,
>      rx_handle_func_attribute, NULL },
>    { "vector",         1, -1, true, false, false, false,
> -    rx_handle_vector_attribute, NULL },
> -  { NULL,             0, 0, false, false, false, false, NULL, NULL }
> -};
> +    rx_handle_vector_attribute, NULL }
> +});
>
>  /* Implement TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE.  */
>
> diff --git a/gcc/config/s390/s390.cc b/gcc/config/s390/s390.cc
> index 64f56d8effa..a8205385451 100644
> --- a/gcc/config/s390/s390.cc
> +++ b/gcc/config/s390/s390.cc
> @@ -1303,7 +1303,7 @@ s390_handle_string_attribute (tree *node, tree name ATTRIBUTE_UNUSED,
>    return NULL_TREE;
>  }
>
> -static const struct attribute_spec s390_attribute_table[] = {
> +TARGET_GNU_ATTRIBUTES (s390_attribute_table, {
>    { "hotpatch", 2, 2, true, false, false, false,
>      s390_handle_hotpatch_attribute, NULL },
>    { "s390_vector_bool", 0, 0, false, true, false, true,
> @@ -1319,11 +1319,8 @@ static const struct attribute_spec s390_attribute_table[] = {
>    { "function_return_reg", 1, 1, true, false, false, false,
>      s390_handle_string_attribute, NULL },
>    { "function_return_mem", 1, 1, true, false, false, false,
> -    s390_handle_string_attribute, NULL },
> -
> -  /* End element.  */
> -  { NULL,        0, 0, false, false, false, false, NULL, NULL }
> -};
> +    s390_handle_string_attribute, NULL }
> +});
>
>  /* Return the alignment for LABEL.  We default to the -falign-labels
>     value except for the literal pool base label.  */
> diff --git a/gcc/config/sh/sh.cc b/gcc/config/sh/sh.cc
> index 6ec2eecf754..8c378b28b6d 100644
> --- a/gcc/config/sh/sh.cc
> +++ b/gcc/config/sh/sh.cc
> @@ -329,7 +329,7 @@ static bool sh_hard_regno_mode_ok (unsigned int, machine_mode);
>  static bool sh_modes_tieable_p (machine_mode, machine_mode);
>  static bool sh_can_change_mode_class (machine_mode, machine_mode, reg_class_t);
>
> -static const struct attribute_spec sh_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (sh_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -348,9 +348,8 @@ static const struct attribute_spec sh_attribute_table[] =
>    { "resbank",           0, 0, true,  false, false, false,
>      sh_handle_resbank_handler_attribute, NULL },
>    { "function_vector",   1, 1, true,  false, false, false,
> -    sh2a_handle_function_vector_handler_attribute, NULL },
> -  { NULL,                0, 0, false, false, false, false, NULL, NULL }
> -};
> +    sh2a_handle_function_vector_handler_attribute, NULL }
> +});
>
>  /* Initialize the GCC target structure.  */
>  #undef TARGET_ATTRIBUTE_TABLE
> diff --git a/gcc/config/sparc/sparc.cc b/gcc/config/sparc/sparc.cc
> index 82e57952414..a78dedc8075 100644
> --- a/gcc/config/sparc/sparc.cc
> +++ b/gcc/config/sparc/sparc.cc
> @@ -721,13 +721,12 @@ static HARD_REG_SET sparc_zero_call_used_regs (HARD_REG_SET);
>
>  #ifdef SUBTARGET_ATTRIBUTE_TABLE
>  /* Table of valid machine attributes.  */
> -static const struct attribute_spec sparc_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (sparc_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         do_diagnostic, handler, exclude } */
> -  SUBTARGET_ATTRIBUTE_TABLE,
> -  { NULL,        0, 0, false, false, false, false, NULL, NULL }
> -};
> +  SUBTARGET_ATTRIBUTE_TABLE
> +});
>  #endif
>
>  char sparc_hard_reg_printed[8];
> diff --git a/gcc/config/stormy16/stormy16.cc b/gcc/config/stormy16/stormy16.cc
> index 10887153906..071043b4128 100644
> --- a/gcc/config/stormy16/stormy16.cc
> +++ b/gcc/config/stormy16/stormy16.cc
> @@ -2377,7 +2377,7 @@ static tree xstormy16_handle_interrupt_attribute
>  static tree xstormy16_handle_below100_attribute
>    (tree *, tree, tree, int, bool *);
>
> -static const struct attribute_spec xstormy16_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (xstormy16_attribute_table,
>  {
>    /* name, min_len, max_len, decl_req, type_req, fn_type_req,
>       affects_type_identity, handler, exclude.  */
> @@ -2386,9 +2386,8 @@ static const struct attribute_spec xstormy16_attribute_table[] =
>    { "BELOW100",  0, 0, false, false, false, false,
>      xstormy16_handle_below100_attribute, NULL },
>    { "below100",  0, 0, false, false, false, false,
> -    xstormy16_handle_below100_attribute, NULL },
> -  { NULL,        0, 0, false, false, false, false, NULL, NULL }
> -};
> +    xstormy16_handle_below100_attribute, NULL }
> +});
>
>  /* Handle an "interrupt" attribute;
>     arguments as in struct attribute_spec.handler.  */
> diff --git a/gcc/config/v850/v850.cc b/gcc/config/v850/v850.cc
> index 416c2841a5c..50c91c68b8b 100644
> --- a/gcc/config/v850/v850.cc
> +++ b/gcc/config/v850/v850.cc
> @@ -3114,7 +3114,7 @@ v850_adjust_insn_length (rtx_insn *insn, int length)
>
>  /* V850 specific attributes.  */
>
> -static const struct attribute_spec v850_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (v850_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -3127,9 +3127,8 @@ static const struct attribute_spec v850_attribute_table[] =
>    { "tda",               0, 0, true,  false, false, false,
>      v850_handle_data_area_attribute, NULL },
>    { "zda",               0, 0, true,  false, false, false,
> -    v850_handle_data_area_attribute, NULL },
> -  { NULL,                0, 0, false, false, false, false, NULL, NULL }
> -};
> +    v850_handle_data_area_attribute, NULL }
> +});
>
>  static void
>  v850_option_override (void)
> diff --git a/gcc/config/visium/visium.cc b/gcc/config/visium/visium.cc
> index 5fadbc80be0..4a1877c2ac1 100644
> --- a/gcc/config/visium/visium.cc
> +++ b/gcc/config/visium/visium.cc
> @@ -145,14 +145,13 @@ static inline bool current_function_has_lr_slot (void);
>
>  /* Supported attributes:
>     interrupt -- specifies this function is an interrupt handler.   */
> -static const struct attribute_spec visium_attribute_table[] =
> +TARGET_GNU_ATTRIBUTES (visium_attribute_table,
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
>    { "interrupt", 0, 0, true, false, false, false, visium_handle_interrupt_attr,
> -    NULL},
> -  { NULL, 0, 0, false, false, false, false, NULL, NULL },
> -};
> +    NULL}
> +});
>
>  static struct machine_function *visium_init_machine_status (void);
>
> diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h
> index 1408301a300..d3fa002593f 100644
> --- a/gcc/cp/cp-objcp-common.h
> +++ b/gcc/cp/cp-objcp-common.h
> @@ -122,13 +122,16 @@ extern tree cxx_simulate_record_decl (location_t, const char *,
>  #undef LANG_HOOKS_FINALIZE_EARLY_DEBUG
>  #define LANG_HOOKS_FINALIZE_EARLY_DEBUG c_common_finalize_early_debug
>
> -/* Attribute hooks.  */
> -#undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
> -#define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE c_common_attribute_table
> -#undef LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE
> -#define LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE c_common_format_attribute_table
> +static const scoped_attribute_specs *const cp_objcp_attribute_table[] =
> +{
> +  &std_attribute_table,
> +  &cxx_gnu_attribute_table,
> +  &c_common_gnu_attribute_table,
> +  &c_common_format_attribute_table
> +};
> +
>  #undef LANG_HOOKS_ATTRIBUTE_TABLE
> -#define LANG_HOOKS_ATTRIBUTE_TABLE cxx_attribute_table
> +#define LANG_HOOKS_ATTRIBUTE_TABLE cp_objcp_attribute_table
>
>  #undef LANG_HOOKS_TREE_INLINING_VAR_MOD_TYPE_P
>  #define LANG_HOOKS_TREE_INLINING_VAR_MOD_TYPE_P cp_var_mod_type_p
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 98b29e9cf81..0a543c5f14c 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -8000,7 +8000,8 @@ extern tree maybe_dummy_object                    (tree, tree *);
>  extern bool is_dummy_object                    (const_tree);
>  extern bool is_byte_access_type                        (tree);
>  extern bool is_byte_access_type_not_plain_char (tree);
> -extern const struct attribute_spec cxx_attribute_table[];
> +extern const struct scoped_attribute_specs cxx_gnu_attribute_table;
> +extern const struct scoped_attribute_specs std_attribute_table;
>  extern tree make_ptrmem_cst                    (tree, tree);
>  extern tree cp_build_type_attribute_variant     (tree, tree);
>  extern tree cp_build_reference_type            (tree, bool);
> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> index 417c92ba76f..fd99d64ca47 100644
> --- a/gcc/cp/tree.cc
> +++ b/gcc/cp/tree.cc
> @@ -5072,7 +5072,7 @@ handle_likeliness_attribute (tree *node, tree name, tree args,
>  }
>
>  /* Table of valid C++ attributes.  */
> -const struct attribute_spec cxx_attribute_table[] =
> +static const attribute_spec cxx_gnu_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -5080,11 +5080,15 @@ const struct attribute_spec cxx_attribute_table[] =
>      handle_init_priority_attribute, NULL },
>    { "abi_tag", 1, -1, false, false, false, true,
>      handle_abi_tag_attribute, NULL },
> -  { NULL, 0, 0, false, false, false, false, NULL, NULL }
> +};
> +
> +const scoped_attribute_specs cxx_gnu_attribute_table =
> +{
> +  "gnu", cxx_gnu_attributes
>  };
>
>  /* Table of C++ standard attributes.  */
> -const struct attribute_spec std_attribute_table[] =
> +static const attribute_spec std_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -5105,10 +5109,11 @@ const struct attribute_spec std_attribute_table[] =
>    { "pre", 0, -1, false, false, false, false,
>      handle_contract_attribute, NULL },
>    { "post", 0, -1, false, false, false, false,
> -    handle_contract_attribute, NULL },
> -  { NULL, 0, 0, false, false, false, false, NULL, NULL }
> +    handle_contract_attribute, NULL }
>  };
>
> +const scoped_attribute_specs std_attribute_table = { nullptr, std_attributes };
> +
>  /* Handle an "init_priority" attribute; arguments as in
>     struct attribute_spec.handler.  */
>  static tree
> @@ -5704,7 +5709,6 @@ void
>  init_tree (void)
>  {
>    list_hash_table = hash_table<list_hasher>::create_ggc (61);
> -  register_scoped_attributes (std_attribute_table, NULL);
>  }
>
>  /* Returns the kind of special function that DECL (a FUNCTION_DECL)
> diff --git a/gcc/d/d-attribs.cc b/gcc/d/d-attribs.cc
> index c0dc0e24ded..f6411058072 100644
> --- a/gcc/d/d-attribs.cc
> +++ b/gcc/d/d-attribs.cc
> @@ -162,7 +162,7 @@ extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[] =
>
>  /* Table of machine-independent attributes.
>     For internal use (marking of built-ins) only.  */
> -const attribute_spec d_langhook_common_attribute_table[] =
> +static const attribute_spec d_langhook_common_attributes[] =
>  {
>    ATTR_SPEC ("noreturn", 0, 0, true, false, false, false,
>              handle_noreturn_attribute, attr_noreturn_exclusions),
> @@ -190,11 +190,15 @@ const attribute_spec d_langhook_common_attribute_table[] =
>              handle_fnspec_attribute, NULL),
>    ATTR_SPEC ("omp declare simd", 0, -1, true,  false, false, false,
>              handle_omp_declare_simd_attribute, NULL),
> -  ATTR_SPEC (NULL, 0, 0, false, false, false, false, NULL, NULL),
> +};
> +
> +const scoped_attribute_specs d_langhook_common_attribute_table =
> +{
> +  "gnu", d_langhook_common_attributes
>  };
>
>  /* Table of D language attributes exposed by `gcc.attribute' UDAs.  */
> -const attribute_spec d_langhook_attribute_table[] =
> +static const attribute_spec d_langhook_gnu_attributes[] =
>  {
>    ATTR_SPEC ("noinline", 0, 0, true, false, false, false,
>              d_handle_noinline_attribute, attr_noinline_exclusions),
> @@ -238,9 +242,12 @@ const attribute_spec d_langhook_attribute_table[] =
>              d_handle_used_attribute, NULL),
>    ATTR_SPEC ("visibility", 1, 1, false, false, false, false,
>              d_handle_visibility_attribute, NULL),
> -  ATTR_SPEC (NULL, 0, 0, false, false, false, false, NULL, NULL),
>  };
>
> +const scoped_attribute_specs d_langhook_gnu_attribute_table =
> +{
> +  "gnu", d_langhook_gnu_attributes
> +};
>
>  /* Insert the type attribute ATTRNAME with value VALUE into TYPE.
>     Returns a new variant of the original type declaration.  */
> @@ -283,20 +290,14 @@ uda_attribute_p (const char *name)
>
>    /* Search both our language, and target attribute tables.
>       Common and format attributes are kept internal.  */
> -  for (const attribute_spec *p = d_langhook_attribute_table; p->name; p++)
> -    {
> -      if (get_identifier (p->name) == ident)
> -       return true;
> -    }
> +  for (const attribute_spec &p : d_langhook_gnu_attributes)
> +    if (get_identifier (p.name) == ident)
> +      return true;
>
> -  if (targetm.attribute_table)
> -    {
> -      for (const attribute_spec *p = targetm.attribute_table; p->name; p++)
> -       {
> -         if (get_identifier (p->name) == ident)
> -           return true;
> -       }
> -    }
> +  for (auto scoped_attributes : targetm.attribute_table)
> +    for (const attribute_spec &p : scoped_attributes->attributes)
> +      if (get_identifier (p.name) == ident)
> +       return true;
>
>    return false;
>  }
> diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc
> index 61fc1608b40..dcbffec3752 100644
> --- a/gcc/d/d-lang.cc
> +++ b/gcc/d/d-lang.cc
> @@ -1927,6 +1927,12 @@ d_get_sarif_source_language (const char *)
>    return "d";
>  }
>
> +const scoped_attribute_specs *const d_langhook_attribute_table[] =
> +{
> +  &d_langhook_gnu_attribute_table,
> +  &d_langhook_common_attribute_table,
> +};
> +
>  /* Definitions for our language-specific hooks.  */
>
>  #undef LANG_HOOKS_NAME
> @@ -1938,7 +1944,6 @@ d_get_sarif_source_language (const char *)
>  #undef LANG_HOOKS_HANDLE_OPTION
>  #undef LANG_HOOKS_POST_OPTIONS
>  #undef LANG_HOOKS_PARSE_FILE
> -#undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
>  #undef LANG_HOOKS_ATTRIBUTE_TABLE
>  #undef LANG_HOOKS_GET_ALIAS_SET
>  #undef LANG_HOOKS_TYPES_COMPATIBLE_P
> @@ -1971,7 +1976,6 @@ d_get_sarif_source_language (const char *)
>  #define LANG_HOOKS_HANDLE_OPTION           d_handle_option
>  #define LANG_HOOKS_POST_OPTIONS                    d_post_options
>  #define LANG_HOOKS_PARSE_FILE              d_parse_file
> -#define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE   d_langhook_common_attribute_table
>  #define LANG_HOOKS_ATTRIBUTE_TABLE         d_langhook_attribute_table
>  #define LANG_HOOKS_GET_ALIAS_SET           d_get_alias_set
>  #define LANG_HOOKS_TYPES_COMPATIBLE_P      d_types_compatible_p
> diff --git a/gcc/d/d-tree.h b/gcc/d/d-tree.h
> index d19c3f50bd9..46a28737daa 100644
> --- a/gcc/d/d-tree.h
> +++ b/gcc/d/d-tree.h
> @@ -520,8 +520,8 @@ extern tree insert_decl_attribute (tree, const char *, tree = NULL_TREE);
>  extern void apply_user_attributes (Dsymbol *, tree);
>
>  /* In d-builtins.cc.  */
> -extern const attribute_spec d_langhook_attribute_table[];
> -extern const attribute_spec d_langhook_common_attribute_table[];
> +extern const struct scoped_attribute_specs d_langhook_gnu_attribute_table;
> +extern const struct scoped_attribute_specs d_langhook_common_attribute_table;
>  extern Type *build_frontend_type (tree);
>
>  extern tree d_builtin_function (tree);
> diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
> index d83ca73b1af..abc23b2bb73 100644
> --- a/gcc/doc/tm.texi
> +++ b/gcc/doc/tm.texi
> @@ -10528,12 +10528,33 @@ Target-specific attributes may be defined for functions, data and types.
>  These are described using the following target hooks; they also need to
>  be documented in @file{extend.texi}.
>
> -@deftypevr {Target Hook} {const struct attribute_spec *} TARGET_ATTRIBUTE_TABLE
> -If defined, this target hook points to an array of @samp{struct
> -attribute_spec} (defined in @file{tree-core.h}) specifying the machine
> -specific attributes for this target and some of the restrictions on the
> -entities to which these attributes are applied and the arguments they
> -take.
> +@deftypevr {Target Hook} {array_slice<const struct scoped_attribute_specs *const>} TARGET_ATTRIBUTE_TABLE
> +If defined, this target hook provides an array of
> +@samp{scoped_attribute_spec}s (defined in @file{attribs.h}) that specify the
> +machine-specific attributes for this target.  The information includes some
> +of the restrictions on the entities to which these attributes are applied
> +and the arguments that the attributes take.
> +
> +In C and C++, these attributes are associated with two syntaxes:
> +the traditional GNU @code{__attribute__} syntax and the standard
> +@samp{[[]]} syntax.  Attributes that support the GNU syntax must be
> +placed in the @code{gnu} namespace.  Such attributes can then also be
> +written @samp{[[gnu::@dots{}]]}.  Attributes that use only the standard
> +syntax should be placed in whichever namespace the attribute specification
> +requires.  For example, a target might choose to support vendor-specific
> +@samp{[[]]} attributes that the vendor places in their own namespace.
> +
> +Targets that only define attributes in the @code{gnu} namespace
> +can uase the following shorthand to define the table:
> +
> +@smallexample
> +TARGET_GNU_ATTRIBUTES (@var{cpu_attribute_table}, @{
> +  @{ "@var{attribute1}", @dots{} @},
> +  @{ "@var{attribute2}", @dots{} @},
> +  @dots{},
> +  @{ "@var{attributen}", @dots{} @},
> +@});
> +@end smallexample
>  @end deftypevr
>
>  @deftypefn {Target Hook} bool TARGET_ATTRIBUTE_TAKES_IDENTIFIER_P (const_tree @var{name})
> diff --git a/gcc/fortran/f95-lang.cc b/gcc/fortran/f95-lang.cc
> index 350e6e379eb..99dd76226a2 100644
> --- a/gcc/fortran/f95-lang.cc
> +++ b/gcc/fortran/f95-lang.cc
> @@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "cpp.h"
>  #include "trans-types.h"
>  #include "trans-const.h"
> +#include "attribs.h"
>
>  /* Language-dependent contents of an identifier.  */
>
> @@ -87,7 +88,7 @@ gfc_handle_omp_declare_target_attribute (tree *, tree, tree, int, bool *)
>  }
>
>  /* Table of valid Fortran attributes.  */
> -static const struct attribute_spec gfc_attribute_table[] =
> +static const attribute_spec gfc_gnu_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -97,7 +98,16 @@ static const struct attribute_spec gfc_attribute_table[] =
>      gfc_handle_omp_declare_target_attribute, NULL },
>    { "oacc function", 0, -1, true,  false, false, false,
>      gfc_handle_omp_declare_target_attribute, NULL },
> -  { NULL,                0, 0, false, false, false, false, NULL, NULL }
> +};
> +
> +static const scoped_attribute_specs gfc_gnu_attribute_table =
> +{
> +  "gnu", gfc_gnu_attributes
> +};
> +
> +static const scoped_attribute_specs *const gfc_attribute_table[] =
> +{
> +  &gfc_gnu_attribute_table
>  };
>
>  /* Get a value for the SARIF v2.1.0 "artifact.sourceLanguage" property,
> diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
> index a729086bafb..61cc0e1ae66 100644
> --- a/gcc/jit/dummy-frontend.cc
> +++ b/gcc/jit/dummy-frontend.cc
> @@ -87,7 +87,7 @@ static const struct attribute_spec::exclusions attr_const_pure_exclusions[] =
>  };
>
>  /* Table of machine-independent attributes supported in libgccjit.  */
> -const struct attribute_spec jit_attribute_table[] =
> +static const attribute_spec jit_gnu_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -128,22 +128,36 @@ const struct attribute_spec jit_attribute_table[] =
>    /* For internal use only.  The leading '*' both prevents its usage in
>       source code and signals that it may be overridden by machine tables.  */
>    { "*tm regparm",            0, 0, false, true, true, false,
> -                             ignore_attribute, NULL },
> -  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
> +                             ignore_attribute, NULL }
> +};
> +
> +static const scoped_attribute_specs jit_gnu_attribute_table =
> +{
> +  "gnu", jit_gnu_attributes
>  };
>
>  /* Give the specifications for the format attributes, used by C and all
>     descendants.  */
>
> -const struct attribute_spec jit_format_attribute_table[] =
> +static const attribute_spec jit_format_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
>    { "format",                 3, 3, false, true,  true, false,
>                               handle_format_attribute, NULL },
>    { "format_arg",             1, 1, false, true,  true, false,
> -                             handle_format_arg_attribute, NULL },
> -  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
> +                             handle_format_arg_attribute, NULL }
> +};
> +
> +static const scoped_attribute_specs jit_format_attribute_table =
> +{
> +  "gnu", jit_format_attributes
> +};
> +
> +static const scoped_attribute_specs *const jit_attribute_table[] =
> +{
> +  &jit_gnu_attribute_table,
> +  &jit_format_attribute_table
>  };
>
>  /* Attribute handlers.  */
> @@ -719,10 +733,8 @@ jit_langhook_getdecls (void)
>  #define LANG_HOOKS_GETDECLS            jit_langhook_getdecls
>
>  /* Attribute hooks.  */
> -#undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
> -#define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE jit_attribute_table
> -#undef LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE
> -#define LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE jit_format_attribute_table
> +#undef LANG_HOOKS_ATTRIBUTE_TABLE
> +#define LANG_HOOKS_ATTRIBUTE_TABLE jit_attribute_table
>
>  #undef  LANG_HOOKS_DEEP_UNSHARING
>  #define LANG_HOOKS_DEEP_UNSHARING      true
> diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
> index c6d18526360..c9cb65759c2 100644
> --- a/gcc/langhooks-def.h
> +++ b/gcc/langhooks-def.h
> @@ -153,9 +153,7 @@ extern const char *lhd_get_sarif_source_language (const char *);
>  #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE lhd_get_sarif_source_language
>
>  /* Attribute hooks.  */
> -#define LANG_HOOKS_ATTRIBUTE_TABLE             NULL
> -#define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE      NULL
> -#define LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE      NULL
> +#define LANG_HOOKS_ATTRIBUTE_TABLE             {}
>
>  /* Tree inlining hooks.  */
>  #define LANG_HOOKS_TREE_INLINING_VAR_MOD_TYPE_P \
> @@ -367,8 +365,6 @@ extern void lhd_end_section (void);
>    LANG_HOOKS_PRINT_ERROR_FUNCTION, \
>    LANG_HOOKS_TO_TARGET_CHARSET, \
>    LANG_HOOKS_ATTRIBUTE_TABLE, \
> -  LANG_HOOKS_COMMON_ATTRIBUTE_TABLE, \
> -  LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE, \
>    LANG_HOOKS_TREE_INLINING_INITIALIZER, \
>    LANG_HOOKS_TREE_DUMP_INITIALIZER, \
>    LANG_HOOKS_DECLS, \
> diff --git a/gcc/langhooks.h b/gcc/langhooks.h
> index cca75285fc2..2785a0070fc 100644
> --- a/gcc/langhooks.h
> +++ b/gcc/langhooks.h
> @@ -532,9 +532,7 @@ struct lang_hooks
>       table of attributes specific to the language, a table of
>       attributes common to two or more languages (to allow easy
>       sharing), and a table of attributes for checking formats.  */
> -  const struct attribute_spec *attribute_table;
> -  const struct attribute_spec *common_attribute_table;
> -  const struct attribute_spec *format_attribute_table;
> +  array_slice<const struct scoped_attribute_specs *const> attribute_table;
>
>    struct lang_hooks_for_tree_inlining tree_inlining;
>
> diff --git a/gcc/lto/lto-lang.cc b/gcc/lto/lto-lang.cc
> index 14d419c2013..a8adf110b20 100644
> --- a/gcc/lto/lto-lang.cc
> +++ b/gcc/lto/lto-lang.cc
> @@ -94,7 +94,7 @@ static const struct attribute_spec::exclusions attr_const_pure_exclusions[] =
>  };
>
>  /* Table of machine-independent attributes supported in GIMPLE.  */
> -const struct attribute_spec lto_attribute_table[] =
> +static const attribute_spec lto_gnu_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -135,14 +135,18 @@ const struct attribute_spec lto_attribute_table[] =
>    /* For internal use only.  The leading '*' both prevents its usage in
>       source code and signals that it may be overridden by machine tables.  */
>    { "*tm regparm",            0, 0, false, true, true, false,
> -                             ignore_attribute, NULL },
> -  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
> +                             ignore_attribute, NULL }
> +};
> +
> +static const scoped_attribute_specs lto_gnu_attribute_table =
> +{
> +  "gnu", lto_gnu_attributes
>  };
>
>  /* Give the specifications for the format attributes, used by C and all
>     descendants.  */
>
> -const struct attribute_spec lto_format_attribute_table[] =
> +static const attribute_spec lto_format_attributes[] =
>  {
>    /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
>         affects_type_identity, handler, exclude } */
> @@ -150,7 +154,17 @@ const struct attribute_spec lto_format_attribute_table[] =
>                               handle_format_attribute, NULL },
>    { "format_arg",             1, 1, false, true,  true, false,
>                               handle_format_arg_attribute, NULL },
> -  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
> +};
> +
> +static const scoped_attribute_specs lto_format_attribute_table =
> +{
> +  "gnu", lto_format_attributes
> +};
> +
> +static const scoped_attribute_specs *const lto_attribute_table[] =
> +{
> +  &lto_gnu_attribute_table,
> +  &lto_format_attribute_table
>  };
>
>  enum built_in_attribute
> @@ -1463,10 +1477,8 @@ static void lto_init_ts (void)
>  #define LANG_HOOKS_EH_PERSONALITY lto_eh_personality
>
>  /* Attribute hooks.  */
> -#undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
> -#define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE lto_attribute_table
> -#undef LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE
> -#define LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE lto_format_attribute_table
> +#undef LANG_HOOKS_ATTRIBUTE_TABLE
> +#define LANG_HOOKS_ATTRIBUTE_TABLE lto_attribute_table
>
>  #undef LANG_HOOKS_BEGIN_SECTION
>  #define LANG_HOOKS_BEGIN_SECTION lto_obj_begin_section
> diff --git a/gcc/plugin.h b/gcc/plugin.h
> index ee0a53ec4c9..f306adfb3b9 100644
> --- a/gcc/plugin.h
> +++ b/gcc/plugin.h
> @@ -201,8 +201,7 @@ invoke_plugin_callbacks (int event ATTRIBUTE_UNUSED,
>
>  extern void register_attribute (const struct attribute_spec *attr);
>  /* The default argument for the third parameter is given in attribs.h.  */
> -extern struct scoped_attributes* register_scoped_attributes (const struct attribute_spec *,
> -                                                            const char *,
> +extern struct scoped_attributes* register_scoped_attributes (const struct scoped_attribute_spec &,
>                                                              bool);
>
>  #endif /* PLUGIN_H */
> diff --git a/gcc/target-def.h b/gcc/target-def.h
> index 847698a1e59..d03b039ab24 100644
> --- a/gcc/target-def.h
> +++ b/gcc/target-def.h
> @@ -118,6 +118,20 @@
>  #define TARGET_FUNCTION_INCOMING_ARG TARGET_FUNCTION_ARG
>  #endif
>
> +/* Declare a target attribute table called NAME that only has GNU attributes.
> +   There should be no null trailing element.  E.g.:
> +
> +     TARGET_GNU_ATTRIBUTES (aarch64_attribute_table,
> +     {
> +       { "aarch64_vector_pcs", ... },
> +       ...
> +     });  */
> +
> +#define TARGET_GNU_ATTRIBUTES(NAME, ...) \
> +  static const attribute_spec NAME##_2[] = __VA_ARGS__; \
> +  static const scoped_attribute_specs NAME##_1 = { "gnu", NAME##_2 }; \
> +  static const scoped_attribute_specs *const NAME[] = { &NAME##_1 }
> +
>  #include "target-hooks-def.h"
>
>  #include "hooks.h"
> diff --git a/gcc/target.def b/gcc/target.def
> index 0996da0f71a..06c5d3b5c2e 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -2218,15 +2218,36 @@ merging.",
>   merge_type_attributes)
>
>  /* Table of machine attributes and functions to handle them.
> -   Ignored if NULL.  */
> +   Ignored if empty.  */
>  DEFHOOKPOD
>  (attribute_table,
> - "If defined, this target hook points to an array of @samp{struct\n\
> -attribute_spec} (defined in @file{tree-core.h}) specifying the machine\n\
> -specific attributes for this target and some of the restrictions on the\n\
> -entities to which these attributes are applied and the arguments they\n\
> -take.",
> - const struct attribute_spec *, NULL)
> + "If defined, this target hook provides an array of\n\
> +@samp{scoped_attribute_spec}s (defined in @file{attribs.h}) that specify the\n\
> +machine-specific attributes for this target.  The information includes some\n\
> +of the restrictions on the entities to which these attributes are applied\n\
> +and the arguments that the attributes take.\n\
> +\n\
> +In C and C++, these attributes are associated with two syntaxes:\n\
> +the traditional GNU @code{__attribute__} syntax and the standard\n\
> +@samp{[[]]} syntax.  Attributes that support the GNU syntax must be\n\
> +placed in the @code{gnu} namespace.  Such attributes can then also be\n\
> +written @samp{[[gnu::@dots{}]]}.  Attributes that use only the standard\n\
> +syntax should be placed in whichever namespace the attribute specification\n\
> +requires.  For example, a target might choose to support vendor-specific\n\
> +@samp{[[]]} attributes that the vendor places in their own namespace.\n\
> +\n\
> +Targets that only define attributes in the @code{gnu} namespace\n\
> +can uase the following shorthand to define the table:\n\
> +\n\
> +@smallexample\n\
> +TARGET_GNU_ATTRIBUTES (@var{cpu_attribute_table}, @{\n\
> +  @{ \"@var{attribute1}\", @dots{} @},\n\
> +  @{ \"@var{attribute2}\", @dots{} @},\n\
> +  @dots{},\n\
> +  @{ \"@var{attributen}\", @dots{} @},\n\
> +@});\n\
> +@end smallexample",
> + array_slice<const struct scoped_attribute_specs *const>, {})
>
>  /* Return true iff attribute NAME expects a plain identifier as its first
>     argument.  */
> diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
> index 00d05102e7a..2831a0368f6 100644
> --- a/gcc/tree-inline.cc
> +++ b/gcc/tree-inline.cc
> @@ -4094,17 +4094,16 @@ inline_forbidden_p (tree fndecl)
>  static bool
>  function_attribute_inlinable_p (const_tree fndecl)
>  {
> -  if (targetm.attribute_table)
> +  for (auto scoped_attributes : targetm.attribute_table)
>      {
>        const_tree a;
>
>        for (a = DECL_ATTRIBUTES (fndecl); a; a = TREE_CHAIN (a))
>         {
>           const_tree name = get_attribute_name (a);
> -         int i;
>
> -         for (i = 0; targetm.attribute_table[i].name != NULL; i++)
> -           if (is_attribute_p (targetm.attribute_table[i].name, name))
> +         for (const attribute_spec &attribute : scoped_attributes->attributes)
> +           if (is_attribute_p (attribute.name, name))
>               return targetm.function_attribute_inlinable_p (fndecl);
>         }
>      }
  

Patch

diff --git a/gcc/ada/gcc-interface/gigi.h b/gcc/ada/gcc-interface/gigi.h
index eb5496f50db..63ccf311c23 100644
--- a/gcc/ada/gcc-interface/gigi.h
+++ b/gcc/ada/gcc-interface/gigi.h
@@ -350,7 +350,7 @@  struct attrib
 };
 
 /* Table of machine-independent internal attributes.  */
-extern const struct attribute_spec gnat_internal_attribute_table[];
+extern const struct scoped_attribute_specs gnat_internal_attribute_table;
 
 /* Define the entries in the standard data array.  */
 enum standard_datatypes
diff --git a/gcc/ada/gcc-interface/misc.cc b/gcc/ada/gcc-interface/misc.cc
index 7d6d4466d56..01e8267f884 100644
--- a/gcc/ada/gcc-interface/misc.cc
+++ b/gcc/ada/gcc-interface/misc.cc
@@ -1352,6 +1352,11 @@  get_lang_specific (tree node)
   return TYPE_LANG_SPECIFIC (node);
 }
 
+const struct scoped_attribute_specs *const gnat_attribute_table[] =
+{
+  &gnat_internal_attribute_table
+};
+
 /* Definitions for our language-specific hooks.  */
 
 #undef  LANG_HOOKS_NAME
@@ -1417,7 +1422,7 @@  get_lang_specific (tree node)
 #undef  LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO
 #define LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO gnat_get_fixed_point_type_info
 #undef  LANG_HOOKS_ATTRIBUTE_TABLE
-#define LANG_HOOKS_ATTRIBUTE_TABLE	gnat_internal_attribute_table
+#define LANG_HOOKS_ATTRIBUTE_TABLE	gnat_attribute_table
 #undef  LANG_HOOKS_BUILTIN_FUNCTION
 #define LANG_HOOKS_BUILTIN_FUNCTION	gnat_builtin_function
 #undef  LANG_HOOKS_INIT_TS
diff --git a/gcc/ada/gcc-interface/utils.cc b/gcc/ada/gcc-interface/utils.cc
index 4e2ed173fbe..c38e0e6f57d 100644
--- a/gcc/ada/gcc-interface/utils.cc
+++ b/gcc/ada/gcc-interface/utils.cc
@@ -136,7 +136,7 @@  static tree fake_attribute_handler (tree *, tree, tree, int, bool *);
 
 /* Table of machine-independent internal attributes for Ada.  We support
    this minimal set of attributes to accommodate the needs of builtins.  */
-const struct attribute_spec gnat_internal_attribute_table[] =
+static const attribute_spec gnat_internal_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -217,9 +217,11 @@  const struct attribute_spec gnat_internal_attribute_table[] =
   /* This is handled entirely in the front end.  */
   { "hardbool",     0, 0,  false, true, false, true,
     fake_attribute_handler, NULL },
+};
 
-  { NULL,           0, 0,  false, false, false, false,
-    NULL, NULL }
+const scoped_attribute_specs gnat_internal_attribute_table =
+{
+  "gnu", gnat_internal_attributes
 };
 
 /* Associates a GNAT tree node to a GCC tree node. It is used in
diff --git a/gcc/attribs.cc b/gcc/attribs.cc
index d1d9e5a28c1..6725fe78f2c 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -39,7 +39,7 @@  along with GCC; see the file COPYING3.  If not see
 
 /* Table of the tables of attributes (common, language, format, machine)
    searched.  */
-static const struct attribute_spec *attribute_tables[4];
+static array_slice<const scoped_attribute_specs *const> attribute_tables[2];
 
 /* Substring representation.  */
 
@@ -102,13 +102,6 @@  static const struct attribute_spec *lookup_scoped_attribute_spec (const_tree,
 
 static bool attributes_initialized = false;
 
-/* Default empty table of attributes.  */
-
-static const struct attribute_spec empty_attribute_table[] =
-{
-  { NULL, 0, 0, false, false, false, false, NULL, NULL }
-};
-
 /* Return base name of the attribute.  Ie '__attr__' is turned into 'attr'.
    To avoid need for copying, we simply return length of the string.  */
 
@@ -118,21 +111,19 @@  extract_attribute_substring (struct substring *str)
   canonicalize_attr_name (str->str, str->length);
 }
 
-/* Insert an array of attributes ATTRIBUTES into a namespace.  This
-   array must be NULL terminated.  NS is the name of attribute
-   namespace.  IGNORED_P is true iff all unknown attributes in this
-   namespace should be ignored for the purposes of -Wattributes.  The
-   function returns the namespace into which the attributes have been
-   registered.  */
+/* Insert SPECS into its namespace.  IGNORED_P is true iff all unknown
+   attributes in this namespace should be ignored for the purposes of
+   -Wattributes.  The function returns the namespace into which the
+   attributes have been registered.  */
 
 scoped_attributes *
-register_scoped_attributes (const struct attribute_spec *attributes,
-			    const char *ns, bool ignored_p /*=false*/)
+register_scoped_attributes (const scoped_attribute_specs &specs,
+			    bool ignored_p /*=false*/)
 {
   scoped_attributes *result = NULL;
 
   /* See if we already have attributes in the namespace NS.  */
-  result = find_attribute_namespace (ns);
+  result = find_attribute_namespace (specs.ns);
 
   if (result == NULL)
     {
@@ -143,7 +134,7 @@  register_scoped_attributes (const struct attribute_spec *attributes,
 	attributes_table.create (64);
 
       memset (&sa, 0, sizeof (sa));
-      sa.ns = ns;
+      sa.ns = specs.ns;
       sa.attributes.create (64);
       sa.ignored_p = ignored_p;
       result = attributes_table.safe_push (sa);
@@ -153,10 +144,10 @@  register_scoped_attributes (const struct attribute_spec *attributes,
     result->ignored_p |= ignored_p;
 
   /* Really add the attributes to their namespace now.  */
-  for (unsigned i = 0; attributes[i].name != NULL; ++i)
+  for (const attribute_spec &attribute : specs.attributes)
     {
-      result->attributes.safe_push (attributes[i]);
-      register_scoped_attribute (&attributes[i], result);
+      result->attributes.safe_push (attribute);
+      register_scoped_attribute (&attribute, result);
     }
 
   gcc_assert (result != NULL);
@@ -183,49 +174,40 @@  find_attribute_namespace (const char* ns)
 static void
 check_attribute_tables (void)
 {
-  for (size_t i = 0; i < ARRAY_SIZE (attribute_tables); i++)
-    for (size_t j = 0; attribute_tables[i][j].name != NULL; j++)
-      {
-	/* The name must not begin and end with __.  */
-	const char *name = attribute_tables[i][j].name;
-	int len = strlen (name);
+  hash_set<pair_hash<nofree_string_hash, nofree_string_hash>> names;
 
-	gcc_assert (!(name[0] == '_' && name[1] == '_'
-		      && name[len - 1] == '_' && name[len - 2] == '_'));
+  for (auto scoped_array : attribute_tables)
+    for (auto scoped_attributes : scoped_array)
+      for (const attribute_spec &attribute : scoped_attributes->attributes)
+	{
+	  /* The name must not begin and end with __.  */
+	  const char *name = attribute.name;
+	  int len = strlen (name);
+
+	  gcc_assert (!(name[0] == '_' && name[1] == '_'
+			&& name[len - 1] == '_' && name[len - 2] == '_'));
 
-	/* The minimum and maximum lengths must be consistent.  */
-	gcc_assert (attribute_tables[i][j].min_length >= 0);
+	  /* The minimum and maximum lengths must be consistent.  */
+	  gcc_assert (attribute.min_length >= 0);
 
-	gcc_assert (attribute_tables[i][j].max_length == -1
-		    || (attribute_tables[i][j].max_length
-			>= attribute_tables[i][j].min_length));
+	  gcc_assert (attribute.max_length == -1
+		      || attribute.max_length >= attribute.min_length);
 
-	/* An attribute cannot require both a DECL and a TYPE.  */
-	gcc_assert (!attribute_tables[i][j].decl_required
-		    || !attribute_tables[i][j].type_required);
+	  /* An attribute cannot require both a DECL and a TYPE.  */
+	  gcc_assert (!attribute.decl_required
+		      || !attribute.type_required);
 
 	  /* If an attribute requires a function type, in particular
 	     it requires a type.  */
-	gcc_assert (!attribute_tables[i][j].function_type_required
-		    || attribute_tables[i][j].type_required);
-      }
-
-  /* Check that each name occurs just once in each table.  */
-  for (size_t i = 0; i < ARRAY_SIZE (attribute_tables); i++)
-    for (size_t j = 0; attribute_tables[i][j].name != NULL; j++)
-      for (size_t k = j + 1; attribute_tables[i][k].name != NULL; k++)
-	gcc_assert (strcmp (attribute_tables[i][j].name,
-			    attribute_tables[i][k].name));
-
-  /* Check that no name occurs in more than one table.  Names that
-     begin with '*' are exempt, and may be overridden.  */
-  for (size_t i = 0; i < ARRAY_SIZE (attribute_tables); i++)
-    for (size_t j = i + 1; j < ARRAY_SIZE (attribute_tables); j++)
-      for (size_t k = 0; attribute_tables[i][k].name != NULL; k++)
-	for (size_t l = 0; attribute_tables[j][l].name != NULL; l++)
-	  gcc_assert (attribute_tables[i][k].name[0] == '*'
-		      || strcmp (attribute_tables[i][k].name,
-				 attribute_tables[j][l].name));
+	  gcc_assert (!attribute.function_type_required
+		      || attribute.type_required);
+
+	  /* Check that no name occurs more than once.  Names that
+	     begin with '*' are exempt, and may be overridden.  */
+	  const char *ns = scoped_attributes->ns;
+	  if (name[0] != '*' && names.add ({ ns ? ns : "", name }))
+	    gcc_unreachable ();
+	}
 }
 
 /* Used to stash pointers to allocated memory so that we can free them at
@@ -281,7 +263,7 @@  handle_ignored_attributes_option (vec<char *> *v)
       canonicalize_attr_name (vendor_start, vendor_len);
       /* We perform all this hijinks so that we don't have to copy OPT.  */
       tree vendor_id = get_identifier_with_length (vendor_start, vendor_len);
-      const char *attr;
+      array_slice<const attribute_spec> attrs;
       /* In the "vendor::" case, we should ignore *any* attribute coming
 	 from this attribute namespace.  */
       if (attr_len > 0)
@@ -293,22 +275,23 @@  handle_ignored_attributes_option (vec<char *> *v)
 	    }
 	  canonicalize_attr_name (attr_start, attr_len);
 	  tree attr_id = get_identifier_with_length (attr_start, attr_len);
-	  attr = IDENTIFIER_POINTER (attr_id);
+	  const char *attr = IDENTIFIER_POINTER (attr_id);
 	  /* If we've already seen this vendor::attr, ignore it.  Attempting to
 	     register it twice would lead to a crash.  */
 	  if (lookup_scoped_attribute_spec (vendor_id, attr_id))
 	    continue;
+	  /* Create a table with extra attributes which we will register.
+	     We can't free it here, so squirrel away the pointers.  */
+	  attribute_spec *table = new attribute_spec {
+	    attr, 0, -2, false, false, false, false, nullptr, nullptr
+	  };
+	  ignored_attributes_table.safe_push (table);
+	  attrs = { table, 1 };
 	}
-      else
-	attr = nullptr;
-      /* Create a table with extra attributes which we will register.
-	 We can't free it here, so squirrel away the pointers.  */
-      attribute_spec *table = new attribute_spec[2];
-      ignored_attributes_table.safe_push (table);
-      table[0] = { attr, 0, -2, false, false, false, false, nullptr, nullptr };
-      table[1] = { nullptr, 0, 0, false, false, false, false, nullptr,
-		   nullptr };
-      register_scoped_attributes (table, IDENTIFIER_POINTER (vendor_id), !attr);
+      const scoped_attribute_specs scoped_specs = {
+	IDENTIFIER_POINTER (vendor_id), attrs
+      };
+      register_scoped_attributes (scoped_specs, attrs.empty ());
     }
 }
 
@@ -328,27 +311,18 @@  free_attr_data ()
 void
 init_attributes (void)
 {
-  size_t i;
-
   if (attributes_initialized)
     return;
 
-  attribute_tables[0] = lang_hooks.common_attribute_table;
-  attribute_tables[1] = lang_hooks.attribute_table;
-  attribute_tables[2] = lang_hooks.format_attribute_table;
-  attribute_tables[3] = targetm.attribute_table;
-
-  /* Translate NULL pointers to pointers to the empty table.  */
-  for (i = 0; i < ARRAY_SIZE (attribute_tables); i++)
-    if (attribute_tables[i] == NULL)
-      attribute_tables[i] = empty_attribute_table;
+  attribute_tables[0] = lang_hooks.attribute_table;
+  attribute_tables[1] = targetm.attribute_table;
 
   if (flag_checking)
     check_attribute_tables ();
 
-  for (i = 0; i < ARRAY_SIZE (attribute_tables); ++i)
-    /* Put all the GNU attributes into the "gnu" namespace.  */
-    register_scoped_attributes (attribute_tables[i], "gnu");
+  for (auto scoped_array : attribute_tables)
+    for (auto scoped_attributes : scoped_array)
+      register_scoped_attributes (*scoped_attributes);
 
   vec<char *> *ignored = (vec<char *> *) flag_ignored_attributes;
   handle_ignored_attributes_option (ignored);
@@ -2642,10 +2616,6 @@  attr_access::array_as_string (tree type) const
 namespace selftest
 {
 
-/* Helper types to verify the consistency attribute exclusions.  */
-
-typedef std::pair<const char *, const char *> excl_pair;
-
 /* Self-test to verify that each attribute exclusion is symmetric,
    meaning that if attribute A is encoded as incompatible with
    attribute B then the opposite relationship is also encoded.
@@ -2660,55 +2630,54 @@  test_attribute_exclusions ()
   /* Iterate over the array of attribute tables first (with TI0 as
      the index) and over the array of attribute_spec in each table
      (with SI0 as the index).  */
-  const size_t ntables = ARRAY_SIZE (attribute_tables);
+  hash_set<excl_hash_traits> excl_set;
 
-  /* Set of pairs of mutually exclusive attributes.  */
-  typedef hash_set<excl_hash_traits> exclusion_set;
-  exclusion_set excl_set;
+  for (auto scoped_array : attribute_tables)
+    for (auto scoped_attributes : scoped_array)
+      for (const attribute_spec &attribute : scoped_attributes->attributes)
+	{
+	  const attribute_spec::exclusions *excl = attribute.exclude;
 
-  for (size_t ti0 = 0; ti0 != ntables; ++ti0)
-    for (size_t s0 = 0; attribute_tables[ti0][s0].name; ++s0)
-      {
-	const attribute_spec::exclusions *excl
-	  = attribute_tables[ti0][s0].exclude;
+	  /* Skip each attribute that doesn't define exclusions.  */
+	  if (!excl)
+	    continue;
 
-	/* Skip each attribute that doesn't define exclusions.  */
-	if (!excl)
-	  continue;
+	  /* Skip standard (non-GNU) attributes, since currently the
+	     exclusions are implicitly for GNU attributes only.
+	     Also, C++ likely and unlikely get rewritten to gnu::hot
+	     and gnu::cold, so symmetry isn't necessary there.  */
+	  if (!scoped_attributes->ns)
+	    continue;
 
-	const char *attr_name = attribute_tables[ti0][s0].name;
+	  const char *attr_name = attribute.name;
 
-	/* Iterate over the set of exclusions for every attribute
-	   (with EI0 as the index) adding the exclusions defined
-	   for each to the set.  */
-	for (size_t ei0 = 0; excl[ei0].name; ++ei0)
-	  {
-	    const char *excl_name = excl[ei0].name;
+	  /* Iterate over the set of exclusions for every attribute
+	     (with EI0 as the index) adding the exclusions defined
+	     for each to the set.  */
+	  for (size_t ei0 = 0; excl[ei0].name; ++ei0)
+	    {
+	      const char *excl_name = excl[ei0].name;
 
-	    if (!strcmp (attr_name, excl_name))
-	      continue;
+	      if (!strcmp (attr_name, excl_name))
+		continue;
 
-	    excl_set.add (excl_pair (attr_name, excl_name));
-	  }
-      }
+	      excl_set.add ({ attr_name, excl_name });
+	    }
+	}
 
   /* Traverse the set of mutually exclusive pairs of attributes
      and verify that they are symmetric.  */
-  for (exclusion_set::iterator it = excl_set.begin ();
-       it != excl_set.end ();
-       ++it)
-    {
-      if (!excl_set.contains (excl_pair ((*it).second, (*it).first)))
-	{
-	  /* An exclusion for an attribute has been found that
-	     doesn't have a corresponding exclusion in the opposite
-	     direction.  */
-	  char desc[120];
-	  sprintf (desc, "'%s' attribute exclusion '%s' must be symmetric",
-		   (*it).first, (*it).second);
-	  fail (SELFTEST_LOCATION, desc);
-	}
-    }
+  for (auto excl_pair : excl_set)
+    if (!excl_set.contains ({ excl_pair.second, excl_pair.first }))
+      {
+	/* An exclusion for an attribute has been found that
+	   doesn't have a corresponding exclusion in the opposite
+	   direction.  */
+	char desc[120];
+	sprintf (desc, "'%s' attribute exclusion '%s' must be symmetric",
+		 excl_pair.first, excl_pair.second);
+	fail (SELFTEST_LOCATION, desc);
+      }
 }
 
 void
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 84a43658a70..fdeebff1cd9 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -20,6 +20,13 @@  along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_ATTRIBS_H
 #define GCC_ATTRIBS_H
 
+/* A set of attributes that belong to the same namespace, given by NS.  */
+struct scoped_attribute_specs
+{
+  const char *ns;
+  array_slice<const attribute_spec> attributes;
+};
+
 extern const struct attribute_spec *lookup_attribute_spec (const_tree);
 extern void free_attr_data ();
 extern void init_attributes (void);
@@ -42,9 +49,8 @@  extern tree make_attribute (const char *, const char *, tree);
 extern bool attribute_ignored_p (tree);
 extern bool attribute_ignored_p (const attribute_spec *const);
 
-extern struct scoped_attributes* register_scoped_attributes (const struct attribute_spec *,
-							     const char *,
-							     bool = false);
+extern struct scoped_attributes *
+  register_scoped_attributes (const scoped_attribute_specs &, bool = false);
 
 extern char *sorted_attr_string (tree);
 extern bool common_function_versions (tree, tree);
diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index a041c3b91eb..cf54ef68f20 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -288,7 +288,7 @@  static const struct attribute_spec::exclusions attr_stack_protect_exclusions[] =
 /* Table of machine-independent attributes common to all C-like languages.
 
    Current list of processed common attributes: nonnull.  */
-const struct attribute_spec c_common_attribute_table[] =
+const struct attribute_spec c_common_gnu_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -574,23 +574,31 @@  const struct attribute_spec c_common_attribute_table[] =
   { "fd_arg_write",       1, 1, false, true, true, false,
             handle_fd_arg_attribute, NULL},         
   { "null_terminated_string_arg", 1, 1, false, true, true, false,
-			      handle_null_terminated_string_arg_attribute, NULL},
-  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
+			      handle_null_terminated_string_arg_attribute, NULL}
+};
+
+const struct scoped_attribute_specs c_common_gnu_attribute_table =
+{
+  "gnu", c_common_gnu_attributes
 };
 
 /* Give the specifications for the format attributes, used by C and all
    descendants.
 
    Current list of processed format attributes: format, format_arg.  */
-const struct attribute_spec c_common_format_attribute_table[] =
+const struct attribute_spec c_common_format_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
   { "format",                 3, 3, false, true,  true, false,
 			      handle_format_attribute, NULL },
   { "format_arg",             1, 1, false, true,  true, false,
-			      handle_format_arg_attribute, NULL },
-  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
+			      handle_format_arg_attribute, NULL }
+};
+
+const struct scoped_attribute_specs c_common_format_attribute_table =
+{
+  "gnu", c_common_format_attributes
 };
 
 /* Returns TRUE iff the attribute indicated by ATTR_ID takes a plain
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 1fdba7ef3ea..92ddad27806 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -821,8 +821,8 @@  enum conversion_safety {
 extern struct visibility_flags visibility_options;
 
 /* Attribute table common to the C front ends.  */
-extern const struct attribute_spec c_common_attribute_table[];
-extern const struct attribute_spec c_common_format_attribute_table[];
+extern const struct scoped_attribute_specs c_common_gnu_attribute_table;
+extern const struct scoped_attribute_specs c_common_format_attribute_table;
 
 /* Pointer to function to lazily generate the VAR_DECL for __FUNCTION__ etc.
    ID is the identifier to use, NAME is the string.
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 0076725a61d..547bebe03c9 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -4635,7 +4635,7 @@  handle_std_noreturn_attribute (tree *node, tree name, tree args,
 }
 
 /* Table of supported standard (C2x) attributes.  */
-const struct attribute_spec std_attribute_table[] =
+static const attribute_spec std_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -4650,8 +4650,12 @@  const struct attribute_spec std_attribute_table[] =
   { "nodiscard", 0, 1, false, false, false, false,
     handle_nodiscard_attribute, NULL },
   { "noreturn", 0, 0, false, false, false, false,
-    handle_std_noreturn_attribute, NULL },
-  { NULL, 0, 0, false, false, false, false, NULL, NULL }
+    handle_std_noreturn_attribute, NULL }
+};
+
+const scoped_attribute_specs std_attribute_table =
+{
+  nullptr, std_attributes
 };
 
 /* Create the predefined scalar types of C,
@@ -4667,8 +4671,6 @@  c_init_decl_processing (void)
   /* Initialize reserved words for parser.  */
   c_parse_init ();
 
-  register_scoped_attributes (std_attribute_table, NULL);
-
   current_function_decl = NULL_TREE;
 
   gcc_obstack_init (&parser_obstack);
diff --git a/gcc/c/c-objc-common.h b/gcc/c/c-objc-common.h
index ede451cef6b..4f1925c8b07 100644
--- a/gcc/c/c-objc-common.h
+++ b/gcc/c/c-objc-common.h
@@ -72,11 +72,15 @@  along with GCC; see the file COPYING3.  If not see
 #undef LANG_HOOKS_FINALIZE_EARLY_DEBUG
 #define LANG_HOOKS_FINALIZE_EARLY_DEBUG c_common_finalize_early_debug
 
-/* Attribute hooks.  */
-#undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
-#define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE c_common_attribute_table
-#undef LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE
-#define LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE c_common_format_attribute_table
+static const scoped_attribute_specs *const c_objc_attribute_table[] =
+{
+  &std_attribute_table,
+  &c_common_gnu_attribute_table,
+  &c_common_format_attribute_table
+};
+
+#undef LANG_HOOKS_ATTRIBUTE_TABLE
+#define LANG_HOOKS_ATTRIBUTE_TABLE c_objc_attribute_table
 
 #undef LANG_HOOKS_TREE_DUMP_DUMP_TREE_FN
 #define LANG_HOOKS_TREE_DUMP_DUMP_TREE_FN c_dump_tree
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index df6f1cefd02..69e154abdbd 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -910,6 +910,8 @@  extern vec<tree> incomplete_record_decls;
 
 extern const char *c_get_sarif_source_language (const char *filename);
 
+extern const struct scoped_attribute_specs std_attribute_table;
+
 #if CHECKING_P
 namespace selftest {
   extern void run_c_tests (void);
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index cb65ccc8465..920e17d3a95 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -2839,7 +2839,7 @@  handle_aarch64_vector_pcs_attribute (tree *node, tree name, tree,
 }
 
 /* Table of machine attributes.  */
-static const struct attribute_spec aarch64_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (aarch64_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -2850,9 +2850,8 @@  static const struct attribute_spec aarch64_attribute_table[] =
 			  NULL },
   { "Advanced SIMD type", 1, 1, false, true,  false, true,  NULL, NULL },
   { "SVE type",		  3, 3, false, true,  false, true,  NULL, NULL },
-  { "SVE sizeless type",  0, 0, false, true,  false, true,  NULL, NULL },
-  { NULL,                 0, 0, false, false, false, false, NULL, NULL }
-};
+  { "SVE sizeless type",  0, 0, false, true,  false, true,  NULL, NULL }
+});
 
 typedef enum aarch64_cond_code
 {
diff --git a/gcc/config/alpha/alpha.cc b/gcc/config/alpha/alpha.cc
index db6b34be9cb..6aa93783226 100644
--- a/gcc/config/alpha/alpha.cc
+++ b/gcc/config/alpha/alpha.cc
@@ -7482,14 +7482,13 @@  common_object_handler (tree *node, tree name ATTRIBUTE_UNUSED,
   return NULL_TREE;
 }
 
-static const struct attribute_spec vms_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (vms_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
   { COMMON_OBJECT,   0, 1, true,  false, false, false, common_object_handler,
-    NULL },
-  { NULL,            0, 0, false, false, false, false, NULL, NULL }
-};
+    NULL }
+});
 
 void
 vms_output_aligned_decl_common(FILE *file, tree decl, const char *name,
diff --git a/gcc/config/arc/arc.cc b/gcc/config/arc/arc.cc
index e209ad2d327..e87d7614feb 100644
--- a/gcc/config/arc/arc.cc
+++ b/gcc/config/arc/arc.cc
@@ -187,44 +187,6 @@  static tree arc_handle_secure_attribute (tree *, tree, tree, int, bool *);
 static tree arc_handle_uncached_attribute (tree *, tree, tree, int, bool *);
 static tree arc_handle_aux_attribute (tree *, tree, tree, int, bool *);
 
-/* Initialized arc_attribute_table to NULL since arc doesnot have any
-   machine specific supported attributes.  */
-const struct attribute_spec arc_attribute_table[] =
-{
- /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
-      affects_type_identity, handler, exclude } */
-  { "interrupt", 1, 1, true, false, false, true,
-    arc_handle_interrupt_attribute, NULL },
-  /* Function calls made to this symbol must be done indirectly, because
-     it may lie outside of the 21/25 bit addressing range of a normal function
-     call.  */
-  { "long_call",    0, 0, false, true,  true,  false, NULL, NULL },
-  /* Whereas these functions are always known to reside within the 25 bit
-     addressing range of unconditionalized bl.  */
-  { "medium_call",   0, 0, false, true,  true, false, NULL, NULL },
-  /* And these functions are always known to reside within the 21 bit
-     addressing range of blcc.  */
-  { "short_call",   0, 0, false, true,  true,  false, NULL, NULL },
-  /* Function which are not having the prologue and epilogue generated
-     by the compiler.  */
-  { "naked", 0, 0, true, false, false,  false, arc_handle_fndecl_attribute,
-    NULL },
-  /* Functions calls made using jli instruction.  The pointer in JLI
-     table is found latter.  */
-  { "jli_always",    0, 0, false, true,  true, false,  NULL, NULL },
-  /* Functions calls made using jli instruction.  The pointer in JLI
-     table is given as input parameter.  */
-  { "jli_fixed",    1, 1, false, true,  true, false, arc_handle_jli_attribute,
-    NULL },
-  /* Call a function using secure-mode.  */
-  { "secure_call",  1, 1, false, true, true, false, arc_handle_secure_attribute,
-    NULL },
-   /* Bypass caches using .di flag.  */
-  { "uncached", 0, 0, false, true, false, false, arc_handle_uncached_attribute,
-    NULL },
-  { "aux", 0, 1, true, false, false, false, arc_handle_aux_attribute, NULL },
-  { NULL, 0, 0, false, false, false, false, NULL, NULL }
-};
 static int arc_comp_type_attributes (const_tree, const_tree);
 static void arc_file_start (void);
 static void arc_internal_label (FILE *, const char *, unsigned long);
@@ -770,6 +732,42 @@  static rtx arc_legitimize_address_0 (rtx, rtx, machine_mode mode);
 
 #include "target-def.h"
 
+TARGET_GNU_ATTRIBUTES (arc_attribute_table,
+{
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
+      affects_type_identity, handler, exclude } */
+  { "interrupt", 1, 1, true, false, false, true,
+    arc_handle_interrupt_attribute, NULL },
+  /* Function calls made to this symbol must be done indirectly, because
+     it may lie outside of the 21/25 bit addressing range of a normal function
+     call.  */
+  { "long_call",    0, 0, false, true,  true,  false, NULL, NULL },
+  /* Whereas these functions are always known to reside within the 25 bit
+     addressing range of unconditionalized bl.  */
+  { "medium_call",   0, 0, false, true,  true, false, NULL, NULL },
+  /* And these functions are always known to reside within the 21 bit
+     addressing range of blcc.  */
+  { "short_call",   0, 0, false, true,  true,  false, NULL, NULL },
+  /* Function which are not having the prologue and epilogue generated
+     by the compiler.  */
+  { "naked", 0, 0, true, false, false,  false, arc_handle_fndecl_attribute,
+    NULL },
+  /* Functions calls made using jli instruction.  The pointer in JLI
+     table is found latter.  */
+  { "jli_always",    0, 0, false, true,  true, false,  NULL, NULL },
+  /* Functions calls made using jli instruction.  The pointer in JLI
+     table is given as input parameter.  */
+  { "jli_fixed",    1, 1, false, true,  true, false, arc_handle_jli_attribute,
+    NULL },
+  /* Call a function using secure-mode.  */
+  { "secure_call",  1, 1, false, true, true, false, arc_handle_secure_attribute,
+    NULL },
+   /* Bypass caches using .di flag.  */
+  { "uncached", 0, 0, false, true, false, false, arc_handle_uncached_attribute,
+    NULL },
+  { "aux", 0, 1, true, false, false, false, arc_handle_aux_attribute, NULL }
+});
+
 #undef TARGET_ASM_ALIGNED_HI_OP
 #define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
 #undef TARGET_ASM_ALIGNED_SI_OP
diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
index 620ef7bfb2f..ed2459128ff 100644
--- a/gcc/config/arm/arm.cc
+++ b/gcc/config/arm/arm.cc
@@ -332,7 +332,7 @@  static rtx_insn *thumb1_md_asm_adjust (vec<rtx> &, vec<rtx> &,
 static const char *arm_identify_fpu_from_isa (sbitmap);
 
 /* Table of machine attributes.  */
-static const struct attribute_spec arm_attribute_table[] =
+static const attribute_spec arm_gnu_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -380,8 +380,17 @@  static const struct attribute_spec arm_attribute_table[] =
     arm_handle_cmse_nonsecure_entry, NULL },
   { "cmse_nonsecure_call", 0, 0, false, false, false, true,
     arm_handle_cmse_nonsecure_call, NULL },
-  { "Advanced SIMD type", 1, 1, false, true, false, true, NULL, NULL },
-  { NULL, 0, 0, false, false, false, false, NULL, NULL }
+  { "Advanced SIMD type", 1, 1, false, true, false, true, NULL, NULL }
+};
+
+static const scoped_attribute_specs arm_gnu_attribute_table =
+{
+  "gnu", arm_gnu_attributes
+};
+
+static const scoped_attribute_specs *const arm_attribute_table[] =
+{
+  &arm_gnu_attribute_table
 };
 
 /* Initialize the GCC target structure.  */
diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc
index 5e0217de36f..59d217efc45 100644
--- a/gcc/config/avr/avr.cc
+++ b/gcc/config/avr/avr.cc
@@ -10442,7 +10442,7 @@  avr_eval_addr_attrib (rtx x)
 
 
 /* AVR attributes.  */
-static const struct attribute_spec avr_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (avr_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -10467,9 +10467,8 @@  static const struct attribute_spec avr_attribute_table[] =
   { "address",   1, 1, true, false, false,  false,
     avr_handle_addr_attribute, NULL },
   { "absdata",   0, 0, true, false, false,  false,
-    avr_handle_absdata_attribute, NULL },
-  { NULL,        0, 0, false, false, false, false, NULL, NULL }
-};
+    avr_handle_absdata_attribute, NULL }
+});
 
 
 /* Return true if we support address space AS for the architecture in effect
diff --git a/gcc/config/bfin/bfin.cc b/gcc/config/bfin/bfin.cc
index 5718babb6b2..c02136f5e0c 100644
--- a/gcc/config/bfin/bfin.cc
+++ b/gcc/config/bfin/bfin.cc
@@ -4896,7 +4896,7 @@  bfin_handle_l2_attribute (tree *node, tree ARG_UNUSED (name),
 }
 
 /* Table of valid machine attributes.  */
-static const struct attribute_spec bfin_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (bfin_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -4921,9 +4921,8 @@  static const struct attribute_spec bfin_attribute_table[] =
     bfin_handle_l1_data_attribute, NULL },
   { "l1_data_B", 0, 0, true, false, false, false,
     bfin_handle_l1_data_attribute, NULL },
-  { "l2", 0, 0, true, false, false, false, bfin_handle_l2_attribute, NULL },
-  { NULL, 0, 0, false, false, false, false, NULL, NULL }
-};
+  { "l2", 0, 0, true, false, false, false, bfin_handle_l2_attribute, NULL }
+});
 
 /* Implementation of TARGET_ASM_INTEGER.  When using FD-PIC, we need to
    tell the assembler to generate pointers to function descriptors in
diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc
index 63637ece78e..c619865f1fc 100644
--- a/gcc/config/bpf/bpf.cc
+++ b/gcc/config/bpf/bpf.cc
@@ -139,7 +139,7 @@  bpf_handle_preserve_access_index_attribute (tree *node, tree name,
 
 /* Target-specific attributes.  */
 
-static const struct attribute_spec bpf_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (bpf_attribute_table,
 {
   /* Syntax: { name, min_len, max_len, decl_required, type_required,
 	       function_type_required, affects_type_identity, handler,
@@ -156,11 +156,8 @@  static const struct attribute_spec bpf_attribute_table[] =
 
  /* Support for `naked' function attribute.  */
  { "naked", 0, 1, false, false, false, false,
-   bpf_handle_fndecl_attribute, NULL },
-
- /* The last attribute spec is set to be NULL.  */
- { NULL,	0,  0, false, false, false, false, NULL, NULL }
-};
+   bpf_handle_fndecl_attribute, NULL }
+});
 
 #undef TARGET_ATTRIBUTE_TABLE
 #define TARGET_ATTRIBUTE_TABLE bpf_attribute_table
diff --git a/gcc/config/csky/csky.cc b/gcc/config/csky/csky.cc
index 731f47cb2c0..ac089feea62 100644
--- a/gcc/config/csky/csky.cc
+++ b/gcc/config/csky/csky.cc
@@ -211,16 +211,15 @@  const int csky_debugger_regno[FIRST_PSEUDO_REGISTER] =
 /* Table of machine attributes.  */
 static tree csky_handle_fndecl_attribute (tree *, tree, tree, int, bool *);
 static tree csky_handle_isr_attribute (tree *, tree, tree, int, bool *);
-static const struct attribute_spec csky_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (csky_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
   { "naked",	 0, 0, true,  false, false, false, csky_handle_fndecl_attribute, NULL },
   /* Interrupt Service Routines have special prologue and epilogue requirements.  */
   { "interrupt", 0, 1, false, false, false, false, csky_handle_isr_attribute,	 NULL },
-  { "isr",	 0, 1, false, false, false, false, csky_handle_isr_attribute,	 NULL },
-  { NULL,	 0, 0, false, false, false, false, NULL,			 NULL }
-};
+  { "isr",	 0, 1, false, false, false, false, csky_handle_isr_attribute,	 NULL }
+});
 
 /* A C structure for machine-specific, per-function data.
    This is added to the cfun structure.  */
diff --git a/gcc/config/epiphany/epiphany.cc b/gcc/config/epiphany/epiphany.cc
index 68e748c688e..e10e64de823 100644
--- a/gcc/config/epiphany/epiphany.cc
+++ b/gcc/config/epiphany/epiphany.cc
@@ -458,7 +458,7 @@  epiphany_init_reg_tables (void)
                      They unmask them while calling an interruptible
 		     function, though.  */
 
-static const struct attribute_spec epiphany_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (epiphany_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -468,9 +468,8 @@  static const struct attribute_spec epiphany_attribute_table[] =
     epiphany_handle_forwarder_attribute, NULL },
   { "long_call",  0, 0, false, true, true, false, NULL, NULL },
   { "short_call", 0, 0, false, true, true, false, NULL, NULL },
-  { "disinterrupt", 0, 0, false, true, true, true, NULL, NULL },
-  { NULL,         0, 0, false, false, false, false, NULL, NULL }
-};
+  { "disinterrupt", 0, 0, false, true, true, true, NULL, NULL }
+});
 
 /* Handle an "interrupt" attribute; arguments as in
    struct attribute_spec.handler.  */
diff --git a/gcc/config/gcn/gcn.cc b/gcc/config/gcn/gcn.cc
index 6a2aaefceca..795a852bb53 100644
--- a/gcc/config/gcn/gcn.cc
+++ b/gcc/config/gcn/gcn.cc
@@ -357,14 +357,12 @@  gcn_handle_amdgpu_hsa_kernel_attribute (tree *node, tree name,
  
    Create target-specific __attribute__ types.  */
 
-static const struct attribute_spec gcn_attribute_table[] = {
+TARGET_GNU_ATTRIBUTES (gcn_attribute_table, {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
      affects_type_identity } */
   {"amdgpu_hsa_kernel", 0, GCN_KERNEL_ARG_TYPES, false, true,
-   true, true, gcn_handle_amdgpu_hsa_kernel_attribute, NULL},
-  /* End element.  */
-  {NULL, 0, 0, false, false, false, false, NULL, NULL}
-};
+   true, true, gcn_handle_amdgpu_hsa_kernel_attribute, NULL}
+});
 
 /* }}}  */
 /* {{{ Registers and modes.  */
diff --git a/gcc/config/h8300/h8300.cc b/gcc/config/h8300/h8300.cc
index 4bbb1b711e8..5936cdca177 100644
--- a/gcc/config/h8300/h8300.cc
+++ b/gcc/config/h8300/h8300.cc
@@ -4909,7 +4909,7 @@  h8300_insert_attributes (tree node, tree *attributes)
    tiny_data: This variable lives in the tiny data area and can be
    referenced with 16-bit absolute memory references.  */
 
-static const struct attribute_spec h8300_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (h8300_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -4926,9 +4926,8 @@  static const struct attribute_spec h8300_attribute_table[] =
   { "eightbit_data",     0, 0, true,  false, false, false,
     h8300_handle_eightbit_data_attribute, NULL },
   { "tiny_data",         0, 0, true,  false, false, false,
-    h8300_handle_tiny_data_attribute, NULL },
-  { NULL,                0, 0, false, false, false, false, NULL, NULL }
-};
+    h8300_handle_tiny_data_attribute, NULL }
+});
 
 
 /* Handle an attribute requiring a FUNCTION_DECL; arguments as in
diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc
index df7d24352d1..351c777c666 100644
--- a/gcc/config/i386/i386-options.cc
+++ b/gcc/config/i386/i386-options.cc
@@ -3951,7 +3951,7 @@  handle_nodirect_extern_access_attribute (tree *pnode, tree name,
 }
 
 /* Table of valid machine attributes.  */
-const struct attribute_spec ix86_attribute_table[] =
+static const attribute_spec ix86_gnu_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -4031,10 +4031,12 @@  const struct attribute_spec ix86_attribute_table[] =
   { "cf_check", 0, 0, true, false, false, false,
     ix86_handle_fndecl_attribute, NULL },
   { "nodirect_extern_access", 0, 0, true, false, false, false,
-    handle_nodirect_extern_access_attribute, NULL },
+    handle_nodirect_extern_access_attribute, NULL }
+};
 
-  /* End element.  */
-  { NULL, 0, 0, false, false, false, false, NULL, NULL }
+const scoped_attribute_specs ix86_gnu_attribute_table =
+{
+  "gnu", ix86_gnu_attributes
 };
 
 #include "gt-i386-options.h"
diff --git a/gcc/config/i386/i386-options.h b/gcc/config/i386/i386-options.h
index 68666067fea..6274c594647 100644
--- a/gcc/config/i386/i386-options.h
+++ b/gcc/config/i386/i386-options.h
@@ -82,7 +82,7 @@  void ix86_function_specific_print (FILE *, int,
 				   struct cl_target_option *);
 bool ix86_valid_target_attribute_p (tree, tree, tree, int);
 
-extern const struct attribute_spec ix86_attribute_table[];
+extern const struct scoped_attribute_specs ix86_gnu_attribute_table;
 
 
 #endif  /* GCC_I386_OPTIONS_H */
diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index 7b72aabf0da..e0eff9f1430 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -25832,6 +25832,11 @@  ix86_run_selftests (void)
 
 #endif /* CHECKING_P */
 
+static const scoped_attribute_specs *const ix86_attribute_table[] =
+{
+  &ix86_gnu_attribute_table
+};
+
 /* Initialize the GCC target structure.  */
 #undef TARGET_RETURN_IN_MEMORY
 #define TARGET_RETURN_IN_MEMORY ix86_return_in_memory
diff --git a/gcc/config/ia64/ia64.cc b/gcc/config/ia64/ia64.cc
index c241e1a50fc..f7766c25622 100644
--- a/gcc/config/ia64/ia64.cc
+++ b/gcc/config/ia64/ia64.cc
@@ -358,7 +358,7 @@  static bool ia64_expand_vec_perm_const_1 (struct expand_vec_perm_d *d);
 
 
 /* Table of valid machine attributes.  */
-static const struct attribute_spec ia64_attribute_table[] =
+static const attribute_spec ia64_gnu_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -370,8 +370,17 @@  static const struct attribute_spec ia64_attribute_table[] =
     ia64_vms_common_object_attribute, NULL },
 #endif
   { "version_id",      1, 1, true, false, false, false,
-    ia64_handle_version_id_attribute, NULL },
-  { NULL,	       0, 0, false, false, false, false, NULL, NULL }
+    ia64_handle_version_id_attribute, NULL }
+};
+
+static const scoped_attribute_specs ia64_gnu_attribute_table =
+{
+  "gnu", ia64_gnu_attributes
+};
+
+static const scoped_attribute_specs *const ia64_attribute_table[] =
+{
+  &ia64_gnu_attribute_table
 };
 
 /* Initialize the GCC target structure.  */
diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc
index c782f571abc..bf73ab60072 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -7812,15 +7812,13 @@  loongarch_handle_model_attribute (tree *node, tree name, tree arg, int,
   return NULL_TREE;
 }
 
-static const struct attribute_spec loongarch_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (loongarch_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
   { "model", 1, 1, true, false, false, false,
-    loongarch_handle_model_attribute, NULL },
-  /* The last attribute spec is set to be NULL.  */
-  {}
-};
+    loongarch_handle_model_attribute, NULL }
+});
 
 bool
 loongarch_use_anchors_for_symbol_p (const_rtx symbol)
diff --git a/gcc/config/m32c/m32c.cc b/gcc/config/m32c/m32c.cc
index e18efc3c7f2..c63c75a6709 100644
--- a/gcc/config/m32c/m32c.cc
+++ b/gcc/config/m32c/m32c.cc
@@ -2999,7 +2999,7 @@  current_function_special_page_vector (rtx x)
 
 #undef TARGET_ATTRIBUTE_TABLE
 #define TARGET_ATTRIBUTE_TABLE m32c_attribute_table
-static const struct attribute_spec m32c_attribute_table[] = {
+TARGET_GNU_ATTRIBUTES (m32c_attribute_table, {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
   { "interrupt", 0, 0, false, false, false, false, interrupt_handler, NULL },
@@ -3007,9 +3007,8 @@  static const struct attribute_spec m32c_attribute_table[] = {
   { "fast_interrupt", 0, 0, false, false, false, false,
     interrupt_handler, NULL },
   { "function_vector", 1, 1, true,  false, false, false,
-    function_vector_handler, NULL },
-  { NULL, 0, 0, false, false, false, false, NULL, NULL }
-};
+    function_vector_handler, NULL }
+});
 
 #undef TARGET_COMP_TYPE_ATTRIBUTES
 #define TARGET_COMP_TYPE_ATTRIBUTES m32c_comp_type_attributes
diff --git a/gcc/config/m32r/m32r.cc b/gcc/config/m32r/m32r.cc
index 63a1798da3d..1a9c8ef1391 100644
--- a/gcc/config/m32r/m32r.cc
+++ b/gcc/config/m32r/m32r.cc
@@ -112,15 +112,14 @@  static HOST_WIDE_INT m32r_starting_frame_offset (void);
 
 /* M32R specific attributes.  */
 
-static const struct attribute_spec m32r_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (m32r_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
   { "interrupt", 0, 0, true,  false, false, false, NULL, NULL },
   { "model",     1, 1, true,  false, false, false, m32r_handle_model_attribute,
-    NULL },
-  { NULL,        0, 0, false, false, false, false, NULL, NULL }
-};
+    NULL }
+});
 
 /* Initialize the GCC target structure.  */
 #undef  TARGET_ATTRIBUTE_TABLE
diff --git a/gcc/config/m68k/m68k.cc b/gcc/config/m68k/m68k.cc
index 145a92d8710..001cf5bd997 100644
--- a/gcc/config/m68k/m68k.cc
+++ b/gcc/config/m68k/m68k.cc
@@ -361,7 +361,7 @@  static void m68k_asm_final_postscan_insn (FILE *, rtx_insn *insn, rtx [], int);
 #undef TARGET_ASM_FINAL_POSTSCAN_INSN
 #define TARGET_ASM_FINAL_POSTSCAN_INSN m68k_asm_final_postscan_insn
 
-static const struct attribute_spec m68k_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (m68k_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -370,9 +370,8 @@  static const struct attribute_spec m68k_attribute_table[] =
   { "interrupt_handler", 0, 0, true,  false, false, false,
     m68k_handle_fndecl_attribute, NULL },
   { "interrupt_thread", 0, 0, true,  false, false, false,
-    m68k_handle_fndecl_attribute, NULL },
-  { NULL, 0, 0, false, false, false, false, NULL, NULL }
-};
+    m68k_handle_fndecl_attribute, NULL }
+});
 
 struct gcc_target targetm = TARGET_INITIALIZER;
 
diff --git a/gcc/config/mcore/mcore.cc b/gcc/config/mcore/mcore.cc
index 6f1d7af7937..ca672547494 100644
--- a/gcc/config/mcore/mcore.cc
+++ b/gcc/config/mcore/mcore.cc
@@ -151,16 +151,15 @@  static bool	  mcore_modes_tieable_p		(machine_mode, machine_mode);
 
 /* MCore specific attributes.  */
 
-static const struct attribute_spec mcore_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (mcore_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
   { "dllexport", 0, 0, true,  false, false, false, NULL, NULL },
   { "dllimport", 0, 0, true,  false, false, false, NULL, NULL },
   { "naked",     0, 0, true,  false, false, false,
-    mcore_handle_naked_attribute, NULL },
-  { NULL,        0, 0, false, false, false, false, NULL, NULL }
-};
+    mcore_handle_naked_attribute, NULL }
+});
 
 /* Initialize the GCC target structure.  */
 #undef  TARGET_ASM_EXTERNAL_LIBCALL
diff --git a/gcc/config/microblaze/microblaze.cc b/gcc/config/microblaze/microblaze.cc
index 60ad55120d2..3ea177b835e 100644
--- a/gcc/config/microblaze/microblaze.cc
+++ b/gcc/config/microblaze/microblaze.cc
@@ -218,15 +218,14 @@  int break_handler;
 int fast_interrupt;
 int save_volatiles;
 
-const struct attribute_spec microblaze_attribute_table[] = {
+TARGET_GNU_ATTRIBUTES (microblaze_attribute_table, {
   /* name         min_len, max_len, decl_req, type_req, fn_type_req,
      affects_type_identity, handler, exclude */
   {"interrupt_handler",	0,       0,    true, false, false, false, NULL, NULL },
   {"break_handler",	0,       0,    true, false, false, false, NULL, NULL },
   {"fast_interrupt",	0,       0,    true, false, false, false, NULL, NULL },
-  {"save_volatiles",	0,       0,    true, false, false, false, NULL, NULL },
-  { NULL,        	0,       0,   false, false, false, false, NULL, NULL }
-};
+  {"save_volatiles",	0,       0,    true, false, false, false, NULL, NULL }
+});
 
 static int microblaze_interrupt_function_p (tree);
 
diff --git a/gcc/config/mips/mips.cc b/gcc/config/mips/mips.cc
index a304e1c5637..0cf49c13bf4 100644
--- a/gcc/config/mips/mips.cc
+++ b/gcc/config/mips/mips.cc
@@ -611,7 +611,7 @@  static tree mips_handle_use_shadow_register_set_attr (tree *, tree, tree, int,
 						      bool *);
 
 /* The value of TARGET_ATTRIBUTE_TABLE.  */
-static const struct attribute_spec mips_attribute_table[] = {
+TARGET_GNU_ATTRIBUTES (mips_attribute_table, {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
   { "long_call",   0, 0, false, true,  true,  false, NULL, NULL },
@@ -636,9 +636,8 @@  static const struct attribute_spec mips_attribute_table[] = {
     mips_handle_use_shadow_register_set_attr, NULL },
   { "keep_interrupts_masked",	0, 0, false, true,  true, false, NULL, NULL },
   { "use_debug_exception_return", 0, 0, false, true, true, false, NULL, NULL },
-  { "use_hazard_barrier_return", 0, 0, true, false, false, false, NULL, NULL },
-  { NULL,	   0, 0, false, false, false, false, NULL, NULL }
-};
+  { "use_hazard_barrier_return", 0, 0, true, false, false, false, NULL, NULL }
+});
 
 /* A table describing all the processors GCC knows about; see
    mips-cpus.def for details.  */
diff --git a/gcc/config/msp430/msp430.cc b/gcc/config/msp430/msp430.cc
index 061a9c77961..85f499f175d 100644
--- a/gcc/config/msp430/msp430.cc
+++ b/gcc/config/msp430/msp430.cc
@@ -2057,7 +2057,7 @@  static const struct attribute_spec::exclusions attr_either_exclusions[] =
 #define TARGET_ATTRIBUTE_TABLE		msp430_attribute_table
 
 /* Table of MSP430-specific attributes.  */
-const struct attribute_spec msp430_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (msp430_attribute_table,
   {
     /* { name, min_num_args, max_num_args, decl_req, type_req, fn_type_req,
 	 affects_type_identity, handler, exclude } */
@@ -2075,10 +2075,8 @@  const struct attribute_spec msp430_attribute_table[] =
     { ATTR_UPPER,       0, 0, true,  false, false, false, msp430_section_attr,
       attr_upper_exclusions },
     { ATTR_EITHER,      0, 0, true,  false, false, false, msp430_section_attr,
-      attr_either_exclusions },
-
-    { NULL,		0, 0, false, false, false, false, NULL,  NULL }
-  };
+      attr_either_exclusions }
+  });
 
 #undef TARGET_HANDLE_GENERIC_ATTRIBUTE
 #define TARGET_HANDLE_GENERIC_ATTRIBUTE msp430_handle_generic_attribute
diff --git a/gcc/config/nds32/nds32.cc b/gcc/config/nds32/nds32.cc
index 1f8de2a514a..e0a73985b66 100644
--- a/gcc/config/nds32/nds32.cc
+++ b/gcc/config/nds32/nds32.cc
@@ -288,7 +288,7 @@  static const int nds32_reg_alloc_order_for_speed[] =
 };
 
 /* Defining target-specific uses of __attribute__.  */
-static const struct attribute_spec nds32_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (nds32_attribute_table,
 {
   /* Syntax: { name, min_len, max_len, decl_required, type_required,
 	       function_type_required, affects_type_identity, handler,
@@ -326,11 +326,8 @@  static const struct attribute_spec nds32_attribute_table[] =
 
   /* FOR BACKWARD COMPATIBILITY,
      this attribute also tells no prologue/epilogue.  */
-  { "no_prologue",  0,  0, false, false, false, false, NULL, NULL },
-
-  /* The last attribute spec is set to be NULL.  */
-  { NULL,           0,  0, false, false, false, false, NULL, NULL }
-};
+  { "no_prologue",  0,  0, false, false, false, false, NULL, NULL }
+});
 
 
 /* ------------------------------------------------------------------------ */
diff --git a/gcc/config/nvptx/nvptx.cc b/gcc/config/nvptx/nvptx.cc
index 634c31673be..d5497631429 100644
--- a/gcc/config/nvptx/nvptx.cc
+++ b/gcc/config/nvptx/nvptx.cc
@@ -5834,16 +5834,15 @@  nvptx_handle_shared_attribute (tree *node, tree name, tree ARG_UNUSED (args),
 }
 
 /* Table of valid machine attributes.  */
-static const struct attribute_spec nvptx_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (nvptx_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
   { "kernel", 0, 0, true, false,  false, false, nvptx_handle_kernel_attribute,
     NULL },
   { "shared", 0, 0, true, false,  false, false, nvptx_handle_shared_attribute,
-    NULL },
-  { NULL, 0, 0, false, false, false, false, NULL, NULL }
-};
+    NULL }
+});
 
 /* Limit vector alignments to BIGGEST_ALIGNMENT.  */
 
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index e36b5fb9bd0..154d80b48e7 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -421,7 +421,7 @@  static tree riscv_handle_type_attribute (tree *, tree, tree, int, bool *);
 static void riscv_legitimize_poly_move (machine_mode, rtx, rtx, rtx);
 
 /* Defining target-specific uses of __attribute__.  */
-static const struct attribute_spec riscv_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (riscv_attribute_table,
 {
   /* Syntax: { name, min_len, max_len, decl_required, type_required,
 	       function_type_required, affects_type_identity, handler,
@@ -437,11 +437,8 @@  static const struct attribute_spec riscv_attribute_table[] =
   /* The following two are used for the built-in properties of the Vector type
      and are not used externally */
   {"RVV sizeless type", 4, 4, false, true, false, true, NULL, NULL},
-  {"RVV type", 0, 0, false, true, false, true, NULL, NULL},
-
-  /* The last attribute spec is set to be NULL.  */
-  { NULL,	0,  0, false, false, false, false, NULL, NULL }
-};
+  {"RVV type", 0, 0, false, true, false, true, NULL, NULL}
+});
 
 /* Order for the CLOBBERs/USEs of gpr_save.  */
 static const unsigned gpr_save_reg_order[] = {
diff --git a/gcc/config/rl78/rl78.cc b/gcc/config/rl78/rl78.cc
index 0cbd6bf780a..b2e9b04b691 100644
--- a/gcc/config/rl78/rl78.cc
+++ b/gcc/config/rl78/rl78.cc
@@ -898,7 +898,7 @@  rl78_handle_vector_attribute (tree * node,
 #define TARGET_ATTRIBUTE_TABLE		rl78_attribute_table
 
 /* Table of RL78-specific attributes.  */
-const struct attribute_spec rl78_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (rl78_attribute_table,
 {
   /* Name, min_len, max_len, decl_req, type_req, fn_type_req,
      affects_type_identity, handler, exclude.  */
@@ -911,9 +911,8 @@  const struct attribute_spec rl78_attribute_table[] =
   { "saddr",          0, 0, true, false, false, false,
     rl78_handle_saddr_attribute, NULL },
   { "vector",         1, -1, true, false, false, false,
-	rl78_handle_vector_attribute, NULL },
-  { NULL,             0, 0, false, false, false, false, NULL, NULL }
-};
+	rl78_handle_vector_attribute, NULL }
+});
 
 
 
diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc
index 5f56c3ed85b..cc3f97ae4c5 100644
--- a/gcc/config/rs6000/rs6000.cc
+++ b/gcc/config/rs6000/rs6000.cc
@@ -1255,7 +1255,7 @@  static const char alt_reg_names[][8] =
 
 /* Table of valid machine attributes.  */
 
-static const struct attribute_spec rs6000_attribute_table[] =
+static const attribute_spec rs6000_gnu_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -1272,7 +1272,16 @@  static const struct attribute_spec rs6000_attribute_table[] =
 #ifdef SUBTARGET_ATTRIBUTE_TABLE
   SUBTARGET_ATTRIBUTE_TABLE,
 #endif
-  { NULL,        0, 0, false, false, false, false, NULL, NULL }
+};
+
+static const scoped_attribute_specs rs6000_gnu_attribute_table =
+{
+  "gnu", rs6000_gnu_attributes
+};
+
+static const scoped_attribute_specs *const rs6000_attribute_table[] =
+{
+  &rs6000_gnu_attribute_table
 };
 
 #ifndef TARGET_PROFILE_KERNEL
diff --git a/gcc/config/rx/rx.cc b/gcc/config/rx/rx.cc
index 245c6a4413d..0754e286552 100644
--- a/gcc/config/rx/rx.cc
+++ b/gcc/config/rx/rx.cc
@@ -2760,7 +2760,7 @@  rx_handle_vector_attribute (tree * node,
 }
 
 /* Table of RX specific attributes.  */
-const struct attribute_spec rx_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (rx_attribute_table,
 {
   /* Name, min_len, max_len, decl_req, type_req, fn_type_req,
      affects_type_identity, handler, exclude.  */
@@ -2771,9 +2771,8 @@  const struct attribute_spec rx_attribute_table[] =
   { "naked",          0, 0, true, false, false, false,
     rx_handle_func_attribute, NULL },
   { "vector",         1, -1, true, false, false, false,
-    rx_handle_vector_attribute, NULL },
-  { NULL,             0, 0, false, false, false, false, NULL, NULL }
-};
+    rx_handle_vector_attribute, NULL }
+});
 
 /* Implement TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE.  */
 
diff --git a/gcc/config/s390/s390.cc b/gcc/config/s390/s390.cc
index 64f56d8effa..a8205385451 100644
--- a/gcc/config/s390/s390.cc
+++ b/gcc/config/s390/s390.cc
@@ -1303,7 +1303,7 @@  s390_handle_string_attribute (tree *node, tree name ATTRIBUTE_UNUSED,
   return NULL_TREE;
 }
 
-static const struct attribute_spec s390_attribute_table[] = {
+TARGET_GNU_ATTRIBUTES (s390_attribute_table, {
   { "hotpatch", 2, 2, true, false, false, false,
     s390_handle_hotpatch_attribute, NULL },
   { "s390_vector_bool", 0, 0, false, true, false, true,
@@ -1319,11 +1319,8 @@  static const struct attribute_spec s390_attribute_table[] = {
   { "function_return_reg", 1, 1, true, false, false, false,
     s390_handle_string_attribute, NULL },
   { "function_return_mem", 1, 1, true, false, false, false,
-    s390_handle_string_attribute, NULL },
-
-  /* End element.  */
-  { NULL,        0, 0, false, false, false, false, NULL, NULL }
-};
+    s390_handle_string_attribute, NULL }
+});
 
 /* Return the alignment for LABEL.  We default to the -falign-labels
    value except for the literal pool base label.  */
diff --git a/gcc/config/sh/sh.cc b/gcc/config/sh/sh.cc
index 6ec2eecf754..8c378b28b6d 100644
--- a/gcc/config/sh/sh.cc
+++ b/gcc/config/sh/sh.cc
@@ -329,7 +329,7 @@  static bool sh_hard_regno_mode_ok (unsigned int, machine_mode);
 static bool sh_modes_tieable_p (machine_mode, machine_mode);
 static bool sh_can_change_mode_class (machine_mode, machine_mode, reg_class_t);
 
-static const struct attribute_spec sh_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (sh_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -348,9 +348,8 @@  static const struct attribute_spec sh_attribute_table[] =
   { "resbank",           0, 0, true,  false, false, false,
     sh_handle_resbank_handler_attribute, NULL },
   { "function_vector",   1, 1, true,  false, false, false,
-    sh2a_handle_function_vector_handler_attribute, NULL },
-  { NULL,                0, 0, false, false, false, false, NULL, NULL }
-};
+    sh2a_handle_function_vector_handler_attribute, NULL }
+});
 
 /* Initialize the GCC target structure.  */
 #undef TARGET_ATTRIBUTE_TABLE
diff --git a/gcc/config/sparc/sparc.cc b/gcc/config/sparc/sparc.cc
index 82e57952414..a78dedc8075 100644
--- a/gcc/config/sparc/sparc.cc
+++ b/gcc/config/sparc/sparc.cc
@@ -721,13 +721,12 @@  static HARD_REG_SET sparc_zero_call_used_regs (HARD_REG_SET);
 
 #ifdef SUBTARGET_ATTRIBUTE_TABLE
 /* Table of valid machine attributes.  */
-static const struct attribute_spec sparc_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (sparc_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        do_diagnostic, handler, exclude } */
-  SUBTARGET_ATTRIBUTE_TABLE,
-  { NULL,        0, 0, false, false, false, false, NULL, NULL }
-};
+  SUBTARGET_ATTRIBUTE_TABLE
+});
 #endif
 
 char sparc_hard_reg_printed[8];
diff --git a/gcc/config/stormy16/stormy16.cc b/gcc/config/stormy16/stormy16.cc
index 10887153906..071043b4128 100644
--- a/gcc/config/stormy16/stormy16.cc
+++ b/gcc/config/stormy16/stormy16.cc
@@ -2377,7 +2377,7 @@  static tree xstormy16_handle_interrupt_attribute
 static tree xstormy16_handle_below100_attribute
   (tree *, tree, tree, int, bool *);
 
-static const struct attribute_spec xstormy16_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (xstormy16_attribute_table,
 {
   /* name, min_len, max_len, decl_req, type_req, fn_type_req,
      affects_type_identity, handler, exclude.  */
@@ -2386,9 +2386,8 @@  static const struct attribute_spec xstormy16_attribute_table[] =
   { "BELOW100",  0, 0, false, false, false, false,
     xstormy16_handle_below100_attribute, NULL },
   { "below100",  0, 0, false, false, false, false,
-    xstormy16_handle_below100_attribute, NULL },
-  { NULL,        0, 0, false, false, false, false, NULL, NULL }
-};
+    xstormy16_handle_below100_attribute, NULL }
+});
 
 /* Handle an "interrupt" attribute;
    arguments as in struct attribute_spec.handler.  */
diff --git a/gcc/config/v850/v850.cc b/gcc/config/v850/v850.cc
index 416c2841a5c..50c91c68b8b 100644
--- a/gcc/config/v850/v850.cc
+++ b/gcc/config/v850/v850.cc
@@ -3114,7 +3114,7 @@  v850_adjust_insn_length (rtx_insn *insn, int length)
 
 /* V850 specific attributes.  */
 
-static const struct attribute_spec v850_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (v850_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -3127,9 +3127,8 @@  static const struct attribute_spec v850_attribute_table[] =
   { "tda",               0, 0, true,  false, false, false,
     v850_handle_data_area_attribute, NULL },
   { "zda",               0, 0, true,  false, false, false,
-    v850_handle_data_area_attribute, NULL },
-  { NULL,                0, 0, false, false, false, false, NULL, NULL }
-};
+    v850_handle_data_area_attribute, NULL }
+});
 
 static void
 v850_option_override (void)
diff --git a/gcc/config/visium/visium.cc b/gcc/config/visium/visium.cc
index 5fadbc80be0..4a1877c2ac1 100644
--- a/gcc/config/visium/visium.cc
+++ b/gcc/config/visium/visium.cc
@@ -145,14 +145,13 @@  static inline bool current_function_has_lr_slot (void);
 
 /* Supported attributes:
    interrupt -- specifies this function is an interrupt handler.   */
-static const struct attribute_spec visium_attribute_table[] =
+TARGET_GNU_ATTRIBUTES (visium_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
   { "interrupt", 0, 0, true, false, false, false, visium_handle_interrupt_attr,
-    NULL},
-  { NULL, 0, 0, false, false, false, false, NULL, NULL },
-};
+    NULL}
+});
 
 static struct machine_function *visium_init_machine_status (void);
 
diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h
index 1408301a300..d3fa002593f 100644
--- a/gcc/cp/cp-objcp-common.h
+++ b/gcc/cp/cp-objcp-common.h
@@ -122,13 +122,16 @@  extern tree cxx_simulate_record_decl (location_t, const char *,
 #undef LANG_HOOKS_FINALIZE_EARLY_DEBUG
 #define LANG_HOOKS_FINALIZE_EARLY_DEBUG c_common_finalize_early_debug
 
-/* Attribute hooks.  */
-#undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
-#define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE c_common_attribute_table
-#undef LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE
-#define LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE c_common_format_attribute_table
+static const scoped_attribute_specs *const cp_objcp_attribute_table[] =
+{
+  &std_attribute_table,
+  &cxx_gnu_attribute_table,
+  &c_common_gnu_attribute_table,
+  &c_common_format_attribute_table
+};
+
 #undef LANG_HOOKS_ATTRIBUTE_TABLE
-#define LANG_HOOKS_ATTRIBUTE_TABLE cxx_attribute_table
+#define LANG_HOOKS_ATTRIBUTE_TABLE cp_objcp_attribute_table
 
 #undef LANG_HOOKS_TREE_INLINING_VAR_MOD_TYPE_P
 #define LANG_HOOKS_TREE_INLINING_VAR_MOD_TYPE_P cp_var_mod_type_p
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 98b29e9cf81..0a543c5f14c 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8000,7 +8000,8 @@  extern tree maybe_dummy_object			(tree, tree *);
 extern bool is_dummy_object			(const_tree);
 extern bool is_byte_access_type			(tree);
 extern bool is_byte_access_type_not_plain_char	(tree);
-extern const struct attribute_spec cxx_attribute_table[];
+extern const struct scoped_attribute_specs cxx_gnu_attribute_table;
+extern const struct scoped_attribute_specs std_attribute_table;
 extern tree make_ptrmem_cst			(tree, tree);
 extern tree cp_build_type_attribute_variant     (tree, tree);
 extern tree cp_build_reference_type		(tree, bool);
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 417c92ba76f..fd99d64ca47 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -5072,7 +5072,7 @@  handle_likeliness_attribute (tree *node, tree name, tree args,
 }
 
 /* Table of valid C++ attributes.  */
-const struct attribute_spec cxx_attribute_table[] =
+static const attribute_spec cxx_gnu_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -5080,11 +5080,15 @@  const struct attribute_spec cxx_attribute_table[] =
     handle_init_priority_attribute, NULL },
   { "abi_tag", 1, -1, false, false, false, true,
     handle_abi_tag_attribute, NULL },
-  { NULL, 0, 0, false, false, false, false, NULL, NULL }
+};
+
+const scoped_attribute_specs cxx_gnu_attribute_table =
+{
+  "gnu", cxx_gnu_attributes
 };
 
 /* Table of C++ standard attributes.  */
-const struct attribute_spec std_attribute_table[] =
+static const attribute_spec std_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -5105,10 +5109,11 @@  const struct attribute_spec std_attribute_table[] =
   { "pre", 0, -1, false, false, false, false,
     handle_contract_attribute, NULL },
   { "post", 0, -1, false, false, false, false,
-    handle_contract_attribute, NULL },
-  { NULL, 0, 0, false, false, false, false, NULL, NULL }
+    handle_contract_attribute, NULL }
 };
 
+const scoped_attribute_specs std_attribute_table = { nullptr, std_attributes };
+
 /* Handle an "init_priority" attribute; arguments as in
    struct attribute_spec.handler.  */
 static tree
@@ -5704,7 +5709,6 @@  void
 init_tree (void)
 {
   list_hash_table = hash_table<list_hasher>::create_ggc (61);
-  register_scoped_attributes (std_attribute_table, NULL);
 }
 
 /* Returns the kind of special function that DECL (a FUNCTION_DECL)
diff --git a/gcc/d/d-attribs.cc b/gcc/d/d-attribs.cc
index c0dc0e24ded..f6411058072 100644
--- a/gcc/d/d-attribs.cc
+++ b/gcc/d/d-attribs.cc
@@ -162,7 +162,7 @@  extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[] =
 
 /* Table of machine-independent attributes.
    For internal use (marking of built-ins) only.  */
-const attribute_spec d_langhook_common_attribute_table[] =
+static const attribute_spec d_langhook_common_attributes[] =
 {
   ATTR_SPEC ("noreturn", 0, 0, true, false, false, false,
 	     handle_noreturn_attribute, attr_noreturn_exclusions),
@@ -190,11 +190,15 @@  const attribute_spec d_langhook_common_attribute_table[] =
 	     handle_fnspec_attribute, NULL),
   ATTR_SPEC ("omp declare simd", 0, -1, true,  false, false, false,
 	     handle_omp_declare_simd_attribute, NULL),
-  ATTR_SPEC (NULL, 0, 0, false, false, false, false, NULL, NULL),
+};
+
+const scoped_attribute_specs d_langhook_common_attribute_table =
+{
+  "gnu", d_langhook_common_attributes
 };
 
 /* Table of D language attributes exposed by `gcc.attribute' UDAs.  */
-const attribute_spec d_langhook_attribute_table[] =
+static const attribute_spec d_langhook_gnu_attributes[] =
 {
   ATTR_SPEC ("noinline", 0, 0, true, false, false, false,
 	     d_handle_noinline_attribute, attr_noinline_exclusions),
@@ -238,9 +242,12 @@  const attribute_spec d_langhook_attribute_table[] =
 	     d_handle_used_attribute, NULL),
   ATTR_SPEC ("visibility", 1, 1, false, false, false, false,
 	     d_handle_visibility_attribute, NULL),
-  ATTR_SPEC (NULL, 0, 0, false, false, false, false, NULL, NULL),
 };
 
+const scoped_attribute_specs d_langhook_gnu_attribute_table =
+{
+  "gnu", d_langhook_gnu_attributes
+};
 
 /* Insert the type attribute ATTRNAME with value VALUE into TYPE.
    Returns a new variant of the original type declaration.  */
@@ -283,20 +290,14 @@  uda_attribute_p (const char *name)
 
   /* Search both our language, and target attribute tables.
      Common and format attributes are kept internal.  */
-  for (const attribute_spec *p = d_langhook_attribute_table; p->name; p++)
-    {
-      if (get_identifier (p->name) == ident)
-	return true;
-    }
+  for (const attribute_spec &p : d_langhook_gnu_attributes)
+    if (get_identifier (p.name) == ident)
+      return true;
 
-  if (targetm.attribute_table)
-    {
-      for (const attribute_spec *p = targetm.attribute_table; p->name; p++)
-	{
-	  if (get_identifier (p->name) == ident)
-	    return true;
-	}
-    }
+  for (auto scoped_attributes : targetm.attribute_table)
+    for (const attribute_spec &p : scoped_attributes->attributes)
+      if (get_identifier (p.name) == ident)
+	return true;
 
   return false;
 }
diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc
index 61fc1608b40..dcbffec3752 100644
--- a/gcc/d/d-lang.cc
+++ b/gcc/d/d-lang.cc
@@ -1927,6 +1927,12 @@  d_get_sarif_source_language (const char *)
   return "d";
 }
 
+const scoped_attribute_specs *const d_langhook_attribute_table[] =
+{
+  &d_langhook_gnu_attribute_table,
+  &d_langhook_common_attribute_table,
+};
+
 /* Definitions for our language-specific hooks.  */
 
 #undef LANG_HOOKS_NAME
@@ -1938,7 +1944,6 @@  d_get_sarif_source_language (const char *)
 #undef LANG_HOOKS_HANDLE_OPTION
 #undef LANG_HOOKS_POST_OPTIONS
 #undef LANG_HOOKS_PARSE_FILE
-#undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
 #undef LANG_HOOKS_ATTRIBUTE_TABLE
 #undef LANG_HOOKS_GET_ALIAS_SET
 #undef LANG_HOOKS_TYPES_COMPATIBLE_P
@@ -1971,7 +1976,6 @@  d_get_sarif_source_language (const char *)
 #define LANG_HOOKS_HANDLE_OPTION	    d_handle_option
 #define LANG_HOOKS_POST_OPTIONS		    d_post_options
 #define LANG_HOOKS_PARSE_FILE		    d_parse_file
-#define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE   d_langhook_common_attribute_table
 #define LANG_HOOKS_ATTRIBUTE_TABLE	    d_langhook_attribute_table
 #define LANG_HOOKS_GET_ALIAS_SET	    d_get_alias_set
 #define LANG_HOOKS_TYPES_COMPATIBLE_P	    d_types_compatible_p
diff --git a/gcc/d/d-tree.h b/gcc/d/d-tree.h
index d19c3f50bd9..46a28737daa 100644
--- a/gcc/d/d-tree.h
+++ b/gcc/d/d-tree.h
@@ -520,8 +520,8 @@  extern tree insert_decl_attribute (tree, const char *, tree = NULL_TREE);
 extern void apply_user_attributes (Dsymbol *, tree);
 
 /* In d-builtins.cc.  */
-extern const attribute_spec d_langhook_attribute_table[];
-extern const attribute_spec d_langhook_common_attribute_table[];
+extern const struct scoped_attribute_specs d_langhook_gnu_attribute_table;
+extern const struct scoped_attribute_specs d_langhook_common_attribute_table;
 extern Type *build_frontend_type (tree);
 
 extern tree d_builtin_function (tree);
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index d83ca73b1af..abc23b2bb73 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -10528,12 +10528,33 @@  Target-specific attributes may be defined for functions, data and types.
 These are described using the following target hooks; they also need to
 be documented in @file{extend.texi}.
 
-@deftypevr {Target Hook} {const struct attribute_spec *} TARGET_ATTRIBUTE_TABLE
-If defined, this target hook points to an array of @samp{struct
-attribute_spec} (defined in @file{tree-core.h}) specifying the machine
-specific attributes for this target and some of the restrictions on the
-entities to which these attributes are applied and the arguments they
-take.
+@deftypevr {Target Hook} {array_slice<const struct scoped_attribute_specs *const>} TARGET_ATTRIBUTE_TABLE
+If defined, this target hook provides an array of
+@samp{scoped_attribute_spec}s (defined in @file{attribs.h}) that specify the
+machine-specific attributes for this target.  The information includes some
+of the restrictions on the entities to which these attributes are applied
+and the arguments that the attributes take.
+
+In C and C++, these attributes are associated with two syntaxes:
+the traditional GNU @code{__attribute__} syntax and the standard
+@samp{[[]]} syntax.  Attributes that support the GNU syntax must be
+placed in the @code{gnu} namespace.  Such attributes can then also be
+written @samp{[[gnu::@dots{}]]}.  Attributes that use only the standard
+syntax should be placed in whichever namespace the attribute specification
+requires.  For example, a target might choose to support vendor-specific
+@samp{[[]]} attributes that the vendor places in their own namespace.
+
+Targets that only define attributes in the @code{gnu} namespace
+can uase the following shorthand to define the table:
+
+@smallexample
+TARGET_GNU_ATTRIBUTES (@var{cpu_attribute_table}, @{
+  @{ "@var{attribute1}", @dots{} @},
+  @{ "@var{attribute2}", @dots{} @},
+  @dots{},
+  @{ "@var{attributen}", @dots{} @},
+@});
+@end smallexample
 @end deftypevr
 
 @deftypefn {Target Hook} bool TARGET_ATTRIBUTE_TAKES_IDENTIFIER_P (const_tree @var{name})
diff --git a/gcc/fortran/f95-lang.cc b/gcc/fortran/f95-lang.cc
index 350e6e379eb..99dd76226a2 100644
--- a/gcc/fortran/f95-lang.cc
+++ b/gcc/fortran/f95-lang.cc
@@ -39,6 +39,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "cpp.h"
 #include "trans-types.h"
 #include "trans-const.h"
+#include "attribs.h"
 
 /* Language-dependent contents of an identifier.  */
 
@@ -87,7 +88,7 @@  gfc_handle_omp_declare_target_attribute (tree *, tree, tree, int, bool *)
 }
 
 /* Table of valid Fortran attributes.  */
-static const struct attribute_spec gfc_attribute_table[] =
+static const attribute_spec gfc_gnu_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -97,7 +98,16 @@  static const struct attribute_spec gfc_attribute_table[] =
     gfc_handle_omp_declare_target_attribute, NULL },
   { "oacc function", 0, -1, true,  false, false, false,
     gfc_handle_omp_declare_target_attribute, NULL },
-  { NULL,		  0, 0, false, false, false, false, NULL, NULL }
+};
+
+static const scoped_attribute_specs gfc_gnu_attribute_table =
+{
+  "gnu", gfc_gnu_attributes
+};
+
+static const scoped_attribute_specs *const gfc_attribute_table[] =
+{
+  &gfc_gnu_attribute_table
 };
 
 /* Get a value for the SARIF v2.1.0 "artifact.sourceLanguage" property,
diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
index a729086bafb..61cc0e1ae66 100644
--- a/gcc/jit/dummy-frontend.cc
+++ b/gcc/jit/dummy-frontend.cc
@@ -87,7 +87,7 @@  static const struct attribute_spec::exclusions attr_const_pure_exclusions[] =
 };
 
 /* Table of machine-independent attributes supported in libgccjit.  */
-const struct attribute_spec jit_attribute_table[] =
+static const attribute_spec jit_gnu_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -128,22 +128,36 @@  const struct attribute_spec jit_attribute_table[] =
   /* For internal use only.  The leading '*' both prevents its usage in
      source code and signals that it may be overridden by machine tables.  */
   { "*tm regparm",            0, 0, false, true, true, false,
-			      ignore_attribute, NULL },
-  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
+			      ignore_attribute, NULL }
+};
+
+static const scoped_attribute_specs jit_gnu_attribute_table =
+{
+  "gnu", jit_gnu_attributes
 };
 
 /* Give the specifications for the format attributes, used by C and all
    descendants.  */
 
-const struct attribute_spec jit_format_attribute_table[] =
+static const attribute_spec jit_format_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
   { "format",                 3, 3, false, true,  true, false,
 			      handle_format_attribute, NULL },
   { "format_arg",             1, 1, false, true,  true, false,
-			      handle_format_arg_attribute, NULL },
-  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
+			      handle_format_arg_attribute, NULL }
+};
+
+static const scoped_attribute_specs jit_format_attribute_table =
+{
+  "gnu", jit_format_attributes
+};
+
+static const scoped_attribute_specs *const jit_attribute_table[] =
+{
+  &jit_gnu_attribute_table,
+  &jit_format_attribute_table
 };
 
 /* Attribute handlers.  */
@@ -719,10 +733,8 @@  jit_langhook_getdecls (void)
 #define LANG_HOOKS_GETDECLS		jit_langhook_getdecls
 
 /* Attribute hooks.  */
-#undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
-#define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE jit_attribute_table
-#undef LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE
-#define LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE jit_format_attribute_table
+#undef LANG_HOOKS_ATTRIBUTE_TABLE
+#define LANG_HOOKS_ATTRIBUTE_TABLE jit_attribute_table
 
 #undef  LANG_HOOKS_DEEP_UNSHARING
 #define LANG_HOOKS_DEEP_UNSHARING	true
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index c6d18526360..c9cb65759c2 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -153,9 +153,7 @@  extern const char *lhd_get_sarif_source_language (const char *);
 #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE lhd_get_sarif_source_language
 
 /* Attribute hooks.  */
-#define LANG_HOOKS_ATTRIBUTE_TABLE		NULL
-#define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE	NULL
-#define LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE	NULL
+#define LANG_HOOKS_ATTRIBUTE_TABLE		{}
 
 /* Tree inlining hooks.  */
 #define LANG_HOOKS_TREE_INLINING_VAR_MOD_TYPE_P \
@@ -367,8 +365,6 @@  extern void lhd_end_section (void);
   LANG_HOOKS_PRINT_ERROR_FUNCTION, \
   LANG_HOOKS_TO_TARGET_CHARSET, \
   LANG_HOOKS_ATTRIBUTE_TABLE, \
-  LANG_HOOKS_COMMON_ATTRIBUTE_TABLE, \
-  LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE, \
   LANG_HOOKS_TREE_INLINING_INITIALIZER, \
   LANG_HOOKS_TREE_DUMP_INITIALIZER, \
   LANG_HOOKS_DECLS, \
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index cca75285fc2..2785a0070fc 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -532,9 +532,7 @@  struct lang_hooks
      table of attributes specific to the language, a table of
      attributes common to two or more languages (to allow easy
      sharing), and a table of attributes for checking formats.  */
-  const struct attribute_spec *attribute_table;
-  const struct attribute_spec *common_attribute_table;
-  const struct attribute_spec *format_attribute_table;
+  array_slice<const struct scoped_attribute_specs *const> attribute_table;
 
   struct lang_hooks_for_tree_inlining tree_inlining;
 
diff --git a/gcc/lto/lto-lang.cc b/gcc/lto/lto-lang.cc
index 14d419c2013..a8adf110b20 100644
--- a/gcc/lto/lto-lang.cc
+++ b/gcc/lto/lto-lang.cc
@@ -94,7 +94,7 @@  static const struct attribute_spec::exclusions attr_const_pure_exclusions[] =
 };
 
 /* Table of machine-independent attributes supported in GIMPLE.  */
-const struct attribute_spec lto_attribute_table[] =
+static const attribute_spec lto_gnu_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -135,14 +135,18 @@  const struct attribute_spec lto_attribute_table[] =
   /* For internal use only.  The leading '*' both prevents its usage in
      source code and signals that it may be overridden by machine tables.  */
   { "*tm regparm",            0, 0, false, true, true, false,
-			      ignore_attribute, NULL },
-  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
+			      ignore_attribute, NULL }
+};
+
+static const scoped_attribute_specs lto_gnu_attribute_table =
+{
+  "gnu", lto_gnu_attributes
 };
 
 /* Give the specifications for the format attributes, used by C and all
    descendants.  */
 
-const struct attribute_spec lto_format_attribute_table[] =
+static const attribute_spec lto_format_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
@@ -150,7 +154,17 @@  const struct attribute_spec lto_format_attribute_table[] =
 			      handle_format_attribute, NULL },
   { "format_arg",             1, 1, false, true,  true, false,
 			      handle_format_arg_attribute, NULL },
-  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
+};
+
+static const scoped_attribute_specs lto_format_attribute_table =
+{
+  "gnu", lto_format_attributes
+};
+
+static const scoped_attribute_specs *const lto_attribute_table[] =
+{
+  &lto_gnu_attribute_table,
+  &lto_format_attribute_table
 };
 
 enum built_in_attribute
@@ -1463,10 +1477,8 @@  static void lto_init_ts (void)
 #define LANG_HOOKS_EH_PERSONALITY lto_eh_personality
 
 /* Attribute hooks.  */
-#undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
-#define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE lto_attribute_table
-#undef LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE
-#define LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE lto_format_attribute_table
+#undef LANG_HOOKS_ATTRIBUTE_TABLE
+#define LANG_HOOKS_ATTRIBUTE_TABLE lto_attribute_table
 
 #undef LANG_HOOKS_BEGIN_SECTION
 #define LANG_HOOKS_BEGIN_SECTION lto_obj_begin_section
diff --git a/gcc/plugin.h b/gcc/plugin.h
index ee0a53ec4c9..f306adfb3b9 100644
--- a/gcc/plugin.h
+++ b/gcc/plugin.h
@@ -201,8 +201,7 @@  invoke_plugin_callbacks (int event ATTRIBUTE_UNUSED,
 
 extern void register_attribute (const struct attribute_spec *attr);
 /* The default argument for the third parameter is given in attribs.h.  */
-extern struct scoped_attributes* register_scoped_attributes (const struct attribute_spec *,
-							     const char *,
+extern struct scoped_attributes* register_scoped_attributes (const struct scoped_attribute_spec &,
 							     bool);
 
 #endif /* PLUGIN_H */
diff --git a/gcc/target-def.h b/gcc/target-def.h
index 847698a1e59..d03b039ab24 100644
--- a/gcc/target-def.h
+++ b/gcc/target-def.h
@@ -118,6 +118,20 @@ 
 #define TARGET_FUNCTION_INCOMING_ARG TARGET_FUNCTION_ARG
 #endif
 
+/* Declare a target attribute table called NAME that only has GNU attributes.
+   There should be no null trailing element.  E.g.:
+
+     TARGET_GNU_ATTRIBUTES (aarch64_attribute_table,
+     {
+       { "aarch64_vector_pcs", ... },
+       ...
+     });  */
+
+#define TARGET_GNU_ATTRIBUTES(NAME, ...) \
+  static const attribute_spec NAME##_2[] = __VA_ARGS__; \
+  static const scoped_attribute_specs NAME##_1 = { "gnu", NAME##_2 }; \
+  static const scoped_attribute_specs *const NAME[] = { &NAME##_1 }
+
 #include "target-hooks-def.h"
 
 #include "hooks.h"
diff --git a/gcc/target.def b/gcc/target.def
index 0996da0f71a..06c5d3b5c2e 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -2218,15 +2218,36 @@  merging.",
  merge_type_attributes)
 
 /* Table of machine attributes and functions to handle them.
-   Ignored if NULL.  */
+   Ignored if empty.  */
 DEFHOOKPOD
 (attribute_table,
- "If defined, this target hook points to an array of @samp{struct\n\
-attribute_spec} (defined in @file{tree-core.h}) specifying the machine\n\
-specific attributes for this target and some of the restrictions on the\n\
-entities to which these attributes are applied and the arguments they\n\
-take.",
- const struct attribute_spec *, NULL)
+ "If defined, this target hook provides an array of\n\
+@samp{scoped_attribute_spec}s (defined in @file{attribs.h}) that specify the\n\
+machine-specific attributes for this target.  The information includes some\n\
+of the restrictions on the entities to which these attributes are applied\n\
+and the arguments that the attributes take.\n\
+\n\
+In C and C++, these attributes are associated with two syntaxes:\n\
+the traditional GNU @code{__attribute__} syntax and the standard\n\
+@samp{[[]]} syntax.  Attributes that support the GNU syntax must be\n\
+placed in the @code{gnu} namespace.  Such attributes can then also be\n\
+written @samp{[[gnu::@dots{}]]}.  Attributes that use only the standard\n\
+syntax should be placed in whichever namespace the attribute specification\n\
+requires.  For example, a target might choose to support vendor-specific\n\
+@samp{[[]]} attributes that the vendor places in their own namespace.\n\
+\n\
+Targets that only define attributes in the @code{gnu} namespace\n\
+can uase the following shorthand to define the table:\n\
+\n\
+@smallexample\n\
+TARGET_GNU_ATTRIBUTES (@var{cpu_attribute_table}, @{\n\
+  @{ \"@var{attribute1}\", @dots{} @},\n\
+  @{ \"@var{attribute2}\", @dots{} @},\n\
+  @dots{},\n\
+  @{ \"@var{attributen}\", @dots{} @},\n\
+@});\n\
+@end smallexample",
+ array_slice<const struct scoped_attribute_specs *const>, {})
 
 /* Return true iff attribute NAME expects a plain identifier as its first
    argument.  */
diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
index 00d05102e7a..2831a0368f6 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -4094,17 +4094,16 @@  inline_forbidden_p (tree fndecl)
 static bool
 function_attribute_inlinable_p (const_tree fndecl)
 {
-  if (targetm.attribute_table)
+  for (auto scoped_attributes : targetm.attribute_table)
     {
       const_tree a;
 
       for (a = DECL_ATTRIBUTES (fndecl); a; a = TREE_CHAIN (a))
 	{
 	  const_tree name = get_attribute_name (a);
-	  int i;
 
-	  for (i = 0; targetm.attribute_table[i].name != NULL; i++)
-	    if (is_attribute_p (targetm.attribute_table[i].name, name))
+	  for (const attribute_spec &attribute : scoped_attributes->attributes)
+	    if (is_attribute_p (attribute.name, name))
 	      return targetm.function_attribute_inlinable_p (fndecl);
 	}
     }