sdesmalen created this revision. Herald added subscribers: jdoerfert, kristof.beyls. Herald added a reviewer: aaron.ballman. Herald added a project: All. sdesmalen requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
This patch adds all the language-level function keywords defined in: https://github.com/ARM-software/acle/pull/188 (merged) https://github.com/ARM-software/acle/pull/261 (update after D148700 landed) The keywords are used to control PSTATE.ZA and PSTATE.SM, which are respectively used for enabling the use of the ZA matrix array and Streaming mode. This information needs to be available on call sites, since the use of ZA or streaming mode may have to be enabled or disabled around the call-site (depending on the IR attributes set on the caller and the callee). For calls to functions from a function pointer, there is no IR declaration available, so the IR attributes must be added explicitly to the call-site. With the exception of '__arm_locally_streaming' and '__arm_new_za' the information is part of the function's interface, not just the function definition, and thus needs to be propagated through the FunctionProtoType::ExtProtoInfo. This patch adds the defintions of these keywords, as well as codegen and semantic analysis to ensure conversions between function pointers are valid and that no conflicting keywords are set. For example, '__arm_streaming' and '__arm_streaming_compatible' are mutually exclusive. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D152142 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/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Sema/Sema.h clang/lib/AST/Type.cpp clang/lib/AST/TypePrinter.cpp clang/lib/CodeGen/CGCall.cpp clang/lib/CodeGen/CodeGenModule.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaDeclAttr.cpp clang/lib/Sema/SemaDeclCXX.cpp clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/SemaOverload.cpp clang/lib/Sema/SemaType.cpp clang/test/AST/ast-dump-sme-attributes.cpp clang/test/CodeGen/aarch64-sme-intrinsics/aarch64-sme-attrs.cpp clang/test/Modules/aarch64-sme-keywords.cppm 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,229 @@ +// 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 + +void sme_arm_streaming(void) __arm_streaming; +void sme_arm_streaming_compatible(void) __arm_streaming_compatible; + +__arm_new_za void sme_arm_new_za(void) {} +void sme_arm_shared_za(void) __arm_shared_za; +void sme_arm_preserves_za(void) __arm_preserves_za; + +__arm_new_za void sme_arm_streaming_new_za(void) __arm_streaming {} +void sme_arm_streaming_shared_za(void) __arm_streaming __arm_shared_za; +void sme_arm_streaming_preserves_za(void) __arm_streaming __arm_preserves_za; + +__arm_new_za void sme_arm_sc_new_za(void) __arm_streaming_compatible {} +void sme_arm_sc_shared_za(void) __arm_streaming_compatible __arm_shared_za; +void sme_arm_sc_preserves_za(void) __arm_streaming_compatible __arm_preserves_za; + +void sme_arm_shared_preserves_za(void) __arm_shared_za __arm_preserves_za; + +__arm_locally_streaming void sme_arm_locally_streaming(void) { } +__arm_locally_streaming void sme_arm_streaming_and_locally_streaming(void) __arm_streaming { } +__arm_locally_streaming void sme_arm_streaming_and_streaming_compatible(void) __arm_streaming_compatible { } + +__arm_locally_streaming __arm_new_za void sme_arm_ls_new_za(void) { } +__arm_locally_streaming void sme_arm_ls_shared_za(void) __arm_shared_za { } +__arm_locally_streaming void sme_arm_ls_preserves_za(void) __arm_preserves_za { } + +// Valid attributes on function pointers + +void streaming_ptr(void) __arm_streaming; +typedef void (*fptrty1) (void) __arm_streaming; +fptrty1 call_streaming_func() { return streaming_ptr; } + +void streaming_compatible_ptr(void) __arm_streaming_compatible; +typedef void (*fptrty2) (void) __arm_streaming_compatible; +fptrty2 call_sc_func() { return streaming_compatible_ptr; } + +void shared_za_ptr(void) __arm_shared_za; +typedef void (*fptrty3) (void) __arm_shared_za; +fptrty3 call_shared_za_func() { return shared_za_ptr; } + +void preserves_za_ptr(void) __arm_preserves_za; +typedef void (*fptrty4) (void) __arm_preserves_za; +fptrty4 call_preserve_za_func() { return preserves_za_ptr; } + +void shared_preserves_za_ptr(void) __arm_shared_za __arm_preserves_za; +typedef void (*fptrty5) (void) __arm_shared_za __arm_preserves_za; +fptrty5 call_shared_preserve_za_func() { return shared_preserves_za_ptr; } + +typedef void (*fptrty6) (void); +fptrty6 cast_nza_func_to_normal() { return sme_arm_new_za; } +fptrty6 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' are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'__arm_streaming_compatible' and '__arm_streaming' are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +void streaming_mode(void) __arm_streaming __arm_streaming_compatible; + +// expected-cpp-error@+4 {{'__arm_streaming' and '__arm_streaming_compatible' are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'__arm_streaming' and '__arm_streaming_compatible' are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +void streaming_compatible(void) __arm_streaming_compatible __arm_streaming; + +// expected-cpp-error@+2 {{'__arm_new_za' and '__arm_shared_za' are not compatible}} +// expected-error@+1 {{'__arm_new_za' and '__arm_shared_za' are not compatible}} +__arm_new_za void new_shared_za(void) __arm_shared_za {} + +// expected-cpp-error@+2 {{'__arm_new_za' and '__arm_preserves_za' are not compatible}} +// expected-error@+1 {{'__arm_new_za' and '__arm_preserves_za' are not compatible}} +__arm_new_za void new_preserves_za(void) __arm_preserves_za {} + +// Invalid attributes on function pointers + +// expected-cpp-error@+4 {{'__arm_streaming_compatible' and '__arm_streaming' are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'__arm_streaming_compatible' and '__arm_streaming' are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +void streaming_ptr_invalid(void) __arm_streaming __arm_streaming_compatible; +// expected-cpp-error@+4 {{'__arm_streaming_compatible' and '__arm_streaming' are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'__arm_streaming_compatible' and '__arm_streaming' are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +typedef void (*fptrty7) (void) __arm_streaming __arm_streaming_compatible; +fptrty7 invalid_streaming_func() { return streaming_ptr_invalid; } + +// expected-warning@+2 {{'__arm_streaming' only applies to non-K&R-style functions}} +// expected-error@+1 {{'__arm_streaming' only applies to function types; type here is 'void ()'}} +void function_no_prototype() __arm_streaming; + +// +// Check for incorrect conversions of function pointers with the attributes +// + +typedef void (*n_ptrty) (void); +typedef void (*s_ptrty) (void) __arm_streaming; +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 (*)() __arm_streaming') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}} +// expected-error@+1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 's_ptrty' (aka 'void (*)(void) __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 (*)() __arm_streaming')}} +// expected-error@+1 {{incompatible function pointer types returning 's_ptrty' (aka 'void (*)(void) __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; } + +// Test an instance where the result type is not a prototyped function, such that we still get a diagnostic. +typedef void (*nonproto_n_ptrty) (); +// expected-cpp-error@+2 {{cannot initialize return object of type 'nonproto_n_ptrty' (aka 'void (*)()') with an lvalue of type 's_ptrty' (aka 'void (*)() __arm_streaming')}} +// expected-error@+1 {{incompatible function pointer types returning 's_ptrty' (aka 'void (*)(void) __arm_streaming') from a function with result type 'nonproto_n_ptrty' (aka 'void (*)()')}} +nonproto_n_ptrty return_invalid_fptr_streaming_nonprotonormal(s_ptrty f) { return f; } + +typedef void (*sc_ptrty) (void) __arm_streaming_compatible; +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 (*)() __arm_streaming_compatible') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}} +// expected-error@+1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 'sc_ptrty' (aka 'void (*)(void) __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 (*)() __arm_streaming_compatible')}} +// expected-error@+1 {{incompatible function pointer types returning 'sc_ptrty' (aka 'void (*)(void) __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 void (*sz_ptrty) (void) __arm_shared_za; +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 (*)() __arm_shared_za') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}} +// expected-error@+1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 'sz_ptrty' (aka 'void (*)(void) __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 (*)() __arm_shared_za')}} +// expected-error@+1 {{incompatible function pointer types returning 'sz_ptrty' (aka 'void (*)(void) __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 void (*pz_ptrty) (void) __arm_preserves_za; +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 (*)() __arm_preserves_za') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}} +// expected-error@+1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 'pz_ptrty' (aka 'void (*)(void) __arm_preserves_za')}} +pz_ptrty return_invalid_fptr_preserves_za_normal(n_ptrty f) { return f; } +// No diagnostics, the preserves_za hint should be dropped silently. +n_ptrty return_invalid_fptr_normal_preserves_za(pz_ptrty f) { return f; } + +// Test template instantiations +#ifdef __cplusplus +template <typename T> T templated(T x) __arm_streaming { return x; } +template <> int templated<int>(int x) __arm_streaming { return x + 1; } +template <> float templated<float>(float x) __arm_streaming { 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) __arm_streaming' against 'short (short)'}} +template short templated<short>(short); +#endif + +// Conflicting attributes on redeclarations + +// expected-error@+5 {{function declared ''void (void) __arm_streaming_compatible'' was previously declared ''void (void) __arm_streaming'' with different SME function attributes}} +// expected-note@+3 {{previous declaration is here}} +// expected-cpp-error@+3 {{function declared ''void () __arm_streaming_compatible'' was previously declared ''void () __arm_streaming'' with different SME function attributes}} +// expected-cpp-note@+1 {{previous declaration is here}} +void redecl(void) __arm_streaming; +void redecl(void) __arm_streaming_compatible { } + +// expected-error@+5 {{function declared ''void (void) __arm_shared_za'' was previously declared ''void (void) __arm_shared_za __arm_preserves_za'' with different SME function attributes}} +// expected-note@+3 {{previous declaration is here}} +// expected-cpp-error@+3 {{function declared ''void () __arm_shared_za'' was previously declared ''void () __arm_shared_za __arm_preserves_za'' with different SME function attributes}} +// expected-cpp-note@+1 {{previous declaration is here}} +void redecl_nopreserve_za(void) __arm_shared_za __arm_preserves_za; +void redecl_nopreserve_za(void) __arm_shared_za{ } + +#ifdef __cplusplus +struct S { + virtual void shared_za_memberfn(void) __arm_shared_za; +}; + +struct S2 : public S { +// expected-cpp-error@+2 {{virtual function 'shared_za_memberfn' has different attributes ('void ()') than the function it overrides (which has 'void () __arm_shared_za')}} +// expected-cpp-note@-5 {{overridden virtual function is here}} + __arm_new_za void shared_za_memberfn(void) override {} +}; + +// Check that the attribute propagates through template instantiations. +template <typename Ty> +struct S3 { + static constexpr int value = 0; +}; + +template <> +struct S3<void (*)()> { + static constexpr int value = 1; +}; + +template <> +struct S3<void (* __arm_streaming)()> { + static constexpr int value = 2; +}; + +void normal_func(void) {} +void streaming_func(void) __arm_streaming {} + +static_assert(S3<decltype(+normal_func)>::value == 1, "why are we picking the wrong specialization?"); +static_assert(S3<decltype(+streaming_func)>::value == 2, "why are we picking the wrong specialization?"); + +template<typename T> int test_templated_f(T); +template<> constexpr int test_templated_f<void(*)(void)>(void(*)(void)) { return 1; } +template<> constexpr int test_templated_f<void(*)(void)__arm_streaming>(void(*)(void)__arm_streaming) { return 2; } + +static_assert(test_templated_f(&normal_func) == 1, "Instantiated to wrong function"); +static_assert(test_templated_f(&streaming_func) == 2, "Instantiated to wrong function"); + +// expected-cpp-error@+2 {{'__arm_streaming' only applies to function types; type here is 'int'}} +// expected-error@+1 {{'__arm_streaming' only applies to function types; type here is 'int'}} +int invalid_type_for_attribute __arm_streaming; + +// Test overloads +constexpr int overload(void f(void)) { return 1; } +constexpr int overload(void f(void) __arm_streaming) { return 2; } +static_assert(overload(&normal_func) == 1, "Overloaded to wrong function"); +static_assert(overload(&streaming_func) == 2, "Overloaded to wrong function"); + +#endif // ifdef __cplusplus Index: clang/test/Modules/aarch64-sme-keywords.cppm =================================================================== --- /dev/null +++ clang/test/Modules/aarch64-sme-keywords.cppm @@ -0,0 +1,65 @@ +// REQUIRES: aarch64-registered-target +// +// RUN: rm -rf %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 -triple aarch64 -target-feature +sme %t/A.cppm -emit-module-interface -o %t/A.pcm +// RUN: %clang_cc1 -std=c++20 -triple aarch64 -target-feature +sme -fprebuilt-module-path=%t -I%t %t/Use.cpp -emit-llvm +// RUN: cat %t/Use.ll | FileCheck %s + +//--- A.cppm +module; +export module A; + +export void f_streaming(void) __arm_streaming { } +export void f_streaming_compatible(void) __arm_streaming_compatible { } +export void f_shared_za(void) __arm_shared_za { } +export void f_preserves_za(void) __arm_preserves_za { } + +//--- Use.cpp +// expected-no-diagnostics +import A; + +// CHECK: define dso_local void @_Z18f_shared_za_callerv() #[[SHARED_ZA_DEF:[0-9]+]] { +// CHECK: entry: +// CHECK: call void @_ZW1A11f_shared_zav() #[[SHARED_ZA_USE:[0-9]+]] +// CHECK: call void @_ZW1A14f_preserves_zav() #[[PRESERVES_ZA_USE:[0-9]+]] +// CHECK: ret void +// CHECK: } +// +// CHECK:declare void @_ZW1A11f_shared_zav() #[[SHARED_ZA_DECL:[0-9]+]] +// +// CHECK:declare void @_ZW1A14f_preserves_zav() #[[PRESERVES_ZA_DECL:[0-9]+]] +// +// CHECK:; Function Attrs: mustprogress noinline nounwind optnone +// CHECK:define dso_local void @_Z21f_nonstreaming_callerv() #[[NORMAL_DEF:[0-9]+]] { +// CHECK:entry: +// CHECK: call void @_ZW1A11f_streamingv() #[[STREAMING_USE:[0-9]+]] +// CHECK: call void @_ZW1A22f_streaming_compatiblev() #[[STREAMING_COMPATIBLE_USE:[0-9]+]] +// CHECK: ret void +// CHECK:} +// +// CHECK:declare void @_ZW1A11f_streamingv() #[[STREAMING_DECL:[0-9]+]] +// +// CHECK:declare void @_ZW1A22f_streaming_compatiblev() #[[STREAMING_COMPATIBLE_DECL:[0-9]+]] +// +// CHECK-DAG: attributes #[[SHARED_ZA_DEF]] = {{{.*}} "aarch64_pstate_za_shared" {{.*}}} +// CHECK-DAG: attributes #[[SHARED_ZA_DECL]] = {{{.*}} "aarch64_pstate_za_shared" {{.*}}} +// CHECK-DAG: attributes #[[PRESERVES_ZA_DECL]] = {{{.*}} "aarch64_pstate_za_preserved" {{.*}}} +// CHECK-DAG: attributes #[[NORMAL_DEF]] = {{{.*}}} +// CHECK-DAG: attributes #[[STREAMING_DECL]] = {{{.*}} "aarch64_pstate_sm_enabled" {{.*}}} +// CHECK-DAG: attributes #[[STREAMING_COMPATIBLE_DECL]] = {{{.*}} "aarch64_pstate_sm_compatible" {{.*}}} +// CHECK-DAG: attributes #[[SHARED_ZA_USE]] = { "aarch64_pstate_za_shared" } +// CHECK-DAG: attributes #[[PRESERVES_ZA_USE]] = { "aarch64_pstate_za_preserved" } +// CHECK-DAG: attributes #[[STREAMING_USE]] = { "aarch64_pstate_sm_enabled" } +// CHECK-DAG: attributes #[[STREAMING_COMPATIBLE_USE]] = { "aarch64_pstate_sm_compatible" } + +void f_shared_za_caller(void) __arm_shared_za { + f_shared_za(); + f_preserves_za(); +} + +void f_nonstreaming_caller(void) { + f_streaming(); + f_streaming_compatible(); +} 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,264 @@ +// 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 == + +int streaming_decl(void) __arm_streaming; +int streaming_compatible_decl(void) __arm_streaming_compatible; +int shared_za_decl(void) __arm_shared_za; +int preserves_za_decl(void) __arm_preserves_za; +int private_za_decl(void); + +// == FUNCTION DEFINITIONS == + +// CHECK-LABEL: @streaming_caller() +// CHECK-SAME: #[[SM_ENABLED:[0-9]+]] +// CHECK: call i32 @normal_callee() +// + int streaming_caller() __arm_streaming { + 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]+]] +// + int streaming_callee() __arm_streaming { + 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() +// + int streaming_compatible_caller() __arm_streaming_compatible { + return normal_callee(); +} + +// CHECK-LABEL: @streaming_compatible_callee() +// CHECK-SAME: #[[SM_COMPATIBLE]] +// CHECK: call i32 @streaming_compatible_decl() #[[SM_COMPATIBLE_CALL:[0-9]+]] +// + int streaming_compatible_callee() __arm_streaming_compatible { + 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() +// +__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]+]] +// +__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() +// + int shared_za_caller() __arm_shared_za { + return normal_callee(); +} + +// CHECK-LABEL: @shared_za_callee() +// CHECK-SAME: #[[ZA_SHARED]] +// CHECK: call i32 @shared_za_decl() #[[ZA_SHARED_CALL:[0-9]+]] +// + int shared_za_callee() __arm_shared_za { + 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() +// + int preserves_za_caller() __arm_preserves_za { + return normal_callee(); +} + +// CHECK-LABEL: @preserves_za_callee() +// CHECK-SAME: #[[ZA_PRESERVED]] +// CHECK: call i32 @preserves_za_decl() #[[ZA_PRESERVED_CALL:[0-9]+]] +// + int preserves_za_callee() __arm_preserves_za { + 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() +// +__arm_new_za int new_za_caller() { + return normal_callee(); +} + +// CHECK-LABEL: @new_za_callee() +// CHECK-SAME: #[[ZA_NEW]] +// CHECK: call i32 @private_za_decl() +// +__arm_new_za int new_za_callee() { + return private_za_decl(); +} + +// CHECK: declare i32 @private_za_decl() + + +// Ensure that the attributes are correctly propagated to function types +// and also to callsites. +typedef void (*s_ptrty) (int, int) __arm_streaming; +typedef void (*sc_ptrty) (int, int) __arm_streaming_compatible; +typedef void (*sz_ptrty) (int, int) __arm_shared_za; +typedef void (*pz_ptrty) (int, int) __arm_preserves_za; + +// 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_shared_za( +// CHECK-SAME: #[[ZA_SHARED]] +// CHECK: call void [[F:%.*]](i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ZA_SHARED_CALL]] +// +void test_shared_za(sz_ptrty f, int x, int y) __arm_shared_za { 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 test_preserved_za(pz_ptrty f, int x, int y) __arm_shared_za { 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 that having the attribute in different places (on declaration and on type) +// both results in the attribute being applied to the type. +// + +// CHECK-LABEL: @_Z24test_same_type_streamingv( +// CHECK: call void @_Z10streaming1v() #[[SM_ENABLED_CALL]] +// CHECK: call void @_Z10streaming2v() #[[SM_ENABLED_CALL]] +// CHECK: call void @_Z20same_type_streaming1v() #[[SM_ENABLED_CALL]] +// CHECK: call void @_Z20same_type_streaming2v() #[[SM_ENABLED_CALL]] +// CHECK: ret void +// CHECK: } +// CHECK: declare void @_Z10streaming1v() #[[SM_ENABLED_DECL]] +// CHECK: declare void @_Z10streaming2v() #[[SM_ENABLED_DECL]] +// CHECK: declare void @_Z20same_type_streaming1v() #[[SM_ENABLED_DECL]] +// CHECK: declare void @_Z20same_type_streaming2v() #[[SM_ENABLED_DECL]] +void streaming1(void) __arm_streaming; +void streaming2() __arm_streaming; +decltype(streaming1) same_type_streaming1; +decltype(streaming2) same_type_streaming2; +void test_same_type_streaming() { + streaming1(); + streaming2(); + same_type_streaming1(); + same_type_streaming2(); +} + +// +// 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 overloadedfn(int x) __arm_streaming { 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) __arm_streaming { return x; }; + return F(x); +} + +// CHECK-LABEL: @_Z27test_template_instantiationv( +// CHECK-SAME: #[[NORMAL_DEF]] +// CHECK: call noundef i32 @_Z15template_functyIiET_S0_(i32 noundef 12) #[[SM_ENABLED_CALL]] +// +// CHECK: @_Z15template_functyIiET_S0_( +// CHECK-SAME: #[[SM_ENABLED]] +template <typename Ty> +Ty template_functy(Ty x) __arm_streaming { return x; } +int test_template_instantiation() { return template_functy(12); } + +// +// Test that arm_locally_streaming is inherited by future redeclarations, +// even when they don't specify the attribute. +// + +// CHECK: define {{.*}} @_Z25locally_streaming_inheritv( +// CHECK-SAME: #[[SM_BODY]] +__arm_locally_streaming void locally_streaming_inherit(); +void locally_streaming_inherit() { + streaming_decl(); +} + +// CHECK: attributes #[[SM_ENABLED]] = { mustprogress noinline nounwind optnone "aarch64_pstate_sm_enabled" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" } +// CHECK: attributes #[[NORMAL_DECL]] = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" } +// CHECK: attributes #[[SM_ENABLED_DECL]] = { "aarch64_pstate_sm_enabled" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" } +// CHECK: attributes #[[SM_COMPATIBLE]] = { mustprogress noinline nounwind optnone "aarch64_pstate_sm_compatible" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" } +// CHECK: attributes #[[SM_COMPATIBLE_DECL]] = { "aarch64_pstate_sm_compatible" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" } +// CHECK: attributes #[[SM_BODY]] = { mustprogress noinline nounwind optnone "aarch64_pstate_sm_body" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" } +// CHECK: attributes #[[ZA_SHARED]] = { mustprogress noinline nounwind optnone "aarch64_pstate_za_shared" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" } +// CHECK: attributes #[[ZA_SHARED_DECL]] = { "aarch64_pstate_za_shared" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" } +// CHECK: attributes #[[ZA_PRESERVED]] = { mustprogress noinline nounwind optnone "aarch64_pstate_za_preserved" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" } +// CHECK: attributes #[[ZA_PRESERVED_DECL]] = { "aarch64_pstate_za_preserved" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" } +// CHECK: attributes #[[ZA_NEW]] = { mustprogress noinline nounwind optnone "aarch64_pstate_za_new" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" } +// CHECK: attributes #[[NORMAL_DEF]] = { mustprogress noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+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" } Index: clang/test/AST/ast-dump-sme-attributes.cpp =================================================================== --- /dev/null +++ clang/test/AST/ast-dump-sme-attributes.cpp @@ -0,0 +1,66 @@ +// Test without serialization: +// RUN: %clang_cc1 -triple aarch64 -target-feature +sme -std=c++2a -ast-dump -ast-dump-filter Foo %s | FileCheck -strict-whitespace %s + +// Test with serialization: +// RUN: %clang_cc1 -std=c++20 -triple aarch64 -target-feature +sme -emit-pch -o %t %s +// RUN: %clang_cc1 -x c++ -std=c++20 -triple aarch64 -target-feature +sme -include-pch %t -ast-dump-all -ast-dump-filter Foo /dev/null \ +// RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//" \ +// RUN: | FileCheck --strict-whitespace %s + +struct Foo { +// CHECK: |-CXXRecordDecl {{.*}} implicit struct Foo +// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_streaming 'void () __arm_streaming' +// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_streaming_compatible 'void () __arm_streaming_compatible' +// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_locally_streaming 'void ()' +// CHECK-NEXT: | `-ArmLocallyStreamingAttr +// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_shared_za 'void () __arm_shared_za' +// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_new_za 'void ()' +// CHECK-NEXT: | `-ArmNewZAAttr +// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_preserves_za 'void () __arm_preserves_za' + void f_streaming() __arm_streaming; + void f_streaming_compatible() __arm_streaming_compatible; + __arm_locally_streaming void f_locally_streaming(); + void f_shared_za() __arm_shared_za; + __arm_new_za void f_new_za(); + void f_preserves_za() __arm_preserves_za; + + +// CHECK: |-CXXMethodDecl {{.*}} test_lambda 'int (int)' implicit-inline +// CHECK: `-CompoundStmt +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl +// CHECK-NEXT: | `-LambdaExpr +// CHECK-NEXT: | |-CXXRecordDecl +// CHECK: | | |-CXXMethodDecl {{.*}} used constexpr operator() 'int (int) __arm_streaming const' inline +// CHECK: | | |-CXXConversionDecl {{.*}} implicit constexpr operator int (*)(int) __arm_streaming 'int (*() const noexcept)(int) __arm_streaming' inline +// CHECK: | | |-CXXMethodDecl {{.*}} implicit __invoke 'int (int) __arm_streaming' static inline +// CHECK: `-ReturnStmt +// CHECK: `-CXXOperatorCallExpr +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'int (*)(int) __arm_streaming const' <FunctionToPointerDecay> +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int (int) __arm_streaming const' lvalue CXXMethod {{.*}} 'operator()' 'int (int) __arm_streaming const' + int test_lambda(int x) { + auto F = [](int x) __arm_streaming { return x; }; + return F(x); + } + +// CHECK: |-TypedefDecl {{.*}} referenced s_ptrty 'void (*)(int, int) __arm_streaming' +// CHECK-NEXT: | `-PointerType {{.*}} 'void (*)(int, int) __arm_streaming' +// CHECK-NEXT: | `-ParenType {{.*}} 'void (int, int) __arm_streaming' sugar +// CHECK-NEXT: | `-FunctionProtoType {{.*}} 'void (int, int) __arm_streaming' cdecl + typedef void (*s_ptrty) (int, int) __arm_streaming; + +// CHECK: `-CXXMethodDecl {{.*}} test_streaming_ptrty 'void (s_ptrty, int, int)' implicit-inline +// CHECK-NEXT: |-ParmVarDecl {{.*}} used f 's_ptrty':'void (*)(int, int) __arm_streaming' +// CHECK-NEXT: |-ParmVarDecl {{.*}} used x 'int' +// CHECK-NEXT: |-ParmVarDecl {{.*}} used y 'int' +// CHECK: `-CompoundStmt +// CHECK-NEXT: `-ReturnStmt +// CHECK-NEXT: `-CallExpr +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 's_ptrty':'void (*)(int, int) __arm_streaming' <LValueToRValue> +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 's_ptrty':'void (*)(int, int) __arm_streaming' lvalue ParmVar {{.*}} 'f' 's_ptrty':'void (*)(int, int) __arm_streaming' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue> +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'x' 'int' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue> +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'y' 'int' + void test_streaming_ptrty(s_ptrty f, int x, int y) { return f(x, y); }; +}; Index: clang/lib/Sema/SemaType.cpp =================================================================== --- clang/lib/Sema/SemaType.cpp +++ clang/lib/Sema/SemaType.cpp @@ -128,7 +128,6 @@ case ParsedAttr::AT_VectorCall: \ case ParsedAttr::AT_AArch64VectorPcs: \ case ParsedAttr::AT_AArch64SVEPcs: \ - case ParsedAttr::AT_ArmStreaming: \ case ParsedAttr::AT_AMDGPUKernelCall: \ case ParsedAttr::AT_MSABI: \ case ParsedAttr::AT_SysVABI: \ @@ -143,6 +142,10 @@ case ParsedAttr::AT_NoReturn: \ case ParsedAttr::AT_Regparm: \ case ParsedAttr::AT_CmseNSCall: \ + case ParsedAttr::AT_ArmStreaming: \ + case ParsedAttr::AT_ArmStreamingCompatible: \ + case ParsedAttr::AT_ArmSharedZA: \ + case ParsedAttr::AT_ArmPreservesZA: \ case ParsedAttr::AT_AnyX86NoCallerSavedRegisters: \ case ParsedAttr::AT_AnyX86NoCfCheck: \ CALLING_CONV_ATTRS_CASELIST @@ -7747,6 +7750,26 @@ llvm_unreachable("unexpected attribute kind!"); } +static bool checkMutualExclusion(TypeProcessingState &state, + const FunctionProtoType::ExtProtoInfo &EPI, + ParsedAttr &Attr, + AttributeCommonInfo::Kind OtherKind) { + auto OtherAttr = std::find_if( + state.getCurrentAttributes().begin(), state.getCurrentAttributes().end(), + [OtherKind](const ParsedAttr &A) { return A.getKind() == OtherKind; }); + if (OtherAttr == state.getCurrentAttributes().end() || OtherAttr->isInvalid()) + return false; + + Sema &S = state.getSema(); + S.Diag(Attr.getLoc(), diag::err_attributes_are_not_compatible) + << *OtherAttr << Attr + << (OtherAttr->isRegularKeywordAttribute() || + Attr.isRegularKeywordAttribute()); + S.Diag(OtherAttr->getLoc(), diag::note_conflicting_attribute); + Attr.setInvalid(); + return true; +} + /// Process an individual function attribute. Returns true to /// indicate that the attribute was handled, false if it wasn't. static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr, @@ -7876,6 +7899,55 @@ return true; } + if (attr.getKind() == ParsedAttr::AT_ArmStreaming || + attr.getKind() == ParsedAttr::AT_ArmStreamingCompatible || + 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_wrong_decl_type) << + attr << attr.isRegularKeywordAttribute() << ExpectedFunctionWithProtoType; + attr.setInvalid(); + return false; + } + + FunctionProtoType::ExtProtoInfo EPI = FnTy->getExtProtoInfo(); + switch (attr.getKind()) { + case ParsedAttr::AT_ArmStreaming: + if (checkMutualExclusion(state, EPI, attr, + ParsedAttr::AT_ArmStreamingCompatible)) + return true; + EPI.setArmSMEAttribute(FunctionType::SME_PStateSMEnabledMask); + break; + case ParsedAttr::AT_ArmStreamingCompatible: + if (checkMutualExclusion(state, EPI, attr, ParsedAttr::AT_ArmStreaming)) + return true; + EPI.setArmSMEAttribute(FunctionType::SME_PStateSMCompatibleMask); + 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/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -1686,6 +1686,26 @@ Changed = true; } + // Drop the 'arm_preserves_za' if not present in the target type (we can do + // that because it is merely a hint). + if (const auto *FromFPT = dyn_cast<FunctionProtoType>(FromFn)) { + FunctionProtoType::ExtProtoInfo ExtInfo = FromFPT->getExtProtoInfo(); + if (ExtInfo.AArch64SMEAttributes & + FunctionType::SME_PStateZAPreservedMask) { + unsigned ToFlags = 0; + if (const auto *ToFPT = dyn_cast<FunctionProtoType>(ToFn)) + ToFlags = ToFPT->getExtProtoInfo().AArch64SMEAttributes; + if (!(ToFlags & FunctionType::SME_PStateZAPreservedMask)) { + ExtInfo.setArmSMEAttribute(FunctionType::SME_PStateZAPreservedMask, + false); + QualType QT = Context.getFunctionType( + FromFPT->getReturnType(), FromFPT->getParamTypes(), ExtInfo); + FromFn = QT->getAs<FunctionType>(); + Changed = true; + } + } + } + // Drop 'noexcept' if not present in target type. if (const auto *FromFPT = dyn_cast<FunctionProtoType>(FromFn)) { const auto *ToFPT = cast<FunctionProtoType>(ToFn); Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -9542,6 +9542,30 @@ ColonLoc, result, VK, OK); } +// Check that the SME attributes for PSTATE.ZA and PSTATE.SM are compatible. +bool Sema::IsInvalidSMECallConversion(QualType FromType, QualType ToType) { + unsigned FromAttributes = 0, ToAttributes = 0; + if (const auto *FromFn = + dyn_cast<FunctionProtoType>(Context.getCanonicalType(FromType))) + FromAttributes = + FromFn->getAArch64SMEAttributes() & FunctionType::SME_AttributeMask; + if (const auto *ToFn = + dyn_cast<FunctionProtoType>(Context.getCanonicalType(ToType))) + ToAttributes = + ToFn->getAArch64SMEAttributes() & FunctionType::SME_AttributeMask; + + if (FromAttributes == ToAttributes) + return false; + + // Make an exception for preserves_za, which can be dropped because it's + // only a hint. + unsigned Changed = FromAttributes ^ ToAttributes; + bool DropsPreservesZA = + Changed == FunctionType::SME_PStateZAPreservedMask && + (ToAttributes & FunctionType::SME_PStateZAPreservedMask); + return !DropsPreservesZA; +} + // 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. @@ -9708,6 +9732,8 @@ return Sema::IncompatibleFunctionPointer; if (IsInvalidCmseNSCallConversion(S, ltrans, rtrans)) return Sema::IncompatibleFunctionPointer; + if (S.IsInvalidSMECallConversion(ltrans, rtrans)) + return Sema::IncompatibleFunctionPointer; return ConvTy; } Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -17687,6 +17687,14 @@ } } + // SME attributes must match when overriding a function declaration. + if (IsInvalidSMECallConversion(Old->getType(), New->getType())) { + Diag(New->getLocation(), diag::err_conflicting_overriding_attributes) + << New << New->getType() << Old->getType(); + Diag(Old->getLocation(), diag::note_overridden_virtual_function); + return true; + } + // Virtual overrides must have the same code_seg. const auto *OldCSA = Old->getAttr<CodeSegAttr>(); const auto *NewCSA = New->getAttr<CodeSegAttr>(); Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -5349,9 +5349,6 @@ case ParsedAttr::AT_AArch64SVEPcs: CC = CC_AArch64SVEPCS; break; - case ParsedAttr::AT_ArmStreaming: - CC = CC_C; // FIXME: placeholder until real SME support is added. - break; case ParsedAttr::AT_AMDGPUKernelCall: CC = CC_AMDGPUKernelCall; break; @@ -9439,6 +9436,29 @@ handleArmBuiltinAliasAttr(S, D, AL); break; + case ParsedAttr::AT_ArmLocallyStreaming: + handleSimpleAttribute<ArmLocallyStreamingAttr>(S, D, AL); + break; + + case ParsedAttr::AT_ArmNewZA: + if (auto *FPT = dyn_cast<FunctionProtoType>(D->getFunctionType())) { + if (FPT->getAArch64SMEAttributes() & + FunctionType::SME_PStateZASharedMask) { + S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible) + << AL << "'__arm_shared_za'" << true; + AL.setInvalid(); + } + if (FPT->getAArch64SMEAttributes() & + FunctionType::SME_PStateZAPreservedMask) { + S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible) + << AL << "'__arm_preserves_za'" << true; + AL.setInvalid(); + } + if (AL.isInvalid()) + return; + } + handleSimpleAttribute<ArmNewZAAttr>(S, D, AL); + break; case ParsedAttr::AT_AcquireHandle: handleAcquireHandleAttr(S, D, AL); break; Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -3755,6 +3755,15 @@ } } + // It is not allowed to redeclare an SME function with different SME + // attributes. + if (IsInvalidSMECallConversion(Old->getType(), New->getType())) { + Diag(New->getLocation(), diag::err_sme_attr_mismatch) + << New->getType() << Old->getType(); + Diag(OldLocation, diag::note_previous_declaration); + return true; + } + // If a function is first declared with a calling convention, but is later // declared or defined without one, all following decls assume the calling // convention of the first. Index: clang/lib/CodeGen/CodeGenModule.cpp =================================================================== --- clang/lib/CodeGen/CodeGenModule.cpp +++ clang/lib/CodeGen/CodeGenModule.cpp @@ -2070,6 +2070,16 @@ 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. + // The same holds for 'arm_new_za'. + if (D->hasAttr<ArmLocallyStreamingAttr>()) + B.addAttribute("aarch64_pstate_sm_body"); + + if (D->hasAttr<ArmNewZAAttr>()) + B.addAttribute("aarch64_pstate_za_new"); + // 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 @@ -1768,6 +1768,15 @@ 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_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, @@ -2368,6 +2377,12 @@ llvm::toStringRef(CodeGenOpts.UniformWGSize)); } } + + if (TargetDecl->hasAttr<ArmLocallyStreamingAttr>()) + FuncAttrs.addAttribute("aarch64_pstate_sm_body"); + + if (TargetDecl->hasAttr<ArmNewZAAttr>()) + FuncAttrs.addAttribute("aarch64_pstate_za_new"); } // Attach "no-builtins" attributes to: Index: clang/lib/AST/TypePrinter.cpp =================================================================== --- clang/lib/AST/TypePrinter.cpp +++ clang/lib/AST/TypePrinter.cpp @@ -938,6 +938,21 @@ FunctionType::ExtInfo Info = T->getExtInfo(); + if ((T->getAArch64SMEAttributes() & + FunctionType::SME_PStateSMCompatibleMask) && + !InsideCCAttribute) + OS << " __arm_streaming_compatible"; + if ((T->getAArch64SMEAttributes() & FunctionType::SME_PStateSMEnabledMask) && + !InsideCCAttribute) + OS << " __arm_streaming"; + if ((T->getAArch64SMEAttributes() & FunctionType::SME_PStateZASharedMask) && + !InsideCCAttribute) + OS << " __arm_shared_za"; + if ((T->getAArch64SMEAttributes() & + FunctionType::SME_PStateZAPreservedMask) && + !InsideCCAttribute) + OS << " __arm_preserves_za"; + printFunctionAfter(Info, OS); if (!T->getMethodQuals().empty()) @@ -1761,6 +1776,18 @@ OS << "__arm_streaming"; return; } + if (T->getAttrKind() == attr::ArmStreamingCompatible) { + OS << "__arm_streaming_compatible"; + return; + } + if (T->getAttrKind() == attr::ArmSharedZA) { + OS << "__arm_shared_za"; + return; + } + if (T->getAttrKind() == attr::ArmPreservesZA) { + OS << "__arm_preserves_za"; + return; + } OS << " __attribute__(("; switch (T->getAttrKind()) { @@ -1803,6 +1830,9 @@ case attr::AnnotateType: case attr::WebAssemblyFuncref: case attr::ArmStreaming: + case attr::ArmStreamingCompatible: + case attr::ArmSharedZA: + case attr::ArmPreservesZA: llvm_unreachable("This attribute should have been handled already"); case attr::NSReturnsRetained: Index: clang/lib/AST/Type.cpp =================================================================== --- clang/lib/AST/Type.cpp +++ clang/lib/AST/Type.cpp @@ -3368,6 +3368,12 @@ argSlot[i] = params[i]; } + // Propagate the SME ACLE attributes. + if (epi.AArch64SMEAttributes != SME_NormalFunction) { + auto &ExtraBits = *getTrailingObjects<FunctionTypeExtraBitfields>(); + ExtraBits.setAArch64SMEAttributes(epi.AArch64SMEAttributes); + } + // Fill in the exception type array if present. if (getExceptionSpecType() == EST_Dynamic) { auto &ExtraBits = *getTrailingObjects<FunctionTypeExtraBitfields>(); @@ -3561,6 +3567,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/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -7022,6 +7022,8 @@ NestedNameSpecInfo &IdInfo, bool EnteringContext); + bool IsInvalidSMECallConversion(QualType FromType, QualType ToType); + /// The parser has parsed a nested-name-specifier /// 'template[opt] template-name < template-args >::'. /// Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2015,6 +2015,10 @@ "than the function it overrides}1,2">; def note_overridden_virtual_function : Note< "overridden virtual function is here">; +def err_conflicting_overriding_attributes : Error< + "virtual function %0 has different attributes " + "%diff{($) than the function it overrides (which has $)|" + "than the function it overrides}1,2">; def err_conflicting_overriding_cc_attributes : Error< "virtual function %0 has different calling convention attributes " "%diff{($) than the function it overrides (which has calling convention $)|" @@ -3583,6 +3587,9 @@ "the vecreturn attribute can only be used on a class or structure with one member, which must be a vector">; def err_attribute_vecreturn_only_pod_record : Error< "the vecreturn attribute can only be used on a POD (plain old data) class or structure (i.e. no virtual functions)">; +def err_sme_attr_mismatch : Error< + "function declared '%0' was previously declared '%1'" + " with different SME function attributes">; def err_cconv_change : Error< "function declared '%0' here was previously declared " "%select{'%2'|without calling convention}1">; Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -6554,9 +6554,6 @@ def ArmSmeStreamingDocs : Documentation { let Category = DocCatType; let Content = [{ -.. Note:: This attribute has not been implemented yet, but once it is - implemented, it will behave as described below. - The ``__arm_streaming`` keyword is only available on AArch64 targets. It applies to function types and specifies that the function has a âstreaming interfaceâ. This means that: @@ -6588,6 +6585,146 @@ }]; } +def ArmSmeStreamingCompatibleDocs : Documentation { + let Category = DocCatType; + let Content = [{ +The ``__arm_streaming_compatible`` keyword is only available on AArch64 targets. +It applies to function types and specifies that the function has a +âstreaming compatible interfaceâ. This means that: + +* the function requires the Scalable Matrix Extension (SME) + +* the function may be entered in either normal mode (PSTATE.SM=0) or + in streaming mode (PSTATE.SM=1). + +* the function must return in the same mode in which it entered the function. + +* the code executed in the function is compatible with either mode. + +* the function does not have a K&R-style unprototyped function type. + +See `Procedure Call Standard for the Arm® 64-bit Architecture (AArch64) +<https://github.com/ARM-software/abi-aa>`_ for more details about +streaming-compatible functions. + +Clang manages PSTATE.SM automatically; it is not the source code's +responsibility to do this. Clang will ensure that the generated code in +streaming-compatible functions is valid in either mode (PSTATE.SM=0 or +PSTATE.SM=1). For example, if an ``__arm_streaming_compatible`` function calls a +normal function, Clang generates code to temporarily switch out of streaming +mode before calling the function and switch back to streaming-mode on return if +``PSTATE.SM`` is ``1`` on entry of the caller. If ``PSTATE.SM`` is ``0`` on +entry to the ``__arm_streaming_compatible`` function, the call will be executed +without changing modes. + +``__arm_streaming_compatible`` can appear anywhere that a standard ``[[â¦]]`` +type attribute can appear. + +See `Arm C Language Extensions <https://github.com/ARM-software/acle>`_ +for more details about this extension, and for other related SME features. + }]; +} + +def ArmSmeSharedZADocs : Documentation { + let Category = DocCatType; + let Content = [{ +The ``__arm_shared_za`` keyword is only available on AArch64 targets. +It applies to function types and specifies that the function shares +ZA state with its caller. This means that: + +* the function requires the Scalable Matrix Extension (SME) + +* the function enters with ZA in an active state. + +* the function returns with ZA in an active state for normal returns. + +* the function does not have a K&R-style unprototyped function type. + +See `Procedure Call Standard for the Arm® 64-bit Architecture (AArch64) +<https://github.com/ARM-software/abi-aa>`_ for more details about shared-ZA +functions. + +``__arm_shared_za`` can appear anywhere that a standard ``[[â¦]]`` +type attribute can appear. + +See `Arm C Language Extensions <https://github.com/ARM-software/acle>`_ +for more details about this extension, and for other related SME features. + }]; +} + +def ArmSmePreservesZADocs : Documentation { + let Category = DocCatType; + let Content = [{ +The ``__arm_preserves_za`` keyword is only available on AArch64 targets. +It applies to function types and specifies that the function does not +modify ZA state. + +``__arm_preserves_za`` can appear anywhere that a standard ``[[â¦]]`` +type attribute can appear, however the keyword does not apply to functions +having a K&R-style unprototyped function type. + +See `Arm C Language Extensions <https://github.com/ARM-software/acle>`_ +for more details about this extension, and for other related SME features. + }]; +} + + +def ArmSmeLocallyStreamingDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``__arm_locally_streaming`` keyword is only available on AArch64 targets. +It applies to function declarations and specifies that all the statements in the +function are executed in streaming mode. This means that: + +* the function requires the Scalable Matrix Extension (SME) + +* the program automatically puts the machine into streaming mode before + executing the statements and automatically restores the previous mode + afterwards. + +Clang manages PSTATE.SM automatically; it is not the source code's +responsibility to do this. For example, Clang will emit code to enable +streaming mode at the start of the function, and disable streaming mode +at the end of the function. + +``__arm_locally_streaming`` can appear anywhere that a standard ``[[â¦]]`` +declaration attribute can appear. + +See `Arm C Language Extensions <https://github.com/ARM-software/acle>`_ +for more details about this extension, and for other related SME features. + }]; +} + +def ArmSmeNewZADocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``__arm_new_za`` keyword is only available on AArch64 targets. +It applies to function declarations and specifies that the function will be set +up with a fresh ZA context. + +This means that: + +* the function requires the Scalable Matrix Extension (SME) + +* the function will commit any lazy-saves if available. + +* the function will create a new ZA context and enable PSTATE.ZA. + +* the function will disable PSTATE.ZA (by setting it to 0) before returning. + +For ``__arm_new_za`` functions Clang will set up the ZA context automatically +on entry to the function, and disable it before returning. For example, if ZA is +in a dormant state Clang will generate the code to commit a lazy-save and set up +a new ZA state before executing user code. + +``__arm_new_za`` can appear anywhere that a standard ``[[â¦]]`` declaration +attribute can appear. + +See `Arm C Language Extensions <https://github.com/ARM-software/acle>`_ +for more details about this extension, and for other related SME features. + }]; +} + def AlwaysInlineDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -2443,6 +2443,39 @@ let Documentation = [ArmSmeStreamingDocs]; } +def ArmStreamingCompatible : TypeAttr, TargetSpecificAttr<TargetAArch64SME> { + let Spellings = [RegularKeyword<"__arm_streaming_compatible">]; + let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>; + let Documentation = [ArmSmeStreamingCompatibleDocs]; +} + +def ArmSharedZA : TypeAttr, TargetSpecificAttr<TargetAArch64SME> { + let Spellings = [RegularKeyword<"__arm_shared_za">]; + let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>; + let Documentation = [ArmSmeSharedZADocs]; +} + +def ArmPreservesZA : TypeAttr, TargetSpecificAttr<TargetAArch64SME> { + let Spellings = [RegularKeyword<"__arm_preserves_za">]; + let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>; + let Documentation = [ArmSmePreservesZADocs]; +} + +def ArmLocallyStreaming : InheritableAttr, TargetSpecificAttr<TargetAArch64SME> { + let Spellings = [RegularKeyword<"__arm_locally_streaming">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [ArmSmeLocallyStreamingDocs]; +} + +def ArmNewZA : InheritableAttr, TargetSpecificAttr<TargetAArch64SME> { + let Spellings = [RegularKeyword<"__arm_new_za">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [ArmSmeNewZADocs]; +} +def : MutualExclusions<[ArmNewZA, ArmSharedZA]>; +def : MutualExclusions<[ArmNewZA, ArmPreservesZA]>; + + def Pure : InheritableAttr { let Spellings = [GCC<"pure">]; let Documentation = [Undocumented]; 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 @@ -3946,6 +3946,19 @@ /// 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 << 0, + SME_PStateSMCompatibleMask = 1 << 1, + SME_PStateZASharedMask = 1 << 2, + SME_PStateZAPreservedMask = 1 << 3, + 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. @@ -3955,14 +3968,26 @@ /// [implimits] 8 bits would be enough here. unsigned NumExceptionType : 16; + /// Any AArch64 SME ACLE type attributes that need to be propagated + /// on declarations and function pointers. + unsigned AArch64SMEAttributes : 8; + public: - FunctionTypeExtraBitfields() : NumExceptionType(0) {} + FunctionTypeExtraBitfields() + : NumExceptionType(0), AArch64SMEAttributes(SME_NormalFunction) {} void setNumExceptionType(unsigned N) { assert(N < 65536 && "Not enough bits to encode exceptions"); NumExceptionType = N; } unsigned getNumExceptionType() const { return NumExceptionType; } + + void setAArch64SMEAttributes(unsigned Attrs) { + assert(Attrs <= SME_AttributeMask && + "Not enough bits to encode SME attributes"); + AArch64SMEAttributes = Attrs; + } + unsigned getAArch64SMEAttributes() const { return AArch64SMEAttributes; } }; protected: @@ -4143,16 +4168,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); @@ -4161,7 +4190,16 @@ } bool requiresFunctionProtoTypeExtraBitfields() const { - return ExceptionSpec.Type == EST_Dynamic; + return ExceptionSpec.Type == EST_Dynamic || + AArch64SMEAttributes != SME_NormalFunction; + } + + void setArmSMEAttribute(AArch64SMETypeAttributes Kind, bool Enable = true) { + if (Enable) + AArch64SMEAttributes |= Kind; + else + AArch64SMEAttributes = + (AArch64SMEAttributes ^ Kind) & AArch64SMEAttributes; } }; @@ -4288,6 +4326,7 @@ EPI.TypeQuals = getMethodQuals(); EPI.RefQualifier = getRefQualifier(); EPI.ExtParameterInfos = getExtParameterInfosOrNull(); + EPI.AArch64SMEAttributes = getAArch64SMEAttributes(); return EPI; } @@ -4469,6 +4508,15 @@ 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>() + ->getAArch64SMEAttributes(); + } + 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