From cc8390bfe30d08633d62a1b53e6669e386c95600 Mon Sep 17 00:00:00 2001 From: Erich Keane Date: Mon, 9 Mar 2020 10:15:45 -0700 Subject: [PATCH] Permit attribute 'used' with 'target' multiversioning. This adds infrastructure for a multiversioning whitelist, plus adds 'used' to the allowed list with 'target'. The behavior here mirrors the implementation in GCC, where 'used' only applies to the single declaration and doesn't apply to the ifunc or resolver. This is not being applied to cpu_dispatch and cpu_specific, since the rules are more complicated for cpu_specific, which emits multiple symbols. Additionally, the author isn't currently aware of uses in the wild of this combination, but is aware of a number of target+used combinations. --- clang/lib/Sema/SemaDecl.cpp | 16 +++++++++++++++- clang/test/CodeGen/attr-target-mv.c | 19 +++++++++++++++++++ clang/test/Sema/attr-target-mv.c | 11 ++++++----- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 66359f4faf6a..da40ac188e54 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -9952,6 +9952,18 @@ static bool CheckMultiVersionValue(Sema &S, const FunctionDecl *FD) { return false; } +// Provide a white-list of attributes that are allowed to be combined with +// multiversion functions. +static bool AttrCompatibleWithMultiVersion(attr::Kind Kind, + MultiVersionKind MVType) { + switch (Kind) { + default: + return false; + case attr::Used: + return MVType == MultiVersionKind::Target; + } +} + static bool HasNonMultiVersionAttributes(const FunctionDecl *FD, MultiVersionKind MVType) { for (const Attr *A : FD->attrs()) { @@ -9967,7 +9979,9 @@ static bool HasNonMultiVersionAttributes(const FunctionDecl *FD, return true; break; default: - return true; + if (!AttrCompatibleWithMultiVersion(A->getKind(), MVType)) + return true; + break; } } return false; diff --git a/clang/test/CodeGen/attr-target-mv.c b/clang/test/CodeGen/attr-target-mv.c index c089b06d238c..b273eb846e1d 100644 --- a/clang/test/CodeGen/attr-target-mv.c +++ b/clang/test/CodeGen/attr-target-mv.c @@ -50,6 +50,15 @@ void bar5() { int __attribute__((target("avx"))) changed_to_mv(void) { return 0;} int __attribute__((target("fma4"))) changed_to_mv(void) { return 1;} +__attribute__((target("default"), used)) inline void foo_used(int i, double d) {} +__attribute__((target("avx,sse4.2"))) inline void foo_used(int i, double d) {} + +__attribute__((target("default"))) inline void foo_used2(int i, double d) {} +__attribute__((target("avx,sse4.2"), used)) inline void foo_used2(int i, double d) {} + +// LINUX: @llvm.used = appending global [2 x i8*] [i8* bitcast (void (i32, double)* @foo_used to i8*), i8* bitcast (void (i32, double)* @foo_used2.avx_sse4.2 to i8*)], section "llvm.metadata" +// WINDOWS: @llvm.used = appending global [2 x i8*] [i8* bitcast (void (i32, double)* @foo_used to i8*), i8* bitcast (void (i32, double)* @foo_used2.avx_sse4.2 to i8*)], section "llvm.metadata" + // LINUX: @foo.ifunc = weak_odr ifunc i32 (), i32 ()* ()* @foo.resolver // LINUX: @foo_inline.ifunc = weak_odr ifunc i32 (), i32 ()* ()* @foo_inline.resolver // LINUX: @foo_decls.ifunc = weak_odr ifunc void (), void ()* ()* @foo_decls.resolver @@ -203,6 +212,16 @@ int __attribute__((target("fma4"))) changed_to_mv(void) { return 1;} // WINDOWS: define dso_local i32 @changed_to_mv.avx() // WINDOWS: define dso_local i32 @changed_to_mv.fma4() +// LINUX: define linkonce void @foo_used(i32 %{{.*}}, double %{{.*}}) +// LINUX-NOT: @foo_used.avx_sse4.2( +// LINUX-NOT: @foo_used2( +// LINUX: define linkonce void @foo_used2.avx_sse4.2(i32 %{{.*}}, double %{{.*}}) + +// WINDOWS: define linkonce_odr dso_local void @foo_used(i32 %{{.*}}, double %{{.*}}) +// WINDOWS-NOT: @foo_used.avx_sse4.2( +// WINDOWS-NOT: @foo_used2( +// WINDOWS: define linkonce_odr dso_local void @foo_used2.avx_sse4.2(i32 %{{.*}}, double %{{.*}}) + // LINUX: declare i32 @foo.arch_sandybridge() // WINDOWS: declare dso_local i32 @foo.arch_sandybridge() diff --git a/clang/test/Sema/attr-target-mv.c b/clang/test/Sema/attr-target-mv.c index 664ade1c0fa5..e9156a6c73e7 100644 --- a/clang/test/Sema/attr-target-mv.c +++ b/clang/test/Sema/attr-target-mv.c @@ -77,21 +77,22 @@ int prev_no_target2(void); int __attribute__((target("arch=ivybridge"))) prev_no_target2(void); void __attribute__((target("sse4.2"))) addtl_attrs(void); -//expected-error@+1 {{attribute 'target' multiversioning cannot be combined}} -void __attribute__((used,target("arch=sandybridge"))) addtl_attrs(void); +//expected-error@+2 {{attribute 'target' multiversioning cannot be combined}} +void __attribute__((no_caller_saved_registers,target("arch=sandybridge"))) +addtl_attrs(void); //expected-error@+1 {{attribute 'target' multiversioning cannot be combined}} -void __attribute__((target("default"), used)) addtl_attrs2(void); +void __attribute__((target("default"), no_caller_saved_registers)) addtl_attrs2(void); //expected-error@+2 {{attribute 'target' multiversioning cannot be combined}} //expected-note@+2 {{function multiversioning caused by this declaration}} -void __attribute__((used,target("sse4.2"))) addtl_attrs3(void); +void __attribute__((no_caller_saved_registers,target("sse4.2"))) addtl_attrs3(void); void __attribute__((target("arch=sandybridge"))) addtl_attrs3(void); void __attribute__((target("sse4.2"))) addtl_attrs4(void); void __attribute__((target("arch=sandybridge"))) addtl_attrs4(void); //expected-error@+1 {{attribute 'target' multiversioning cannot be combined}} -void __attribute__((used,target("arch=ivybridge"))) addtl_attrs4(void); +void __attribute__((no_caller_saved_registers,target("arch=ivybridge"))) addtl_attrs4(void); int __attribute__((target("sse4.2"))) diff_cc(void); // expected-error@+1 {{multiversioned function declaration has a different calling convention}}