summaryrefslogtreecommitdiff
path: root/0174-Backport-SME-aarch64-Add-arm_streaming-_compatible-a.patch
diff options
context:
space:
mode:
Diffstat (limited to '0174-Backport-SME-aarch64-Add-arm_streaming-_compatible-a.patch')
-rw-r--r--0174-Backport-SME-aarch64-Add-arm_streaming-_compatible-a.patch1178
1 files changed, 1178 insertions, 0 deletions
diff --git a/0174-Backport-SME-aarch64-Add-arm_streaming-_compatible-a.patch b/0174-Backport-SME-aarch64-Add-arm_streaming-_compatible-a.patch
new file mode 100644
index 0000000..381f4ce
--- /dev/null
+++ b/0174-Backport-SME-aarch64-Add-arm_streaming-_compatible-a.patch
@@ -0,0 +1,1178 @@
+From 70b732b4518dd0e44b9e6bfaaad78492b8db8f29 Mon Sep 17 00:00:00 2001
+From: Richard Sandiford <richard.sandiford@arm.com>
+Date: Tue, 5 Dec 2023 10:11:23 +0000
+Subject: [PATCH 075/157] [Backport][SME] aarch64: Add
+ arm_streaming(_compatible) attributes
+
+Reference: https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=2c9a54b4238308b127c3b60b01a591363131e7db
+
+This patch adds support for recognising the SME arm::streaming
+and arm::streaming_compatible attributes. These attributes
+respectively describe whether the processor is definitely in
+"streaming mode" (PSTATE.SM==1), whether the processor is
+definitely not in streaming mode (PSTATE.SM==0), or whether
+we don't know at compile time either way.
+
+As far as the compiler is concerned, this effectively creates three
+ISA submodes: streaming mode enables things that are not available
+in non-streaming mode, non-streaming mode enables things that not
+available in streaming mode, and streaming-compatible mode has to stick
+to the common subset. This means that some instructions are conditional
+on PSTATE.SM==1 and some are conditional on PSTATE.SM==0.
+
+I wondered about recording the streaming state in a new variable.
+However, the set of available instructions is also influenced by
+PSTATE.ZA (added later), so I think it makes sense to view this
+as an instance of a more general mechanism. Also, keeping the
+PSTATE.SM state in the same flag variable as the other ISA
+features makes it possible to sum up the requirements of an
+ACLE function in a single value.
+
+The patch therefore adds a new set of feature flags called "ISA modes".
+Unlike the other two sets of flags (optional features and architecture-
+level features), these ISA modes are not controlled directly by
+command-line parameters or "target" attributes.
+
+arm::streaming and arm::streaming_compatible are function type attributes
+rather than function declaration attributes. This means that we need
+to find somewhere to copy the type information across to a function's
+target options. The patch does this in aarch64_set_current_function.
+
+We also need to record which ISA mode a callee expects/requires
+to be active on entry. (The same mode is then active on return.)
+The patch extends the current UNSPEC_CALLEE_ABI cookie to include
+this information, as well as the PCS variant that it recorded
+previously.
+
+The attributes can also be written __arm_streaming and
+__arm_streaming_compatible. This has two advantages: it triggers
+an error on compilers that don't understand the attributes, and it
+eases use on C, where [[...]] attributes were only added in C23.
+
+gcc/
+ * config/aarch64/aarch64-isa-modes.def: New file.
+ * config/aarch64/aarch64.h: Include it in the feature enumerations.
+ (AARCH64_FL_SM_STATE, AARCH64_FL_ISA_MODES): New constants.
+ (AARCH64_FL_DEFAULT_ISA_MODE): Likewise.
+ (AARCH64_ISA_MODE): New macro.
+ (CUMULATIVE_ARGS): Add an isa_mode field.
+ * config/aarch64/aarch64-protos.h (aarch64_gen_callee_cookie): Declare.
+ (aarch64_tlsdesc_abi_id): Return an arm_pcs.
+ * config/aarch64/aarch64.cc (attr_streaming_exclusions)
+ (aarch64_gnu_attributes, aarch64_gnu_attribute_table)
+ (aarch64_arm_attributes, aarch64_arm_attribute_table): New tables.
+ (aarch64_attribute_table): Redefine to include the gnu and arm
+ attributes.
+ (aarch64_fntype_pstate_sm, aarch64_fntype_isa_mode): New functions.
+ (aarch64_fndecl_pstate_sm, aarch64_fndecl_isa_mode): Likewise.
+ (aarch64_gen_callee_cookie, aarch64_callee_abi): Likewise.
+ (aarch64_insn_callee_cookie, aarch64_insn_callee_abi): Use them.
+ (aarch64_function_arg, aarch64_output_mi_thunk): Likewise.
+ (aarch64_init_cumulative_args): Initialize the isa_mode field.
+ (aarch64_output_mi_thunk): Use aarch64_gen_callee_cookie to get
+ the ABI cookie.
+ (aarch64_override_options): Add the ISA mode to the feature set.
+ (aarch64_temporary_target::copy_from_fndecl): Likewise.
+ (aarch64_fndecl_options, aarch64_handle_attr_arch): Likewise.
+ (aarch64_set_current_function): Maintain the correct ISA mode.
+ (aarch64_tlsdesc_abi_id): Return an arm_pcs.
+ (aarch64_comp_type_attributes): Handle arm::streaming and
+ arm::streaming_compatible.
+ * config/aarch64/aarch64-c.cc (aarch64_define_unconditional_macros):
+ Define __arm_streaming and __arm_streaming_compatible.
+ * config/aarch64/aarch64.md (tlsdesc_small_<mode>): Use
+ aarch64_gen_callee_cookie to get the ABI cookie.
+ * config/aarch64/t-aarch64 (TM_H): Add all feature-related .def files.
+
+gcc/testsuite/
+ * gcc.target/aarch64/sme/aarch64-sme.exp: New harness.
+ * gcc.target/aarch64/sme/streaming_mode_1.c: New test.
+ * gcc.target/aarch64/sme/streaming_mode_2.c: Likewise.
+ * gcc.target/aarch64/sme/keyword_macros_1.c: Likewise.
+ * g++.target/aarch64/sme/aarch64-sme.exp: New harness.
+ * g++.target/aarch64/sme/streaming_mode_1.C: New test.
+ * g++.target/aarch64/sme/streaming_mode_2.C: Likewise.
+ * g++.target/aarch64/sme/keyword_macros_1.C: Likewise.
+ * gcc.target/aarch64/auto-init-1.c: Only expect the call insn
+ to contain 1 (const_int 0), not 2.
+---
+ gcc/config/aarch64/aarch64-c.cc | 14 ++
+ gcc/config/aarch64/aarch64-isa-modes.def | 35 +++
+ gcc/config/aarch64/aarch64-protos.h | 3 +-
+ gcc/config/aarch64/aarch64.cc | 233 +++++++++++++++---
+ gcc/config/aarch64/aarch64.h | 24 +-
+ gcc/config/aarch64/aarch64.md | 3 +-
+ gcc/config/aarch64/t-aarch64 | 5 +-
+ .../g++.target/aarch64/sme/aarch64-sme.exp | 40 +++
+ .../g++.target/aarch64/sme/keyword_macros_1.C | 4 +
+ .../g++.target/aarch64/sme/streaming_mode_1.C | 142 +++++++++++
+ .../g++.target/aarch64/sme/streaming_mode_2.C | 25 ++
+ .../gcc.target/aarch64/auto-init-1.c | 3 +-
+ .../gcc.target/aarch64/sme/aarch64-sme.exp | 40 +++
+ .../gcc.target/aarch64/sme/keyword_macros_1.c | 4 +
+ .../gcc.target/aarch64/sme/streaming_mode_1.c | 130 ++++++++++
+ .../gcc.target/aarch64/sme/streaming_mode_2.c | 25 ++
+ 16 files changed, 685 insertions(+), 45 deletions(-)
+ create mode 100644 gcc/config/aarch64/aarch64-isa-modes.def
+ create mode 100644 gcc/testsuite/g++.target/aarch64/sme/aarch64-sme.exp
+ create mode 100644 gcc/testsuite/g++.target/aarch64/sme/keyword_macros_1.C
+ create mode 100644 gcc/testsuite/g++.target/aarch64/sme/streaming_mode_1.C
+ create mode 100644 gcc/testsuite/g++.target/aarch64/sme/streaming_mode_2.C
+ create mode 100644 gcc/testsuite/gcc.target/aarch64/sme/aarch64-sme.exp
+ create mode 100644 gcc/testsuite/gcc.target/aarch64/sme/keyword_macros_1.c
+ create mode 100644 gcc/testsuite/gcc.target/aarch64/sme/streaming_mode_1.c
+ create mode 100644 gcc/testsuite/gcc.target/aarch64/sme/streaming_mode_2.c
+
+diff --git a/gcc/config/aarch64/aarch64-c.cc b/gcc/config/aarch64/aarch64-c.cc
+index 4085ad840..397745fbd 100644
+--- a/gcc/config/aarch64/aarch64-c.cc
++++ b/gcc/config/aarch64/aarch64-c.cc
+@@ -72,6 +72,20 @@ aarch64_define_unconditional_macros (cpp_reader *pfile)
+ builtin_define_with_int_value ("__ARM_SIZEOF_WCHAR_T", WCHAR_TYPE_SIZE / 8);
+
+ builtin_define ("__GCC_ASM_FLAG_OUTPUTS__");
++
++ /* Define keyword attributes like __arm_streaming as macros that expand
++ to the associated [[...]] attribute. Use __extension__ in the attribute
++ for C, since the [[...]] syntax was only added in C23. */
++#define DEFINE_ARM_KEYWORD_MACRO(NAME) \
++ builtin_define_with_value ("__arm_" NAME, \
++ lang_GNU_CXX () \
++ ? "[[arm::" NAME "]]" \
++ : "[[__extension__ arm::" NAME "]]", 0);
++
++ DEFINE_ARM_KEYWORD_MACRO ("streaming");
++ DEFINE_ARM_KEYWORD_MACRO ("streaming_compatible");
++
++#undef DEFINE_ARM_KEYWORD_MACRO
+ }
+
+ /* Undefine/redefine macros that depend on the current backend state and may
+diff --git a/gcc/config/aarch64/aarch64-isa-modes.def b/gcc/config/aarch64/aarch64-isa-modes.def
+new file mode 100644
+index 000000000..5915c98a8
+--- /dev/null
++++ b/gcc/config/aarch64/aarch64-isa-modes.def
+@@ -0,0 +1,35 @@
++/* Copyright (C) 2023 Free Software Foundation, Inc.
++
++ This file is part of GCC.
++
++ GCC is free software; you can redistribute it and/or modify it
++ under the terms of the GNU General Public License as published
++ by the Free Software Foundation; either version 3, or (at your
++ option) any later version.
++
++ GCC is distributed in the hope that it will be useful, but WITHOUT
++ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
++ License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with GCC; see the file COPYING3. If not see
++ <http://www.gnu.org/licenses/>. */
++
++/* This file defines a set of "ISA modes"; in other words, it defines
++ various bits of runtime state that control the set of available
++ instructions or that affect the semantics of instructions in some way.
++
++ Before using #include to read this file, define a macro:
++
++ DEF_AARCH64_ISA_MODE(NAME)
++
++ where NAME is the name of the mode. */
++
++/* Indicates that PSTATE.SM is known to be 1 or 0 respectively. These
++ modes are mutually exclusive. If neither mode is active then the state
++ of PSTATE.SM is not known at compile time. */
++DEF_AARCH64_ISA_MODE(SM_ON)
++DEF_AARCH64_ISA_MODE(SM_OFF)
++
++#undef DEF_AARCH64_ISA_MODE
+diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h
+index 14a568140..9b03410dc 100644
+--- a/gcc/config/aarch64/aarch64-protos.h
++++ b/gcc/config/aarch64/aarch64-protos.h
+@@ -772,6 +772,7 @@ bool aarch64_constant_address_p (rtx);
+ bool aarch64_emit_approx_div (rtx, rtx, rtx);
+ bool aarch64_emit_approx_sqrt (rtx, rtx, bool);
+ tree aarch64_vector_load_decl (tree);
++rtx aarch64_gen_callee_cookie (aarch64_feature_flags, arm_pcs);
+ void aarch64_expand_call (rtx, rtx, rtx, bool);
+ bool aarch64_expand_cpymem (rtx *);
+ bool aarch64_expand_setmem (rtx *);
+@@ -851,7 +852,7 @@ bool aarch64_is_mov_xn_imm (unsigned HOST_WIDE_INT);
+ bool aarch64_use_return_insn_p (void);
+ const char *aarch64_output_casesi (rtx *);
+
+-unsigned int aarch64_tlsdesc_abi_id ();
++arm_pcs aarch64_tlsdesc_abi_id ();
+ enum aarch64_symbol_type aarch64_classify_symbol (rtx, HOST_WIDE_INT);
+ enum aarch64_symbol_type aarch64_classify_tls_symbol (rtx);
+ enum reg_class aarch64_regno_regclass (unsigned);
+diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
+index 114252a3c..904166b21 100644
+--- a/gcc/config/aarch64/aarch64.cc
++++ b/gcc/config/aarch64/aarch64.cc
+@@ -2985,8 +2985,18 @@ handle_aarch64_vector_pcs_attribute (tree *node, tree name, tree,
+ gcc_unreachable ();
+ }
+
++/* Mutually-exclusive function type attributes for controlling PSTATE.SM. */
++static const struct attribute_spec::exclusions attr_streaming_exclusions[] =
++{
++ /* Attribute name exclusion applies to:
++ function, type, variable */
++ { "streaming", false, true, false },
++ { "streaming_compatible", false, true, false },
++ { NULL, false, false, false }
++};
++
+ /* Table of machine attributes. */
+-TARGET_GNU_ATTRIBUTES (aarch64_attribute_table,
++static const attribute_spec aarch64_gnu_attributes[] =
+ {
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
+ affects_type_identity, handler, exclude } */
+@@ -2998,7 +3008,31 @@ TARGET_GNU_ATTRIBUTES (aarch64_attribute_table,
+ { "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 }
+-});
++};
++
++static const scoped_attribute_specs aarch64_gnu_attribute_table =
++{
++ "gnu", aarch64_gnu_attributes
++};
++
++static const attribute_spec aarch64_arm_attributes[] =
++{
++ { "streaming", 0, 0, false, true, true, true,
++ NULL, attr_streaming_exclusions },
++ { "streaming_compatible", 0, 0, false, true, true, true,
++ NULL, attr_streaming_exclusions },
++};
++
++static const scoped_attribute_specs aarch64_arm_attribute_table =
++{
++ "arm", aarch64_arm_attributes
++};
++
++static const scoped_attribute_specs *const aarch64_attribute_table[] =
++{
++ &aarch64_gnu_attribute_table,
++ &aarch64_arm_attribute_table
++};
+
+ /* An ISA extension in the co-processor and main instruction set space. */
+ struct aarch64_option_extension
+@@ -4301,6 +4335,48 @@ aarch64_fntype_abi (const_tree fntype)
+ return default_function_abi;
+ }
+
++/* Return the state of PSTATE.SM on entry to functions of type FNTYPE. */
++
++static aarch64_feature_flags
++aarch64_fntype_pstate_sm (const_tree fntype)
++{
++ if (lookup_attribute ("arm", "streaming", TYPE_ATTRIBUTES (fntype)))
++ return AARCH64_FL_SM_ON;
++
++ if (lookup_attribute ("arm", "streaming_compatible",
++ TYPE_ATTRIBUTES (fntype)))
++ return 0;
++
++ return AARCH64_FL_SM_OFF;
++}
++
++/* Return the ISA mode on entry to functions of type FNTYPE. */
++
++static aarch64_feature_flags
++aarch64_fntype_isa_mode (const_tree fntype)
++{
++ return aarch64_fntype_pstate_sm (fntype);
++}
++
++/* Return the state of PSTATE.SM when compiling the body of
++ function FNDECL. This might be different from the state of
++ PSTATE.SM on entry. */
++
++static aarch64_feature_flags
++aarch64_fndecl_pstate_sm (const_tree fndecl)
++{
++ return aarch64_fntype_pstate_sm (TREE_TYPE (fndecl));
++}
++
++/* Return the ISA mode that should be used to compile the body of
++ function FNDECL. */
++
++static aarch64_feature_flags
++aarch64_fndecl_isa_mode (const_tree fndecl)
++{
++ return aarch64_fndecl_pstate_sm (fndecl);
++}
++
+ /* Implement TARGET_COMPATIBLE_VECTOR_TYPES_P. */
+
+ static bool
+@@ -4363,17 +4439,46 @@ aarch64_reg_save_mode (unsigned int regno)
+ gcc_unreachable ();
+ }
+
+-/* Implement TARGET_INSN_CALLEE_ABI. */
++/* Given the ISA mode on entry to a callee and the ABI of the callee,
++ return the CONST_INT that should be placed in an UNSPEC_CALLEE_ABI rtx. */
+
+-const predefined_function_abi &
+-aarch64_insn_callee_abi (const rtx_insn *insn)
++rtx
++aarch64_gen_callee_cookie (aarch64_feature_flags isa_mode, arm_pcs pcs_variant)
++{
++ return gen_int_mode ((unsigned int) isa_mode
++ | (unsigned int) pcs_variant << AARCH64_NUM_ISA_MODES,
++ DImode);
++}
++
++/* COOKIE is a CONST_INT from an UNSPEC_CALLEE_ABI rtx. Return the
++ callee's ABI. */
++
++static const predefined_function_abi &
++aarch64_callee_abi (rtx cookie)
++{
++ return function_abis[UINTVAL (cookie) >> AARCH64_NUM_ISA_MODES];
++}
++
++/* INSN is a call instruction. Return the CONST_INT stored in its
++ UNSPEC_CALLEE_ABI rtx. */
++
++static rtx
++aarch64_insn_callee_cookie (const rtx_insn *insn)
+ {
+ rtx pat = PATTERN (insn);
+ gcc_assert (GET_CODE (pat) == PARALLEL);
+ rtx unspec = XVECEXP (pat, 0, 1);
+ gcc_assert (GET_CODE (unspec) == UNSPEC
+ && XINT (unspec, 1) == UNSPEC_CALLEE_ABI);
+- return function_abis[INTVAL (XVECEXP (unspec, 0, 0))];
++ return XVECEXP (unspec, 0, 0);
++}
++
++/* Implement TARGET_INSN_CALLEE_ABI. */
++
++const predefined_function_abi &
++aarch64_insn_callee_abi (const rtx_insn *insn)
++{
++ return aarch64_callee_abi (aarch64_insn_callee_cookie (insn));
+ }
+
+ /* Implement TARGET_HARD_REGNO_CALL_PART_CLOBBERED. The callee only saves
+@@ -8117,7 +8222,7 @@ aarch64_function_arg (cumulative_args_t pcum_v, const function_arg_info &arg)
+ || pcum->pcs_variant == ARM_PCS_SVE);
+
+ if (arg.end_marker_p ())
+- return gen_int_mode (pcum->pcs_variant, DImode);
++ return aarch64_gen_callee_cookie (pcum->isa_mode, pcum->pcs_variant);
+
+ aarch64_layout_arg (pcum_v, arg);
+ return pcum->aapcs_reg;
+@@ -8138,9 +8243,15 @@ aarch64_init_cumulative_args (CUMULATIVE_ARGS *pcum,
+ pcum->aapcs_nextnvrn = 0;
+ pcum->aapcs_nextnprn = 0;
+ if (fntype)
+- pcum->pcs_variant = (arm_pcs) fntype_abi (fntype).id ();
++ {
++ pcum->pcs_variant = (arm_pcs) fntype_abi (fntype).id ();
++ pcum->isa_mode = aarch64_fntype_isa_mode (fntype);
++ }
+ else
+- pcum->pcs_variant = ARM_PCS_AAPCS64;
++ {
++ pcum->pcs_variant = ARM_PCS_AAPCS64;
++ pcum->isa_mode = AARCH64_FL_DEFAULT_ISA_MODE;
++ }
+ pcum->aapcs_reg = NULL_RTX;
+ pcum->aapcs_arg_processed = false;
+ pcum->aapcs_stack_words = 0;
+@@ -10627,7 +10738,9 @@ aarch64_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
+ }
+ funexp = XEXP (DECL_RTL (function), 0);
+ funexp = gen_rtx_MEM (FUNCTION_MODE, funexp);
+- rtx callee_abi = gen_int_mode (fndecl_abi (function).id (), DImode);
++ auto isa_mode = aarch64_fntype_isa_mode (TREE_TYPE (function));
++ auto pcs_variant = arm_pcs (fndecl_abi (function).id ());
++ rtx callee_abi = aarch64_gen_callee_cookie (isa_mode, pcs_variant);
+ insn = emit_call_insn (gen_sibcall (funexp, const0_rtx, callee_abi));
+ SIBLING_CALL_P (insn) = 1;
+
+@@ -18618,6 +18731,7 @@ aarch64_override_options (void)
+ SUBTARGET_OVERRIDE_OPTIONS;
+ #endif
+
++ auto isa_mode = AARCH64_FL_DEFAULT_ISA_MODE;
+ if (cpu && arch)
+ {
+ /* If both -mcpu and -march are specified, warn if they are not
+@@ -18630,25 +18744,25 @@ aarch64_override_options (void)
+ }
+
+ selected_arch = arch->arch;
+- aarch64_set_asm_isa_flags (arch_isa);
++ aarch64_set_asm_isa_flags (arch_isa | isa_mode);
+ }
+ else if (cpu)
+ {
+ selected_arch = cpu->arch;
+- aarch64_set_asm_isa_flags (cpu_isa);
++ aarch64_set_asm_isa_flags (cpu_isa | isa_mode);
+ }
+ else if (arch)
+ {
+ cpu = &all_cores[arch->ident];
+ selected_arch = arch->arch;
+- aarch64_set_asm_isa_flags (arch_isa);
++ aarch64_set_asm_isa_flags (arch_isa | isa_mode);
+ }
+ else
+ {
+ /* No -mcpu or -march specified, so use the default CPU. */
+ cpu = &all_cores[TARGET_CPU_DEFAULT];
+ selected_arch = cpu->arch;
+- aarch64_set_asm_isa_flags (cpu->flags);
++ aarch64_set_asm_isa_flags (cpu->flags | isa_mode);
+ }
+
+ selected_tune = tune ? tune->ident : cpu->ident;
+@@ -18821,6 +18935,21 @@ aarch64_save_restore_target_globals (tree new_tree)
+ TREE_TARGET_GLOBALS (new_tree) = save_target_globals_default_opts ();
+ }
+
++/* Return the target_option_node for FNDECL, or the current options
++ if FNDECL is null. */
++
++static tree
++aarch64_fndecl_options (tree fndecl)
++{
++ if (!fndecl)
++ return target_option_current_node;
++
++ if (tree options = DECL_FUNCTION_SPECIFIC_TARGET (fndecl))
++ return options;
++
++ return target_option_default_node;
++}
++
+ /* Implement TARGET_SET_CURRENT_FUNCTION. Unpack the codegen decisions
+ like tuning and ISA features from the DECL_FUNCTION_SPECIFIC_TARGET
+ of the function, if such exists. This function may be called multiple
+@@ -18830,25 +18959,24 @@ aarch64_save_restore_target_globals (tree new_tree)
+ static void
+ aarch64_set_current_function (tree fndecl)
+ {
+- if (!fndecl || fndecl == aarch64_previous_fndecl)
+- return;
+-
+- tree old_tree = (aarch64_previous_fndecl
+- ? DECL_FUNCTION_SPECIFIC_TARGET (aarch64_previous_fndecl)
+- : NULL_TREE);
+-
+- tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
++ tree old_tree = aarch64_fndecl_options (aarch64_previous_fndecl);
++ tree new_tree = aarch64_fndecl_options (fndecl);
+
+- /* If current function has no attributes but the previous one did,
+- use the default node. */
+- if (!new_tree && old_tree)
+- new_tree = target_option_default_node;
++ auto new_isa_mode = (fndecl
++ ? aarch64_fndecl_isa_mode (fndecl)
++ : AARCH64_FL_DEFAULT_ISA_MODE);
++ auto isa_flags = TREE_TARGET_OPTION (new_tree)->x_aarch64_isa_flags;
+
+ /* If nothing to do, return. #pragma GCC reset or #pragma GCC pop to
+ the default have been handled by aarch64_save_restore_target_globals from
+ aarch64_pragma_target_parse. */
+- if (old_tree == new_tree)
+- return;
++ if (old_tree == new_tree
++ && (!fndecl || aarch64_previous_fndecl)
++ && (isa_flags & AARCH64_FL_ISA_MODES) == new_isa_mode)
++ {
++ gcc_assert (AARCH64_ISA_MODE == new_isa_mode);
++ return;
++ }
+
+ aarch64_previous_fndecl = fndecl;
+
+@@ -18856,7 +18984,28 @@ aarch64_set_current_function (tree fndecl)
+ cl_target_option_restore (&global_options, &global_options_set,
+ TREE_TARGET_OPTION (new_tree));
+
++ /* The ISA mode can vary based on function type attributes and
++ function declaration attributes. Make sure that the target
++ options correctly reflect these attributes. */
++ if ((isa_flags & AARCH64_FL_ISA_MODES) != new_isa_mode)
++ {
++ auto base_flags = (aarch64_asm_isa_flags & ~AARCH64_FL_ISA_MODES);
++ aarch64_set_asm_isa_flags (base_flags | new_isa_mode);
++
++ aarch64_override_options_internal (&global_options);
++ new_tree = build_target_option_node (&global_options,
++ &global_options_set);
++ DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = new_tree;
++
++ tree new_optimize = build_optimization_node (&global_options,
++ &global_options_set);
++ if (new_optimize != optimization_default_node)
++ DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl) = new_optimize;
++ }
++
+ aarch64_save_restore_target_globals (new_tree);
++
++ gcc_assert (AARCH64_ISA_MODE == new_isa_mode);
+ }
+
+ /* Enum describing the various ways we can handle attributes.
+@@ -18906,7 +19055,7 @@ aarch64_handle_attr_arch (const char *str)
+ {
+ gcc_assert (tmp_arch);
+ selected_arch = tmp_arch->arch;
+- aarch64_set_asm_isa_flags (tmp_flags);
++ aarch64_set_asm_isa_flags (tmp_flags | AARCH64_ISA_MODE);
+ return true;
+ }
+
+@@ -18947,7 +19096,7 @@ aarch64_handle_attr_cpu (const char *str)
+ gcc_assert (tmp_cpu);
+ selected_tune = tmp_cpu->ident;
+ selected_arch = tmp_cpu->arch;
+- aarch64_set_asm_isa_flags (tmp_flags);
++ aarch64_set_asm_isa_flags (tmp_flags | AARCH64_ISA_MODE);
+ return true;
+ }
+
+@@ -19047,7 +19196,7 @@ aarch64_handle_attr_isa_flags (char *str)
+ features if the user wants to handpick specific features. */
+ if (strncmp ("+nothing", str, 8) == 0)
+ {
+- isa_flags = 0;
++ isa_flags = AARCH64_ISA_MODE;
+ str += 8;
+ }
+
+@@ -19552,7 +19701,7 @@ aarch64_can_inline_p (tree caller, tree callee)
+ /* Return the ID of the TLDESC ABI, initializing the descriptor if hasn't
+ been already. */
+
+-unsigned int
++arm_pcs
+ aarch64_tlsdesc_abi_id ()
+ {
+ predefined_function_abi &tlsdesc_abi = function_abis[ARM_PCS_TLSDESC];
+@@ -19566,7 +19715,7 @@ aarch64_tlsdesc_abi_id ()
+ SET_HARD_REG_BIT (full_reg_clobbers, regno);
+ tlsdesc_abi.initialize (ARM_PCS_TLSDESC, full_reg_clobbers);
+ }
+- return tlsdesc_abi.id ();
++ return ARM_PCS_TLSDESC;
+ }
+
+ /* Return true if SYMBOL_REF X binds locally. */
+@@ -27270,22 +27419,26 @@ aarch64_simd_clone_usable (struct cgraph_node *node)
+ static int
+ aarch64_comp_type_attributes (const_tree type1, const_tree type2)
+ {
+- auto check_attr = [&](const char *name) {
+- tree attr1 = lookup_attribute (name, TYPE_ATTRIBUTES (type1));
+- tree attr2 = lookup_attribute (name, TYPE_ATTRIBUTES (type2));
++ auto check_attr = [&](const char *ns, const char *name) {
++ tree attr1 = lookup_attribute (ns, name, TYPE_ATTRIBUTES (type1));
++ tree attr2 = lookup_attribute (ns, name, TYPE_ATTRIBUTES (type2));
+ if (!attr1 && !attr2)
+ return true;
+
+ return attr1 && attr2 && attribute_value_equal (attr1, attr2);
+ };
+
+- if (!check_attr ("aarch64_vector_pcs"))
++ if (!check_attr ("gnu", "aarch64_vector_pcs"))
++ return 0;
++ if (!check_attr ("gnu", "Advanced SIMD type"))
++ return 0;
++ if (!check_attr ("gnu", "SVE type"))
+ return 0;
+- if (!check_attr ("Advanced SIMD type"))
++ if (!check_attr ("gnu", "SVE sizeless type"))
+ return 0;
+- if (!check_attr ("SVE type"))
++ if (!check_attr ("arm", "streaming"))
+ return 0;
+- if (!check_attr ("SVE sizeless type"))
++ if (!check_attr ("arm", "streaming_compatible"))
+ return 0;
+ return 1;
+ }
+diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h
+index 19b82b4f3..84215c8c3 100644
+--- a/gcc/config/aarch64/aarch64.h
++++ b/gcc/config/aarch64/aarch64.h
+@@ -157,10 +157,13 @@
+
+ #ifndef USED_FOR_TARGET
+
+-/* Define an enum of all features (architectures and extensions). */
++/* Define an enum of all features (ISA modes, architectures and extensions).
++ The ISA modes must come first. */
+ enum class aarch64_feature : unsigned char {
++#define DEF_AARCH64_ISA_MODE(IDENT) IDENT,
+ #define AARCH64_OPT_EXTENSION(A, IDENT, C, D, E, F) IDENT,
+ #define AARCH64_ARCH(A, B, IDENT, D, E) IDENT,
++#include "aarch64-isa-modes.def"
+ #include "aarch64-option-extensions.def"
+ #include "aarch64-arches.def"
+ };
+@@ -169,16 +172,34 @@ enum class aarch64_feature : unsigned char {
+ #define HANDLE(IDENT) \
+ constexpr auto AARCH64_FL_##IDENT \
+ = aarch64_feature_flags (1) << int (aarch64_feature::IDENT);
++#define DEF_AARCH64_ISA_MODE(IDENT) HANDLE (IDENT)
+ #define AARCH64_OPT_EXTENSION(A, IDENT, C, D, E, F) HANDLE (IDENT)
+ #define AARCH64_ARCH(A, B, IDENT, D, E) HANDLE (IDENT)
++#include "aarch64-isa-modes.def"
+ #include "aarch64-option-extensions.def"
+ #include "aarch64-arches.def"
+ #undef HANDLE
+
++constexpr auto AARCH64_FL_SM_STATE = AARCH64_FL_SM_ON | AARCH64_FL_SM_OFF;
++
++constexpr unsigned int AARCH64_NUM_ISA_MODES = (0
++#define DEF_AARCH64_ISA_MODE(IDENT) + 1
++#include "aarch64-isa-modes.def"
++);
++
++/* The mask of all ISA modes. */
++constexpr auto AARCH64_FL_ISA_MODES
++ = (aarch64_feature_flags (1) << AARCH64_NUM_ISA_MODES) - 1;
++
++/* The default ISA mode, for functions with no attributes that specify
++ something to the contrary. */
++constexpr auto AARCH64_FL_DEFAULT_ISA_MODE = AARCH64_FL_SM_OFF;
++
+ #endif
+
+ /* Macros to test ISA flags. */
+
++#define AARCH64_ISA_MODE (aarch64_isa_flags & AARCH64_FL_ISA_MODES)
+ #define AARCH64_ISA_CRC (aarch64_isa_flags & AARCH64_FL_CRC)
+ #define AARCH64_ISA_CRYPTO (aarch64_isa_flags & AARCH64_FL_CRYPTO)
+ #define AARCH64_ISA_FP (aarch64_isa_flags & AARCH64_FL_FP)
+@@ -904,6 +925,7 @@ enum arm_pcs
+ typedef struct
+ {
+ enum arm_pcs pcs_variant;
++ aarch64_feature_flags isa_mode;
+ int aapcs_arg_processed; /* No need to lay out this argument again. */
+ int aapcs_ncrn; /* Next Core register number. */
+ int aapcs_nextncrn; /* Next next core register number. */
+diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
+index c0977a3da..29a665e45 100644
+--- a/gcc/config/aarch64/aarch64.md
++++ b/gcc/config/aarch64/aarch64.md
+@@ -7017,7 +7017,8 @@
+ {
+ if (TARGET_SVE)
+ {
+- rtx abi = gen_int_mode (aarch64_tlsdesc_abi_id (), DImode);
++ rtx abi = aarch64_gen_callee_cookie (AARCH64_ISA_MODE,
++ aarch64_tlsdesc_abi_id ());
+ rtx_insn *call
+ = emit_call_insn (gen_tlsdesc_small_sve_<mode> (operands[0], abi));
+ RTL_CONST_CALL_P (call) = 1;
+diff --git a/gcc/config/aarch64/t-aarch64 b/gcc/config/aarch64/t-aarch64
+index 6a21a248f..10cd8f093 100644
+--- a/gcc/config/aarch64/t-aarch64
++++ b/gcc/config/aarch64/t-aarch64
+@@ -20,7 +20,10 @@
+
+ TM_H += $(srcdir)/config/aarch64/aarch64-fusion-pairs.def \
+ $(srcdir)/config/aarch64/aarch64-tuning-flags.def \
+- $(srcdir)/config/aarch64/aarch64-option-extensions.def
++ $(srcdir)/config/aarch64/aarch64-option-extensions.def \
++ $(srcdir)/config/aarch64/aarch64-cores.def \
++ $(srcdir)/config/aarch64/aarch64-isa-modes.def \
++ $(srcdir)/config/aarch64/aarch64-arches.def
+ OPTIONS_H_EXTRA += $(srcdir)/config/aarch64/aarch64-cores.def \
+ $(srcdir)/config/aarch64/aarch64-arches.def
+
+diff --git a/gcc/testsuite/g++.target/aarch64/sme/aarch64-sme.exp b/gcc/testsuite/g++.target/aarch64/sme/aarch64-sme.exp
+new file mode 100644
+index 000000000..72fcd0bd9
+--- /dev/null
++++ b/gcc/testsuite/g++.target/aarch64/sme/aarch64-sme.exp
+@@ -0,0 +1,40 @@
++# Specific regression driver for AArch64 SME.
++# Copyright (C) 2009-2023 Free Software Foundation, Inc.
++#
++# This file is part of GCC.
++#
++# GCC is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 3, or (at your option)
++# any later version.
++#
++# GCC is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++# General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with GCC; see the file COPYING3. If not see
++# <http://www.gnu.org/licenses/>. */
++
++# GCC testsuite that uses the `dg.exp' driver.
++
++# Exit immediately if this isn't an AArch64 target.
++if {![istarget aarch64*-*-*] } {
++ return
++}
++
++# Load support procs.
++load_lib g++-dg.exp
++
++# Initialize `dg'.
++dg-init
++
++aarch64-with-arch-dg-options "" {
++ # Main loop.
++ dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \
++ "" ""
++}
++
++# All done.
++dg-finish
+diff --git a/gcc/testsuite/g++.target/aarch64/sme/keyword_macros_1.C b/gcc/testsuite/g++.target/aarch64/sme/keyword_macros_1.C
+new file mode 100644
+index 000000000..032485adf
+--- /dev/null
++++ b/gcc/testsuite/g++.target/aarch64/sme/keyword_macros_1.C
+@@ -0,0 +1,4 @@
++/* { dg-options "-std=c++11 -pedantic-errors" } */
++
++void f1 () __arm_streaming;
++void f2 () __arm_streaming_compatible;
+diff --git a/gcc/testsuite/g++.target/aarch64/sme/streaming_mode_1.C b/gcc/testsuite/g++.target/aarch64/sme/streaming_mode_1.C
+new file mode 100644
+index 000000000..c3de726e7
+--- /dev/null
++++ b/gcc/testsuite/g++.target/aarch64/sme/streaming_mode_1.C
+@@ -0,0 +1,142 @@
++// { dg-options "" }
++
++void sc_a () [[arm::streaming_compatible]];
++void sc_a (); // { dg-error "ambiguating new declaration" "" { xfail *-*-* } }
++
++void sc_b ();
++void sc_b () [[arm::streaming_compatible]]; // { dg-error "ambiguating new declaration" }
++
++void sc_c () [[arm::streaming_compatible]];
++void sc_c () {} // Inherits attribute from declaration (confusingly).
++
++void sc_d ();
++void sc_d () [[arm::streaming_compatible]] {} // { dg-error "ambiguating new declaration" }
++
++void sc_e () [[arm::streaming_compatible]] {}
++void sc_e (); // { dg-error "ambiguating new declaration" "" { xfail *-*-* } }
++
++void sc_f () {}
++void sc_f () [[arm::streaming_compatible]]; // { dg-error "ambiguating new declaration" }
++
++extern void (*sc_g) ();
++extern void (*sc_g) () [[arm::streaming_compatible]]; // { dg-error "conflicting declaration" }
++
++extern void (*sc_h) () [[arm::streaming_compatible]];
++extern void (*sc_h) (); // { dg-error "conflicting declaration" }
++
++//----------------------------------------------------------------------------
++
++void s_a () [[arm::streaming]];
++void s_a (); // { dg-error "ambiguating new declaration" "" { xfail *-*-* } }
++
++void s_b ();
++void s_b () [[arm::streaming]]; // { dg-error "ambiguating new declaration" }
++
++void s_c () [[arm::streaming]];
++void s_c () {} // Inherits attribute from declaration (confusingly).
++
++void s_d ();
++void s_d () [[arm::streaming]] {} // { dg-error "ambiguating new declaration" }
++
++void s_e () [[arm::streaming]] {}
++void s_e (); // { dg-error "ambiguating new declaration" "" { xfail *-*-* } }
++
++void s_f () {}
++void s_f () [[arm::streaming]]; // { dg-error "ambiguating new declaration" }
++
++extern void (*s_g) ();
++extern void (*s_g) () [[arm::streaming]]; // { dg-error "conflicting declaration" }
++
++extern void (*s_h) () [[arm::streaming]];
++extern void (*s_h) (); // { dg-error "conflicting declaration" }
++
++//----------------------------------------------------------------------------
++
++void mixed_a () [[arm::streaming]];
++void mixed_a () [[arm::streaming_compatible]]; // { dg-error "ambiguating new declaration" }
++
++void mixed_b () [[arm::streaming_compatible]];
++void mixed_b () [[arm::streaming]]; // { dg-error "ambiguating new declaration" }
++
++void mixed_c () [[arm::streaming]];
++void mixed_c () [[arm::streaming_compatible]] {} // { dg-error "ambiguating new declaration" }
++
++void mixed_d () [[arm::streaming_compatible]];
++void mixed_d () [[arm::streaming]] {} // { dg-error "ambiguating new declaration" }
++
++void mixed_e () [[arm::streaming]] {}
++void mixed_e () [[arm::streaming_compatible]]; // { dg-error "ambiguating new declaration" }
++
++void mixed_f () [[arm::streaming_compatible]] {}
++void mixed_f () [[arm::streaming]]; // { dg-error "ambiguating new declaration" }
++
++extern void (*mixed_g) () [[arm::streaming_compatible]];
++extern void (*mixed_g) () [[arm::streaming]]; // { dg-error "conflicting declaration" }
++
++extern void (*mixed_h) () [[arm::streaming]];
++extern void (*mixed_h) () [[arm::streaming_compatible]]; // { dg-error "conflicting declaration" }
++
++//----------------------------------------------------------------------------
++
++void contradiction_1 () [[arm::streaming, arm::streaming_compatible]]; // { dg-warning "conflicts with attribute" }
++void contradiction_2 () [[arm::streaming_compatible, arm::streaming]]; // { dg-warning "conflicts with attribute" }
++
++int [[arm::streaming_compatible]] int_attr; // { dg-warning "attribute ignored" }
++void [[arm::streaming_compatible]] ret_attr (); // { dg-warning "attribute ignored" }
++void *[[arm::streaming]] ptr_attr; // { dg-warning "only applies to function types" }
++
++typedef void s_callback () [[arm::streaming]];
++typedef void sc_callback () [[arm::streaming_compatible]];
++
++typedef void contradiction_callback_1 () [[arm::streaming, arm::streaming_compatible]]; // { dg-warning "conflicts with attribute" }
++typedef void contradiction_callback_2 () [[arm::streaming_compatible, arm::streaming]]; // { dg-warning "conflicts with attribute" }
++
++void (*contradiction_callback_ptr_1) () [[arm::streaming, arm::streaming_compatible]]; // { dg-warning "conflicts with attribute" }
++void (*contradiction_callback_ptr_2) () [[arm::streaming_compatible, arm::streaming]]; // { dg-warning "conflicts with attribute" }
++
++struct s {
++ void (*contradiction_callback_ptr_1) () [[arm::streaming, arm::streaming_compatible]]; // { dg-warning "conflicts with attribute" }
++ void (*contradiction_callback_ptr_2) () [[arm::streaming_compatible, arm::streaming]]; // { dg-warning "conflicts with attribute" }
++};
++
++//----------------------------------------------------------------------------
++
++void keyword_ok_1 () __arm_streaming;
++void keyword_ok_1 () __arm_streaming;
++
++void keyword_ok_2 () __arm_streaming;
++void keyword_ok_2 () [[arm::streaming]];
++
++void keyword_ok_3 () [[arm::streaming]];
++void keyword_ok_3 () __arm_streaming;
++
++void keyword_ok_4 () __arm_streaming [[arm::streaming]];
++
++void keyword_ok_5 () __arm_streaming_compatible;
++void keyword_ok_5 () [[arm::streaming_compatible]];
++
++//----------------------------------------------------------------------------
++
++void keyword_contradiction_1 () __arm_streaming;
++void keyword_contradiction_1 (); // { dg-error "ambiguating new declaration" "" { xfail *-*-* } }
++
++void keyword_contradiction_2 ();
++void keyword_contradiction_2 () __arm_streaming; // { dg-error "ambiguating new declaration" }
++
++void keyword_contradiction_3 () __arm_streaming;
++void keyword_contradiction_3 () [[arm::streaming_compatible]]; // { dg-error "ambiguating new declaration" }
++
++void keyword_contradiction_4 () [[arm::streaming_compatible]];
++void keyword_contradiction_4 () __arm_streaming; // { dg-error "ambiguating new declaration" }
++
++//----------------------------------------------------------------------------
++
++struct s1
++{
++ virtual void f () [[arm::streaming]];
++};
++
++struct s2 : public s1
++{
++ void f () override; // { dg-error "conflicting type attributes" }
++};
+diff --git a/gcc/testsuite/g++.target/aarch64/sme/streaming_mode_2.C b/gcc/testsuite/g++.target/aarch64/sme/streaming_mode_2.C
+new file mode 100644
+index 000000000..f2dd2db9b
+--- /dev/null
++++ b/gcc/testsuite/g++.target/aarch64/sme/streaming_mode_2.C
+@@ -0,0 +1,25 @@
++// { dg-options "" }
++
++void sc_fn () [[arm::streaming_compatible]];
++void s_fn () [[arm::streaming]];
++void ns_fn ();
++
++void (*sc_fn_ptr) () [[arm::streaming_compatible]];
++void (*s_fn_ptr) () [[arm::streaming]];
++void (*ns_fn_ptr) ();
++
++void
++f ()
++{
++ sc_fn_ptr = sc_fn;
++ sc_fn_ptr = s_fn; // { dg-error "invalid conversion" }
++ sc_fn_ptr = ns_fn; // { dg-error "invalid conversion" }
++
++ s_fn_ptr = sc_fn; // { dg-error "invalid conversion" }
++ s_fn_ptr = s_fn;
++ s_fn_ptr = ns_fn; // { dg-error "invalid conversion" }
++
++ ns_fn_ptr = sc_fn; // { dg-error "invalid conversion" }
++ ns_fn_ptr = s_fn; // { dg-error "invalid conversion" }
++ ns_fn_ptr = ns_fn;
++}
+diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-1.c b/gcc/testsuite/gcc.target/aarch64/auto-init-1.c
+index 0fa470880..45bb02561 100644
+--- a/gcc/testsuite/gcc.target/aarch64/auto-init-1.c
++++ b/gcc/testsuite/gcc.target/aarch64/auto-init-1.c
+@@ -29,4 +29,5 @@ void foo()
+ return;
+ }
+
+-/* { dg-final { scan-rtl-dump-times "const_int 0" 11 "expand" } } */
++/* Includes 1 for the call instruction and 1 for a nop. */
++/* { dg-final { scan-rtl-dump-times "const_int 0" 10 "expand" } } */
+diff --git a/gcc/testsuite/gcc.target/aarch64/sme/aarch64-sme.exp b/gcc/testsuite/gcc.target/aarch64/sme/aarch64-sme.exp
+new file mode 100644
+index 000000000..c990e5924
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/aarch64/sme/aarch64-sme.exp
+@@ -0,0 +1,40 @@
++# Specific regression driver for AArch64 SME.
++# Copyright (C) 2009-2023 Free Software Foundation, Inc.
++#
++# This file is part of GCC.
++#
++# GCC is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 3, or (at your option)
++# any later version.
++#
++# GCC is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++# General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with GCC; see the file COPYING3. If not see
++# <http://www.gnu.org/licenses/>. */
++
++# GCC testsuite that uses the `dg.exp' driver.
++
++# Exit immediately if this isn't an AArch64 target.
++if {![istarget aarch64*-*-*] } {
++ return
++}
++
++# Load support procs.
++load_lib gcc-dg.exp
++
++# Initialize `dg'.
++dg-init
++
++aarch64-with-arch-dg-options "" {
++ # Main loop.
++ dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \
++ "" ""
++}
++
++# All done.
++dg-finish
+diff --git a/gcc/testsuite/gcc.target/aarch64/sme/keyword_macros_1.c b/gcc/testsuite/gcc.target/aarch64/sme/keyword_macros_1.c
+new file mode 100644
+index 000000000..8f1b83676
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/aarch64/sme/keyword_macros_1.c
+@@ -0,0 +1,4 @@
++/* { dg-options "-std=c90 -pedantic-errors" } */
++
++void f1 () __arm_streaming;
++void f2 () __arm_streaming_compatible;
+diff --git a/gcc/testsuite/gcc.target/aarch64/sme/streaming_mode_1.c b/gcc/testsuite/gcc.target/aarch64/sme/streaming_mode_1.c
+new file mode 100644
+index 000000000..8874b05b8
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/aarch64/sme/streaming_mode_1.c
+@@ -0,0 +1,130 @@
++// { dg-options "" }
++
++void sc_a () [[arm::streaming_compatible]];
++void sc_a (); // { dg-error "conflicting types" }
++
++void sc_b ();
++void sc_b () [[arm::streaming_compatible]]; // { dg-error "conflicting types" }
++
++void sc_c () [[arm::streaming_compatible]];
++void sc_c () {} // Inherits attribute from declaration (confusingly).
++
++void sc_d ();
++void sc_d () [[arm::streaming_compatible]] {} // { dg-error "conflicting types" }
++
++void sc_e () [[arm::streaming_compatible]] {}
++void sc_e (); // { dg-error "conflicting types" }
++
++void sc_f () {}
++void sc_f () [[arm::streaming_compatible]]; // { dg-error "conflicting types" }
++
++extern void (*sc_g) ();
++extern void (*sc_g) () [[arm::streaming_compatible]]; // { dg-error "conflicting types" }
++
++extern void (*sc_h) () [[arm::streaming_compatible]];
++extern void (*sc_h) (); // { dg-error "conflicting types" }
++
++//----------------------------------------------------------------------------
++
++void s_a () [[arm::streaming]];
++void s_a (); // { dg-error "conflicting types" }
++
++void s_b ();
++void s_b () [[arm::streaming]]; // { dg-error "conflicting types" }
++
++void s_c () [[arm::streaming]];
++void s_c () {} // Inherits attribute from declaration (confusingly).
++
++void s_d ();
++void s_d () [[arm::streaming]] {} // { dg-error "conflicting types" }
++
++void s_e () [[arm::streaming]] {}
++void s_e (); // { dg-error "conflicting types" }
++
++void s_f () {}
++void s_f () [[arm::streaming]]; // { dg-error "conflicting types" }
++
++extern void (*s_g) ();
++extern void (*s_g) () [[arm::streaming]]; // { dg-error "conflicting types" }
++
++extern void (*s_h) () [[arm::streaming]];
++extern void (*s_h) (); // { dg-error "conflicting types" }
++
++//----------------------------------------------------------------------------
++
++void mixed_a () [[arm::streaming]];
++void mixed_a () [[arm::streaming_compatible]]; // { dg-error "conflicting types" }
++
++void mixed_b () [[arm::streaming_compatible]];
++void mixed_b () [[arm::streaming]]; // { dg-error "conflicting types" }
++
++void mixed_c () [[arm::streaming]];
++void mixed_c () [[arm::streaming_compatible]] {} // { dg-error "conflicting types" }
++
++void mixed_d () [[arm::streaming_compatible]];
++void mixed_d () [[arm::streaming]] {} // { dg-error "conflicting types" }
++
++void mixed_e () [[arm::streaming]] {}
++void mixed_e () [[arm::streaming_compatible]]; // { dg-error "conflicting types" }
++
++void mixed_f () [[arm::streaming_compatible]] {}
++void mixed_f () [[arm::streaming]]; // { dg-error "conflicting types" }
++
++extern void (*mixed_g) () [[arm::streaming_compatible]];
++extern void (*mixed_g) () [[arm::streaming]]; // { dg-error "conflicting types" }
++
++extern void (*mixed_h) () [[arm::streaming]];
++extern void (*mixed_h) () [[arm::streaming_compatible]]; // { dg-error "conflicting types" }
++
++//----------------------------------------------------------------------------
++
++void contradiction_1 () [[arm::streaming, arm::streaming_compatible]]; // { dg-warning "conflicts with attribute" }
++void contradiction_2 () [[arm::streaming_compatible, arm::streaming]]; // { dg-warning "conflicts with attribute" }
++
++int [[arm::streaming_compatible]] int_attr; // { dg-warning "only applies to function types" }
++void [[arm::streaming_compatible]] ret_attr (); // { dg-warning "only applies to function types" }
++void *[[arm::streaming]] ptr_attr; // { dg-warning "only applies to function types" }
++
++typedef void s_callback () [[arm::streaming]];
++typedef void sc_callback () [[arm::streaming_compatible]];
++
++typedef void contradiction_callback_1 () [[arm::streaming, arm::streaming_compatible]]; // { dg-warning "conflicts with attribute" }
++typedef void contradiction_callback_2 () [[arm::streaming_compatible, arm::streaming]]; // { dg-warning "conflicts with attribute" }
++
++void (*contradiction_callback_ptr_1) () [[arm::streaming, arm::streaming_compatible]]; // { dg-warning "conflicts with attribute" }
++void (*contradiction_callback_ptr_2) () [[arm::streaming_compatible, arm::streaming]]; // { dg-warning "conflicts with attribute" }
++
++struct s {
++ void (*contradiction_callback_ptr_1) () [[arm::streaming, arm::streaming_compatible]]; // { dg-warning "conflicts with attribute" }
++ void (*contradiction_callback_ptr_2) () [[arm::streaming_compatible, arm::streaming]]; // { dg-warning "conflicts with attribute" }
++};
++
++//----------------------------------------------------------------------------
++
++void keyword_ok_1 () __arm_streaming;
++void keyword_ok_1 () __arm_streaming;
++
++void keyword_ok_2 () __arm_streaming;
++void keyword_ok_2 () [[arm::streaming]];
++
++void keyword_ok_3 () [[arm::streaming]];
++void keyword_ok_3 () __arm_streaming;
++
++void keyword_ok_4 () __arm_streaming [[arm::streaming]];
++
++void keyword_ok_5 () __arm_streaming_compatible;
++void keyword_ok_5 () [[arm::streaming_compatible]];
++
++//----------------------------------------------------------------------------
++
++void keyword_contradiction_1 () __arm_streaming;
++void keyword_contradiction_1 (); // { dg-error "conflicting types" }
++
++void keyword_contradiction_2 ();
++void keyword_contradiction_2 () __arm_streaming; // { dg-error "conflicting types" }
++
++void keyword_contradiction_3 () __arm_streaming;
++void keyword_contradiction_3 () [[arm::streaming_compatible]]; // { dg-error "conflicting types" }
++
++void keyword_contradiction_4 () [[arm::streaming_compatible]];
++void keyword_contradiction_4 () __arm_streaming; // { dg-error "conflicting types" }
+diff --git a/gcc/testsuite/gcc.target/aarch64/sme/streaming_mode_2.c b/gcc/testsuite/gcc.target/aarch64/sme/streaming_mode_2.c
+new file mode 100644
+index 000000000..e8be0f821
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/aarch64/sme/streaming_mode_2.c
+@@ -0,0 +1,25 @@
++// { dg-options "" }
++
++void sc_fn () [[arm::streaming_compatible]];
++void s_fn () [[arm::streaming]];
++void ns_fn ();
++
++void (*sc_fn_ptr) () [[arm::streaming_compatible]];
++void (*s_fn_ptr) () [[arm::streaming]];
++void (*ns_fn_ptr) ();
++
++void
++f ()
++{
++ sc_fn_ptr = sc_fn;
++ sc_fn_ptr = s_fn; // { dg-error "incompatible pointer type" }
++ sc_fn_ptr = ns_fn; // { dg-error "incompatible pointer type" }
++
++ s_fn_ptr = sc_fn; // { dg-error "incompatible pointer type" }
++ s_fn_ptr = s_fn;
++ s_fn_ptr = ns_fn; // { dg-error "incompatible pointer type" }
++
++ ns_fn_ptr = sc_fn; // { dg-error "incompatible pointer type" }
++ ns_fn_ptr = s_fn; // { dg-error "incompatible pointer type" }
++ ns_fn_ptr = ns_fn;
++}
+--
+2.33.0
+