Permit attribute 'used' with 'target' multiversioning.
authorErich Keane <erich.keane@intel.com>
Mon, 9 Mar 2020 17:15:45 +0000 (10:15 -0700)
committerErich Keane <erich.keane@intel.com>
Mon, 9 Mar 2020 19:38:03 +0000 (12:38 -0700)
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
clang/test/CodeGen/attr-target-mv.c
clang/test/Sema/attr-target-mv.c

index 66359f4..da40ac1 100644 (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:
-      return true;
+      if (!AttrCompatibleWithMultiVersion(A->getKind(), MVType))
+        return true;
+      break;
     }
   }
   return false;
index c089b06..b273eb8 100644 (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()
 
index 664ade1..e9156a6 100644 (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}}