sdesmalen updated this revision to Diff 438624.
sdesmalen added a comment.

- Increased test-coverage by adding positive tests for:
  - template instantiations
  - function overloading
  - lambda function with attribute
  - (indirect) pointer to pointer to an attributed function type.

- Also added negative tests for:
  - passing an operand to an arm_streaming attribute
  - Adding an arm_streaming attribute to a non-function type.

- Rewritten the CHECK lines for aarch64-sme-attrs.cpp, such that the attributes 
are checked with named labels, e.g. #[[SM_ENABLED_CALL]].


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D127762/new/

https://reviews.llvm.org/D127762

Files:
  clang/include/clang/AST/Type.h
  clang/include/clang/AST/TypeProperties.td
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/lib/AST/Type.cpp
  clang/lib/AST/TypePrinter.cpp
  clang/lib/Basic/Targets/AArch64.cpp
  clang/lib/Basic/Targets/AArch64.h
  clang/lib/CodeGen/CGCall.cpp
  clang/lib/CodeGen/CodeGenModule.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/lib/Sema/SemaType.cpp
  clang/test/CodeGen/aarch64-sme-intrinsics/aarch64-sme-attrs.cpp
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/Sema/aarch64-sme-attrs-no-sme.c
  clang/test/Sema/aarch64-sme-func-attrs.c

Index: clang/test/Sema/aarch64-sme-func-attrs.c
===================================================================
--- /dev/null
+++ clang/test/Sema/aarch64-sme-func-attrs.c
@@ -0,0 +1,223 @@
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme -fsyntax-only -verify=expected-cpp -x c++ %s
+
+// Valid attributes
+
+__attribute__((arm_streaming)) void sme_arm_streaming(void);
+__attribute__((arm_streaming_compatible)) void sme_arm_streaming_compatible(void);
+
+__attribute__((arm_new_za)) void sme_arm_new_za(void);
+__attribute__((arm_shared_za)) void sme_arm_shared_za(void);
+__attribute__((arm_preserves_za)) void sme_arm_preserves_za(void);
+
+__attribute__((arm_streaming, arm_new_za)) void sme_arm_streaming_new_za(void);
+__attribute__((arm_streaming, arm_shared_za)) void sme_arm_streaming_shared_za(void);
+__attribute__((arm_streaming, arm_preserves_za)) void sme_arm_streaming_preserves_za(void);
+
+__attribute__((arm_streaming_compatible, arm_new_za)) void sme_arm_sc_new_za(void);
+__attribute__((arm_streaming_compatible, arm_shared_za)) void sme_arm_sc_shared_za(void);
+__attribute__((arm_streaming_compatible, arm_preserves_za)) void sme_arm_sc_preserves_za(void);
+
+__attribute__((arm_shared_za, arm_preserves_za)) void sme_arm_shared_preserves_za(void);
+
+__attribute__((arm_locally_streaming)) void sme_arm_locally_streaming(void) { }
+__attribute__((arm_locally_streaming, arm_streaming)) void sme_arm_streaming_and_locally_streaming(void) { }
+__attribute__((arm_locally_streaming, arm_streaming_compatible)) void sme_arm_streaming_and_streaming_compatible(void) { }
+
+__attribute__((arm_locally_streaming, arm_new_za)) void sme_arm_ls_new_za(void) { }
+__attribute__((arm_locally_streaming, arm_shared_za)) void sme_arm_ls_shared_za(void) { }
+__attribute__((arm_locally_streaming, arm_preserves_za)) void sme_arm_ls_preserves_za(void) { }
+
+// Valid attributes on function pointers
+
+void __attribute__((arm_streaming)) streaming_ptr(void);
+typedef __attribute__((arm_streaming)) void (*fptrty1) (void);
+fptrty1 call_streaming_func() { return streaming_ptr; }
+
+void __attribute__((arm_streaming_compatible)) streaming_compatible_ptr(void);
+typedef __attribute__((arm_streaming_compatible)) void (*fptrty2) (void);
+fptrty2 call_sc_func() { return streaming_compatible_ptr; }
+
+void __attribute__((arm_new_za)) new_za_ptr(void);
+typedef __attribute__((arm_new_za)) void (*fptrty3) (void);
+fptrty3 call_new_za_func() { return new_za_ptr; }
+
+void __attribute__((arm_shared_za)) shared_za_ptr(void);
+typedef __attribute__((arm_shared_za)) void (*fptrty4) (void);
+fptrty4 call_shared_za_func() { return shared_za_ptr; }
+
+void __attribute__((arm_preserves_za)) preserves_za_ptr(void);
+typedef __attribute__((arm_preserves_za)) void (*fptrty5) (void);
+fptrty5 call_preserve_za_func() { return preserves_za_ptr; }
+
+void __attribute__((arm_shared_za, arm_preserves_za)) shared_preserves_za_ptr(void);
+typedef __attribute__((arm_shared_za, arm_preserves_za)) void (*fptrty6) (void);
+fptrty6 call_shared_preserve_za_func() { return shared_preserves_za_ptr; }
+
+typedef void (*fptrty7) (void);
+fptrty7 cast_ls_func_to_normal() { return sme_arm_locally_streaming; }
+
+// FIXME: Add invalid function pointer assignments such as assigning:
+//   1. A streaming compatible function to a normal function pointer,
+//   2. A locally streaming function to a streaming function pointer,
+// etc.
+
+// Invalid attributes
+
+// expected-cpp-error@+4 {{'arm_streaming_compatible' and 'arm_streaming' attributes are not compatible}}
+// expected-cpp-note@+3 {{conflicting attribute is here}}
+// expected-error@+2 {{'arm_streaming_compatible' and 'arm_streaming' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+__attribute__((arm_streaming, arm_streaming_compatible)) void streaming_mode(void);
+
+// expected-cpp-error@+4 {{'arm_streaming' and 'arm_streaming_compatible' attributes are not compatible}}
+// expected-cpp-note@+3 {{conflicting attribute is here}}
+// expected-error@+2 {{'arm_streaming' and 'arm_streaming_compatible' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+__attribute__((arm_streaming_compatible, arm_streaming)) void streaming_compatible(void);
+
+// expected-cpp-error@+4 {{'arm_shared_za' and 'arm_new_za' attributes are not compatible}}
+// expected-cpp-note@+3 {{conflicting attribute is here}}
+// expected-error@+2 {{'arm_shared_za' and 'arm_new_za' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+__attribute__((arm_new_za, arm_shared_za)) void new_shared_za(void);
+
+// expected-cpp-error@+4 {{'arm_new_za' and 'arm_shared_za' attributes are not compatible}}
+// expected-cpp-note@+3 {{conflicting attribute is here}}
+// expected-error@+2 {{'arm_new_za' and 'arm_shared_za' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+__attribute__((arm_shared_za, arm_new_za)) void shared_new_za(void);
+
+// expected-cpp-error@+4 {{'arm_preserves_za' and 'arm_new_za' attributes are not compatible}}
+// expected-cpp-note@+3 {{conflicting attribute is here}}
+// expected-error@+2 {{'arm_preserves_za' and 'arm_new_za' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+__attribute__((arm_new_za, arm_preserves_za)) void new_preserves_za(void);
+
+// expected-cpp-error@+4 {{'arm_new_za' and 'arm_preserves_za' attributes are not compatible}}
+// expected-cpp-note@+3 {{conflicting attribute is here}}
+// expected-error@+2 {{'arm_new_za' and 'arm_preserves_za' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+__attribute__((arm_preserves_za, arm_new_za)) void preserves_new_za(void);
+
+// Invalid attributes on function pointers
+
+// expected-cpp-error@+4 {{'arm_streaming_compatible' and 'arm_streaming' attributes are not compatible}}
+// expected-cpp-note@+3 {{conflicting attribute is here}}
+// expected-error@+2 {{'arm_streaming_compatible' and 'arm_streaming' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+void __attribute__((arm_streaming, arm_streaming_compatible)) streaming_ptr_invalid(void);
+// expected-cpp-error@+4 {{'arm_streaming_compatible' and 'arm_streaming' attributes are not compatible}}
+// expected-cpp-note@+3 {{conflicting attribute is here}}
+// expected-error@+2 {{'arm_streaming_compatible' and 'arm_streaming' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+typedef __attribute__((arm_streaming, arm_streaming_compatible)) void (*fptrty8) (void);
+fptrty8 invalid_streaming_func() { return streaming_ptr_invalid; }
+
+// expected-cpp-error@+4 {{'arm_shared_za' and 'arm_new_za' attributes are not compatible}}
+// expected-cpp-note@+3 {{conflicting attribute is here}}
+// expected-error@+2 {{'arm_shared_za' and 'arm_new_za' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+void __attribute__((arm_new_za, arm_shared_za)) shared_za_ptr_invalid(void);
+// expected-cpp-error@+4 {{'arm_shared_za' and 'arm_new_za' attributes are not compatible}}
+// expected-cpp-note@+3 {{conflicting attribute is here}}
+// expected-error@+2 {{'arm_shared_za' and 'arm_new_za' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+typedef __attribute__((arm_new_za, arm_shared_za)) void (*fptrty9) (void);
+fptrty9 invalid_shared_za_func() { return shared_za_ptr_invalid; }
+
+// expected-cpp-error@+4 {{'arm_preserves_za' and 'arm_new_za' attributes are not compatible}}
+// expected-cpp-note@+3 {{conflicting attribute is here}}
+// expected-error@+2 {{'arm_preserves_za' and 'arm_new_za' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+void __attribute__((arm_new_za, arm_preserves_za)) preserves_za_ptr_invalid(void);
+// expected-cpp-error@+4 {{'arm_preserves_za' and 'arm_new_za' attributes are not compatible}}
+// expected-cpp-note@+3 {{conflicting attribute is here}}
+// expected-error@+2 {{'arm_preserves_za' and 'arm_new_za' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+typedef __attribute__((arm_new_za, arm_preserves_za)) void (*fptrty10) (void);
+fptrty10 invalid_preserve_za_func() { return preserves_za_ptr_invalid; }
+
+// expected-cpp-error@+2 {{'arm_locally_streaming' attribute only applies to functions}}
+// expected-error@+1 {{'arm_locally_streaming' attribute only applies to functions}}
+typedef __attribute__((arm_locally_streaming)) void (*fptrty11) (void);
+
+// expected-warning@+2 {{'arm_streaming' attribute ignored}}
+// expected-warning@+1 {{'arm_streaming' only applies to function types; type here is 'void ()'}}
+__attribute__((arm_streaming)) void function_no_prototype();
+
+//
+// Check for incorrect conversions of function pointers with the attributes
+//
+
+typedef void (*n_ptrty) (void);
+typedef __attribute__((arm_streaming)) void (*s_ptrty) (void);
+s_ptrty return_valid_streaming_fptr(s_ptrty f) { return f; }
+
+// expected-cpp-error@+2 {{cannot initialize return object of type 's_ptrty' (aka 'void (*)() __attribute__((arm_streaming))') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}}
+// expected-warning@+1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 's_ptrty' (aka 'void (*)(void) __attribute__((arm_streaming))')}}
+s_ptrty return_invalid_fptr_streaming_normal(n_ptrty f) { return f; }
+// expected-cpp-error@+2 {{cannot initialize return object of type 'n_ptrty' (aka 'void (*)()') with an lvalue of type 's_ptrty' (aka 'void (*)() __attribute__((arm_streaming))')}}
+// expected-warning@+1 {{incompatible function pointer types returning 's_ptrty' (aka 'void (*)(void) __attribute__((arm_streaming))') from a function with result type 'n_ptrty' (aka 'void (*)(void)')}}
+n_ptrty return_invalid_fptr_normal_streaming(s_ptrty f) { return f; }
+
+typedef __attribute__((arm_streaming_compatible)) void (*sc_ptrty) (void);
+sc_ptrty return_valid_streaming_compatible_fptr(sc_ptrty f) { return f; }
+
+// expected-cpp-error@+2 {{cannot initialize return object of type 'sc_ptrty' (aka 'void (*)() __attribute__((arm_streaming_compatible))') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}}
+// expected-warning@+1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 'sc_ptrty' (aka 'void (*)(void) __attribute__((arm_streaming_compatible))')}}
+sc_ptrty return_invalid_fptr_streaming_compatible_normal(n_ptrty f) { return f; }
+// expected-cpp-error@+2 {{cannot initialize return object of type 'n_ptrty' (aka 'void (*)()') with an lvalue of type 'sc_ptrty' (aka 'void (*)() __attribute__((arm_streaming_compatible))')}}
+// expected-warning@+1 {{incompatible function pointer types returning 'sc_ptrty' (aka 'void (*)(void) __attribute__((arm_streaming_compatible))') from a function with result type 'n_ptrty' (aka 'void (*)(void)')}}
+n_ptrty return_invalid_fptr_normal_streaming_compatible(sc_ptrty f) { return f; }
+
+typedef __attribute__((arm_new_za)) void (*nz_ptrty) (void);
+nz_ptrty return_valid_new_za_fptr(nz_ptrty f) { return f; }
+
+// expected-cpp-error@+2 {{cannot initialize return object of type 'nz_ptrty' (aka 'void (*)() __attribute__((arm_new_za))') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}}
+// expected-warning@+1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 'nz_ptrty' (aka 'void (*)(void) __attribute__((arm_new_za))')}}
+nz_ptrty return_invalid_fptr_new_za_normal(n_ptrty f) { return f; }
+// expected-cpp-error@+2 {{cannot initialize return object of type 'n_ptrty' (aka 'void (*)()') with an lvalue of type 'nz_ptrty' (aka 'void (*)() __attribute__((arm_new_za))')}}
+// expected-warning@+1 {{incompatible function pointer types returning 'nz_ptrty' (aka 'void (*)(void) __attribute__((arm_new_za))') from a function with result type 'n_ptrty' (aka 'void (*)(void)')}}
+n_ptrty return_invalid_fptr_normal_new_za(nz_ptrty f) { return f; }
+
+typedef __attribute__((arm_shared_za)) void (*sz_ptrty) (void);
+sz_ptrty return_valid_shared_za_fptr(sz_ptrty f) { return f; }
+
+
+// expected-cpp-error@+2 {{cannot initialize return object of type 'sz_ptrty' (aka 'void (*)() __attribute__((arm_shared_za))') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}}
+// expected-warning@+1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 'sz_ptrty' (aka 'void (*)(void) __attribute__((arm_shared_za))')}}
+sz_ptrty return_invalid_fptr_shared_za_normal(n_ptrty f) { return f; }
+// expected-cpp-error@+2 {{cannot initialize return object of type 'n_ptrty' (aka 'void (*)()') with an lvalue of type 'sz_ptrty' (aka 'void (*)() __attribute__((arm_shared_za))')}}
+// expected-warning@+1 {{incompatible function pointer types returning 'sz_ptrty' (aka 'void (*)(void) __attribute__((arm_shared_za))') from a function with result type 'n_ptrty' (aka 'void (*)(void)')}}
+n_ptrty return_invalid_fptr_normal_shared_za(sz_ptrty f) { return f; }
+
+typedef __attribute__((arm_preserves_za)) void (*pz_ptrty) (void);
+pz_ptrty return_valid_preserves_za_fptr(pz_ptrty f) { return f; }
+
+// expected-cpp-error@+2 {{cannot initialize return object of type 'pz_ptrty' (aka 'void (*)() __attribute__((arm_preserves_za))') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}}
+// expected-warning@+1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 'pz_ptrty' (aka 'void (*)(void) __attribute__((arm_preserves_za))')}}
+pz_ptrty return_invalid_fptr_preserves_za_normal(n_ptrty f) { return f; }
+// expected-cpp-error@+2 {{cannot initialize return object of type 'n_ptrty' (aka 'void (*)()') with an lvalue of type 'pz_ptrty' (aka 'void (*)() __attribute__((arm_preserves_za))')}}
+// expected-warning@+1 {{incompatible function pointer types returning 'pz_ptrty' (aka 'void (*)(void) __attribute__((arm_preserves_za))') from a function with result type 'n_ptrty' (aka 'void (*)(void)')}}
+n_ptrty return_invalid_fptr_normal_preserves_za(pz_ptrty f) { return f; }
+
+// Test template instantiations
+#ifdef __cplusplus
+template <typename T> __attribute__((arm_streaming)) T templated(T x) { return x; }
+template <> __attribute__((arm_streaming)) int templated<int>(int x) { return x + 1; }
+template <> __attribute__((arm_streaming)) float templated<float>(float x) { return x + 2; }
+// expected-cpp-error@+2 {{explicit instantiation of 'templated' does not refer to a function template, variable template, member function, member class, or static data member}}
+// expected-cpp-note@-4 {{candidate template ignored: could not match 'short (short) __attribute__((arm_streaming))' against 'short (short)'}}
+template short templated<short>(short);
+#endif
+
+// expected-cpp-error@+2 {{'arm_streaming' attribute takes no arguments}}
+// expected-error@+1 {{'arm_streaming' attribute takes no arguments}}
+__attribute__((arm_streaming(0))) void invalid_streaming_args(void);
+
+// expected-cpp-error@+4 {{attribute only applies to non-K&R-style functions}}
+// expected-cpp-warning@+3 {{'arm_streaming' only applies to function types; type here is 'int'}}
+// expected-error@+2 {{attribute only applies to non-K&R-style functions}}
+// expected-warning@+1 {{'arm_streaming' only applies to function types; type here is 'int'}}
+__attribute__((arm_streaming)) int invalid_type_for_attribute;
Index: clang/test/Sema/aarch64-sme-attrs-no-sme.c
===================================================================
--- /dev/null
+++ clang/test/Sema/aarch64-sme-attrs-no-sme.c
@@ -0,0 +1,36 @@
+// Test that the attribute is ignored if we don't compile for both AArch64 with +sme.
+//
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple x86_64-none-linux-gnu -target-feature +sme -fsyntax-only -verify %s
+
+extern int normal_callee(void);
+
+// expected-warning@+1 {{unknown attribute 'arm_streaming' ignored}}
+__attribute__((arm_streaming))
+int streaming_caller(void) {
+  return normal_callee();
+}
+
+// expected-warning@+1 {{unknown attribute 'arm_locally_streaming' ignored}}
+__attribute__((arm_locally_streaming))
+int locally_streaming_caller(void) {
+  return normal_callee();
+}
+
+// expected-warning@+1 {{unknown attribute 'arm_shared_za' ignored}}
+__attribute__((arm_shared_za))
+int shared_za_caller(void) {
+  return normal_callee();
+}
+
+// expected-warning@+1 {{unknown attribute 'arm_preserves_za' ignored}}
+__attribute__((arm_preserves_za))
+int preserves_za_caller(void) {
+  return normal_callee();
+}
+
+// expected-warning@+1 {{unknown attribute 'arm_new_za' ignored}}
+__attribute__((arm_new_za))
+int new_za_caller(void) {
+  return normal_callee();
+}
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -18,6 +18,7 @@
 // CHECK-NEXT: AnyX86NoCfCheck (SubjectMatchRule_hasType_functionType)
 // CHECK-NEXT: ArcWeakrefUnavailable (SubjectMatchRule_objc_interface)
 // CHECK-NEXT: ArmBuiltinAlias (SubjectMatchRule_function)
+// CHECK-NEXT: ArmLocallyStreaming (SubjectMatchRule_function)
 // CHECK-NEXT: AssumeAligned (SubjectMatchRule_objc_method, SubjectMatchRule_function)
 // CHECK-NEXT: Assumption (SubjectMatchRule_function, SubjectMatchRule_objc_method)
 // CHECK-NEXT: Availability ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
Index: clang/test/CodeGen/aarch64-sme-intrinsics/aarch64-sme-attrs.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGen/aarch64-sme-intrinsics/aarch64-sme-attrs.cpp
@@ -0,0 +1,223 @@
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme \
+// RUN:   -S -O0 -Werror -emit-llvm -o - %s | FileCheck %s
+
+extern "C" {
+
+extern int normal_callee();
+
+// == FUNCTION DECLARATIONS ==
+
+__attribute__((arm_streaming)) int streaming_decl();
+__attribute__((arm_streaming_compatible)) int streaming_compatible_decl();
+__attribute__((arm_shared_za)) int shared_za_decl();
+__attribute__((arm_preserves_za)) int preserves_za_decl();
+__attribute__((arm_new_za)) int new_za_decl();
+
+// == FUNCTION DEFINITIONS ==
+
+// CHECK-LABEL: @streaming_caller()
+// CHECK-SAME: #[[SM_ENABLED:[0-9]+]]
+// CHECK: call i32 @normal_callee()
+//
+__attribute__((arm_streaming)) int streaming_caller() {
+  return normal_callee();
+}
+
+// CHECK: declare i32 @normal_callee() #[[NORMAL_DECL:[0-9]+]]
+
+
+// CHECK-LABEL: @streaming_callee()
+// CHECK-SAME: #[[SM_ENABLED]]
+// CHECK: call i32 @streaming_decl() #[[SM_ENABLED_CALL:[0-9]+]]
+//
+__attribute__((arm_streaming)) int streaming_callee() {
+  return streaming_decl();
+}
+
+// CHECK: declare i32 @streaming_decl() #[[SM_ENABLED_DECL:[0-9]+]]
+
+// CHECK-LABEL: @streaming_compatible_caller()
+// CHECK-SAME: #[[SM_COMPATIBLE:[0-9]+]]
+// CHECK: call i32 @normal_callee()
+//
+__attribute__((arm_streaming_compatible)) int streaming_compatible_caller() {
+  return normal_callee();
+}
+
+// CHECK-LABEL: @streaming_compatible_callee()
+// CHECK-SAME: #[[SM_COMPATIBLE]]
+// CHECK: call i32 @streaming_compatible_decl() #[[SM_COMPATIBLE_CALL:[0-9]+]]
+//
+__attribute__((arm_streaming_compatible)) int streaming_compatible_callee() {
+  return streaming_compatible_decl();
+}
+
+// CHECK: declare i32 @streaming_compatible_decl() #[[SM_COMPATIBLE_DECL:[0-9]+]]
+
+// CHECK-LABEL: @locally_streaming_caller()
+// CHECK-SAME: #[[SM_BODY:[0-9]+]]
+// CHECK: call i32 @normal_callee()
+//
+__attribute__((arm_locally_streaming)) int locally_streaming_caller() {
+  return normal_callee();
+}
+
+// CHECK-LABEL: @locally_streaming_callee()
+// CHECK-SAME: #[[SM_BODY]]
+// CHECK: call i32 @locally_streaming_caller() #[[SM_BODY_CALL:[0-9]+]]
+//
+__attribute__((arm_locally_streaming)) int locally_streaming_callee() {
+  return locally_streaming_caller();
+}
+
+
+// CHECK-LABEL: @shared_za_caller()
+// CHECK-SAME: #[[ZA_SHARED:[0-9]+]]
+// CHECK: call i32 @normal_callee()
+//
+__attribute__((arm_shared_za)) int shared_za_caller() {
+  return normal_callee();
+}
+
+// CHECK-LABEL: @shared_za_callee()
+// CHECK-SAME: #[[ZA_SHARED]]
+// CHECK: call i32 @shared_za_decl() #[[ZA_SHARED_CALL:[0-9]+]]
+//
+__attribute__((arm_shared_za)) int shared_za_callee() {
+  return shared_za_decl();
+}
+
+// CHECK: declare i32 @shared_za_decl() #[[ZA_SHARED_DECL:[0-9]+]]
+
+
+// CHECK-LABEL: @preserves_za_caller()
+// CHECK-SAME: #[[ZA_PRESERVED:[0-9]+]]
+// CHECK: call i32 @normal_callee()
+//
+__attribute__((arm_preserves_za)) int preserves_za_caller() {
+  return normal_callee();
+}
+
+// CHECK-LABEL: @preserves_za_callee()
+// CHECK-SAME: #[[ZA_PRESERVED]]
+// CHECK: call i32 @preserves_za_decl() #[[ZA_PRESERVED_CALL:[0-9]+]]
+//
+__attribute__((arm_preserves_za)) int preserves_za_callee() {
+  return preserves_za_decl();
+}
+
+// CHECK: declare i32 @preserves_za_decl() #[[ZA_PRESERVED_DECL:[0-9]+]]
+
+
+// CHECK-LABEL: @new_za_caller()
+// CHECK-SAME: #[[ZA_NEW:[0-9]+]]
+// CHECK: call i32 @normal_callee()
+//
+__attribute__((arm_new_za)) int new_za_caller() {
+  return normal_callee();
+}
+
+// CHECK-LABEL: @new_za_callee()
+// CHECK-SAME: #[[ZA_NEW]]
+// CHECK: call i32 @new_za_decl() #[[ZA_NEW_CALL:[0-9]+]]
+//
+__attribute__((arm_new_za)) int new_za_callee() {
+  return new_za_decl();
+}
+
+// CHECK: declare i32 @new_za_decl() #[[ZA_NEW_DECL:[0-9]+]]
+
+
+// Ensure that the attributes are correctly propagated to function types
+// and also to callsites.
+typedef void __attribute__((arm_streaming)) (*s_ptrty) (int, int);
+typedef void __attribute__((arm_streaming_compatible)) (*sc_ptrty) (int, int);
+typedef void __attribute__((arm_new_za)) (*nz_ptrty) (int, int);
+typedef void __attribute__((arm_shared_za)) (*sz_ptrty) (int, int);
+typedef void __attribute__((arm_preserves_za)) (*pz_ptrty) (int, int);
+
+// CHECK-LABEL: @test_streaming_ptrty(
+// CHECK-SAME: #[[NORMAL_DEF:[0-9]+]]
+// CHECK: call void [[F:%.*]](i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[SM_ENABLED_CALL]]
+//
+void test_streaming_ptrty(s_ptrty f, int x, int y) { return f(x, y); }
+// CHECK-LABEL: @test_streaming_compatible_ptrty(
+// CHECK-SAME: #[[NORMAL_DEF]]
+// CHECK: call void [[F:%.*]](i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[SM_COMPATIBLE_CALL]]
+//
+void test_streaming_compatible_ptrty(sc_ptrty f, int x, int y) { return f(x, y); }
+// CHECK-LABEL: @test_new_za(
+// CHECK-SAME: #[[ZA_SHARED]]
+// CHECK: call void [[F:%.*]](i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ZA_NEW_CALL]]
+//
+void __attribute__((arm_shared_za)) test_new_za(nz_ptrty f, int x, int y) { return f(x, y); }
+// CHECK-LABEL: @test_shared_za(
+// CHECK-SAME: #[[ZA_SHARED]]
+// CHECK: call void [[F:%.*]](i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ZA_SHARED_CALL]]
+//
+void __attribute__((arm_shared_za)) test_shared_za(sz_ptrty f, int x, int y) { return f(x, y); }
+// CHECK-LABEL: @test_preserved_za(
+// CHECK-SAME: #[[ZA_SHARED]]
+// CHECK: call void [[F:%.*]](i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ZA_PRESERVED_CALL]]
+//
+void __attribute__((arm_shared_za)) test_preserved_za(pz_ptrty f, int x, int y) { return f(x, y); }
+
+// CHECK-LABEL: @test_indirect_streaming_ptrty(
+// CHECK-SAME: #[[NORMAL_DEF:[0-9]+]]
+// CHECK: call void [[F:%.*]](i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[SM_ENABLED_CALL]]
+//
+typedef s_ptrty **indirect_s_ptrty;
+void test_indirect_streaming_ptrty(indirect_s_ptrty fptr, int x, int y) { return (**fptr)(x, y); }
+} // extern "C"
+
+//
+// Test overloading; the attribute is not required for overloaded types and
+// does not apply if not specified.
+//
+
+// CHECK-LABEL: @_Z12overloadedfni(
+// CHECK-SAME: #[[SM_ENABLED]]
+int __attribute__((arm_streaming)) overloadedfn(int x) { return x; }
+// CHECK-LABEL: @_Z12overloadedfnf(
+// CHECK-SAME: #[[NORMAL_DEF]]
+//
+float overloadedfn(float x) { return x; }
+// CHECK-LABEL: @_Z13test_overloadi(
+// CHECK-SAME: #[[NORMAL_DEF]]
+//
+int test_overload(int x) { return overloadedfn(x); }
+// CHECK-LABEL: @_Z13test_overloadf(
+// CHECK-SAME: #[[NORMAL_DEF]]
+//
+float test_overload(float x) { return overloadedfn(x); }
+
+// CHECK-LABEL: @_Z11test_lambdai(
+// CHECK-SAME: #[[NORMAL_DEF]]
+// CHECK: call noundef i32 @"_ZZ11test_lambdaiENK3$_0clEi"({{.*}}) #[[SM_ENABLED_CALL]]
+//
+// CHECK: @"_ZZ11test_lambdaiENK3$_0clEi"(
+// CHECK-SAME: #[[SM_ENABLED]]
+int test_lambda(int x) {
+  auto F = [](int x) __attribute__((arm_streaming)) { return x; };
+  return F(x);
+}
+
+// CHECK: attributes #[[SM_ENABLED]] = { mustprogress noinline nounwind optnone "aarch64_pstate_sm_enabled" "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" }
+// CHECK: attributes #[[NORMAL_DECL]] = { "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" }
+// CHECK: attributes #[[SM_ENABLED_DECL]] = { "aarch64_pstate_sm_enabled" "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" }
+// CHECK: attributes #[[SM_COMPATIBLE]] = { mustprogress noinline nounwind optnone "aarch64_pstate_sm_compatible" "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" }
+// CHECK: attributes #[[SM_COMPATIBLE_DECL]] = { "aarch64_pstate_sm_compatible" "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" }
+// CHECK: attributes #[[SM_BODY]] = { mustprogress noinline nounwind optnone "aarch64_pstate_sm_body" "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" }
+// CHECK: attributes #[[ZA_SHARED]] = { mustprogress noinline nounwind optnone "aarch64_pstate_za_shared" "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" }
+// CHECK: attributes #[[ZA_SHARED_DECL]] = { "aarch64_pstate_za_shared" "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" }
+// CHECK: attributes #[[ZA_PRESERVED]] = { mustprogress noinline nounwind optnone "aarch64_pstate_za_preserved" "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" }
+// CHECK: attributes #[[ZA_PRESERVED_DECL]] = { "aarch64_pstate_za_preserved" "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" }
+// CHECK: attributes #[[ZA_NEW]] = { mustprogress noinline nounwind optnone "aarch64_pstate_za_new" "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" }
+// CHECK: attributes #[[ZA_NEW_DECL]] = { "aarch64_pstate_za_new" "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" }
+// CHECK: attributes #[[NORMAL_DEF]] = { mustprogress noinline nounwind optnone "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" }
+// CHECK: attributes #[[SM_ENABLED_CALL]] = { "aarch64_pstate_sm_enabled" }
+// CHECK: attributes #[[SM_COMPATIBLE_CALL]] = { "aarch64_pstate_sm_compatible" }
+// CHECK: attributes #[[SM_BODY_CALL]] = { "aarch64_pstate_sm_body" }
+// CHECK: attributes #[[ZA_SHARED_CALL]] = { "aarch64_pstate_za_shared" }
+// CHECK: attributes #[[ZA_PRESERVED_CALL]] = { "aarch64_pstate_za_preserved" }
+// CHECK: attributes #[[ZA_NEW_CALL]] = { "aarch64_pstate_za_new" }
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ clang/lib/Sema/SemaType.cpp
@@ -136,6 +136,11 @@
   case ParsedAttr::AT_NoReturn:                                                \
   case ParsedAttr::AT_Regparm:                                                 \
   case ParsedAttr::AT_CmseNSCall:                                              \
+  case ParsedAttr::AT_ArmStreaming:                                            \
+  case ParsedAttr::AT_ArmStreamingCompatible:                                  \
+  case ParsedAttr::AT_ArmNewZA:                                                \
+  case ParsedAttr::AT_ArmSharedZA:                                             \
+  case ParsedAttr::AT_ArmPreservesZA:                                          \
   case ParsedAttr::AT_AnyX86NoCallerSavedRegisters:                            \
   case ParsedAttr::AT_AnyX86NoCfCheck:                                         \
     CALLING_CONV_ATTRS_CASELIST
@@ -7689,6 +7694,53 @@
     return true;
   }
 
+  if (attr.getKind() == ParsedAttr::AT_ArmStreaming ||
+      attr.getKind() == ParsedAttr::AT_ArmStreamingCompatible ||
+      attr.getKind() == ParsedAttr::AT_ArmNewZA ||
+      attr.getKind() == ParsedAttr::AT_ArmSharedZA ||
+      attr.getKind() == ParsedAttr::AT_ArmPreservesZA){
+    if (S.CheckAttrTarget(attr) || S.CheckAttrNoArgs(attr))
+      return true;
+
+    if (!unwrapped.isFunctionType())
+      return false;
+
+    const FunctionProtoType *FnTy = unwrapped.get()->getAs<FunctionProtoType>();
+    if (!FnTy) {
+      // SME ACLE attributes are not supported on K&R-style unprototyped C
+      // functions.
+      S.Diag(attr.getLoc(), diag::warn_attribute_ignored) << attr;
+      attr.setInvalid();
+      return false;
+    }
+
+    FunctionProtoType::ExtProtoInfo EPI = FnTy->getExtProtoInfo();
+    switch (attr.getKind()) {
+    case ParsedAttr::AT_ArmStreaming:
+      EPI.setArmSMEAttribute(FunctionType::SME_PStateSMEnabledMask);
+      break;
+    case ParsedAttr::AT_ArmStreamingCompatible:
+      EPI.setArmSMEAttribute(FunctionType::SME_PStateSMCompatibleMask);
+      break;
+    case ParsedAttr::AT_ArmNewZA:
+      EPI.setArmSMEAttribute(FunctionType::SME_PStateZANewMask);
+      break;
+    case ParsedAttr::AT_ArmSharedZA:
+      EPI.setArmSMEAttribute(FunctionType::SME_PStateZASharedMask);
+      break;
+    case ParsedAttr::AT_ArmPreservesZA:
+      EPI.setArmSMEAttribute(FunctionType::SME_PStateZAPreservedMask);
+      break;
+    default:
+      llvm_unreachable("Unsupported attribute");
+    }
+
+    QualType newtype = S.Context.getFunctionType(FnTy->getReturnType(),
+                                                 FnTy->getParamTypes(), EPI);
+    type = unwrapped.wrap(S, newtype->getAs<FunctionType>());
+    return true;
+  }
+
   if (attr.getKind() == ParsedAttr::AT_NoThrow) {
     // Delay if this is not a function type.
     if (!unwrapped.isFunctionType())
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -9211,6 +9211,21 @@
       ColonLoc, result, VK, OK);
 }
 
+// Check that the SME attributes for PSTATE.ZA and PSTATE.SM are compatible.
+static bool IsInvalidSMECallConversion(Sema &S, QualType FromType,
+                                       QualType ToType) {
+  if (const auto *ToFn =
+          dyn_cast<FunctionProtoType>(S.Context.getCanonicalType(ToType)))
+    if (const auto *FromFn =
+            dyn_cast<FunctionProtoType>(S.Context.getCanonicalType(FromType)))
+      return (ToFn->getAArch64SMEAttributes() &
+              FunctionType::SME_AttributeMask) !=
+             (FromFn->getAArch64SMEAttributes() &
+              FunctionType::SME_AttributeMask);
+
+  return false;
+}
+
 // Check if we have a conversion between incompatible cmse function pointer
 // types, that is, a conversion between a function pointer with the
 // cmse_nonsecure_call attribute and one without.
@@ -9369,6 +9384,8 @@
     return Sema::IncompatibleFunctionPointer;
   if (IsInvalidCmseNSCallConversion(S, ltrans, rtrans))
     return Sema::IncompatibleFunctionPointer;
+  if (IsInvalidSMECallConversion(S, ltrans, rtrans))
+    return Sema::IncompatibleFunctionPointer;
   return ConvTy;
 }
 
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -274,6 +274,7 @@
   if (const auto *A = D->getAttr<AttrTy>()) {
     S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible) << AL << A;
     S.Diag(A->getLocation(), diag::note_conflicting_attribute);
+    AL.setInvalid();
     return true;
   }
   return false;
@@ -8048,6 +8049,46 @@
     handleSimpleAttribute<NoDestroyAttr>(S, D, A);
 }
 
+static void handleSMEAttrs(Sema &S, Decl *D, const ParsedAttr &A) {
+  // Handle mutually exclusive SME function attributes:
+  //  -  arm_streaming & arm_streaming_compatible
+  //  -  arm_new_za & arm_preserves_za
+  //  -  arm_new_za & arm_shared_za
+  switch (A.getKind()) {
+  case ParsedAttr::AT_ArmStreaming:
+    if (checkAttrMutualExclusion<ArmStreamingCompatibleAttr>(S, D, A))
+      return;
+    handleSimpleAttribute<ArmStreamingAttr>(S, D, A);
+    break;
+  case ParsedAttr::AT_ArmStreamingCompatible:
+    if (checkAttrMutualExclusion<ArmStreamingAttr>(S, D, A))
+      return;
+    handleSimpleAttribute<ArmStreamingCompatibleAttr>(S, D, A);
+    break;
+  case ParsedAttr::AT_ArmLocallyStreaming:
+    handleSimpleAttribute<ArmLocallyStreamingAttr>(S, D, A);
+    break;
+  case ParsedAttr::AT_ArmSharedZA:
+    if (checkAttrMutualExclusion<ArmNewZAAttr>(S, D, A))
+      return;
+    handleSimpleAttribute<ArmSharedZAAttr>(S, D, A);
+    break;
+  case ParsedAttr::AT_ArmPreservesZA:
+    if (checkAttrMutualExclusion<ArmNewZAAttr>(S, D, A))
+      return;
+    handleSimpleAttribute<ArmPreservesZAAttr>(S, D, A);
+    break;
+  case ParsedAttr::AT_ArmNewZA:
+    if (checkAttrMutualExclusion<ArmPreservesZAAttr>(S, D, A) ||
+        checkAttrMutualExclusion<ArmSharedZAAttr>(S, D, A))
+      return;
+    handleSimpleAttribute<ArmNewZAAttr>(S, D, A);
+    break;
+  default:
+    llvm_unreachable("unexpected attribute kind");
+  }
+}
+
 static void handleUninitializedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   assert(cast<VarDecl>(D)->getStorageDuration() == SD_Automatic &&
          "uninitialized is only valid on automatic duration variables");
@@ -9046,6 +9087,15 @@
     handleArmBuiltinAliasAttr(S, D, AL);
     break;
 
+  case ParsedAttr::AT_ArmStreaming:
+  case ParsedAttr::AT_ArmStreamingCompatible:
+  case ParsedAttr::AT_ArmLocallyStreaming:
+  case ParsedAttr::AT_ArmSharedZA:
+  case ParsedAttr::AT_ArmPreservesZA:
+  case ParsedAttr::AT_ArmNewZA:
+    handleSMEAttrs(S, D, AL);
+    break;
+
   case ParsedAttr::AT_AcquireHandle:
     handleAcquireHandleAttr(S, D, AL);
     break;
Index: clang/lib/CodeGen/CodeGenModule.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenModule.cpp
+++ clang/lib/CodeGen/CodeGenModule.cpp
@@ -1919,6 +1919,12 @@
     return;
   }
 
+  // We only need to handle the 'arm_locally_streaming' attribute as a
+  // special case here (as opposed to e.g. 'arm_streaming'), because it
+  // is not set from the prototype, but rather from the definition.
+  if (D->hasAttr<ArmLocallyStreamingAttr>())
+    B.addAttribute("aarch64_pstate_sm_body");
+
   // Track whether we need to add the optnone LLVM attribute,
   // starting with the default for this optimization level.
   bool ShouldAddOptNone =
Index: clang/lib/CodeGen/CGCall.cpp
===================================================================
--- clang/lib/CodeGen/CGCall.cpp
+++ clang/lib/CodeGen/CGCall.cpp
@@ -1763,6 +1763,17 @@
   if (!isUnresolvedExceptionSpec(FPT->getExceptionSpecType()) &&
       FPT->isNothrow())
     FuncAttrs.addAttribute(llvm::Attribute::NoUnwind);
+
+  if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateSMEnabledMask)
+    FuncAttrs.addAttribute("aarch64_pstate_sm_enabled");
+  if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateSMCompatibleMask)
+    FuncAttrs.addAttribute("aarch64_pstate_sm_compatible");
+  if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateZANewMask)
+    FuncAttrs.addAttribute("aarch64_pstate_za_new");
+  if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateZASharedMask)
+    FuncAttrs.addAttribute("aarch64_pstate_za_shared");
+  if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateZAPreservedMask)
+    FuncAttrs.addAttribute("aarch64_pstate_za_preserved");
 }
 
 static void AddAttributesFromAssumes(llvm::AttrBuilder &FuncAttrs,
@@ -2186,6 +2197,24 @@
                                llvm::toStringRef(CodeGenOpts.UniformWGSize));
       }
     }
+
+    if (TargetDecl->hasAttr<ArmStreamingAttr>())
+      FuncAttrs.addAttribute("aarch64_pstate_sm_enabled");
+
+    if (TargetDecl->hasAttr<ArmLocallyStreamingAttr>())
+      FuncAttrs.addAttribute("aarch64_pstate_sm_body");
+
+    if (TargetDecl->hasAttr<ArmStreamingCompatibleAttr>())
+      FuncAttrs.addAttribute("aarch64_pstate_sm_compatible");
+
+    if (TargetDecl->hasAttr<ArmSharedZAAttr>())
+      FuncAttrs.addAttribute("aarch64_pstate_za_shared");
+
+    if (TargetDecl->hasAttr<ArmPreservesZAAttr>())
+      FuncAttrs.addAttribute("aarch64_pstate_za_preserved");
+
+    if (TargetDecl->hasAttr<ArmNewZAAttr>())
+      FuncAttrs.addAttribute("aarch64_pstate_za_new");
   }
 
   // Attach "no-builtins" attributes to:
Index: clang/lib/Basic/Targets/AArch64.h
===================================================================
--- clang/lib/Basic/Targets/AArch64.h
+++ clang/lib/Basic/Targets/AArch64.h
@@ -54,6 +54,7 @@
   bool HasLSE;
   bool HasFlagM;
   bool HasMOPS;
+  bool HasSME;
 
   llvm::AArch64::ArchKind ArchKind;
 
Index: clang/lib/Basic/Targets/AArch64.cpp
===================================================================
--- clang/lib/Basic/Targets/AArch64.cpp
+++ clang/lib/Basic/Targets/AArch64.cpp
@@ -513,6 +513,7 @@
     .Cases("aarch64", "arm64", "arm", true)
     .Case("neon", FPU & NeonMode)
     .Cases("sve", "sve2", "sve2-bitperm", "sve2-aes", "sve2-sha3", "sve2-sm4", "f64mm", "f32mm", "i8mm", "bf16", FPU & SveMode)
+    .Case("sme", HasSME)
     .Case("ls64", HasLS64)
     .Default(false);
 }
@@ -544,12 +545,18 @@
   HasMatmulFP32 = false;
   HasLSE = false;
   HasMOPS = false;
+  HasSME = false;
 
   ArchKind = llvm::AArch64::ArchKind::INVALID;
 
   for (const auto &Feature : Features) {
     if (Feature == "+neon")
       FPU |= NeonMode;
+    if (Feature == "+sme") {
+      HasSME = true;
+      HasBFloat16 = true;
+      HasFullFP16 = true;
+    }
     if (Feature == "+sve") {
       FPU |= SveMode;
       HasFullFP16 = true;
Index: clang/lib/AST/TypePrinter.cpp
===================================================================
--- clang/lib/AST/TypePrinter.cpp
+++ clang/lib/AST/TypePrinter.cpp
@@ -913,6 +913,24 @@
 
   FunctionType::ExtInfo Info = T->getExtInfo();
 
+  if ((T->getAArch64SMEAttributes() &
+       FunctionType::SME_PStateSMCompatibleMask) &&
+      !InsideCCAttribute)
+    OS << " __attribute__((arm_streaming_compatible))";
+  if ((T->getAArch64SMEAttributes() & FunctionType::SME_PStateSMEnabledMask) &&
+      !InsideCCAttribute)
+    OS << " __attribute__((arm_streaming))";
+  if ((T->getAArch64SMEAttributes() & FunctionType::SME_PStateZANewMask) &&
+      !InsideCCAttribute)
+    OS << " __attribute__((arm_new_za))";
+  if ((T->getAArch64SMEAttributes() & FunctionType::SME_PStateZASharedMask) &&
+      !InsideCCAttribute)
+    OS << " __attribute__((arm_shared_za))";
+  if ((T->getAArch64SMEAttributes() &
+       FunctionType::SME_PStateZAPreservedMask) &&
+      !InsideCCAttribute)
+    OS << " __attribute__((arm_preserves_za))";
+
   printFunctionAfter(Info, OS);
 
   if (!T->getMethodQuals().empty())
@@ -1784,6 +1802,12 @@
    break;
   }
   case attr::AArch64VectorPcs: OS << "aarch64_vector_pcs"; break;
+  case attr::ArmStreaming: OS << "arm_streaming"; break;
+  case attr::ArmStreamingCompatible: OS << "arm_streaming_compatible"; break;
+  case attr::ArmLocallyStreaming: OS << "arm_locally_streaming"; break;
+  case attr::ArmNewZA: OS << "arm_new_za"; break;
+  case attr::ArmPreservesZA: OS << "arm_preserves_za"; break;
+  case attr::ArmSharedZA: OS << "arm_shared_za"; break;
   case attr::AArch64SVEPcs: OS << "aarch64_sve_pcs"; break;
   case attr::AMDGPUKernelCall: OS << "amdgpu_kernel"; break;
   case attr::IntelOclBicc: OS << "inteloclbicc"; break;
Index: clang/lib/AST/Type.cpp
===================================================================
--- clang/lib/AST/Type.cpp
+++ clang/lib/AST/Type.cpp
@@ -3230,6 +3230,12 @@
     argSlot[i] = params[i];
   }
 
+  // Propagate the SME ACLE attributes.
+  if (epi.AArch64SMEAttributes != SME_NormalFunction) {
+    auto &ExtraBits = *getTrailingObjects<FunctionTypeExtraBitfields>();
+    ExtraBits.AArch64SMEAttributes = epi.AArch64SMEAttributes;
+  }
+
   // Fill in the exception type array if present.
   if (getExceptionSpecType() == EST_Dynamic) {
     auto &ExtraBits = *getTrailingObjects<FunctionTypeExtraBitfields>();
@@ -3423,6 +3429,8 @@
     for (unsigned i = 0; i != NumParams; ++i)
       ID.AddInteger(epi.ExtParameterInfos[i].getOpaqueValue());
   }
+  ID.AddInteger(epi.AArch64SMEAttributes);
+
   epi.ExtInfo.Profile(ID);
   ID.AddBoolean(epi.HasTrailingReturn);
 }
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -6208,6 +6208,89 @@
 }];
 }
 
+def ArmSmeStreamingDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+The ``arm_streaming`` attribute is defined by the Arm C Language Extensions
+(ACLE) for SME. It is used to mark a function as being a streaming function for
+which ``PSTATE.SM`` must be ``1`` on entry and on exit of the function.
+
+By adding this attribute, Clang will insert the appropriate ``smstart`` and
+``smstop`` instructions before and after the call to guarantee that these
+conditions are satisfied.
+  }];
+}
+
+def ArmSmeLocallyStreamingDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+The ``arm_locally_streaming`` attribute is defined by the Arm C Language Extensions
+(ACLE) for SME. It is used to mark a function's body (not the interface) as requiring
+``PSTATE.SM`` to be ``1``, although the function is expected to be called with
+``PSTATE.SM=0`` and return with ``PSTATE.SM`` unchanged.
+
+By adding this attribute, Clang will insert the appropriate ``smstart`` and
+``smstop`` instructions in the prologue and epilogue of the function.
+  }];
+}
+
+def ArmSmeStreamingCompatibleDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+The ``arm_streaming_compatible`` attribute is defined by the Arm C Language
+Extensions (ACLE) for SME. It is used to mark a function as being a streaming
+compatible function for which ``PSTATE.SM`` can either be ``0`` or ``1`` at
+runtime. Additionally, the ABI specifies that the value of ``PSTATE.SM`` is
+passed in register ``X0``.
+
+By adding this attribute, Clang will pass an implicit parameter with the value
+of ``PSTATE.SM`` in ``X0`` to streaming-compatible functions and will insert the
+appropriate ``smstart`` and ``smstop`` instructions when there are calls to
+other functions that are not streaming compatible.
+
+Clang will also avoid generating instructions that are illegal in either
+streaming mode or normal mode.
+  }];
+}
+
+def ArmSmeSharedZADocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+The ``arm_shared_za`` attribute is defined by the Arm C Language Extensions
+(ACLE) for SME. It is used to mark a function as sharing the state of ZA, the
+acculator array, with that of it's callers.
+
+By adding this attribute, callers of this function will know that the contents
+of ZA may be used for passing or returning data, and can be modified. Clang may
+assume that ``PSTATE.ZA`` is ``1`` and will avoid setting up a lazy-save
+mechanism for calls to functions marked as ``arm_shared_za``.
+  }];
+}
+
+def ArmSmeNewZADocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+The ``arm_new_za`` attribute is defined by the Arm C Language Extensions (ACLE)
+for SME.  It is used to mark a function as a private ZA function that requires a
+new state for ZA.
+
+By adding this attribute, Clang emits the appropriate ``smstart`` instruction to
+allow the use of ZA and will additionally commit a lazy-save if the state of ZA
+is dormant. It also emits the appropriate ``smstop`` in the function's epilogue.
+  }];
+}
+
+def ArmSmePreservesZADocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+The ``arm_preserves_za`` attribute is defined by the Arm C Language Extensions
+(ACLE) for SME. If a function is marked as ``arm_preserves_za``, it is a hint to
+the compiler that the function and any of it's callees will preserve the state
+of ZA.
+  }];
+}
+
+
 def ArmMveStrictPolymorphismDocs : Documentation {
     let Category = DocCatType;
     let Content = [{
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -372,6 +372,9 @@
 }
 def TargetARM : TargetArch<["arm", "thumb", "armeb", "thumbeb"]>;
 def TargetAArch64 : TargetArch<["aarch64"]>;
+def TargetAArch64SME : TargetArch<["aarch64"]> {
+  let CustomCode = [{ Target.hasFeature("sme") }];
+}
 def TargetAnyArm : TargetArch<!listconcat(TargetARM.Arches, TargetAArch64.Arches)>;
 def TargetAVR : TargetArch<["avr"]>;
 def TargetBPF : TargetArch<["bpfel", "bpfeb"]>;
@@ -2327,6 +2330,42 @@
   let Documentation = [AArch64VectorPcsDocs];
 }
 
+def ArmStreamingCompatible : DeclOrTypeAttr, TargetSpecificAttr<TargetAArch64SME> {
+  let Spellings = [Clang<"arm_streaming_compatible">];
+  let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
+  let Documentation = [ArmSmeStreamingCompatibleDocs];
+}
+
+def ArmStreaming : DeclOrTypeAttr, TargetSpecificAttr<TargetAArch64SME> {
+  let Spellings = [Clang<"arm_streaming">];
+  let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
+  let Documentation = [ArmSmeStreamingDocs];
+}
+
+def ArmLocallyStreaming : DeclOrTypeAttr, TargetSpecificAttr<TargetAArch64SME> {
+  let Spellings = [Clang<"arm_locally_streaming">];
+  let Subjects = SubjectList<[Function], ErrorDiag>;
+  let Documentation = [ArmSmeLocallyStreamingDocs];
+}
+
+def ArmSharedZA : DeclOrTypeAttr, TargetSpecificAttr<TargetAArch64SME> {
+  let Spellings = [Clang<"arm_shared_za">];
+  let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
+  let Documentation = [ArmSmeSharedZADocs];
+}
+
+def ArmPreservesZA : DeclOrTypeAttr, TargetSpecificAttr<TargetAArch64SME> {
+  let Spellings = [Clang<"arm_preserves_za">];
+  let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
+  let Documentation = [ArmSmePreservesZADocs];
+}
+
+def ArmNewZA : DeclOrTypeAttr, TargetSpecificAttr<TargetAArch64SME> {
+  let Spellings = [Clang<"arm_new_za">];
+  let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
+  let Documentation = [ArmSmeNewZADocs];
+}
+
 def AArch64SVEPcs: DeclOrTypeAttr {
   let Spellings = [Clang<"aarch64_sve_pcs">];
   let Documentation = [AArch64SVEPcsDocs];
Index: clang/include/clang/AST/TypeProperties.td
===================================================================
--- clang/include/clang/AST/TypeProperties.td
+++ clang/include/clang/AST/TypeProperties.td
@@ -323,6 +323,9 @@
                     ? node->getExtParameterInfos()
                     : llvm::ArrayRef<FunctionProtoType::ExtParameterInfo>() }];
   }
+  def : Property<"AArch64SMEAttributes", UInt32> {
+    let Read = [{ node->getAArch64SMEAttributes() }];
+  }
 
   def : Creator<[{
     auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
@@ -338,6 +341,7 @@
     epi.ExceptionSpec = exceptionSpecifier;
     epi.ExtParameterInfos =
       extParameterInfo.empty() ? nullptr : extParameterInfo.data();
+    epi.AArch64SMEAttributes = AArch64SMEAttributes;
     return ctx.getFunctionType(returnType, parameters, epi);
   }]>;
 }
Index: clang/include/clang/AST/Type.h
===================================================================
--- clang/include/clang/AST/Type.h
+++ clang/include/clang/AST/Type.h
@@ -3855,6 +3855,20 @@
   /// because TrailingObjects cannot handle repeated types.
   struct ExceptionType { QualType Type; };
 
+  /// The AArch64 SME ACLE (Arm C/C++ Language Extensions) define a number
+  /// of function type attributes that can be set on function types, including
+  /// function pointers.
+  enum AArch64SMETypeAttributes : unsigned {
+    SME_NormalFunction = 0,
+    SME_PStateSMEnabledMask = 1,
+    SME_PStateSMCompatibleMask = 2,
+    SME_PStateZANewMask = 4,
+    SME_PStateZASharedMask = 8,
+    SME_PStateZAPreservedMask = 16,
+    SME_AttributeMask = 255 // We only support maximum 8 bits because of the
+                            // bitmask in FunctionTypeExtraBitfields
+  };
+
   /// A simple holder for various uncommon bits which do not fit in
   /// FunctionTypeBitfields. Aligned to alignof(void *) to maintain the
   /// alignment of subsequent objects in TrailingObjects.
@@ -3863,6 +3877,13 @@
     /// A whole unsigned is not needed here and according to
     /// [implimits] 8 bits would be enough here.
     unsigned NumExceptionType = 0;
+
+    /// Any AArch64 SME ACLE type attributes that need to be propagated
+    /// on declarations and function pointers.
+    unsigned AArch64SMEAttributes : 8;
+
+    FunctionTypeExtraBitfields()
+        : AArch64SMEAttributes(SME_NormalFunction) {}
   };
 
 protected:
@@ -4040,16 +4061,20 @@
     FunctionType::ExtInfo ExtInfo;
     bool Variadic : 1;
     bool HasTrailingReturn : 1;
+    unsigned AArch64SMEAttributes : 8;
     Qualifiers TypeQuals;
     RefQualifierKind RefQualifier = RQ_None;
     ExceptionSpecInfo ExceptionSpec;
     const ExtParameterInfo *ExtParameterInfos = nullptr;
     SourceLocation EllipsisLoc;
 
-    ExtProtoInfo() : Variadic(false), HasTrailingReturn(false) {}
+    ExtProtoInfo()
+        : Variadic(false), HasTrailingReturn(false),
+          AArch64SMEAttributes(SME_NormalFunction) {}
 
     ExtProtoInfo(CallingConv CC)
-        : ExtInfo(CC), Variadic(false), HasTrailingReturn(false) {}
+        : ExtInfo(CC), Variadic(false), HasTrailingReturn(false),
+          AArch64SMEAttributes(SME_NormalFunction) {}
 
     ExtProtoInfo withExceptionSpec(const ExceptionSpecInfo &ESI) {
       ExtProtoInfo Result(*this);
@@ -4058,7 +4083,12 @@
     }
 
     bool requiresFunctionProtoTypeExtraBitfields() const {
-      return ExceptionSpec.Type == EST_Dynamic;
+      return ExceptionSpec.Type == EST_Dynamic ||
+             AArch64SMEAttributes != SME_NormalFunction;
+    }
+
+    void setArmSMEAttribute(AArch64SMETypeAttributes Kind) {
+      AArch64SMEAttributes |= Kind;
     }
   };
 
@@ -4185,6 +4215,7 @@
     EPI.TypeQuals = getMethodQuals();
     EPI.RefQualifier = getRefQualifier();
     EPI.ExtParameterInfos = getExtParameterInfosOrNull();
+    EPI.AArch64SMEAttributes = getAArch64SMEAttributes();
     return EPI;
   }
 
@@ -4367,6 +4398,14 @@
     return getTrailingObjects<ExtParameterInfo>();
   }
 
+  /// Return a bitmask describing the SME attributes on the function type, see
+  /// AArch64SMETypeAttributes for their values.
+  unsigned getAArch64SMEAttributes() const {
+    if (!hasExtraBitfields())
+      return SME_NormalFunction;
+    return getTrailingObjects<FunctionTypeExtraBitfields>()->AArch64SMEAttributes;
+  }
+
   ExtParameterInfo getExtParameterInfo(unsigned I) const {
     assert(I < getNumParams() && "parameter index out of range");
     if (hasExtParameterInfos())
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to