erichkeane updated this revision to Diff 174950.
erichkeane added a subscriber: grooverdan.
erichkeane added a comment.

Fix @rsmith s comments, rebase on the big CPUDispatch refactor.


https://reviews.llvm.org/D51650

Files:
  include/clang/AST/Decl.h
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/AST/Decl.cpp
  lib/CodeGen/CodeGenModule.cpp
  lib/CodeGen/CodeGenModule.h
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaDeclAttr.cpp
  test/CodeGen/attr-target-clones.c
  test/Misc/pragma-attribute-supported-attributes-list.test
  test/Sema/attr-cpuspecific.c
  test/Sema/attr-target-clones.c

Index: test/Sema/attr-target-clones.c
===================================================================
--- /dev/null
+++ test/Sema/attr-target-clones.c
@@ -0,0 +1,87 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu  -fsyntax-only -verify %s
+
+// expected-error@+1 {{'target_clones' multiversioning requires a default target}}
+void __attribute__((target_clones("sse4.2", "arch=sandybridge")))
+no_default(void);
+
+// expected-error@+2 {{'target_clones' and 'target' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+void __attribute__((target("sse4.2"), target_clones("arch=sandybridge")))
+ignored_attr(void);
+// expected-error@+2 {{'target' and 'target_clones' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+void __attribute__((target_clones("arch=sandybridge,default"), target("sse4.2")))
+ignored_attr2(void);
+
+int redecl(void);
+int __attribute__((target_clones("sse4.2", "default"))) redecl(void) { return 1; }
+
+int __attribute__((target_clones("sse4.2", "default"))) redecl2(void);
+int __attribute__((target_clones("sse4.2", "default"))) redecl2(void) { return 1; }
+
+int __attribute__((target_clones("sse4.2", "default"))) redecl3(void);
+int redecl3(void);
+
+int __attribute__((target_clones("sse4.2", "arch=atom", "default"))) redecl4(void);
+// expected-error@+3 {{'target_clones' attribute does not match previous declaration}}
+// expected-note@-2 {{previous declaration is here}}
+int __attribute__((target_clones("sse4.2", "arch=sandybridge", "default")))
+redecl4(void) { return 1; }
+
+int __attribute__((target("sse4.2"))) redef2(void) { return 1; }
+// expected-error@+2 {{multiversioning attributes cannot be combined}}
+// expected-note@-2 {{previous declaration is here}}
+int __attribute__((target_clones("sse4.2", "default"))) redef2(void) { return 1; }
+
+int __attribute__((target_clones("sse4.2,default"))) redef3(void) { return 1; }
+// expected-error@+2 {{redefinition of 'redef3'}}
+// expected-note@-2 {{previous definition is here}}
+int __attribute__((target_clones("sse4.2,default"))) redef3(void) { return 1; }
+
+int __attribute__((target_clones("sse4.2,default"))) redef4(void) { return 1; }
+// expected-error@+2 {{redefinition of 'redef4'}}
+// expected-note@-2 {{previous definition is here}}
+int __attribute__((target_clones("sse4.2,default"))) redef4(void) { return 1; }
+
+// Duplicates are allowed, however they alter name mangling.
+// expected-warning@+2 {{mixing 'target_clones' specifier mechanisms is permitted}}
+// expected-warning@+1 2 {{version list contains duplicate entries}}
+int __attribute__((target_clones("arch=atom,arch=atom", "arch=atom,default")))
+dupes(void) { return 1; }
+
+// expected-warning@+1 {{unsupported '' in the 'target_clones' attribute string;}}
+void __attribute__((target_clones("")))
+empty_target_1(void);
+// expected-warning@+1 {{unsupported '' in the 'target_clones' attribute string;}}
+void __attribute__((target_clones(",default")))
+empty_target_2(void);
+// expected-warning@+1 {{unsupported '' in the 'target_clones' attribute string;}}
+void __attribute__((target_clones("default,")))
+empty_target_3(void);
+// expected-warning@+1 {{unsupported '' in the 'target_clones' attribute string;}}
+void __attribute__((target_clones("default, ,avx2")))
+empty_target_4(void);
+
+// expected-warning@+1 {{unsupported '' in the 'target_clones' attribute string;}}
+void __attribute__((target_clones("default,avx2", "")))
+empty_target_5(void);
+
+// expected-warning@+1 {{version list contains duplicate entries}}
+void __attribute__((target_clones("default", "default")))
+dupe_default(void);
+
+// expected-warning@+1 {{version list contains duplicate entries}}
+void __attribute__((target_clones("avx2,avx2,default")))
+dupe_normal(void);
+
+// expected-error@+2 {{'target_clones' and 'target_clones' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
+void __attribute__((target_clones("avx2,default"), target_clones("arch=atom,default")))
+dupe_normal2(void);
+
+int mv_after_use(void);
+int useage() {
+  return mv_after_use();
+}
+// expected-error@+1 {{function declaration cannot become a multiversioned function after first usage}}
+int __attribute__((target_clones("sse4.2", "default"))) mv_after_use(void) { return 1; }
Index: test/Sema/attr-cpuspecific.c
===================================================================
--- test/Sema/attr-cpuspecific.c
+++ test/Sema/attr-cpuspecific.c
@@ -82,7 +82,8 @@
 // expected-note@-2 {{previous declaration is here}}
 void __attribute__((cpu_specific(sandybridge))) addtl_attrs2(void);
 
-// expected-error@+2 {{multiversioning attributes cannot be combined}}
+// expected-error@+2 {{'cpu_dispatch' and 'cpu_specific' attributes are not compatible}}
+// expected-note@+1 {{conflicting attribute is here}}
 void __attribute((cpu_specific(sandybridge), cpu_dispatch(atom, sandybridge)))
 combine_attrs(void);
 
Index: test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- test/Misc/pragma-attribute-supported-attributes-list.test
+++ test/Misc/pragma-attribute-supported-attributes-list.test
@@ -2,7 +2,7 @@
 
 // The number of supported attributes should never go down!
 
-// CHECK: #pragma clang attribute supports 129 attributes:
+// CHECK: #pragma clang attribute supports 130 attributes:
 // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function)
 // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function)
 // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function)
@@ -121,6 +121,7 @@
 // CHECK-NEXT: SwiftIndirectResult (SubjectMatchRule_variable_is_parameter)
 // CHECK-NEXT: TLSModel (SubjectMatchRule_variable_is_thread_local)
 // CHECK-NEXT: Target (SubjectMatchRule_function)
+// CHECK-NEXT: TargetClones (SubjectMatchRule_function)
 // CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member)
 // CHECK-NEXT: TrivialABI (SubjectMatchRule_record)
 // CHECK-NEXT: VecReturn (SubjectMatchRule_record)
Index: test/CodeGen/attr-target-clones.c
===================================================================
--- /dev/null
+++ test/CodeGen/attr-target-clones.c
@@ -0,0 +1,93 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefixes=LINUX,CHECK
+// RUN: %clang_cc1 -triple x86_64-windows-pc -emit-llvm %s -o - | FileCheck %s --check-prefixes=WINDOWS,CHECK
+
+// LINUX: @foo.ifunc = ifunc i32 (), i32 ()* ()* @foo.resolver
+// LINUX: @foo_inline.ifunc = ifunc i32 (), i32 ()* ()* @foo_inline.resolver
+// LINUX: @foo_decls.ifunc = ifunc void (), void ()* ()* @foo_decls.resolver
+// LINUX: @unused.ifunc = ifunc void (), void ()* ()* @unused.resolver
+
+int __attribute__((target_clones("sse4.2, default"))) foo(void) { return 0; }
+// LINUX: define i32 @foo.sse4.2.0()
+// LINUX: define i32 @foo.default.1()
+// LINUX: define i32 ()* @foo.resolver()
+// LINUX: ret i32 ()* @foo.sse4.2.0
+// LINUX: ret i32 ()* @foo.default.1
+
+// WINDOWS: define dso_local i32 @foo.sse4.2.0()
+// WINDOWS: define dso_local i32 @foo.default.1()
+// WINDOWS: define dso_local i32 @foo()
+// WINDOWS: musttail call i32 @foo.sse4.2.0
+// WINDOWS: musttail call i32 @foo.default.1
+
+int bar() {
+  // LINUX: define i32 @bar()
+  // WINDOWS: define dso_local i32 @bar()
+  return foo();
+  // LINUX: call i32 @foo.ifunc()
+  // WINDOWS: call i32 @foo()
+}
+
+inline int __attribute__((target_clones("arch=sandybridge,default,sse4.2")))
+foo_inline(void) { return 0; }
+// LINUX: define linkonce i32 @foo_inline.arch_sandybridge.0() #[[SB:[0-9]+]]
+// LINUX: define linkonce i32 @foo_inline.default.2() #[[DEF:[0-9]+]]
+// LINUX: define linkonce i32 @foo_inline.sse4.2.1() #[[SSE42:[0-9]+]]
+// LINUX: define i32 ()* @foo_inline.resolver()
+// LINUX: ret i32 ()* @foo_inline.arch_sandybridge.0
+// LINUX: ret i32 ()* @foo_inline.sse4.2.1
+// LINUX: ret i32 ()* @foo_inline.default.2
+
+// WINDOWS: define linkonce_odr dso_local i32 @foo_inline.arch_sandybridge.0() #[[SB:[0-9]+]]
+// WINDOWS: define linkonce_odr dso_local i32 @foo_inline.default.2() #[[DEF:[0-9]+]]
+// WINDOWS: define linkonce_odr dso_local i32 @foo_inline.sse4.2.1() #[[SSE42:[0-9]+]]
+// WINDOWS: define dso_local i32 @foo_inline()
+// WINDOWS: musttail call i32 @foo_inline.arch_sandybridge.0
+// WINDOWS: musttail call i32 @foo_inline.sse4.2.1
+// WINDOWS: musttail call i32 @foo_inline.default.2
+
+int bar2() {
+  // LINUX: define i32 @bar2()
+  // WINDOWS: define dso_local i32 @bar2()
+  return foo_inline();
+  // LINUX: call i32 @foo_inline.ifunc()
+  // WINDOWS: call i32 @foo_inline()
+}
+
+inline __attribute__((target_clones("default,default ,sse4.2"))) void foo_decls(void) {}
+// LINUX: define linkonce void @foo_decls.default.1()
+// LINUX: define linkonce void @foo_decls.sse4.2.0()
+// LINUX: define void ()* @foo_decls.resolver()
+// LINUX: ret void ()* @foo_decls.sse4.2.0
+// LINUX: ret void ()* @foo_decls.default.1
+
+// WINDOWS: define linkonce_odr dso_local void @foo_decls.default.1()
+// WINDOWS: define linkonce_odr dso_local void @foo_decls.sse4.2.0()
+// WINDOWS: define dso_local void @foo_decls()
+// WINDOWS: musttail call void @foo_decls.sse4.2.0
+// WINDOWS: musttail call void @foo_decls.default.1
+
+void bar3() {
+  // LINUX: define void @bar3()
+  // WINDOWS: define dso_local void @bar3()
+  foo_decls();
+  // LINUX: call void @foo_decls.ifunc()
+  // WINDOWS: call void @foo_decls()
+}
+
+void __attribute__((target_clones("default, arch=ivybridge"))) unused(void) {}
+// LINUX: define void @unused.default.1()
+// LINUX: define void @unused.arch_ivybridge.0()
+// LINUX: define void ()* @unused.resolver()
+// LINUX: ret void ()* @unused.arch_ivybridge.0
+// LINUX: ret void ()* @unused.default.1
+
+// WINDOWS: define dso_local void @unused.default.1()
+// WINDOWS: define dso_local void @unused.arch_ivybridge.0()
+// WINDOWS: define dso_local void @unused()
+// WINDOWS: musttail call void @unused.arch_ivybridge.0
+// WINDOWS: musttail call void @unused.default.1
+
+// CHECK: attributes #[[SSE42]] =
+// CHECK-SAME: "target-features"="+mmx,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87"
+// CHECK: attributes #[[DEF]] =
+// CHECK-SAME: "target-features"="+mmx,+sse,+sse2,+x87"
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -1889,6 +1889,11 @@
 }
 
 static void handleCPUSpecificAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  if (checkAttrMutualExclusion<TargetAttr>(S, D, AL) ||
+      checkAttrMutualExclusion<TargetClonesAttr>(S, D, AL) ||
+      checkAttrMutualExclusion<CPUDispatchAttr>(S, D, AL) ||
+      checkAttrMutualExclusion<CPUSpecificAttr>(S, D, AL))
+    return;
   FunctionDecl *FD = cast<FunctionDecl>(D);
 
   if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
@@ -3028,33 +3033,40 @@
 bool Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) {
   enum FirstParam { Unsupported, Duplicate };
   enum SecondParam { None, Architecture };
+  enum ThirdParam { Target, TargetClones };
   for (auto Str : {"tune=", "fpmath="})
     if (AttrStr.find(Str) != StringRef::npos)
       return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
-             << Unsupported << None << Str;
+             << Unsupported << None << Str << Target;
 
   TargetAttr::ParsedTargetAttr ParsedAttrs = TargetAttr::parse(AttrStr);
 
   if (!ParsedAttrs.Architecture.empty() &&
       !Context.getTargetInfo().isValidCPUName(ParsedAttrs.Architecture))
     return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
-           << Unsupported << Architecture << ParsedAttrs.Architecture;
+           << Unsupported << Architecture << ParsedAttrs.Architecture << Target;
 
   if (ParsedAttrs.DuplicateArchitecture)
     return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
-           << Duplicate << None << "arch=";
+           << Duplicate << None << "arch=" << Target;
 
   for (const auto &Feature : ParsedAttrs.Features) {
     auto CurFeature = StringRef(Feature).drop_front(); // remove + or -.
     if (!Context.getTargetInfo().isValidFeatureName(CurFeature))
       return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
-             << Unsupported << None << CurFeature;
+             << Unsupported << None << CurFeature << Target;
   }
 
   return false;
 }
 
 static void handleTargetAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  if (checkAttrMutualExclusion<TargetAttr>(S, D, AL) ||
+      checkAttrMutualExclusion<TargetClonesAttr>(S, D, AL) ||
+      checkAttrMutualExclusion<CPUDispatchAttr>(S, D, AL) ||
+      checkAttrMutualExclusion<CPUSpecificAttr>(S, D, AL))
+    return;
+
   StringRef Str;
   SourceLocation LiteralLoc;
   if (!S.checkStringLiteralArgumentAttr(AL, 0, Str, &LiteralLoc) ||
@@ -3086,6 +3098,98 @@
                                 AL.getAttributeSpellingListIndex()));
 }
 
+bool Sema::checkTargetClonesAttrString(SourceLocation LiteralLoc, StringRef Str,
+                                       const ArgsUnion Arg, bool &HasDefault,
+                                       bool &HasCommas,
+                                       SmallVectorImpl<StringRef> &Strings) {
+  enum FirstParam { Unsupported, Duplicate };
+  enum SecondParam { None, Architecture };
+  enum ThirdParam { Target, TargetClones };
+  const StringLiteral *Literal =
+      cast<StringLiteral>(Arg.get<Expr *>()->IgnoreParenCasts());
+  HasCommas = Str.contains(',');
+
+  // Warn on empty at the beginning of a string.
+  if (Str.size() == 0)
+    return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
+           << Unsupported << None << "" << TargetClones;
+
+  std::pair<StringRef, StringRef> Parts = {{}, Str};
+  while (!Parts.second.empty()) {
+    Parts = Parts.second.split(',');
+    StringRef Cur = Parts.first.trim();
+    SourceLocation CurLoc = Literal->getLocationOfByte(
+        Cur.data() - Literal->getString().data(), getSourceManager(),
+        getLangOpts(), Context.getTargetInfo());
+
+    bool DefaultIsDupe = false;
+
+    if (Cur.empty())
+      return Diag(CurLoc, diag::warn_unsupported_target_attribute)
+             << Unsupported << None << "" << TargetClones;
+
+    if (Cur.startswith("arch=")) {
+      if (!Context.getTargetInfo().isValidCPUName(
+              Cur.drop_front(sizeof("arch=") - 1)))
+        return Diag(CurLoc, diag::warn_unsupported_target_attribute)
+               << Unsupported << Architecture
+               << Cur.drop_front(sizeof("arch=") - 1) << TargetClones;
+    } else if (Cur == "default") {
+      DefaultIsDupe = HasDefault;
+      HasDefault = true;
+    } else if (!Context.getTargetInfo().isValidFeatureName(Cur))
+      return Diag(CurLoc, diag::warn_unsupported_target_attribute)
+             << Unsupported << None << Cur << TargetClones;
+
+    if (llvm::find(Strings, Cur) != Strings.end() || DefaultIsDupe)
+      Diag(CurLoc, diag::warn_target_clone_duplicate_options);
+    // Note: Add even if there are duplicates, since it changes name mangling.
+    Strings.push_back(Cur);
+  }
+
+  if (Str.rtrim().endswith(","))
+    return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
+           << Unsupported << None << "" << TargetClones;
+
+  return false;
+}
+
+static void handleTargetClonesAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  if (checkAttrMutualExclusion<TargetAttr>(S, D, AL) ||
+      checkAttrMutualExclusion<TargetClonesAttr>(S, D, AL) ||
+      checkAttrMutualExclusion<CPUDispatchAttr>(S, D, AL) ||
+      checkAttrMutualExclusion<CPUSpecificAttr>(S, D, AL))
+    return;
+
+  SmallVector<StringRef, 2> Strings;
+  bool HasDefault = false;
+  bool HasCommas = false;
+
+  for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) {
+    StringRef CurStr;
+    SourceLocation LiteralLoc;
+    if (!S.checkStringLiteralArgumentAttr(AL, I, CurStr, &LiteralLoc) ||
+        S.checkTargetClonesAttrString(LiteralLoc, CurStr, AL.getArg(I),
+                                      HasDefault, HasCommas, Strings))
+      return;
+  }
+
+  if (HasCommas && AL.getNumArgs() > 1)
+    S.Diag(AL.getLoc(), diag::warn_target_clone_mixed_values);
+
+  if (!HasDefault) {
+    S.Diag(AL.getLoc(), diag::err_target_clone_must_have_default);
+    return;
+  }
+
+  FunctionDecl *FD = cast<FunctionDecl>(D);
+  FD->setIsMultiVersion(true);
+  TargetClonesAttr *NewAttr = ::new (S.Context)
+      TargetClonesAttr(AL.getRange(), S.Context, Strings.data(), Strings.size(),
+                       AL.getAttributeSpellingListIndex());
+  D->addAttr(NewAttr);
+}
+
 static void handleCleanupAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   Expr *E = AL.getArgAsExpr(0);
   SourceLocation Loc = E->getExprLoc();
@@ -6371,6 +6475,9 @@
   case ParsedAttr::AT_Target:
     handleTargetAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_TargetClones:
+    handleTargetClonesAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_MinVectorWidth:
     handleMinVectorWidthAttr(S, D, AL);
     break;
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -9366,20 +9366,6 @@
                                         PrevVD->getType());
 }
 
-namespace MultiVersioning {
-enum Type { None, Target, CPUSpecific, CPUDispatch};
-} // MultiVersionType
-
-static MultiVersioning::Type
-getMultiVersionType(const FunctionDecl *FD) {
-  if (FD->hasAttr<TargetAttr>())
-    return MultiVersioning::Target;
-  if (FD->hasAttr<CPUDispatchAttr>())
-    return MultiVersioning::CPUDispatch;
-  if (FD->hasAttr<CPUSpecificAttr>())
-    return MultiVersioning::CPUSpecific;
-  return MultiVersioning::None;
-}
 /// Check the target attribute of the function for MultiVersion
 /// validity.
 ///
@@ -9419,7 +9405,7 @@
 static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD,
                                              const FunctionDecl *NewFD,
                                              bool CausesMV,
-                                             MultiVersioning::Type MVType) {
+                                             MultiVersionType MVType) {
   enum DoesntSupport {
     FuncTemplates = 0,
     VirtFuncs = 1,
@@ -9439,9 +9425,12 @@
     Linkage = 5
   };
 
-  bool IsCPUSpecificCPUDispatchMVType =
-      MVType == MultiVersioning::CPUDispatch ||
-      MVType == MultiVersioning::CPUSpecific;
+  unsigned MVTypeForDiag = static_cast<unsigned>(MVType) - 1;
+
+  if (MVType == MultiVersionType::None) {
+    assert(OldFD->getMultiVersionType() == MultiVersionType::TargetClones);
+    MVTypeForDiag = static_cast<unsigned>(MultiVersionType::TargetClones) - 1;
+  }
 
   if (OldFD && !OldFD->getType()->getAs<FunctionProtoType>()) {
     S.Diag(OldFD->getLocation(), diag::err_multiversion_noproto);
@@ -9462,58 +9451,60 @@
   // For now, disallow all other attributes.  These should be opt-in, but
   // an analysis of all of them is a future FIXME.
   if (CausesMV && OldFD &&
-      std::distance(OldFD->attr_begin(), OldFD->attr_end()) != 1) {
+      std::distance(OldFD->attr_begin(), OldFD->attr_end()) !=
+          (MVType == MultiVersionType::None ? 0 : 1)) {
     S.Diag(OldFD->getLocation(), diag::err_multiversion_no_other_attrs)
-        << IsCPUSpecificCPUDispatchMVType;
+        << MVTypeForDiag;
     S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here);
     return true;
   }
 
-  if (std::distance(NewFD->attr_begin(), NewFD->attr_end()) != 1)
+  if (std::distance(NewFD->attr_begin(), NewFD->attr_end()) !=
+      (MVType == MultiVersionType::None ? 0 : 1))
     return S.Diag(NewFD->getLocation(), diag::err_multiversion_no_other_attrs)
-           << IsCPUSpecificCPUDispatchMVType;
+           << MVTypeForDiag;
 
   if (NewFD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
     return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support)
-           << IsCPUSpecificCPUDispatchMVType << FuncTemplates;
+           << MVTypeForDiag << FuncTemplates;
 
   if (const auto *NewCXXFD = dyn_cast<CXXMethodDecl>(NewFD)) {
     if (NewCXXFD->isVirtual())
       return S.Diag(NewCXXFD->getLocation(),
                     diag::err_multiversion_doesnt_support)
-             << IsCPUSpecificCPUDispatchMVType << VirtFuncs;
+             << MVTypeForDiag << VirtFuncs;
 
     if (const auto *NewCXXCtor = dyn_cast<CXXConstructorDecl>(NewFD))
       return S.Diag(NewCXXCtor->getLocation(),
                     diag::err_multiversion_doesnt_support)
-             << IsCPUSpecificCPUDispatchMVType << Constructors;
+             << MVTypeForDiag << Constructors;
 
     if (const auto *NewCXXDtor = dyn_cast<CXXDestructorDecl>(NewFD))
       return S.Diag(NewCXXDtor->getLocation(),
                     diag::err_multiversion_doesnt_support)
-             << IsCPUSpecificCPUDispatchMVType << Destructors;
+             << MVTypeForDiag << Destructors;
   }
 
   if (NewFD->isDeleted())
     return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support)
-           << IsCPUSpecificCPUDispatchMVType << DeletedFuncs;
+           << MVTypeForDiag << DeletedFuncs;
 
   if (NewFD->isDefaulted())
     return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support)
-           << IsCPUSpecificCPUDispatchMVType << DefaultedFuncs;
+           << MVTypeForDiag << DefaultedFuncs;
 
-  if (NewFD->isConstexpr() && (MVType == MultiVersioning::CPUDispatch ||
-                               MVType == MultiVersioning::CPUSpecific))
+  if (NewFD->isConstexpr() && (MVType == MultiVersionType::CPUDispatch ||
+                               MVType == MultiVersionType::CPUSpecific))
     return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support)
-           << IsCPUSpecificCPUDispatchMVType << ConstexprFuncs;
+           << MVTypeForDiag << ConstexprFuncs;
 
   QualType NewQType = S.getASTContext().getCanonicalType(NewFD->getType());
   const auto *NewType = cast<FunctionType>(NewQType);
   QualType NewReturnType = NewType->getReturnType();
 
   if (NewReturnType->isUndeducedType())
     return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support)
-           << IsCPUSpecificCPUDispatchMVType << DeducedReturn;
+           << MVTypeForDiag << DeducedReturn;
 
   // Only allow transition to MultiVersion if it hasn't been used.
   if (OldFD && CausesMV && OldFD->isUsed(false))
@@ -9567,19 +9558,19 @@
 ///
 /// Returns true if there was an error, false otherwise.
 static bool CheckMultiVersionFirstFunction(Sema &S, FunctionDecl *FD,
-                                           MultiVersioning::Type MVType,
+                                           MultiVersionType MVType,
                                            const TargetAttr *TA,
                                            const CPUDispatchAttr *CPUDisp,
                                            const CPUSpecificAttr *CPUSpec) {
-  assert(MVType != MultiVersioning::None &&
+  assert(MVType != MultiVersionType::None &&
          "Function lacks multiversion attribute");
 
   // Target only causes MV if it is default, otherwise this is a normal
   // function.
-  if (MVType == MultiVersioning::Target && !TA->isDefaultVersion())
+  if (MVType == MultiVersionType::Target && !TA->isDefaultVersion())
     return false;
 
-  if (MVType == MultiVersioning::Target && CheckMultiVersionValue(S, FD)) {
+  if (MVType == MultiVersionType::Target && CheckMultiVersionValue(S, FD)) {
     FD->setInvalidDecl();
     return true;
   }
@@ -9616,7 +9607,7 @@
   }
 
   if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, true,
-                                       MultiVersioning::Target)) {
+                                       MultiVersionType::Target)) {
     NewFD->setInvalidDecl();
     return true;
   }
@@ -9662,21 +9653,30 @@
   return false;
 }
 
+static bool MultiVersionTypesCompatible(MultiVersionType Old,
+                                        MultiVersionType New) {
+  if (Old == New || Old == MultiVersionType::None ||
+      New == MultiVersionType::None)
+    return true;
+
+  return (Old == MultiVersionType::CPUDispatch &&
+          New == MultiVersionType::CPUSpecific) ||
+         (Old == MultiVersionType::CPUSpecific &&
+          New == MultiVersionType::CPUDispatch);
+}
+
 /// Check the validity of a new function declaration being added to an existing
 /// multiversioned declaration collection.
 static bool CheckMultiVersionAdditionalDecl(
     Sema &S, FunctionDecl *OldFD, FunctionDecl *NewFD,
-    MultiVersioning::Type NewMVType, const TargetAttr *NewTA,
+    MultiVersionType NewMVType, const TargetAttr *NewTA,
     const CPUDispatchAttr *NewCPUDisp, const CPUSpecificAttr *NewCPUSpec,
-    bool &Redeclaration, NamedDecl *&OldDecl, bool &MergeTypeWithPrevious,
-    LookupResult &Previous) {
+    const TargetClonesAttr *NewClones, bool &Redeclaration, NamedDecl *&OldDecl,
+    bool &MergeTypeWithPrevious, LookupResult &Previous) {
 
-  MultiVersioning::Type OldMVType = getMultiVersionType(OldFD);
+  MultiVersionType OldMVType = OldFD->getMultiVersionType();
   // Disallow mixing of multiversioning types.
-  if ((OldMVType == MultiVersioning::Target &&
-       NewMVType != MultiVersioning::Target) ||
-      (NewMVType == MultiVersioning::Target &&
-       OldMVType != MultiVersioning::Target)) {
+  if (!MultiVersionTypesCompatible(OldMVType, NewMVType)) {
     S.Diag(NewFD->getLocation(), diag::err_multiversion_types_mixed);
     S.Diag(OldFD->getLocation(), diag::note_previous_declaration);
     NewFD->setInvalidDecl();
@@ -9701,7 +9701,12 @@
     if (S.IsOverload(NewFD, CurFD, UseMemberUsingDeclRules))
       continue;
 
-    if (NewMVType == MultiVersioning::Target) {
+    switch (NewMVType) {
+    case MultiVersionType::None:
+      assert(OldMVType == MultiVersionType::TargetClones &&
+             "Only target_clones can be omitted in subsequent declarations");
+      break;
+    case MultiVersionType::Target: {
       const auto *CurTA = CurFD->getAttr<TargetAttr>();
       if (CurTA->getFeaturesStr() == NewTA->getFeaturesStr()) {
         NewFD->setIsMultiVersion();
@@ -9718,13 +9723,36 @@
         NewFD->setInvalidDecl();
         return true;
       }
-    } else {
+      break;
+    }
+    case MultiVersionType::TargetClones: {
+      const auto *CurClones = CurFD->getAttr<TargetClonesAttr>();
+      Redeclaration = true;
+      OldDecl = CurFD;
+      MergeTypeWithPrevious = true;
+      NewFD->setIsMultiVersion();
+
+      if (CurClones && NewClones &&
+          (CurClones->featuresStrs_size() != NewClones->featuresStrs_size() ||
+           !std::equal(CurClones->featuresStrs_begin(),
+                       CurClones->featuresStrs_end(),
+                       NewClones->featuresStrs_begin()))) {
+        S.Diag(NewFD->getLocation(), diag::err_target_clone_doesnt_match);
+        S.Diag(CurFD->getLocation(), diag::note_previous_declaration);
+        NewFD->setInvalidDecl();
+        return true;
+      }
+
+      return false;
+    }
+    case MultiVersionType::CPUSpecific:
+    case MultiVersionType::CPUDispatch: {
       const auto *CurCPUSpec = CurFD->getAttr<CPUSpecificAttr>();
       const auto *CurCPUDisp = CurFD->getAttr<CPUDispatchAttr>();
       // Handle CPUDispatch/CPUSpecific versions.
       // Only 1 CPUDispatch function is allowed, this will make it go through
       // the redeclaration errors.
-      if (NewMVType == MultiVersioning::CPUDispatch &&
+      if (NewMVType == MultiVersionType::CPUDispatch &&
           CurFD->hasAttr<CPUDispatchAttr>()) {
         if (CurCPUDisp->cpus_size() == NewCPUDisp->cpus_size() &&
             std::equal(
@@ -9745,7 +9773,7 @@
         NewFD->setInvalidDecl();
         return true;
       }
-      if (NewMVType == MultiVersioning::CPUSpecific && CurCPUSpec) {
+      if (NewMVType == MultiVersionType::CPUSpecific && CurCPUSpec) {
 
         if (CurCPUSpec->cpus_size() == NewCPUSpec->cpus_size() &&
             std::equal(
@@ -9773,15 +9801,15 @@
           }
         }
       }
-      // If the two decls aren't the same MVType, there is no possible error
-      // condition.
+      break;
+    }
     }
   }
 
   // Else, this is simply a non-redecl case.  Checking the 'value' is only
   // necessary in the Target case, since The CPUSpecific/Dispatch cases are
   // handled in the attribute adding step.
-  if (NewMVType == MultiVersioning::Target &&
+  if (NewMVType == MultiVersionType::Target &&
       CheckMultiVersionValue(S, NewFD)) {
     NewFD->setInvalidDecl();
     return true;
@@ -9800,7 +9828,6 @@
   return false;
 }
 
-
 /// Check the validity of a mulitversion function declaration.
 /// Also sets the multiversion'ness' of the function itself.
 ///
@@ -9814,23 +9841,15 @@
   const auto *NewTA = NewFD->getAttr<TargetAttr>();
   const auto *NewCPUDisp = NewFD->getAttr<CPUDispatchAttr>();
   const auto *NewCPUSpec = NewFD->getAttr<CPUSpecificAttr>();
+  const auto *NewTargetClones = NewFD->getAttr<TargetClonesAttr>();
 
-  // Mixing Multiversioning types is prohibited.
-  if ((NewTA && NewCPUDisp) || (NewTA && NewCPUSpec) ||
-      (NewCPUDisp && NewCPUSpec)) {
-    S.Diag(NewFD->getLocation(), diag::err_multiversion_types_mixed);
-    NewFD->setInvalidDecl();
-    return true;
-  }
-
-  MultiVersioning::Type MVType = getMultiVersionType(NewFD);
+  MultiVersionType MVType = NewFD->getMultiVersionType();
 
   // Main isn't allowed to become a multiversion function, however it IS
   // permitted to have 'main' be marked with the 'target' optimization hint.
   if (NewFD->isMain()) {
-    if ((MVType == MultiVersioning::Target && NewTA->isDefaultVersion()) ||
-        MVType == MultiVersioning::CPUDispatch ||
-        MVType == MultiVersioning::CPUSpecific) {
+    if (MVType != MultiVersionType::None &&
+        !(MVType == MultiVersionType::Target && !NewTA->isDefaultVersion())) {
       S.Diag(NewFD->getLocation(), diag::err_multiversion_not_allowed_on_main);
       NewFD->setInvalidDecl();
       return true;
@@ -9843,44 +9862,58 @@
           NewFD->getDeclContext()->getRedeclContext()) {
     // If there's no previous declaration, AND this isn't attempting to cause
     // multiversioning, this isn't an error condition.
-    if (MVType == MultiVersioning::None)
+    if (MVType == MultiVersionType::None)
       return false;
     return CheckMultiVersionFirstFunction(S, NewFD, MVType, NewTA, NewCPUDisp,
                                           NewCPUSpec);
   }
 
   FunctionDecl *OldFD = OldDecl->getAsFunction();
 
-  if (!OldFD->isMultiVersion() && MVType == MultiVersioning::None)
+  if (!OldFD->isMultiVersion() && MVType == MultiVersionType::None)
     return false;
 
-  if (OldFD->isMultiVersion() && MVType == MultiVersioning::None) {
+  // Multiversioned redeclarations aren't allowed to omit the attribute, except
+  // for target_clones.
+  if (OldFD->isMultiVersion() && MVType == MultiVersionType::None &&
+      OldFD->getMultiVersionType() != MultiVersionType::TargetClones) {
     S.Diag(NewFD->getLocation(), diag::err_multiversion_required_in_redecl)
-        << (getMultiVersionType(OldFD) != MultiVersioning::Target);
+        << (OldFD->getMultiVersionType() != MultiVersionType::Target);
     NewFD->setInvalidDecl();
     return true;
   }
 
-  // Handle the target potentially causes multiversioning case.
-  if (!OldFD->isMultiVersion() && MVType == MultiVersioning::Target)
-    return CheckTargetCausesMultiVersioning(S, OldFD, NewFD, NewTA,
-                                            Redeclaration, OldDecl,
-                                            MergeTypeWithPrevious, Previous);
-  // Previous declarations lack CPUDispatch/CPUSpecific.
   if (!OldFD->isMultiVersion()) {
-    S.Diag(OldFD->getLocation(), diag::err_multiversion_required_in_redecl)
-        << 1;
-    S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here);
-    NewFD->setInvalidDecl();
-    return true;
+    switch (MVType) {
+    case MultiVersionType::Target:
+      return CheckTargetCausesMultiVersioning(S, OldFD, NewFD, NewTA,
+                                              Redeclaration, OldDecl,
+                                              MergeTypeWithPrevious, Previous);
+    case MultiVersionType::TargetClones:
+      if (OldFD->isUsed(false)) {
+        NewFD->setInvalidDecl();
+        return S.Diag(NewFD->getLocation(), diag::err_multiversion_after_used);
+      }
+      OldFD->setIsMultiVersion();
+      break;
+    case MultiVersionType::CPUDispatch:
+    case MultiVersionType::CPUSpecific:
+      NewFD->setInvalidDecl();
+      S.Diag(OldFD->getLocation(), diag::err_multiversion_required_in_redecl)
+          << 1;
+      return S.Diag(NewFD->getLocation(),
+                    diag::note_multiversioning_caused_here);
+    case MultiVersionType::None:
+      break;
+    }
   }
 
   // At this point, we have a multiversion function decl (in OldFD) AND an
   // appropriate attribute in the current function decl.  Resolve that these are
   // still compatible with previous declarations.
   return CheckMultiVersionAdditionalDecl(
-      S, OldFD, NewFD, MVType, NewTA, NewCPUDisp, NewCPUSpec, Redeclaration,
-      OldDecl, MergeTypeWithPrevious, Previous);
+      S, OldFD, NewFD, MVType, NewTA, NewCPUDisp, NewCPUSpec, NewTargetClones,
+      Redeclaration, OldDecl, MergeTypeWithPrevious, Previous);
 }
 
 /// Perform semantic checking of a new function declaration.
Index: lib/CodeGen/CodeGenModule.h
===================================================================
--- lib/CodeGen/CodeGenModule.h
+++ lib/CodeGen/CodeGenModule.h
@@ -1319,6 +1319,7 @@
   void EmitAliasDefinition(GlobalDecl GD);
   void emitIFuncDefinition(GlobalDecl GD);
   void emitCPUDispatchDefinition(GlobalDecl GD);
+  void EmitTargetClonesResolver(GlobalDecl GD);
   void EmitObjCPropertyImplementations(const ObjCImplementationDecl *D);
   void EmitObjCIvarInitializations(ObjCImplementationDecl *D);
 
Index: lib/CodeGen/CodeGenModule.cpp
===================================================================
--- lib/CodeGen/CodeGenModule.cpp
+++ lib/CodeGen/CodeGenModule.cpp
@@ -936,6 +936,20 @@
   }
 }
 
+static void AppendTargetClonesMangling(const CodeGenModule &CGM,
+                                       const TargetClonesAttr *Attr,
+                                       unsigned VersionIndex,
+                                       raw_ostream &Out) {
+  Out << '.';
+  StringRef FeatureStr = Attr->getFeatureStr(VersionIndex);
+  if (FeatureStr.startswith("arch="))
+    Out << "arch_" << FeatureStr.substr(sizeof("arch=") - 1);
+  else
+    Out << FeatureStr;
+
+  Out << '.' << Attr->getMangledIndex(VersionIndex);
+}
+
 static std::string getMangledNameImpl(const CodeGenModule &CGM, GlobalDecl GD,
                                       const NamedDecl *ND,
                                       bool OmitMultiVersionMangling = false) {
@@ -966,12 +980,23 @@
 
   if (const auto *FD = dyn_cast<FunctionDecl>(ND))
     if (FD->isMultiVersion() && !OmitMultiVersionMangling) {
-      if (FD->isCPUDispatchMultiVersion() || FD->isCPUSpecificMultiVersion())
+      switch (FD->getMultiVersionType()) {
+      case MultiVersionType::CPUDispatch:
+      case MultiVersionType::CPUSpecific:
         AppendCPUSpecificCPUDispatchMangling(CGM,
                                              FD->getAttr<CPUSpecificAttr>(),
                                              GD.getMultiVersionIndex(), Out);
-      else
+        break;
+      case MultiVersionType::Target:
         AppendTargetMangling(CGM, FD->getAttr<TargetAttr>(), Out);
+        break;
+      case MultiVersionType::TargetClones:
+        AppendTargetClonesMangling(CGM, FD->getAttr<TargetClonesAttr>(),
+                                   GD.getMultiVersionIndex(), Out);
+        break;
+      case MultiVersionType::None:
+        llvm_unreachable("None multiversion type isn't valid here");
+      }
     }
 
   return Out.str();
@@ -1386,11 +1411,13 @@
   StringRef TargetCPU = getTarget().getTargetOpts().CPU;
   std::vector<std::string> Features;
   const auto *FD = dyn_cast_or_null<FunctionDecl>(GD.getDecl());
-  FD = FD ? FD->getMostRecentDecl() : FD;
-  const auto *TD = FD ? FD->getAttr<TargetAttr>() : nullptr;
-  const auto *SD = FD ? FD->getAttr<CPUSpecificAttr>() : nullptr;
+  FD = FD ? FD->getMostRecentDecl() : nullptr;
   bool AddedAttr = false;
-  if (TD || SD) {
+  // CPUDispatch doesn't get the features list, but all other multiversioning
+  // does.  Additionally, non-multiversion 'target' does.
+  if (FD && (FD->hasAttr<TargetAttr>() ||
+             (FD->isMultiVersion() &&
+              FD->getMultiVersionType() != MultiVersionType::CPUDispatch))) {
     llvm::StringMap<bool> FeatureMap;
     getFunctionFeatureMap(FeatureMap, GD);
 
@@ -1402,7 +1429,7 @@
     // While we populated the feature map above, we still need to
     // get and parse the target attribute so we can get the cpu for
     // the function.
-    if (TD) {
+    if (const auto *TD = FD->getAttr<TargetAttr>()) {
       TargetAttr::ParsedTargetAttr ParsedAttr = TD->parse();
       if (ParsedAttr.Architecture != "" &&
           getTarget().isValidCPUName(ParsedAttr.Architecture))
@@ -2417,7 +2444,13 @@
     auto *Spec = FD->getAttr<CPUSpecificAttr>();
     for (unsigned I = 0; I < Spec->cpus_size(); ++I)
       EmitGlobalFunctionDefinition(GD.getWithMultiVersionIndex(I), nullptr);
-    // Requires multiple emits.
+  } else if (FD->getMultiVersionType() == MultiVersionType::TargetClones) {
+    auto *Attr = FD->getAttr<TargetClonesAttr>();
+    for (unsigned I = 0; I < Attr->featuresStrs_size(); ++I)
+      if (Attr->isFirstOfVersion(I))
+        EmitGlobalFunctionDefinition(GD.getWithMultiVersionIndex(I), nullptr);
+    EmitTargetClonesResolver(
+        GD.getWithMultiVersionIndex(Attr->featuresStrs_size()));
   } else
     EmitGlobalFunctionDefinition(GD, GV);
 }
@@ -2620,6 +2653,63 @@
   CGF.EmitMultiVersionResolver(ResolverFunc, Options);
 }
 
+void CodeGenModule::EmitTargetClonesResolver(GlobalDecl GD) {
+  const auto *FD = cast<FunctionDecl>(GD.getDecl());
+  assert(FD && "Not a FunctionDecl?");
+  const auto *TC = FD->getAttr<TargetClonesAttr>();
+  assert(TC && "Not a target_clones Function?");
+
+  QualType CanonTy = Context.getCanonicalType(FD->getType());
+  llvm::Type *DeclTy = getTypes().ConvertFunctionType(CanonTy, FD);
+
+  if (const auto *CXXFD = dyn_cast<CXXMethodDecl>(FD)) {
+    const CGFunctionInfo &FInfo = getTypes().arrangeCXXMethodDeclaration(CXXFD);
+    DeclTy = getTypes().GetFunctionType(FInfo);
+  }
+
+  llvm::Function *ResolverFunc;
+  if (getTarget().supportsIFunc()) {
+    auto *IFunc = cast<llvm::GlobalIFunc>(GetOrCreateMultiVersionResolver(
+        getTarget().supportsIFunc() ? GlobalDecl{} : GD, DeclTy, FD));
+    ResolverFunc = cast<llvm::Function>(IFunc->getResolver());
+  } else
+    ResolverFunc = cast<llvm::Function>(GetOrCreateMultiVersionResolver(
+        getTarget().supportsIFunc() ? GlobalDecl{} : GD, DeclTy, FD));
+
+  SmallVector<CodeGenFunction::MultiVersionResolverOption, 10> Options;
+  for (unsigned VersionIndex = 0; VersionIndex < TC->featuresStrs_size();
+       ++VersionIndex) {
+    if (!TC->isFirstOfVersion(VersionIndex))
+      continue;
+    StringRef Version = TC->getFeatureStr(VersionIndex);
+    std::string MangledName =
+        getMangledName(GD.getWithMultiVersionIndex(VersionIndex));
+    llvm::Constant *Func = GetGlobalValue(MangledName);
+    assert(Func &&
+           "Should have already been created before calling resolver emit");
+
+    StringRef Architecture;
+    llvm::SmallVector<StringRef, 1> Feature;
+
+    if (Version.startswith("arch="))
+      Architecture = Version.drop_front(sizeof("arch=") - 1);
+    else if (Version != "default")
+      Feature.push_back(Version);
+
+    Options.emplace_back(cast<llvm::Function>(Func), Architecture, Feature);
+  }
+
+  const TargetInfo &TI = getTarget();
+  std::stable_sort(
+      Options.begin(), Options.end(),
+      [&TI](const CodeGenFunction::MultiVersionResolverOption &LHS,
+            const CodeGenFunction::MultiVersionResolverOption &RHS) {
+        return TargetMVPriority(TI, LHS) > TargetMVPriority(TI, RHS);
+      });
+  CodeGenFunction CGF(*this);
+  CGF.EmitMultiVersionResolver(ResolverFunc, Options);
+}
+
 /// If a dispatcher for the specified mangled name is not in the module, create
 /// and return an llvm Function with the specified type.
 llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(
@@ -2642,7 +2732,7 @@
   // Since this is the first time we've created this IFunc, make sure
   // that we put this multiversioned function into the list to be
   // replaced later if necessary (target multiversioning only).
-  if (!FD->isCPUDispatchMultiVersion() && !FD->isCPUSpecificMultiVersion())
+  if (FD->getMultiVersionType() == MultiVersionType::Target)
     MultiVersionFuncs.push_back(GD);
 
   if (getTarget().supportsIFunc()) {
@@ -5427,6 +5517,16 @@
     // the attribute.
     Target.initFeatureMap(FeatureMap, getDiags(), TargetCPU,
                           ParsedAttr.Features);
+  } else if (const auto *TC = FD->getAttr<TargetClonesAttr>()) {
+    std::vector<std::string> Features;
+    StringRef VersionStr = TC->getFeatureStr(GD.getMultiVersionIndex());
+
+    if (VersionStr.startswith("arch="))
+      TargetCPU = VersionStr.drop_front(sizeof("arch=") - 1);
+    else if (VersionStr != "default")
+      Features.push_back((StringRef{"+"} + VersionStr).str());
+
+    Target.initFeatureMap(FeatureMap, getDiags(), TargetCPU, Features);
   } else if (const auto *SD = FD->getAttr<CPUSpecificAttr>()) {
     llvm::SmallVector<StringRef, 32> FeaturesTmp;
     Target.getCPUSpecificCPUDispatchFeatures(
Index: lib/AST/Decl.cpp
===================================================================
--- lib/AST/Decl.cpp
+++ lib/AST/Decl.cpp
@@ -2952,6 +2952,18 @@
   return isMultiVersion() && hasAttr<TargetAttr>();
 }
 
+MultiVersionType FunctionDecl::getMultiVersionType() const {
+  if (hasAttr<TargetAttr>())
+    return MultiVersionType::Target;
+  if (hasAttr<CPUDispatchAttr>())
+    return MultiVersionType::CPUDispatch;
+  if (hasAttr<CPUSpecificAttr>())
+    return MultiVersionType::CPUSpecific;
+  if (hasAttr<TargetClonesAttr>())
+    return MultiVersionType::TargetClones;
+  return MultiVersionType::None;
+}
+
 void
 FunctionDecl::setPreviousDeclaration(FunctionDecl *PrevDecl) {
   redeclarable_base::setPreviousDecl(PrevDecl);
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -3376,6 +3376,10 @@
                                       SourceLocation *ArgLocation = nullptr);
   bool checkSectionName(SourceLocation LiteralLoc, StringRef Str);
   bool checkTargetAttr(SourceLocation LiteralLoc, StringRef Str);
+  bool checkTargetClonesAttrString(SourceLocation LiteralLoc, StringRef Str,
+                                   const ArgsUnion Arg, bool &HasDefault,
+                                   bool &HasCommas,
+                                   SmallVectorImpl<StringRef> &Strings);
   bool checkMSInheritanceAttrOnDefinition(
       CXXRecordDecl *RD, SourceRange Range, bool BestCase,
       MSInheritanceAttr::Spelling SemanticSpelling);
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2517,7 +2517,8 @@
   "%0 attribute requires OpenCL version %1%select{| or above}2">;
 def warn_unsupported_target_attribute
     : Warning<"%select{unsupported|duplicate}0%select{| architecture}1 '%2' in"
-              " the 'target' attribute string; 'target' attribute ignored">,
+              " the '%select{target|target_clones}3' attribute string; "
+              "'%select{target|target_clones}3' attribute ignored">,
       InGroup<IgnoredAttributes>;
 def err_attribute_unsupported
     : Error<"%0 attribute is not supported for this target">;
@@ -9502,8 +9503,8 @@
 def err_multiversion_noproto : Error<
   "multiversioned function must have a prototype">;
 def err_multiversion_no_other_attrs : Error<
-  "attribute '%select{target|cpu_specific|cpu_dispatch}0' multiversioning cannot be combined"
-  " with other attributes">;
+  "attribute '%select{target|cpu_specific|cpu_dispatch|target_clones}0' "
+  "multiversioning cannot be combined with other attributes">;
 def err_multiversion_diff : Error<
   "multiversioned function declaration has a different %select{calling convention"
   "|return type|constexpr specification|inline specification|storage class|"
@@ -9529,6 +9530,17 @@
 def warn_dispatch_body_ignored : Warning<
   "body of cpu_dispatch function will be ignored">,
   InGroup<FunctionMultiVersioning>;
+def err_target_clone_must_have_default : Error <
+  "'target_clones' multiversioning requires a default target">;
+def err_target_clone_doesnt_match : Error <
+  "'target_clones' attribute does not match previous declaration">;
+def warn_target_clone_mixed_values : ExtWarn <
+  "mixing 'target_clones' specifier mechanisms is permitted for GCC "
+  "compatibility; use a comma separated sequence of string literals, "
+  "or a string literal containing a comma-separated list of versions">,
+  InGroup<DiagGroup<"target-clones-mixed-specifiers">>;
+def warn_target_clone_duplicate_options : Warning<
+  "version list contains duplicate entries">, InGroup<FunctionMultiVersioning>;
 
 // three-way comparison operator diagnostics
 def err_implied_comparison_category_type_not_found : Error<
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -1594,6 +1594,40 @@
 }];
 }
 
+def TargetClonesDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+Clang supports the ``target_clones("OPTIONS")`` attribute. This attribute may be
+attached to a function declaration and causes function multiversioning, where
+multiple versions of the function will be emitted with different code
+generation options.  Additionally, these versions will be resolved at runtime
+based on the priority of their attribute options. All ``target_clone`` functions
+are considered multiversioned functions.
+
+All multiversioned functions must contain a ``default`` (fallback)
+implementation, otherwise usages of the function are considered invalid.
+Additionally, a function may not become multiversioned after its first use.
+
+The options to ``target_clones`` can either be a target-specific architecture
+(specified as ``arch=CPU``), or one of a list of subtarget features.
+
+Example "subtarget features" from the x86 backend include: "mmx", "sse", "sse4.2",
+"avx", "xop" and largely correspond to the machine specific options handled by
+the front end.
+
+The versions can either be listed as a comma-separated sequence of string
+literals or as a single string literal containing a comma-separated list of
+versions.  For compatibility with GCC, the two formats can be mixed.  For
+example, the following will emit 4 versions of the function:
+
+  .. code-block:: c++
+
+    __attribute__((target_clones("arch=atom,avx2","arch=ivybridge","default")))
+    void foo() {}
+
+}];
+}
+
 def MinVectorWidthDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -2054,6 +2054,38 @@
   }];
 }
 
+def TargetClones : InheritableAttr {
+  let Spellings = [GCC<"target_clones">];
+  let Args = [VariadicStringArgument<"featuresStrs">];
+  let Documentation = [TargetClonesDocs];
+  let Subjects = SubjectList<[Function], ErrorDiag>;
+  let AdditionalMembers = [{
+    StringRef getFeatureStr(unsigned Index) const {
+      return *(featuresStrs_begin() + Index);
+    }
+    // 'default' is always moved to the end, so it isn't considered
+    // when mangling the index.
+    unsigned getMangledIndex(unsigned Index) const {
+      if (getFeatureStr(Index) == "default")
+        return std::count_if(featuresStrs_begin(), featuresStrs_end(),
+                              [](StringRef S) { return S != "default"; });
+
+      return std::count_if(featuresStrs_begin(), featuresStrs_begin() + Index,
+                           [](StringRef S) { return S != "default"; });
+    }
+
+    // True if this is the first of this version to appear in the config string.
+    // This is used to make sure we don't try to emit this function multiple
+    // times.
+    bool isFirstOfVersion(unsigned Index) const {
+      StringRef FeatureStr(getFeatureStr(Index));
+      return 0 == std::count_if(
+                      featuresStrs_begin(), featuresStrs_begin() + Index,
+                      [ FeatureStr ](StringRef S) { return S == FeatureStr; });
+    }
+  }];
+}
+
 def MinVectorWidth : InheritableAttr {
   let Spellings = [Clang<"min_vector_width">];
   let Args = [UnsignedArgument<"VectorWidth">];
Index: include/clang/AST/Decl.h
===================================================================
--- include/clang/AST/Decl.h
+++ include/clang/AST/Decl.h
@@ -1719,6 +1719,14 @@
   unsigned getParameterIndexLarge() const;
 };
 
+enum class MultiVersionType {
+  None,
+  Target,
+  CPUSpecific,
+  CPUDispatch,
+  TargetClones
+};
+
 /// Represents a function declaration or definition.
 ///
 /// Since a given function can be declared several times in a program,
@@ -2226,6 +2234,8 @@
     getCanonicalDecl()->FunctionDeclBits.IsMultiVersion = V;
   }
 
+  MultiVersionType getMultiVersionType() const;
+
   /// True if this function is a multiversioned dispatch function as a part of
   /// the cpu_specific/cpu_dispatch functionality.
   bool isCPUDispatchMultiVersion() const;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to