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.
This commit is contained in:
Erich Keane 2020-03-09 10:15:45 -07:00
parent 209094eeb6
commit cc8390bfe3
3 changed files with 40 additions and 6 deletions

View File

@ -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:
if (!AttrCompatibleWithMultiVersion(A->getKind(), MVType))
return true;
break;
}
}
return false;

View File

@ -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()

View File

@ -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}}