]> granicus.if.org Git - clang/commitdiff
Implement Attribute Target MultiVersioning
authorErich Keane <erich.keane@intel.com>
Mon, 8 Jan 2018 21:34:17 +0000 (21:34 +0000)
committerErich Keane <erich.keane@intel.com>
Mon, 8 Jan 2018 21:34:17 +0000 (21:34 +0000)
GCC's attribute 'target', in addition to being an optimization hint,
also allows function multiversioning. We currently have the former
implemented, this is the latter's implementation.

This works by enabling functions with the same name/signature to coexist,
so that they can all be emitted. Multiversion state is stored in the
FunctionDecl itself, and SemaDecl manages the definitions.
Note that it ends up having to permit redefinition of functions so
that they can all be emitted. Additionally, all versions of the function
must be emitted, so this also manages that.

Note that this includes some additional rules that GCC does not, since
defining something as a MultiVersion function after a usage has been made illegal.

The only 'history rewriting' that happens is if a function is emitted before
it has been converted to a multiversion'ed function, at which point its name
needs to be changed.

Function templates and virtual functions are NOT yet supported (not supported
in GCC either).

Additionally, constructors/destructors are disallowed, but the former is
planned.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@322028 91177308-0d34-0410-b5e6-96231b3b80d8

31 files changed:
include/clang/AST/ASTContext.h
include/clang/AST/Decl.h
include/clang/Basic/Attr.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Basic/TargetInfo.h
include/clang/Basic/X86Target.def
include/clang/Sema/Overload.h
lib/AST/ASTContext.cpp
lib/Basic/Targets/X86.cpp
lib/Basic/Targets/X86.h
lib/CodeGen/CodeGenFunction.cpp
lib/CodeGen/CodeGenFunction.h
lib/CodeGen/CodeGenModule.cpp
lib/CodeGen/CodeGenModule.h
lib/Sema/Sema.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaOverload.cpp
lib/Serialization/ASTReaderDecl.cpp
lib/Serialization/ASTWriterDecl.cpp
test/CodeGen/attr-target-mv-func-ptrs.c [new file with mode: 0644]
test/CodeGen/attr-target-mv-va-args.c [new file with mode: 0644]
test/CodeGen/attr-target-mv.c [new file with mode: 0644]
test/CodeGenCXX/attr-target-mv-diff-ns.cpp [new file with mode: 0644]
test/CodeGenCXX/attr-target-mv-func-ptrs.cpp [new file with mode: 0644]
test/CodeGenCXX/attr-target-mv-member-funcs.cpp [new file with mode: 0644]
test/CodeGenCXX/attr-target-mv-modules.cpp [new file with mode: 0644]
test/CodeGenCXX/attr-target-mv-out-of-line-defs.cpp [new file with mode: 0644]
test/CodeGenCXX/attr-target-mv-overloads.cpp [new file with mode: 0644]
test/Sema/attr-target-mv-bad-target.c [new file with mode: 0644]
test/Sema/attr-target-mv.c [new file with mode: 0644]
test/SemaCXX/attr-target-mv.cpp [new file with mode: 0644]

index 8ddb1385bf72911da837a5370c78d65e1056239d..e152223776e5e2599f0864237cded969883808c9 100644 (file)
@@ -2640,6 +2640,12 @@ public:
   /// it is not used.
   bool DeclMustBeEmitted(const Decl *D);
 
+  /// \brief Visits all versions of a multiversioned function with the passed
+  /// predicate.
+  void forEachMultiversionedFunctionVersion(
+      const FunctionDecl *FD,
+      llvm::function_ref<void(const FunctionDecl *)> Pred) const;
+
   const CXXConstructorDecl *
   getCopyConstructorForExceptionObject(CXXRecordDecl *RD);
 
index 04a832e552a4a93071c883e091c67a52da81f73c..b3eb7750f59e56329c11c1f19b13f6b1ab459212 100644 (file)
@@ -1750,6 +1750,10 @@ private:
   /// parsing it.
   unsigned WillHaveBody : 1;
 
+  /// Indicates that this function is a multiversioned function using attribute
+  /// 'target'.
+  unsigned IsMultiVersion : 1;
+
 protected:
   /// [C++17] Only used by CXXDeductionGuideDecl. Declared here to avoid
   /// increasing the size of CXXDeductionGuideDecl by the size of an unsigned
@@ -1846,9 +1850,9 @@ protected:
         IsExplicitlyDefaulted(false), HasImplicitReturnZero(false),
         IsLateTemplateParsed(false), IsConstexpr(isConstexprSpecified),
         InstantiationIsPending(false), UsesSEHTry(false), HasSkippedBody(false),
-        WillHaveBody(false), IsCopyDeductionCandidate(false), HasODRHash(false),
-        ODRHash(0), EndRangeLoc(NameInfo.getEndLoc()),
-        DNLoc(NameInfo.getInfo()) {}
+        WillHaveBody(false), IsMultiVersion(false),
+        IsCopyDeductionCandidate(false), HasODRHash(false), ODRHash(0),
+        EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) {}
 
   using redeclarable_base = Redeclarable<FunctionDecl>;
 
@@ -2154,6 +2158,15 @@ public:
   bool willHaveBody() const { return WillHaveBody; }
   void setWillHaveBody(bool V = true) { WillHaveBody = V; }
 
+  /// True if this function is considered a multiversioned function.
+  bool isMultiVersion() const { return getCanonicalDecl()->IsMultiVersion; }
+
+  /// Sets the multiversion state for this declaration and all of its
+  /// redeclarations.
+  void setIsMultiVersion(bool V = true) {
+    getCanonicalDecl()->IsMultiVersion = V;
+  }
+
   void setPreviousDeclaration(FunctionDecl * PrevDecl);
 
   FunctionDecl *getCanonicalDecl() override;
index 633c5977a5fa4dbd13dcb8f8945ea734d28cef21..5332bde157c9c75dc30bf9f12344738db2c05ae3 100644 (file)
@@ -1844,12 +1844,27 @@ def Target : InheritableAttr {
       std::vector<std::string> Features;
       StringRef Architecture;
       bool DuplicateArchitecture = false;
+      bool operator ==(const ParsedTargetAttr &Other) const {
+        return DuplicateArchitecture == Other.DuplicateArchitecture &&
+               Architecture == Other.Architecture && Features == Other.Features;
+      }
     };
     ParsedTargetAttr parse() const {
       return parse(getFeaturesStr());
     }
+
+    template<class Compare>
+    ParsedTargetAttr parse(Compare cmp) const {
+      ParsedTargetAttr Attrs = parse();
+      std::sort(std::begin(Attrs.Features), std::end(Attrs.Features), cmp);
+      return Attrs;
+    }
+
+    bool isDefaultVersion() const { return getFeaturesStr() == "default"; }
+
     static ParsedTargetAttr parse(StringRef Features) {
       ParsedTargetAttr Ret;
+      if (Features == "default") return Ret;
       SmallVector<StringRef, 1> AttrFeatures;
       Features.split(AttrFeatures, ",");
 
index 8bf88cc8568fb495e4dd5cfbb8242a0b093cc5f5..d5b5fe3b4afa19b82d46a1d58d45d56c68a3536d 100644 (file)
@@ -9332,4 +9332,35 @@ def warn_shadow_field :
   InGroup<ShadowField>, DefaultIgnore;
 def note_shadow_field : Note<"declared here">;
 
+def err_target_required_in_redecl : Error<
+  "function declaration is missing 'target' attribute in a multiversioned "
+  "function">;
+def note_multiversioning_caused_here : Note<
+  "function multiversioning caused by this declaration">;
+def err_multiversion_after_used : Error<
+  "function declaration cannot become a multiversioned function after first "
+  "usage">;
+def err_bad_multiversion_option : Error<
+  "function multiversioning doesn't support %select{feature|architecture}0 "
+  "'%1'">;
+def err_multiversion_duplicate : Error<
+  "multiversioned function redeclarations require identical target attributes">;
+def err_multiversion_noproto : Error<
+  "multiversioned function must have a prototype">;
+def err_multiversion_no_other_attrs : Error<
+  "attribute 'target' 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|"
+  "linkage}0">;
+def err_multiversion_doesnt_support : Error<
+  "multiversioned functions do not yet support %select{function templates|"
+  "virtual functions|deduced return types|constructors|destructors|"
+  "deleted functions|defaulted functions}0">;
+def err_multiversion_not_allowed_on_main : Error<
+  "'main' cannot be a multiversioned function">;
+def err_multiversion_not_supported : Error<
+ "function multiversioning is not supported on the current target">;
+
 } // end of sema component.
index 6ba58779114f58a2886aa803f938b4e4befa016b..fad923c2726ba7b5ee3a28c3bc8e6a775209bf03 100644 (file)
@@ -920,10 +920,20 @@ public:
     return false;
   }
 
+  /// \brief Identify whether this taret supports multiversioning of functions,
+  /// which requires support for cpu_supports and cpu_is functionality.
+  virtual bool supportsMultiVersioning() const { return false; }
+
   // \brief Validate the contents of the __builtin_cpu_supports(const char*)
   // argument.
   virtual bool validateCpuSupports(StringRef Name) const { return false; }
 
+  // \brief Return the target-specific priority for features/cpus/vendors so
+  // that they can be properly sorted for checking.
+  virtual unsigned multiVersionSortPriority(StringRef Name) const {
+    return 0;
+  }
+
   // \brief Validate the contents of the __builtin_cpu_is(const char*)
   // argument.
   virtual bool validateCpuIs(StringRef Name) const { return false; }
index 21550fe2dd55fec01787c0ee7c8517d7233026fc..0bb758978eb7f40c9fedcb3ca873dfd234b7f1f2 100644 (file)
 //
 //===----------------------------------------------------------------------===//
 
+#ifndef PROC_WITH_FEAT
+#define PROC_WITH_FEAT(ENUM, STRING, IS64BIT, KEYFEATURE)                      \
+  PROC(ENUM, STRING, IS64BIT)
+#endif
 
 #ifndef PROC
 #define PROC(ENUM, STRING, IS64BIT)
 #define PROC_ALIAS(ENUM, ALIAS)
 #endif
 
+#ifndef FEATURE
+#define FEATURE(ENUM)
+#endif
+
 #define PROC_64_BIT true
 #define PROC_32_BIT false
 
@@ -77,7 +85,7 @@ PROC(Nocona, "nocona", PROC_64_BIT)
 /// \name Core
 /// Core microarchitecture based processors.
 //@{
-PROC(Core2, "core2", PROC_64_BIT)
+PROC_WITH_FEAT(Core2, "core2", PROC_64_BIT, FEATURE_SSSE3)
 
 /// This enumerator, like Yonah, is a bit odd. It is another
 /// codename which GCC no longer accepts as an option to -march, but Clang
@@ -89,10 +97,10 @@ PROC(Penryn, "penryn", PROC_64_BIT)
 /// \name Atom
 /// Atom processors
 //@{
-PROC(Bonnell, "bonnell", PROC_64_BIT)
+PROC_WITH_FEAT(Bonnell, "bonnell", PROC_64_BIT, FEATURE_SSSE3)
 PROC_ALIAS(Bonnell, "atom")
 
-PROC(Silvermont, "silvermont", PROC_64_BIT)
+PROC_WITH_FEAT(Silvermont, "silvermont", PROC_64_BIT, FEATURE_SSE4_2)
 PROC_ALIAS(Silvermont, "slm")
 
 PROC(Goldmont, "goldmont", PROC_64_BIT)
@@ -100,44 +108,44 @@ PROC(Goldmont, "goldmont", PROC_64_BIT)
 
 /// \name Nehalem
 /// Nehalem microarchitecture based processors.
-PROC(Nehalem, "nehalem", PROC_64_BIT)
+PROC_WITH_FEAT(Nehalem, "nehalem", PROC_64_BIT, FEATURE_SSE4_2)
 PROC_ALIAS(Nehalem, "corei7")
 
 /// \name Westmere
 /// Westmere microarchitecture based processors.
-PROC(Westmere, "westmere", PROC_64_BIT)
+PROC_WITH_FEAT(Westmere, "westmere", PROC_64_BIT, FEATURE_PCLMUL)
 
 /// \name Sandy Bridge
 /// Sandy Bridge microarchitecture based processors.
-PROC(SandyBridge, "sandybridge", PROC_64_BIT)
+PROC_WITH_FEAT(SandyBridge, "sandybridge", PROC_64_BIT, FEATURE_AVX)
 PROC_ALIAS(SandyBridge, "corei7-avx")
 
 /// \name Ivy Bridge
 /// Ivy Bridge microarchitecture based processors.
-PROC(IvyBridge, "ivybridge", PROC_64_BIT)
+PROC_WITH_FEAT(IvyBridge, "ivybridge", PROC_64_BIT, FEATURE_AVX)
 PROC_ALIAS(IvyBridge, "core-avx-i")
 
 /// \name Haswell
 /// Haswell microarchitecture based processors.
-PROC(Haswell, "haswell", PROC_64_BIT)
+PROC_WITH_FEAT(Haswell, "haswell", PROC_64_BIT, FEATURE_AVX2)
 PROC_ALIAS(Haswell, "core-avx2")
 
 /// \name Broadwell
 /// Broadwell microarchitecture based processors.
-PROC(Broadwell, "broadwell", PROC_64_BIT)
+PROC_WITH_FEAT(Broadwell, "broadwell", PROC_64_BIT, FEATURE_AVX2)
 
 /// \name Skylake Client
 /// Skylake client microarchitecture based processors.
-PROC(SkylakeClient, "skylake", PROC_64_BIT)
+PROC_WITH_FEAT(SkylakeClient, "skylake", PROC_64_BIT, FEATURE_AVX2)
 
 /// \name Skylake Server
 /// Skylake server microarchitecture based processors.
-PROC(SkylakeServer, "skylake-avx512", PROC_64_BIT)
+PROC_WITH_FEAT(SkylakeServer, "skylake-avx512", PROC_64_BIT, FEATURE_AVX512F)
 PROC_ALIAS(SkylakeServer, "skx")
 
 /// \name Cannonlake Client
 /// Cannonlake client microarchitecture based processors.
-PROC(Cannonlake, "cannonlake", PROC_64_BIT)
+PROC_WITH_FEAT(Cannonlake, "cannonlake", PROC_64_BIT, FEATURE_AVX512VBMI)
 
 /// \name Icelake Client
 /// Icelake client microarchitecture based processors.
@@ -145,11 +153,11 @@ PROC(Icelake, "icelake", PROC_64_BIT)
 
 /// \name Knights Landing
 /// Knights Landing processor.
-PROC(KNL, "knl", PROC_64_BIT)
+PROC_WITH_FEAT(KNL, "knl", PROC_64_BIT, FEATURE_AVX512F)
 
 /// \name Knights Mill
 /// Knights Mill processor.
-PROC(KNM, "knm", PROC_64_BIT)
+PROC_WITH_FEAT(KNM, "knm", PROC_64_BIT, FEATURE_AVX5124FMAPS)
 
 /// \name Lakemont
 /// Lakemont microarchitecture based processors.
@@ -186,30 +194,30 @@ PROC(K8SSE3, "k8-sse3", PROC_64_BIT)
 PROC_ALIAS(K8SSE3, "athlon64-sse3")
 PROC_ALIAS(K8SSE3, "opteron-sse3")
 
-PROC(AMDFAM10, "amdfam10", PROC_64_BIT)
+PROC_WITH_FEAT(AMDFAM10, "amdfam10", PROC_64_BIT, FEATURE_SSE4_A)
 PROC_ALIAS(AMDFAM10, "barcelona")
 //@}
 
 /// \name Bobcat
 /// Bobcat architecture processors.
 //@{
-PROC(BTVER1, "btver1", PROC_64_BIT)
-PROC(BTVER2, "btver2", PROC_64_BIT)
+PROC_WITH_FEAT(BTVER1, "btver1", PROC_64_BIT, FEATURE_SSE4_A)
+PROC_WITH_FEAT(BTVER2, "btver2", PROC_64_BIT, FEATURE_BMI)
 //@}
 
 /// \name Bulldozer
 /// Bulldozer architecture processors.
 //@{
-PROC(BDVER1, "bdver1", PROC_64_BIT)
-PROC(BDVER2, "bdver2", PROC_64_BIT)
-PROC(BDVER3, "bdver3", PROC_64_BIT)
-PROC(BDVER4, "bdver4", PROC_64_BIT)
+PROC_WITH_FEAT(BDVER1, "bdver1", PROC_64_BIT, FEATURE_XOP)
+PROC_WITH_FEAT(BDVER2, "bdver2", PROC_64_BIT, FEATURE_FMA)
+PROC_WITH_FEAT(BDVER3, "bdver3", PROC_64_BIT, FEATURE_FMA)
+PROC_WITH_FEAT(BDVER4, "bdver4", PROC_64_BIT, FEATURE_AVX2)
 //@}
 
 /// \name zen
 /// Zen architecture processors.
 //@{
-PROC(ZNVER1, "znver1", PROC_64_BIT)
+PROC_WITH_FEAT(ZNVER1, "znver1", PROC_64_BIT, FEATURE_AVX2)
 //@}
 
 /// This specification is deprecated and will be removed in the future.
@@ -225,8 +233,45 @@ PROC(x86_64, "x86-64", PROC_64_BIT)
 PROC(Geode, "geode", PROC_32_BIT)
 //@}
 
+// List of CPU Supports features in order.  These need to remain in the order
+// required by attribute 'target' checking.  Note that not all are supported/
+// prioritized by GCC, so synchronization with GCC's implementation may require
+// changing some existing values.
+FEATURE(FEATURE_CMOV)
+FEATURE(FEATURE_MMX)
+FEATURE(FEATURE_SSE)
+FEATURE(FEATURE_SSE2)
+FEATURE(FEATURE_SSE3)
+FEATURE(FEATURE_SSSE3)
+FEATURE(FEATURE_SSE4_A)
+FEATURE(FEATURE_SSE4_1)
+FEATURE(FEATURE_SSE4_2)
+FEATURE(FEATURE_POPCNT)
+FEATURE(FEATURE_AES)
+FEATURE(FEATURE_PCLMUL)
+FEATURE(FEATURE_AVX)
+FEATURE(FEATURE_BMI)
+FEATURE(FEATURE_FMA4)
+FEATURE(FEATURE_XOP)
+FEATURE(FEATURE_FMA)
+FEATURE(FEATURE_BMI2)
+FEATURE(FEATURE_AVX2)
+FEATURE(FEATURE_AVX512F)
+FEATURE(FEATURE_AVX512VL)
+FEATURE(FEATURE_AVX512BW)
+FEATURE(FEATURE_AVX512DQ)
+FEATURE(FEATURE_AVX512CD)
+FEATURE(FEATURE_AVX512ER)
+FEATURE(FEATURE_AVX512PF)
+FEATURE(FEATURE_AVX512VBMI)
+FEATURE(FEATURE_AVX512IFMA)
+FEATURE(FEATURE_AVX5124VNNIW)
+FEATURE(FEATURE_AVX5124FMAPS)
+FEATURE(FEATURE_AVX512VPOPCNTDQ)
 
 #undef PROC_64_BIT
 #undef PROC_32_BIT
+#undef FEATURE
 #undef PROC
 #undef PROC_ALIAS
+#undef PROC_WITH_FEAT
index 05cfe53666ca9927c31c0486e2ce989c65ba23d3..eb32b8fffd97ac0af6b1d9000b082b567d4bf334 100644 (file)
@@ -613,6 +613,10 @@ namespace clang {
     /// This inherited constructor is not viable because it would slice the
     /// argument.
     ovl_fail_inhctor_slice,
+
+    /// This candidate was not viable because it is a non-default multiversioned
+    /// function.
+    ovl_non_default_multiversion_function,
   };
 
   /// A list of implicit conversion sequences for the arguments of an
index 2e906247cc1501105e25daf9d0077218932d69b5..c81d57864020f6f2ea0242d56a6ce9499b6e8c1d 100644 (file)
@@ -9483,6 +9483,23 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
   return false;
 }
 
+void ASTContext::forEachMultiversionedFunctionVersion(
+    const FunctionDecl *FD,
+    llvm::function_ref<void(const FunctionDecl *)> Pred) const {
+  assert(FD->isMultiVersion() && "Only valid for multiversioned functions");
+  llvm::SmallDenseSet<const FunctionDecl*, 4> SeenDecls;
+  FD = FD->getCanonicalDecl();
+  for (auto *CurDecl :
+       FD->getDeclContext()->getRedeclContext()->lookup(FD->getDeclName())) {
+    FunctionDecl *CurFD = CurDecl->getAsFunction()->getCanonicalDecl();
+    if (CurFD && hasSameType(CurFD->getType(), FD->getType()) &&
+        std::end(SeenDecls) == llvm::find(SeenDecls, CurFD)) {
+      SeenDecls.insert(CurFD);
+      Pred(CurFD);
+    }
+  }
+}
+
 CallingConv ASTContext::getDefaultCallingConvention(bool IsVariadic,
                                                     bool IsCXXMethod) const {
   // Pass through to the C++ ABI object
index 70433ff066fc0d2bedc1eccad95e71db45b04f42..5d4dfcb8441687259ad2054e4df4faa7bfb8fac5 100644 (file)
@@ -17,6 +17,7 @@
 #include "clang/Basic/TargetBuiltins.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/TargetParser.h"
 
 namespace clang {
 namespace targets {
@@ -1338,6 +1339,63 @@ bool X86TargetInfo::validateCpuSupports(StringRef FeatureStr) const {
       .Default(false);
 }
 
+static llvm::X86::ProcessorFeatures getFeature(StringRef Name) {
+  return llvm::StringSwitch<llvm::X86::ProcessorFeatures>(Name)
+#define X86_FEATURE_COMPAT(VAL, ENUM, STR) .Case(STR, llvm::X86::ENUM)
+#include "llvm/Support/X86TargetParser.def"
+      ;
+  // Note, this function should only be used after ensuring the value is
+  // correct, so it asserts if the value is out of range.
+}
+
+static unsigned getFeaturePriority(llvm::X86::ProcessorFeatures Feat) {
+  enum class FeatPriority {
+#define FEATURE(FEAT) FEAT,
+#include "clang/Basic/X86Target.def"
+  };
+  switch (Feat) {
+#define FEATURE(FEAT)                                                          \
+  case llvm::X86::FEAT:                                                        \
+    return static_cast<unsigned>(FeatPriority::FEAT);
+#include "clang/Basic/X86Target.def"
+  default:
+    llvm_unreachable("No Feature Priority for non-CPUSupports Features");
+  }
+}
+
+unsigned X86TargetInfo::multiVersionSortPriority(StringRef Name) const {
+  // Valid CPUs have a 'key feature' that compares just better than its key
+  // feature.
+  CPUKind Kind = getCPUKind(Name);
+  if (Kind != CK_Generic) {
+    switch (Kind) {
+    default:
+      llvm_unreachable(
+          "CPU Type without a key feature used in 'target' attribute");
+#define PROC_WITH_FEAT(ENUM, STR, IS64, KEY_FEAT)                              \
+  case CK_##ENUM:                                                              \
+    return (getFeaturePriority(llvm::X86::KEY_FEAT) << 1) + 1;
+#include "clang/Basic/X86Target.def"
+    }
+  }
+
+  // Now we know we have a feature, so get its priority and shift it a few so
+  // that we have sufficient room for the CPUs (above).
+  return getFeaturePriority(getFeature(Name)) << 1;
+}
+
+std::string X86TargetInfo::getCPUKindCanonicalName(CPUKind Kind) const {
+  switch (Kind) {
+  case CK_Generic:
+    return "";
+#define PROC(ENUM, STRING, IS64BIT)                                            \
+  case CK_##ENUM:                                                              \
+    return STRING;
+#include "clang/Basic/X86Target.def"
+  }
+  llvm_unreachable("Invalid CPUKind");
+}
+
 // We can't use a generic validation scheme for the cpus accepted here
 // versus subtarget cpus accepted in the target attribute because the
 // variables intitialized by the runtime only support the below currently
index cbd6a2d24fb51b58223f079aa035219a49240842..58b7a09c9ab708e15f0ea2b7ed6c8cb743c9eaab 100644 (file)
@@ -111,6 +111,8 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {
 
   CPUKind getCPUKind(StringRef CPU) const;
 
+  std::string getCPUKindCanonicalName(CPUKind Kind) const;
+
   enum FPMathKind { FP_Default, FP_SSE, FP_387 } FPMath = FP_Default;
 
 public:
@@ -256,6 +258,11 @@ public:
     return checkCPUKind(CPU = getCPUKind(Name));
   }
 
+  bool supportsMultiVersioning() const override {
+    return getTriple().isOSBinFormatELF();
+  }
+  unsigned multiVersionSortPriority(StringRef Name) const override;
+
   bool setFPMath(StringRef Name) override;
 
   CallingConvCheckResult checkCallingConvention(CallingConv CC) const override {
index 6fb21b157367772d40febf7a27ebead55f0cfe26..0cbe071d1b937e67bbccb2238499ff83b64248d8 100644 (file)
@@ -2305,6 +2305,60 @@ void CodeGenFunction::EmitSanitizerStatReport(llvm::SanitizerStatKind SSK) {
   CGM.getSanStats().create(IRB, SSK);
 }
 
+llvm::Value *
+CodeGenFunction::FormResolverCondition(const MultiVersionResolverOption &RO) {
+  llvm::Value *TrueCondition = nullptr;
+  if (!RO.ParsedAttribute.Architecture.empty())
+    TrueCondition = EmitX86CpuIs(RO.ParsedAttribute.Architecture);
+
+  if (!RO.ParsedAttribute.Features.empty()) {
+    SmallVector<StringRef, 8> FeatureList;
+    llvm::for_each(RO.ParsedAttribute.Features,
+                   [&FeatureList](const std::string &Feature) {
+                     FeatureList.push_back(StringRef{Feature}.substr(1));
+                   });
+    llvm::Value *FeatureCmp = EmitX86CpuSupports(FeatureList);
+    TrueCondition = TrueCondition ? Builder.CreateAnd(TrueCondition, FeatureCmp)
+                                  : FeatureCmp;
+  }
+  return TrueCondition;
+}
+
+void CodeGenFunction::EmitMultiVersionResolver(
+    llvm::Function *Resolver, ArrayRef<MultiVersionResolverOption> Options) {
+  assert((getContext().getTargetInfo().getTriple().getArch() ==
+              llvm::Triple::x86 ||
+          getContext().getTargetInfo().getTriple().getArch() ==
+              llvm::Triple::x86_64) &&
+         "Only implemented for x86 targets");
+
+  // Main function's basic block.
+  llvm::BasicBlock *CurBlock = createBasicBlock("entry", Resolver);
+  Builder.SetInsertPoint(CurBlock);
+  EmitX86CpuInit();
+
+  llvm::Function *DefaultFunc = nullptr;
+  for (const MultiVersionResolverOption &RO : Options) {
+    Builder.SetInsertPoint(CurBlock);
+    llvm::Value *TrueCondition = FormResolverCondition(RO);
+
+    if (!TrueCondition) {
+      DefaultFunc = RO.Function;
+    } else {
+      llvm::BasicBlock *RetBlock = createBasicBlock("ro_ret", Resolver);
+      llvm::IRBuilder<> RetBuilder(RetBlock);
+      RetBuilder.CreateRet(RO.Function);
+      CurBlock = createBasicBlock("ro_else", Resolver);
+      Builder.CreateCondBr(TrueCondition, RetBlock, CurBlock);
+    }
+  }
+
+  assert(DefaultFunc && "No default version?");
+  // Emit return from the 'else-ist' block.
+  Builder.SetInsertPoint(CurBlock);
+  Builder.CreateRet(DefaultFunc);
+}
+
 llvm::DebugLoc CodeGenFunction::SourceLocToDebugLoc(SourceLocation Location) {
   if (CGDebugInfo *DI = getDebugInfo())
     return DI->SourceLocToDebugLoc(Location);
index 74750c4d28a7786fd20af33cf0da6020deaaeb0e..cedf327a48039d149ee37c0697865a9c64fb8bc3 100644 (file)
@@ -3988,6 +3988,29 @@ public:
 
   void EmitSanitizerStatReport(llvm::SanitizerStatKind SSK);
 
+  struct MultiVersionResolverOption {
+    llvm::Function *Function;
+    TargetAttr::ParsedTargetAttr ParsedAttribute;
+    unsigned Priority;
+    MultiVersionResolverOption(const TargetInfo &TargInfo, llvm::Function *F,
+                               const clang::TargetAttr::ParsedTargetAttr &PT)
+        : Function(F), ParsedAttribute(PT), Priority(0u) {
+      for (StringRef Feat : PT.Features)
+        Priority = std::max(Priority,
+                            TargInfo.multiVersionSortPriority(Feat.substr(1)));
+
+      if (!PT.Architecture.empty())
+        Priority = std::max(Priority,
+                            TargInfo.multiVersionSortPriority(PT.Architecture));
+    }
+
+    bool operator>(const MultiVersionResolverOption &Other) const {
+      return Priority > Other.Priority;
+    }
+  };
+  void EmitMultiVersionResolver(llvm::Function *Resolver,
+                                ArrayRef<MultiVersionResolverOption> Options);
+
 private:
   QualType getVarArgType(const Expr *Arg);
 
@@ -4004,6 +4027,7 @@ private:
   llvm::Value *EmitX86CpuSupports(const CallExpr *E);
   llvm::Value *EmitX86CpuSupports(ArrayRef<StringRef> FeatureStrs);
   llvm::Value *EmitX86CpuInit();
+  llvm::Value *FormResolverCondition(const MultiVersionResolverOption &RO);
 };
 
 /// Helper class with most of the code for saving a value for a
index df33fc3e82001596cb79a646a3077d4d3ff639ff..85bd53741e180131bf16cfcc44872f5a6d627338 100644 (file)
@@ -395,6 +395,7 @@ void CodeGenModule::Release() {
   applyGlobalValReplacements();
   applyReplacements();
   checkAliases();
+  emitMultiVersionFunctions();
   EmitCXXGlobalInitFunc();
   EmitCXXGlobalDtorFunc();
   EmitCXXThreadLocalInitFunc();
@@ -725,36 +726,48 @@ void CodeGenModule::setTLSMode(llvm::GlobalValue *GV, const VarDecl &D) const {
   GV->setThreadLocalMode(TLM);
 }
 
-StringRef CodeGenModule::getMangledName(GlobalDecl GD) {
-  GlobalDecl CanonicalGD = GD.getCanonicalDecl();
+static void AppendTargetMangling(const CodeGenModule &CGM,
+                                 const TargetAttr *Attr, raw_ostream &Out) {
+  if (Attr->isDefaultVersion())
+    return;
 
-  // Some ABIs don't have constructor variants.  Make sure that base and
-  // complete constructors get mangled the same.
-  if (const auto *CD = dyn_cast<CXXConstructorDecl>(CanonicalGD.getDecl())) {
-    if (!getTarget().getCXXABI().hasConstructorVariants()) {
-      CXXCtorType OrigCtorType = GD.getCtorType();
-      assert(OrigCtorType == Ctor_Base || OrigCtorType == Ctor_Complete);
-      if (OrigCtorType == Ctor_Base)
-        CanonicalGD = GlobalDecl(CD, Ctor_Complete);
-    }
+  Out << '.';
+  const auto &Target = CGM.getTarget();
+  TargetAttr::ParsedTargetAttr Info =
+      Attr->parse([&Target](StringRef LHS, StringRef RHS) {
+                    return Target.multiVersionSortPriority(LHS) >
+                           Target.multiVersionSortPriority(RHS);
+                  });
+
+  bool IsFirst = true;
+
+  if (!Info.Architecture.empty()) {
+    IsFirst = false;
+    Out << "arch_" << Info.Architecture;
   }
 
-  auto FoundName = MangledDeclNames.find(CanonicalGD);
-  if (FoundName != MangledDeclNames.end())
-    return FoundName->second;
+  for (StringRef Feat : Info.Features) {
+    if (!IsFirst)
+      Out << '_';
+    IsFirst = false;
+    Out << Feat.substr(1);
+  }
+}
 
-  const auto *ND = cast<NamedDecl>(GD.getDecl());
+static std::string getMangledNameImpl(const CodeGenModule &CGM, GlobalDecl GD,
+                                      const NamedDecl *ND,
+                                      bool OmitTargetMangling = false) {
   SmallString<256> Buffer;
-  StringRef Str;
-  if (getCXXABI().getMangleContext().shouldMangleDeclName(ND)) {
+  llvm::raw_svector_ostream Out(Buffer);
+  MangleContext &MC = CGM.getCXXABI().getMangleContext();
+  if (MC.shouldMangleDeclName(ND)) {
     llvm::raw_svector_ostream Out(Buffer);
     if (const auto *D = dyn_cast<CXXConstructorDecl>(ND))
-      getCXXABI().getMangleContext().mangleCXXCtor(D, GD.getCtorType(), Out);
+      MC.mangleCXXCtor(D, GD.getCtorType(), Out);
     else if (const auto *D = dyn_cast<CXXDestructorDecl>(ND))
-      getCXXABI().getMangleContext().mangleCXXDtor(D, GD.getDtorType(), Out);
+      MC.mangleCXXDtor(D, GD.getDtorType(), Out);
     else
-      getCXXABI().getMangleContext().mangleName(ND, Out);
-    Str = Out.str();
+      MC.mangleName(ND, Out);
   } else {
     IdentifierInfo *II = ND->getIdentifier();
     assert(II && "Attempt to mangle unnamed decl.");
@@ -764,14 +777,74 @@ StringRef CodeGenModule::getMangledName(GlobalDecl GD) {
         FD->getType()->castAs<FunctionType>()->getCallConv() == CC_X86RegCall) {
       llvm::raw_svector_ostream Out(Buffer);
       Out << "__regcall3__" << II->getName();
-      Str = Out.str();
     } else {
-      Str = II->getName();
+      Out << II->getName();
+    }
+  }
+
+  if (const auto *FD = dyn_cast<FunctionDecl>(ND))
+    if (FD->isMultiVersion() && !OmitTargetMangling)
+      AppendTargetMangling(CGM, FD->getAttr<TargetAttr>(), Out);
+  return Out.str();
+}
+
+void CodeGenModule::UpdateMultiVersionNames(GlobalDecl GD,
+                                            const FunctionDecl *FD) {
+  if (!FD->isMultiVersion())
+    return;
+
+  // Get the name of what this would be without the 'target' attribute.  This
+  // allows us to lookup the version that was emitted when this wasn't a
+  // multiversion function.
+  std::string NonTargetName =
+      getMangledNameImpl(*this, GD, FD, /*OmitTargetMangling=*/true);
+  GlobalDecl OtherGD;
+  if (lookupRepresentativeDecl(NonTargetName, OtherGD)) {
+    assert(OtherGD.getCanonicalDecl()
+               .getDecl()
+               ->getAsFunction()
+               ->isMultiVersion() &&
+           "Other GD should now be a multiversioned function");
+    // OtherFD is the version of this function that was mangled BEFORE
+    // becoming a MultiVersion function.  It potentially needs to be updated.
+    const FunctionDecl *OtherFD =
+        OtherGD.getCanonicalDecl().getDecl()->getAsFunction();
+    std::string OtherName = getMangledNameImpl(*this, OtherGD, OtherFD);
+    // This is so that if the initial version was already the 'default'
+    // version, we don't try to update it.
+    if (OtherName != NonTargetName) {
+      Manglings.erase(NonTargetName);
+      auto Result = Manglings.insert(std::make_pair(OtherName, OtherGD));
+      MangledDeclNames[OtherGD.getCanonicalDecl()] = Result.first->first();
+      if (llvm::GlobalValue *Entry = GetGlobalValue(NonTargetName))
+        Entry->setName(OtherName);
     }
   }
+}
+
+StringRef CodeGenModule::getMangledName(GlobalDecl GD) {
+  GlobalDecl CanonicalGD = GD.getCanonicalDecl();
+
+  // Some ABIs don't have constructor variants.  Make sure that base and
+  // complete constructors get mangled the same.
+  if (const auto *CD = dyn_cast<CXXConstructorDecl>(CanonicalGD.getDecl())) {
+    if (!getTarget().getCXXABI().hasConstructorVariants()) {
+      CXXCtorType OrigCtorType = GD.getCtorType();
+      assert(OrigCtorType == Ctor_Base || OrigCtorType == Ctor_Complete);
+      if (OrigCtorType == Ctor_Base)
+        CanonicalGD = GlobalDecl(CD, Ctor_Complete);
+    }
+  }
+
+  auto FoundName = MangledDeclNames.find(CanonicalGD);
+  if (FoundName != MangledDeclNames.end())
+    return FoundName->second;
+
 
   // Keep the first result in the case of a mangling collision.
-  auto Result = Manglings.insert(std::make_pair(Str, GD));
+  const auto *ND = cast<NamedDecl>(GD.getDecl());
+  auto Result =
+      Manglings.insert(std::make_pair(getMangledNameImpl(*this, GD, ND), GD));
   return MangledDeclNames[CanonicalGD] = Result.first->first();
 }
 
@@ -2061,6 +2134,74 @@ void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD, llvm::GlobalValue *GV) {
 static void ReplaceUsesOfNonProtoTypeWithRealFunction(llvm::GlobalValue *Old,
                                                       llvm::Function *NewFn);
 
+void CodeGenModule::emitMultiVersionFunctions() {
+  for (GlobalDecl GD : MultiVersionFuncs) {
+    SmallVector<CodeGenFunction::MultiVersionResolverOption, 10> Options;
+    const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
+    getContext().forEachMultiversionedFunctionVersion(
+        FD, [this, &GD, &Options](const FunctionDecl *CurFD) {
+          GlobalDecl CurGD{
+              (CurFD->isDefined() ? CurFD->getDefinition() : CurFD)};
+          StringRef MangledName = getMangledName(CurGD);
+          llvm::Constant *Func = GetGlobalValue(MangledName);
+          if (!Func) {
+            if (CurFD->isDefined()) {
+              EmitGlobalFunctionDefinition(CurGD, nullptr);
+              Func = GetGlobalValue(MangledName);
+            } else {
+              const CGFunctionInfo &FI =
+                  getTypes().arrangeGlobalDeclaration(GD);
+              llvm::FunctionType *Ty = getTypes().GetFunctionType(FI);
+              Func = GetAddrOfFunction(CurGD, Ty, /*ForVTable=*/false,
+                                       /*DontDefer=*/false, ForDefinition);
+            }
+            assert(Func && "This should have just been created");
+          }
+          Options.emplace_back(getTarget(), cast<llvm::Function>(Func),
+                               CurFD->getAttr<TargetAttr>()->parse());
+        });
+
+    llvm::Function *ResolverFunc = cast<llvm::Function>(
+        GetGlobalValue((getMangledName(GD) + ".resolver").str()));
+    std::stable_sort(
+        Options.begin(), Options.end(),
+        std::greater<CodeGenFunction::MultiVersionResolverOption>());
+    CodeGenFunction CGF(*this);
+    CGF.EmitMultiVersionResolver(ResolverFunc, Options);
+  }
+}
+
+/// If an ifunc for the specified mangled name is not in the module, create and
+/// return an llvm IFunc Function with the specified type.
+llvm::Constant *
+CodeGenModule::GetOrCreateMultiVersionIFunc(GlobalDecl GD, llvm::Type *DeclTy,
+                                            StringRef MangledName,
+                                            const FunctionDecl *FD) {
+  std::string IFuncName = (MangledName + ".ifunc").str();
+  if (llvm::GlobalValue *IFuncGV = GetGlobalValue(IFuncName))
+    return IFuncGV;
+
+  // 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.
+  MultiVersionFuncs.push_back(GD);
+
+  std::string ResolverName = (MangledName + ".resolver").str();
+  llvm::Type *ResolverType = llvm::FunctionType::get(
+      llvm::PointerType::get(DeclTy,
+                             Context.getTargetAddressSpace(FD->getType())),
+      false);
+  llvm::Constant *Resolver =
+      GetOrCreateLLVMFunction(ResolverName, ResolverType, GlobalDecl{},
+                              /*ForVTable=*/false);
+  llvm::GlobalIFunc *GIF = llvm::GlobalIFunc::create(
+      DeclTy, 0, llvm::Function::ExternalLinkage, "", Resolver, &getModule());
+  GIF->setName(IFuncName);
+  SetCommonAttributes(FD, GIF);
+
+  return GIF;
+}
+
 /// GetOrCreateLLVMFunction - If the specified mangled name is not in the
 /// module, create and return an llvm Function with the specified type. If there
 /// is something in the module with the specified name, return it potentially
@@ -2074,6 +2215,16 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction(
     ForDefinition_t IsForDefinition) {
   const Decl *D = GD.getDecl();
 
+  // Any attempts to use a MultiVersion function should result in retrieving
+  // the iFunc instead. Name Mangling will handle the rest of the changes.
+  if (const FunctionDecl *FD = cast_or_null<FunctionDecl>(D)) {
+    if (FD->isMultiVersion() && FD->getAttr<TargetAttr>()->isDefaultVersion()) {
+      UpdateMultiVersionNames(GD, FD);
+      if (!IsForDefinition)
+        return GetOrCreateMultiVersionIFunc(GD, Ty, MangledName, FD);
+    }
+  }
+
   // Lookup the entry, lazily creating it if necessary.
   llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
   if (Entry) {
index 22c4463b2c810c4726431b160be664f97e82bde4..e63fa287064b0aaf30ec6bd4e3561327362c16f9 100644 (file)
@@ -324,6 +324,10 @@ private:
   /// is defined once we get to the end of the of the translation unit.
   std::vector<GlobalDecl> Aliases;
 
+  /// List of multiversion functions that have to be emitted.  Used to make sure
+  /// we properly emit the iFunc.
+  std::vector<GlobalDecl> MultiVersionFuncs;
+
   typedef llvm::StringMap<llvm::TrackingVH<llvm::Constant> > ReplacementsTy;
   ReplacementsTy Replacements;
 
@@ -1247,6 +1251,12 @@ private:
       llvm::AttributeList ExtraAttrs = llvm::AttributeList(),
       ForDefinition_t IsForDefinition = NotForDefinition);
 
+  llvm::Constant *GetOrCreateMultiVersionIFunc(GlobalDecl GD,
+                                               llvm::Type *DeclTy,
+                                               StringRef MangledName,
+                                               const FunctionDecl *FD);
+  void UpdateMultiVersionNames(GlobalDecl GD, const FunctionDecl *FD);
+
   llvm::Constant *GetOrCreateLLVMGlobal(StringRef MangledName,
                                         llvm::PointerType *PTy,
                                         const VarDecl *D,
@@ -1319,6 +1329,8 @@ private:
 
   void checkAliases();
 
+  void emitMultiVersionFunctions();
+
   /// Emit any vtables which we deferred and still have a use for.
   void EmitDeferredVTables();
 
index 4e57e5ef81c6f26494ecc485139accda8d4b1371..e07f60989d8f5c3df3dd23ebe1d232ae6ad2f04b 100644 (file)
@@ -1647,6 +1647,12 @@ static void noteOverloads(Sema &S, const UnresolvedSetImpl &Overloads,
     }
 
     NamedDecl *Fn = (*It)->getUnderlyingDecl();
+    // Don't print overloads for non-default multiversioned functions.
+    if (const auto *FD = Fn->getAsFunction()) {
+      if (FD->isMultiVersion() &&
+          !FD->getAttr<TargetAttr>()->isDefaultVersion())
+        continue;
+    }
     S.Diag(Fn->getLocation(), diag::note_possible_target_of_call);
     ++ShownOverloads;
   }
index 2b2ab42427bd59ad94f3026933bfd3979a1a1801..24ed78c8bfd8f7dce40e3cd8ff673a92a6fc824e 100644 (file)
@@ -9157,6 +9157,359 @@ bool Sema::shouldLinkDependentDeclWithPrevious(Decl *D, Decl *PrevDecl) {
            D->getFriendObjectKind() != Decl::FOK_None);
 }
 
+/// \brief Check the target attribute of the function for MultiVersion
+/// validity.
+///
+/// Returns true if there was an error, false otherwise.
+static bool CheckMultiVersionValue(Sema &S, const FunctionDecl *FD) {
+  const auto *TA = FD->getAttr<TargetAttr>();
+  assert(TA && "MultiVersion Candidate requires a target attribute");
+  TargetAttr::ParsedTargetAttr ParseInfo = TA->parse();
+  const TargetInfo &TargetInfo = S.Context.getTargetInfo();
+  enum ErrType { Feature = 0, Architecture = 1 };
+
+  if (!ParseInfo.Architecture.empty() &&
+      !TargetInfo.validateCpuIs(ParseInfo.Architecture)) {
+    S.Diag(FD->getLocation(), diag::err_bad_multiversion_option)
+        << Architecture << ParseInfo.Architecture;
+    return true;
+  }
+
+  for (const auto &Feature : ParseInfo.Features) {
+    auto BareFeat = StringRef{Feature}.substr(1);
+    if (Feature[0] == '-') {
+      S.Diag(FD->getLocation(), diag::err_bad_multiversion_option)
+          << Feature << ("no-" + BareFeat).str();
+      return true;
+    }
+
+    if (!TargetInfo.validateCpuSupports(BareFeat) ||
+        !TargetInfo.isValidFeatureName(BareFeat)) {
+      S.Diag(FD->getLocation(), diag::err_bad_multiversion_option)
+          << Feature << BareFeat;
+      return true;
+    }
+  }
+  return false;
+}
+
+static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD,
+                                             const FunctionDecl *NewFD,
+                                             bool CausesMV) {
+  enum DoesntSupport {
+    FuncTemplates = 0,
+    VirtFuncs = 1,
+    DeducedReturn = 2,
+    Constructors = 3,
+    Destructors = 4,
+    DeletedFuncs = 5,
+    DefaultedFuncs = 6
+  };
+  enum Different {
+    CallingConv = 0,
+    ReturnType = 1,
+    ConstexprSpec = 2,
+    InlineSpec = 3,
+    StorageClass = 4,
+    Linkage = 5
+  };
+
+  // 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) {
+    S.Diag(OldFD->getLocation(), diag::err_multiversion_no_other_attrs);
+    S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here);
+    return true;
+  }
+
+  if (std::distance(NewFD->attr_begin(), NewFD->attr_end()) != 1) {
+    S.Diag(NewFD->getLocation(), diag::err_multiversion_no_other_attrs);
+    return true;
+  }
+
+  if (NewFD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate) {
+    S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support)
+        << FuncTemplates;
+    return true;
+  }
+
+
+  if (const auto *NewCXXFD = dyn_cast<CXXMethodDecl>(NewFD)) {
+    if (NewCXXFD->isVirtual()) {
+      S.Diag(NewCXXFD->getLocation(), diag::err_multiversion_doesnt_support)
+          << VirtFuncs;
+      return true;
+    }
+
+    if (const auto *NewCXXCtor = dyn_cast<CXXConstructorDecl>(NewFD)) {
+      S.Diag(NewCXXCtor->getLocation(), diag::err_multiversion_doesnt_support)
+          << Constructors;
+      return true;
+    }
+
+    if (const auto *NewCXXDtor = dyn_cast<CXXDestructorDecl>(NewFD)) {
+      S.Diag(NewCXXDtor->getLocation(), diag::err_multiversion_doesnt_support)
+          << Destructors;
+      return true;
+    }
+  }
+
+  if (NewFD->isDeleted()) {
+    S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support)
+      << DeletedFuncs;
+  }
+  if (NewFD->isDefaulted()) {
+    S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support)
+      << DefaultedFuncs;
+  }
+
+  QualType NewQType = S.getASTContext().getCanonicalType(NewFD->getType());
+  const auto *NewType = cast<FunctionType>(NewQType);
+  QualType NewReturnType = NewType->getReturnType();
+
+  if (NewReturnType->isUndeducedType()) {
+    S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support)
+        << DeducedReturn;
+    return true;
+  }
+
+  // Only allow transition to MultiVersion if it hasn't been used.
+  if (OldFD && CausesMV && OldFD->isUsed(false)) {
+    S.Diag(NewFD->getLocation(), diag::err_multiversion_after_used);
+    return true;
+  }
+
+  // Ensure the return type is identical.
+  if (OldFD) {
+    QualType OldQType = S.getASTContext().getCanonicalType(OldFD->getType());
+    const auto *OldType = cast<FunctionType>(OldQType);
+    FunctionType::ExtInfo OldTypeInfo = OldType->getExtInfo();
+    FunctionType::ExtInfo NewTypeInfo = NewType->getExtInfo();
+
+    if (OldTypeInfo.getCC() != NewTypeInfo.getCC()) {
+      S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << CallingConv;
+      return true;
+    }
+
+    QualType OldReturnType = OldType->getReturnType();
+
+    if (OldReturnType != NewReturnType) {
+      S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << ReturnType;
+      return true;
+    }
+
+    if (OldFD->isConstexpr() != NewFD->isConstexpr()) {
+      S.Diag(NewFD->getLocation(), diag::err_multiversion_diff)
+          << ConstexprSpec;
+      return true;
+    }
+
+    if (OldFD->isInlineSpecified() != NewFD->isInlineSpecified()) {
+      S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << InlineSpec;
+      return true;
+    }
+
+    if (OldFD->getStorageClass() != NewFD->getStorageClass()) {
+      S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << StorageClass;
+      return true;
+    }
+
+    if (OldFD->isExternC() != NewFD->isExternC()) {
+      S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << Linkage;
+      return true;
+    }
+
+    if (S.CheckEquivalentExceptionSpec(
+            OldFD->getType()->getAs<FunctionProtoType>(), OldFD->getLocation(),
+            NewFD->getType()->getAs<FunctionProtoType>(), NewFD->getLocation()))
+      return true;
+  }
+  return false;
+}
+
+/// \brief Check the validity of a mulitversion function declaration.
+/// Also sets the multiversion'ness' of the function itself.
+///
+/// This sets NewFD->isInvalidDecl() to true if there was an error.
+///
+/// Returns true if there was an error, false otherwise.
+static bool CheckMultiVersionFunction(Sema &S, FunctionDecl *NewFD,
+                                      bool &Redeclaration, NamedDecl *&OldDecl,
+                                      bool &MergeTypeWithPrevious,
+                                      LookupResult &Previous) {
+  const auto *NewTA = NewFD->getAttr<TargetAttr>();
+  if (NewFD->isMain()) {
+    if (NewTA && NewTA->isDefaultVersion()) {
+      S.Diag(NewFD->getLocation(), diag::err_multiversion_not_allowed_on_main);
+      NewFD->isInvalidDecl();
+      return true;
+    }
+    return false;
+  }
+
+  // If there is no matching previous decl, only 'default' can
+  // cause MultiVersioning.
+  if (!OldDecl) {
+    if (NewTA && NewTA->isDefaultVersion()) {
+      if (!NewFD->getType()->getAs<FunctionProtoType>()) {
+        S.Diag(NewFD->getLocation(), diag::err_multiversion_noproto);
+        NewFD->setInvalidDecl();
+        return true;
+      }
+      if (CheckMultiVersionAdditionalRules(S, nullptr, NewFD, true)) {
+        NewFD->setInvalidDecl();
+        return true;
+      }
+      if (!S.getASTContext().getTargetInfo().supportsMultiVersioning()) {
+        S.Diag(NewFD->getLocation(), diag::err_multiversion_not_supported);
+        return true;
+      }
+
+      NewFD->setIsMultiVersion();
+    }
+    return false;
+  }
+
+  if (OldDecl->getDeclContext()->getRedeclContext() !=
+      NewFD->getDeclContext()->getRedeclContext())
+    return false;
+
+  FunctionDecl *OldFD = OldDecl->getAsFunction();
+  // Unresolved 'using' statements (the other way OldDecl can be not a function)
+  // likely cannot cause a problem here.
+  if (!OldFD)
+    return false;
+
+  if (!OldFD->isMultiVersion() && !NewTA)
+    return false;
+
+  if (OldFD->isMultiVersion() && !NewTA) {
+    S.Diag(NewFD->getLocation(), diag::err_target_required_in_redecl);
+    NewFD->setInvalidDecl();
+    return true;
+  }
+
+  TargetAttr::ParsedTargetAttr NewParsed = NewTA->parse();
+  // Sort order doesn't matter, it just needs to be consistent.
+  std::sort(NewParsed.Features.begin(), NewParsed.Features.end());
+
+  const auto *OldTA = OldFD->getAttr<TargetAttr>();
+  if (!OldFD->isMultiVersion()) {
+    // If the old decl is NOT MultiVersioned yet, and we don't cause that
+    // to change, this is a simple redeclaration.
+    if (!OldTA || OldTA->getFeaturesStr() == NewTA->getFeaturesStr())
+      return false;
+
+    // Otherwise, this decl causes MultiVersioning.
+    if (!S.getASTContext().getTargetInfo().supportsMultiVersioning()) {
+      S.Diag(NewFD->getLocation(), diag::err_multiversion_not_supported);
+      S.Diag(OldFD->getLocation(), diag::note_previous_declaration);
+      return true;
+    }
+
+    if (!OldFD->getType()->getAs<FunctionProtoType>()) {
+      S.Diag(OldFD->getLocation(), diag::err_multiversion_noproto);
+      S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here);
+      NewFD->setInvalidDecl();
+      return true;
+    }
+
+    if (CheckMultiVersionValue(S, NewFD)) {
+      NewFD->setInvalidDecl();
+      return true;
+    }
+
+    if (CheckMultiVersionValue(S, OldFD)) {
+      S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here);
+      NewFD->setInvalidDecl();
+      return true;
+    }
+
+    TargetAttr::ParsedTargetAttr OldParsed =
+        OldTA->parse(std::less<std::string>());
+
+    if (OldParsed == NewParsed) {
+      S.Diag(NewFD->getLocation(), diag::err_multiversion_duplicate);
+      S.Diag(OldFD->getLocation(), diag::note_previous_declaration);
+      NewFD->setInvalidDecl();
+      return true;
+    }
+
+    for (const auto *FD : OldFD->redecls()) {
+      const auto *CurTA = FD->getAttr<TargetAttr>();
+      if (!CurTA || CurTA->isInherited()) {
+        S.Diag(FD->getLocation(), diag::err_target_required_in_redecl);
+        S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here);
+        NewFD->setInvalidDecl();
+        return true;
+      }
+    }
+
+    if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, true)) {
+      NewFD->setInvalidDecl();
+      return true;
+    }
+
+    OldFD->setIsMultiVersion();
+    NewFD->setIsMultiVersion();
+    Redeclaration = false;
+    MergeTypeWithPrevious = false;
+    OldDecl = nullptr;
+    Previous.clear();
+    return false;
+  }
+
+  bool UseMemberUsingDeclRules =
+      S.CurContext->isRecord() && !NewFD->getFriendObjectKind();
+
+  // Next, check ALL non-overloads to see if this is a redeclaration of a
+  // previous member of the MultiVersion set.
+  for (NamedDecl *ND : Previous) {
+    FunctionDecl *CurFD = ND->getAsFunction();
+    if (!CurFD)
+      continue;
+    if (S.IsOverload(NewFD, CurFD, UseMemberUsingDeclRules))
+      continue;
+
+    const auto *CurTA = CurFD->getAttr<TargetAttr>();
+    if (CurTA->getFeaturesStr() == NewTA->getFeaturesStr()) {
+      NewFD->setIsMultiVersion();
+      Redeclaration = true;
+      OldDecl = ND;
+      return false;
+    }
+
+    TargetAttr::ParsedTargetAttr CurParsed =
+        CurTA->parse(std::less<std::string>());
+
+    if (CurParsed == NewParsed) {
+      S.Diag(NewFD->getLocation(), diag::err_multiversion_duplicate);
+      S.Diag(CurFD->getLocation(), diag::note_previous_declaration);
+      NewFD->setInvalidDecl();
+      return true;
+    }
+  }
+
+  // Else, this is simply a non-redecl case.
+  if (CheckMultiVersionValue(S, NewFD)) {
+    NewFD->setInvalidDecl();
+    return true;
+  }
+
+  if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, false)) {
+    NewFD->setInvalidDecl();
+    return true;
+  }
+
+  NewFD->setIsMultiVersion();
+  Redeclaration = false;
+  MergeTypeWithPrevious = false;
+  OldDecl = nullptr;
+  Previous.clear();
+  return false;
+}
+
 /// \brief Perform semantic checking of a new function declaration.
 ///
 /// Performs semantic analysis of the new function declaration
@@ -9244,6 +9597,10 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
     }
   }
 
+  if (CheckMultiVersionFunction(*this, NewFD, Redeclaration, OldDecl,
+                                MergeTypeWithPrevious, Previous))
+    return Redeclaration;
+
   // C++11 [dcl.constexpr]p8:
   //   A constexpr specifier for a non-static member function that is not
   //   a constructor declares that member function to be const.
index 05434903221931a8decba31d7500c9c57b491cb0..b760cdcd34e57d72181deea979d1f699a52177e8 100644 (file)
@@ -5957,6 +5957,13 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
   Candidate.IgnoreObjectArgument = false;
   Candidate.ExplicitCallArguments = Args.size();
 
+  if (Function->isMultiVersion() &&
+      !Function->getAttr<TargetAttr>()->isDefaultVersion()) {
+    Candidate.Viable = false;
+    Candidate.FailureKind = ovl_non_default_multiversion_function;
+    return;
+  }
+
   if (Constructor) {
     // C++ [class.copy]p3:
     //   A member function template is never instantiated to perform the copy
@@ -6581,6 +6588,12 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
     Candidate.DeductionFailure.Data = FailedAttr;
     return;
   }
+
+  if (Method->isMultiVersion() &&
+      !Method->getAttr<TargetAttr>()->isDefaultVersion()) {
+    Candidate.Viable = false;
+    Candidate.FailureKind = ovl_non_default_multiversion_function;
+  }
 }
 
 /// \brief Add a C++ member function template as a candidate to the candidate
@@ -6984,6 +6997,12 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion,
     Candidate.DeductionFailure.Data = FailedAttr;
     return;
   }
+
+  if (Conversion->isMultiVersion() &&
+      !Conversion->getAttr<TargetAttr>()->isDefaultVersion()) {
+    Candidate.Viable = false;
+    Candidate.FailureKind = ovl_non_default_multiversion_function;
+  }
 }
 
 /// \brief Adds a conversion function template specialization
@@ -9392,6 +9411,8 @@ void Sema::NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,
                                  QualType DestType, bool TakingAddress) {
   if (TakingAddress && !checkAddressOfCandidateIsAvailable(*this, Fn))
     return;
+  if (Fn->isMultiVersion() && !Fn->getAttr<TargetAttr>()->isDefaultVersion())
+    return;
 
   std::string FnDesc;
   OverloadCandidateKind K = ClassifyOverloadCandidate(*this, Found, Fn, FnDesc);
@@ -10193,6 +10214,9 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
     assert(!Available);
     break;
   }
+  case ovl_non_default_multiversion_function:
+    // Do nothing, these should simply be ignored.
+    break;
   }
 }
 
@@ -10929,6 +10953,12 @@ private:
         if (FunctionDecl *Caller = dyn_cast<FunctionDecl>(S.CurContext))
           if (!Caller->isImplicit() && !S.IsAllowedCUDACall(Caller, FunDecl))
             return false;
+      if (FunDecl->isMultiVersion()) {
+        const auto *TA = FunDecl->getAttr<TargetAttr>();
+        assert(TA && "Multiversioned functions require a target attribute");
+        if (!TA->isDefaultVersion())
+          return false;
+      }
 
       // If any candidate has a placeholder return type, trigger its deduction
       // now.
index 0cf9afb0759c9a3644dfc148b3dc60f52ca66e63..f3eab2e6d786c5f5f3452fd73888161118806759 100644 (file)
@@ -792,6 +792,7 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
   FD->IsConstexpr = Record.readInt();
   FD->UsesSEHTry = Record.readInt();
   FD->HasSkippedBody = Record.readInt();
+  FD->IsMultiVersion = Record.readInt();
   FD->IsLateTemplateParsed = Record.readInt();
   FD->setCachedLinkage(Linkage(Record.readInt()));
   FD->EndRangeLoc = ReadSourceLocation();
@@ -2818,6 +2819,21 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) {
                         CtorY->getInheritedConstructor().getConstructor()))
         return false;
     }
+
+    if (FuncX->isMultiVersion() != FuncY->isMultiVersion())
+      return false;
+
+    // Multiversioned functions with different feature strings are represented
+    // as separate declarations.
+    if (FuncX->isMultiVersion()) {
+      const auto *TAX = FuncX->getAttr<TargetAttr>();
+      const auto *TAY = FuncY->getAttr<TargetAttr>();
+      assert(TAX && TAY && "Multiversion Function without target attribute");
+
+      if (TAX->getFeaturesStr() != TAY->getFeaturesStr())
+        return false;
+    }
+
     ASTContext &C = FuncX->getASTContext();
     if (!C.hasSameType(FuncX->getType(), FuncY->getType())) {
       // We can get functions with different types on the redecl chain in C++17
index 341f0e16075cc9004c24714eebba266abbec0e6e..c2c6fd44d78ab1be55c1f3d2720ef349ea541d2a 100644 (file)
@@ -534,6 +534,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
   Record.push_back(D->IsConstexpr);
   Record.push_back(D->UsesSEHTry);
   Record.push_back(D->HasSkippedBody);
+  Record.push_back(D->IsMultiVersion);
   Record.push_back(D->IsLateTemplateParsed);
   Record.push_back(D->getLinkageInternal());
   Record.AddSourceLocation(D->getLocEnd());
@@ -2072,6 +2073,7 @@ void ASTWriter::WriteDeclAbbrevs() {
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Constexpr
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // UsesSEHTry
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // SkippedBody
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // MultiVersion
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // LateParsed
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6));   // LocEnd
diff --git a/test/CodeGen/attr-target-mv-func-ptrs.c b/test/CodeGen/attr-target-mv-func-ptrs.c
new file mode 100644 (file)
index 0000000..5df9a92
--- /dev/null
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+int __attribute__((target("sse4.2"))) foo(int i) { return 0; }
+int __attribute__((target("arch=sandybridge"))) foo(int);
+int __attribute__((target("arch=ivybridge"))) foo(int i) {return 1;}
+int __attribute__((target("default"))) foo(int i) { return 2; }
+
+typedef int (*FuncPtr)(int);
+void func(FuncPtr);
+
+int bar() {
+  func(foo);
+  FuncPtr Free = &foo;
+  FuncPtr Free2 = foo;
+
+  return 0;
+  return Free(1) + Free(2);
+}
+
+// CHECK: @foo.ifunc = ifunc i32 (i32), i32 (i32)* ()* @foo.resolver
+// CHECK: define i32 @foo.sse4.2(
+// CHECK: ret i32 0
+// CHECK: define i32 @foo.arch_ivybridge(
+// CHECK: ret i32 1
+// CHECK: define i32 @foo(
+// CHECK: ret i32 2
+
+// CHECK: define i32 @bar()
+// CHECK: call void @func(i32 (i32)* @foo.ifunc)
+// CHECK: store i32 (i32)* @foo.ifunc
+// CHECK: store i32 (i32)* @foo.ifunc
+
+// CHECK: declare i32 @foo.arch_sandybridge(
diff --git a/test/CodeGen/attr-target-mv-va-args.c b/test/CodeGen/attr-target-mv-va-args.c
new file mode 100644 (file)
index 0000000..c5d92db
--- /dev/null
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+int __attribute__((target("sse4.2"))) foo(int i, ...) { return 0; }
+int __attribute__((target("arch=sandybridge"))) foo(int i, ...);
+int __attribute__((target("arch=ivybridge"))) foo(int i, ...) {return 1;}
+int __attribute__((target("default"))) foo(int i, ...) { return 2; }
+
+int bar() {
+  return foo(1, 'a', 1.1) + foo(2, 2.2, "asdf");
+}
+
+// CHECK: @foo.ifunc = ifunc i32 (i32, ...), i32 (i32, ...)* ()* @foo.resolver
+// CHECK: define i32 @foo.sse4.2(i32 %i, ...)
+// CHECK: ret i32 0
+// CHECK: define i32 @foo.arch_ivybridge(i32 %i, ...)
+// CHECK: ret i32 1
+// CHECK: define i32 @foo(i32 %i, ...)
+// CHECK: ret i32 2
+// CHECK: define i32 @bar()
+// CHECK: call i32 (i32, ...) @foo.ifunc(i32 1, i32 97, double
+// CHECK: call i32 (i32, ...) @foo.ifunc(i32 2, double 2.2{{[0-9Ee+]+}}, i8* getelementptr inbounds 
+// CHECK: define i32 (i32, ...)* @foo.resolver()
+// CHECK: ret i32 (i32, ...)* @foo.arch_sandybridge
+// CHECK: ret i32 (i32, ...)* @foo.arch_ivybridge
+// CHECK: ret i32 (i32, ...)* @foo.sse4.2
+// CHECK: ret i32 (i32, ...)* @foo
+// CHECK: declare i32 @foo.arch_sandybridge(i32, ...)
diff --git a/test/CodeGen/attr-target-mv.c b/test/CodeGen/attr-target-mv.c
new file mode 100644 (file)
index 0000000..a877fd6
--- /dev/null
@@ -0,0 +1,79 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+int __attribute__((target("sse4.2"))) foo(void) { return 0; }
+int __attribute__((target("arch=sandybridge"))) foo(void);
+int __attribute__((target("arch=ivybridge"))) foo(void) {return 1;}
+int __attribute__((target("default"))) foo(void) { return 2; }
+
+int bar() {
+  return foo();
+}
+
+inline int __attribute__((target("sse4.2"))) foo_inline(void) { return 0; }
+inline int __attribute__((target("arch=sandybridge"))) foo_inline(void);
+inline int __attribute__((target("arch=ivybridge"))) foo_inline(void) {return 1;}
+inline int __attribute__((target("default"))) foo_inline(void) { return 2; }
+
+int bar2() {
+  return foo_inline();
+}
+
+inline __attribute__((target("default"))) void foo_decls(void);
+inline __attribute__((target("sse4.2"))) void foo_decls(void);
+void bar3() {
+  foo_decls();
+}
+inline __attribute__((target("default"))) void foo_decls(void) {}
+inline __attribute__((target("sse4.2"))) void foo_decls(void) {}
+
+// CHECK: @foo.ifunc = ifunc i32 (), i32 ()* ()* @foo.resolver
+// CHECK: @foo_inline.ifunc = ifunc i32 (), i32 ()* ()* @foo_inline.resolver
+// CHECK: @foo_decls.ifunc = ifunc void (), void ()* ()* @foo_decls.resolver
+
+// CHECK: define i32 @foo.sse4.2()
+// CHECK: ret i32 0
+// CHECK: define i32 @foo.arch_ivybridge()
+// CHECK: ret i32 1
+// CHECK: define i32 @foo()
+// CHECK: ret i32 2
+// CHECK: define i32 @bar()
+// CHECK: call i32 @foo.ifunc()
+
+// CHECK: define i32 ()* @foo.resolver()
+// CHECK: call void @__cpu_indicator_init()
+// CHECK: ret i32 ()* @foo.arch_sandybridge
+// CHECK: ret i32 ()* @foo.arch_ivybridge
+// CHECK: ret i32 ()* @foo.sse4.2
+// CHECK: ret i32 ()* @foo
+
+// CHECK: define i32 @bar2()
+// CHECK: call i32 @foo_inline.ifunc()
+
+// CHECK: define i32 ()* @foo_inline.resolver()
+// CHECK: call void @__cpu_indicator_init()
+// CHECK: ret i32 ()* @foo_inline.arch_sandybridge
+// CHECK: ret i32 ()* @foo_inline.arch_ivybridge
+// CHECK: ret i32 ()* @foo_inline.sse4.2
+// CHECK: ret i32 ()* @foo_inline
+
+// CHECK: define void @bar3()
+// CHECK: call void @foo_decls.ifunc()
+
+// CHECK: define void ()* @foo_decls.resolver()
+// CHECK: ret void ()* @foo_decls.sse4.2
+// CHECK: ret void ()* @foo_decls
+
+// CHECK: declare i32 @foo.arch_sandybridge()
+
+// CHECK: define available_externally i32 @foo_inline.sse4.2()
+// CHECK: ret i32 0
+
+// CHECK: declare i32 @foo_inline.arch_sandybridge()
+//
+// CHECK: define available_externally i32 @foo_inline.arch_ivybridge()
+// CHECK: ret i32 1
+// CHECK: define available_externally i32 @foo_inline()
+// CHECK: ret i32 2
+
+// CHECK: define available_externally void @foo_decls()
+// CHECK: define available_externally void @foo_decls.sse4.2()
+
diff --git a/test/CodeGenCXX/attr-target-mv-diff-ns.cpp b/test/CodeGenCXX/attr-target-mv-diff-ns.cpp
new file mode 100644 (file)
index 0000000..1852750
--- /dev/null
@@ -0,0 +1,54 @@
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+// Test ensures that this properly differentiates between types in different 
+// namespaces.
+int __attribute__((target("sse4.2"))) foo(int) { return 0; }
+int __attribute__((target("arch=sandybridge"))) foo(int);
+int __attribute__((target("arch=ivybridge"))) foo(int) {return 1;}
+int __attribute__((target("default"))) foo(int) { return 2; }
+
+namespace ns {
+int __attribute__((target("sse4.2"))) foo(int) { return 0; }
+int __attribute__((target("arch=sandybridge"))) foo(int);
+int __attribute__((target("arch=ivybridge"))) foo(int) {return 1;}
+int __attribute__((target("default"))) foo(int) { return 2; }
+}
+
+int bar() {
+  return foo(1) + ns::foo(2);
+}
+
+// CHECK: @_Z3fooi.ifunc = ifunc i32 (i32), i32 (i32)* ()* @_Z3fooi.resolver
+// CHECK: @_ZN2ns3fooEi.ifunc = ifunc i32 (i32), i32 (i32)* ()* @_ZN2ns3fooEi.resolver
+
+// CHECK: define i32 @_Z3fooi.sse4.2(i32)
+// CHECK: ret i32 0
+// CHECK: define i32 @_Z3fooi.arch_ivybridge(i32)
+// CHECK: ret i32 1
+// CHECK: define i32 @_Z3fooi(i32)
+// CHECK: ret i32 2
+
+// CHECK: define i32 @_ZN2ns3fooEi.sse4.2(i32)
+// CHECK: ret i32 0
+// CHECK: define i32 @_ZN2ns3fooEi.arch_ivybridge(i32)
+// CHECK: ret i32 1
+// CHECK: define i32 @_ZN2ns3fooEi(i32)
+// CHECK: ret i32 2
+
+// CHECK: define i32 @_Z3barv()
+// CHECK: call i32 @_Z3fooi.ifunc(i32 1)
+// CHECK: call i32 @_ZN2ns3fooEi.ifunc(i32 2)
+
+// CHECK: define i32 (i32)* @_Z3fooi.resolver()
+// CHECK: ret i32 (i32)* @_Z3fooi.arch_sandybridge
+// CHECK: ret i32 (i32)* @_Z3fooi.arch_ivybridge
+// CHECK: ret i32 (i32)* @_Z3fooi.sse4.2
+// CHECK: ret i32 (i32)* @_Z3fooi
+//
+// CHECK: define i32 (i32)* @_ZN2ns3fooEi.resolver()
+// CHECK: ret i32 (i32)* @_ZN2ns3fooEi.arch_sandybridge
+// CHECK: ret i32 (i32)* @_ZN2ns3fooEi.arch_ivybridge
+// CHECK: ret i32 (i32)* @_ZN2ns3fooEi.sse4.2
+// CHECK: ret i32 (i32)* @_ZN2ns3fooEi
+
+// CHECK: declare i32 @_Z3fooi.arch_sandybridge(i32)
+// CHECK: declare i32 @_ZN2ns3fooEi.arch_sandybridge(i32)
diff --git a/test/CodeGenCXX/attr-target-mv-func-ptrs.cpp b/test/CodeGenCXX/attr-target-mv-func-ptrs.cpp
new file mode 100644 (file)
index 0000000..290d6b5
--- /dev/null
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+void temp();
+void temp(int);
+using FP = void(*)(int);
+void b() {
+  FP f = temp; 
+}
+
+int __attribute__((target("sse4.2"))) foo(int) { return 0; }
+int __attribute__((target("arch=sandybridge"))) foo(int);
+int __attribute__((target("arch=ivybridge"))) foo(int) {return 1;}
+int __attribute__((target("default"))) foo(int) { return 2; }
+
+struct S {
+int __attribute__((target("sse4.2"))) foo(int) { return 0; }
+int __attribute__((target("arch=sandybridge"))) foo(int);
+int __attribute__((target("arch=ivybridge"))) foo(int) {return 1;}
+int __attribute__((target("default"))) foo(int) { return 2; }
+};
+
+using FuncPtr = int (*)(int);
+using MemFuncPtr = int (S::*)(int);
+
+void f(FuncPtr, MemFuncPtr);
+
+int bar() {
+  FuncPtr Free = &foo;
+  MemFuncPtr Member = &S::foo;
+  S s;
+  f(foo, &S::foo);
+  return Free(1) + (s.*Member)(2);
+}
+
+
+// CHECK: @_Z3fooi.ifunc 
+// CHECK: @_ZN1S3fooEi.ifunc
+
+// CHECK: define i32 @_Z3barv()
+// Store to Free of ifunc
+// CHECK: store i32 (i32)* @_Z3fooi.ifunc
+// Store to Member of ifunc
+// CHECK: store { i64, i64 } { i64 ptrtoint (i32 (%struct.S*, i32)* @_ZN1S3fooEi.ifunc to i64), i64 0 }, { i64, i64 }* [[MEMBER:%[a-z]+]]
+
+// Call to 'f' with the ifunc
+// CHECK: call void @_Z1fPFiiEM1SFiiE(i32 (i32)* @_Z3fooi.ifunc
diff --git a/test/CodeGenCXX/attr-target-mv-member-funcs.cpp b/test/CodeGenCXX/attr-target-mv-member-funcs.cpp
new file mode 100644 (file)
index 0000000..2c201c5
--- /dev/null
@@ -0,0 +1,137 @@
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+
+struct S {
+  int __attribute__((target("sse4.2"))) foo(int) { return 0; }
+  int __attribute__((target("arch=sandybridge"))) foo(int);
+  int __attribute__((target("arch=ivybridge"))) foo(int) { return 1; }
+  int __attribute__((target("default"))) foo(int) { return 2; }
+
+  S &__attribute__((target("arch=ivybridge"))) operator=(const S &) {
+    return *this;
+  }
+  S &__attribute__((target("default"))) operator=(const S &) {
+    return *this;
+  }
+};
+
+struct ConvertTo {
+  __attribute__((target("arch=ivybridge"))) operator S() const {
+    return S{};
+  }
+  __attribute__((target("default"))) operator S() const {
+    return S{};
+  }
+};
+
+int bar() {
+  S s;
+  S s2;
+  s2 = s;
+
+  ConvertTo C;
+  s2 = static_cast<S>(C);
+
+  return s.foo(0);
+}
+
+struct S2 {
+  int __attribute__((target("sse4.2"))) foo(int);
+  int __attribute__((target("arch=sandybridge"))) foo(int);
+  int __attribute__((target("arch=ivybridge"))) foo(int);
+  int __attribute__((target("default"))) foo(int);
+};
+
+int bar2() {
+  S2 s;
+  return s.foo(0);
+}
+
+int __attribute__((target("sse4.2"))) S2::foo(int) { return 0; }
+int __attribute__((target("arch=ivybridge"))) S2::foo(int) { return 1; }
+int __attribute__((target("default"))) S2::foo(int) { return 2; }
+
+template<typename T>
+struct templ {
+  int __attribute__((target("sse4.2"))) foo(int) { return 0; }
+  int __attribute__((target("arch=sandybridge"))) foo(int);
+  int __attribute__((target("arch=ivybridge"))) foo(int) { return 1; }
+  int __attribute__((target("default"))) foo(int) { return 2; }
+};
+
+int templ_use() {
+  templ<int> a;
+  templ<double> b;
+  return a.foo(1) + b.foo(2);
+}
+
+// CHECK: @_ZN1SaSERKS_.ifunc = ifunc %struct.S* (%struct.S*, %struct.S*), %struct.S* (%struct.S*, %struct.S*)* ()* @_ZN1SaSERKS_.resolver
+// CHECK: @_ZNK9ConvertTocv1SEv.ifunc = ifunc void (%struct.ConvertTo*), void (%struct.ConvertTo*)* ()* @_ZNK9ConvertTocv1SEv.resolver
+// CHECK: @_ZN1S3fooEi.ifunc = ifunc i32 (%struct.S*, i32), i32 (%struct.S*, i32)* ()* @_ZN1S3fooEi.resolver
+// CHECK: @_ZN2S23fooEi.ifunc = ifunc i32 (%struct.S2*, i32), i32 (%struct.S2*, i32)* ()* @_ZN2S23fooEi.resolver
+// Templates:
+// CHECK: @_ZN5templIiE3fooEi.ifunc = ifunc i32 (%struct.templ*, i32), i32 (%struct.templ*, i32)* ()* @_ZN5templIiE3fooEi.resolver
+// CHECK: @_ZN5templIdE3fooEi.ifunc = ifunc i32 (%struct.templ.0*, i32), i32 (%struct.templ.0*, i32)* ()* @_ZN5templIdE3fooEi.resolver
+
+// CHECK: define i32 @_Z3barv()
+// CHECK: %s = alloca %struct.S, align 1
+// CHECK: %s2 = alloca %struct.S, align 1
+// CHECK: %C = alloca %struct.ConvertTo, align 1
+// CHECK: call dereferenceable(1) %struct.S* @_ZN1SaSERKS_.ifunc(%struct.S* %s2
+// CHECK: call void @_ZNK9ConvertTocv1SEv.ifunc(%struct.ConvertTo* %C)
+// CHECK: call dereferenceable(1) %struct.S* @_ZN1SaSERKS_.ifunc(%struct.S* %s2
+// CHECK: call i32 @_ZN1S3fooEi.ifunc(%struct.S* %s, i32 0)
+
+// CHECK: define %struct.S* (%struct.S*, %struct.S*)* @_ZN1SaSERKS_.resolver()
+// CHECK: ret %struct.S* (%struct.S*, %struct.S*)* @_ZN1SaSERKS_.arch_ivybridge
+// CHECK: ret %struct.S* (%struct.S*, %struct.S*)* @_ZN1SaSERKS_
+
+// CHECK: define void (%struct.ConvertTo*)* @_ZNK9ConvertTocv1SEv.resolver()
+// CHECK: ret void (%struct.ConvertTo*)* @_ZNK9ConvertTocv1SEv.arch_ivybridge
+// CHECK: ret void (%struct.ConvertTo*)* @_ZNK9ConvertTocv1SEv
+
+// CHECK: define i32 (%struct.S*, i32)* @_ZN1S3fooEi.resolver()
+// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.arch_sandybridge
+// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.arch_ivybridge
+// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.sse4.2
+// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi
+
+// CHECK: define i32 @_Z4bar2v()
+// CHECK:call i32 @_ZN2S23fooEi.ifunc
+// define i32 (%struct.S2*, i32)* @_ZN2S23fooEi.resolver()
+// CHECK: ret i32 (%struct.S2*, i32)* @_ZN2S23fooEi.arch_sandybridge
+// CHECK: ret i32 (%struct.S2*, i32)* @_ZN2S23fooEi.arch_ivybridge
+// CHECK: ret i32 (%struct.S2*, i32)* @_ZN2S23fooEi.sse4.2
+// CHECK: ret i32 (%struct.S2*, i32)* @_ZN2S23fooEi
+
+// CHECK: define i32 @_ZN2S23fooEi.sse4.2(%struct.S2* %this, i32)
+// CHECK: define i32 @_ZN2S23fooEi.arch_ivybridge(%struct.S2* %this, i32)
+// CHECK: define i32 @_ZN2S23fooEi(%struct.S2* %this, i32)
+
+// CHECK: define i32 @_Z9templ_usev()
+// CHECK:  call i32 @_ZN5templIiE3fooEi.ifunc
+// CHECK:  call i32 @_ZN5templIdE3fooEi.ifunc
+
+
+// CHECK: define i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi.resolver()
+// CHECK: ret i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi.arch_sandybridge
+// CHECK: ret i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi.arch_ivybridge
+// CHECK: ret i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi.sse4.2
+// CHECK: ret i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi
+//
+// CHECK: define i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi.resolver()
+// CHECK: ret i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi.arch_sandybridge
+// CHECK: ret i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi.arch_ivybridge
+// CHECK: ret i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi.sse4.2
+// CHECK: ret i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi
+
+// CHECK: define linkonce_odr i32 @_ZN1S3fooEi.sse4.2(%struct.S* %this, i32)
+// CHECK: ret i32 0
+
+// CHECK: declare i32 @_ZN1S3fooEi.arch_sandybridge(%struct.S*, i32)
+
+// CHECK: define linkonce_odr i32 @_ZN1S3fooEi.arch_ivybridge(%struct.S* %this, i32)
+// CHECK: ret i32 1
+
+// CHECK: define linkonce_odr i32 @_ZN1S3fooEi(%struct.S* %this, i32)
+// CHECK: ret i32 2
+
diff --git a/test/CodeGenCXX/attr-target-mv-modules.cpp b/test/CodeGenCXX/attr-target-mv-modules.cpp
new file mode 100644 (file)
index 0000000..6ff2046
--- /dev/null
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -fmodules -emit-llvm %s -o - | FileCheck %s
+#pragma clang module build A
+module A {}
+#pragma clang module contents
+#pragma clang module begin A
+__attribute__((target("default"))) void f();
+__attribute__((target("sse4.2"))) void f();
+#pragma clang module end
+#pragma clang module endbuild
+
+#pragma clang module build B
+module B {}
+#pragma clang module contents
+#pragma clang module begin B
+__attribute__((target("default"))) void f();
+__attribute__((target("sse4.2"))) void f();
+#pragma clang module end
+#pragma clang module endbuild
+
+#pragma clang module import A
+#pragma clang module import B
+void g() { f(); }
+
+// Negative tests to validate that the resolver only calls each 1x.
+// CHECK: define void ()* @_Z1fv.resolver
+// CHECK: ret void ()* @_Z1fv.sse4.2
+// CHECK-NOT: ret void ()* @_Z1fv.sse4.2
+// CHECK: ret void ()* @_Z1fv
+// CHECK-NOT: ret void ()* @_Z1fv
diff --git a/test/CodeGenCXX/attr-target-mv-out-of-line-defs.cpp b/test/CodeGenCXX/attr-target-mv-out-of-line-defs.cpp
new file mode 100644 (file)
index 0000000..7072dec
--- /dev/null
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+struct S {
+  int __attribute__((target("sse4.2"))) foo(int);
+  int __attribute__((target("arch=sandybridge"))) foo(int);
+  int __attribute__((target("arch=ivybridge"))) foo(int);
+  int __attribute__((target("default"))) foo(int);
+};
+
+int __attribute__((target("default"))) S::foo(int) { return 2; }
+int __attribute__((target("sse4.2"))) S::foo(int) { return 0; }
+int __attribute__((target("arch=ivybridge"))) S::foo(int) { return 1; }
+
+int bar() {
+  S s;
+  return s.foo(0);
+}
+
+// CHECK: @_ZN1S3fooEi.ifunc = ifunc i32 (%struct.S*, i32), i32 (%struct.S*, i32)* ()* @_ZN1S3fooEi.resolver
+
+// CHECK: define i32 @_ZN1S3fooEi(%struct.S* %this, i32)
+// CHECK: ret i32 2
+
+// CHECK: define i32 @_ZN1S3fooEi.sse4.2(%struct.S* %this, i32)
+// CHECK: ret i32 0
+
+// CHECK: define i32 @_ZN1S3fooEi.arch_ivybridge(%struct.S* %this, i32)
+// CHECK: ret i32 1
+
+// CHECK: define i32 @_Z3barv()
+// CHECK: %s = alloca %struct.S, align 1
+// CHECK: %call = call i32 @_ZN1S3fooEi.ifunc(%struct.S* %s, i32 0)
+
+// CHECK: define i32 (%struct.S*, i32)* @_ZN1S3fooEi.resolver()
+// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.arch_sandybridge
+// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.arch_ivybridge
+// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.sse4.2
+// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi
+
+// CHECK: declare i32 @_ZN1S3fooEi.arch_sandybridge(%struct.S*, i32)
diff --git a/test/CodeGenCXX/attr-target-mv-overloads.cpp b/test/CodeGenCXX/attr-target-mv-overloads.cpp
new file mode 100644 (file)
index 0000000..a1b12c3
--- /dev/null
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+
+int __attribute__((target("sse4.2"))) foo_overload(int) { return 0; }
+int __attribute__((target("arch=sandybridge"))) foo_overload(int);
+int __attribute__((target("arch=ivybridge"))) foo_overload(int) {return 1;}
+int __attribute__((target("default"))) foo_overload(int) { return 2; }
+int __attribute__((target("sse4.2"))) foo_overload(void) { return 0; }
+int __attribute__((target("arch=sandybridge"))) foo_overload(void);
+int __attribute__((target("arch=ivybridge"))) foo_overload(void) {return 1;}
+int __attribute__((target("default"))) foo_overload(void) { return 2; }
+
+int bar2() {
+  return foo_overload() + foo_overload(1);
+}
+
+// CHECK: @_Z12foo_overloadv.ifunc = ifunc i32 (), i32 ()* ()* @_Z12foo_overloadv.resolver
+// CHECK: @_Z12foo_overloadi.ifunc = ifunc i32 (i32), i32 (i32)* ()* @_Z12foo_overloadi.resolver
+
+
+// CHECK: define i32 @_Z12foo_overloadi.sse4.2(i32)
+// CHECK: ret i32 0
+// CHECK: define i32 @_Z12foo_overloadi.arch_ivybridge(i32)
+// CHECK: ret i32 1
+// CHECK: define i32 @_Z12foo_overloadi(i32)
+// CHECK: ret i32 2
+// CHECK: define i32 @_Z12foo_overloadv.sse4.2()
+// CHECK: ret i32 0
+// CHECK: define i32 @_Z12foo_overloadv.arch_ivybridge()
+// CHECK: ret i32 1
+// CHECK: define i32 @_Z12foo_overloadv()
+// CHECK: ret i32 2
+
+// CHECK: define i32 @_Z4bar2v()
+// CHECK: call i32 @_Z12foo_overloadv.ifunc()
+// CHECK: call i32 @_Z12foo_overloadi.ifunc(i32 1)
+
+// CHECK: define i32 ()* @_Z12foo_overloadv.resolver()
+// CHECK: ret i32 ()* @_Z12foo_overloadv.arch_sandybridge
+// CHECK: ret i32 ()* @_Z12foo_overloadv.arch_ivybridge
+// CHECK: ret i32 ()* @_Z12foo_overloadv.sse4.2
+// CHECK: ret i32 ()* @_Z12foo_overloadv
+
+// CHECK: define i32 (i32)* @_Z12foo_overloadi.resolver()
+// CHECK: ret i32 (i32)* @_Z12foo_overloadi.arch_sandybridge
+// CHECK: ret i32 (i32)* @_Z12foo_overloadi.arch_ivybridge
+// CHECK: ret i32 (i32)* @_Z12foo_overloadi.sse4.2
+// CHECK: ret i32 (i32)* @_Z12foo_overloadi
+
+// CHECK: declare i32 @_Z12foo_overloadv.arch_sandybridge()
+// CHECK: declare i32 @_Z12foo_overloadi.arch_sandybridge(i32)
diff --git a/test/Sema/attr-target-mv-bad-target.c b/test/Sema/attr-target-mv-bad-target.c
new file mode 100644 (file)
index 0000000..9cf3c5e
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -triple x86_64-windows-pc  -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple arm-none-eabi  -fsyntax-only -verify %s
+
+int __attribute__((target("sse4.2"))) redecl1(void) { return 1; }
+//expected-error@+2 {{function multiversioning is not supported on the current target}}
+//expected-note@-2 {{previous declaration is here}}
+int __attribute__((target("avx")))  redecl1(void) { return 2; }
+
+//expected-error@+1 {{function multiversioning is not supported on the current target}}
+int __attribute__((target("default"))) with_def(void) { return 1;}
diff --git a/test/Sema/attr-target-mv.c b/test/Sema/attr-target-mv.c
new file mode 100644 (file)
index 0000000..671adff
--- /dev/null
@@ -0,0 +1,103 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu  -fsyntax-only -verify %s
+
+void __attribute__((target("sse4.2"))) no_default(void);
+void __attribute__((target("arch=sandybridge")))  no_default(void);
+
+void use1(void){
+  // expected-error@+1 {{no matching function for call to 'no_default'}}
+  no_default();
+}
+
+void __attribute__((target("sse4.2"))) has_def(void);
+void __attribute__((target("default")))  has_def(void);
+
+void use2(void){
+  // expected-error@+2 {{reference to overloaded function could not be resolved; did you mean to call it?}}
+  // expected-note@-4 {{possible target for call}}
+  +has_def;
+}
+
+int __attribute__((target("sse4.2"))) no_proto();
+// expected-error@-1 {{multiversioned function must have a prototype}}
+// expected-note@+1 {{function multiversioning caused by this declaration}}
+int __attribute__((target("arch=sandybridge"))) no_proto();
+
+// The following should all be legal, since they are just redeclarations.
+int __attribute__((target("sse4.2"))) redecl1(void);
+int __attribute__((target("sse4.2"))) redecl1(void) { return 1; }
+int __attribute__((target("arch=sandybridge")))  redecl1(void) { return 2; }
+
+int __attribute__((target("sse4.2"))) redecl2(void) { return 1; }
+int __attribute__((target("sse4.2"))) redecl2(void);
+int __attribute__((target("arch=sandybridge")))  redecl2(void) { return 2; }
+
+int __attribute__((target("sse4.2"))) redecl3(void) { return 0; }
+int __attribute__((target("arch=ivybridge"))) redecl3(void) { return 1; }
+int __attribute__((target("arch=sandybridge")))  redecl3(void);
+int __attribute__((target("arch=sandybridge")))  redecl3(void) { return 2; }
+
+int __attribute__((target("sse4.2"))) redecl4(void) { return 1; }
+int __attribute__((target("arch=sandybridge")))  redecl4(void) { return 2; }
+int __attribute__((target("arch=sandybridge")))  redecl4(void);
+
+int __attribute__((target("sse4.2"))) redef(void) { return 1; }
+int __attribute__((target("arch=ivybridge"))) redef(void) { return 1; }
+int __attribute__((target("arch=sandybridge")))  redef(void) { return 2; }
+// expected-error@+2 {{redefinition of 'redef'}}
+// expected-note@-2 {{previous definition is here}}
+int __attribute__((target("arch=sandybridge")))  redef(void) { return 2; }
+
+int __attribute__((target("default"))) redef2(void) { return 1;}
+// expected-error@+2 {{redefinition of 'redef2'}}
+// expected-note@-2 {{previous definition is here}}
+int __attribute__((target("default"))) redef2(void) { return 1;}
+
+int __attribute__((target("sse4.2"))) mv_after_use(void) { return 1; }
+int use3(void) {
+  return mv_after_use();
+}
+
+// expected-error@+1 {{function declaration cannot become a multiversioned function after first usage}}
+int __attribute__((target("arch=sandybridge")))  mv_after_use(void) { return 2; }
+
+int __attribute__((target("sse4.2,arch=sandybridge"))) mangle(void) { return 1; }
+//expected-error@+2 {{multiversioned function redeclarations require identical target attributes}}
+//expected-note@-2 {{previous declaration is here}}
+int __attribute__((target("arch=sandybridge,sse4.2")))  mangle(void) { return 2; }
+
+int prev_no_target(void);
+int __attribute__((target("arch=sandybridge")))  prev_no_target(void) { return 2; }
+// expected-error@-2 {{function declaration is missing 'target' attribute in a multiversioned function}}
+// expected-note@+1 {{function multiversioning caused by this declaration}}
+int __attribute__((target("arch=ivybridge")))  prev_no_target(void) { return 2; }
+
+int __attribute__((target("arch=sandybridge")))  prev_no_target2(void);
+int prev_no_target2(void);
+// expected-error@-1 {{function declaration is missing 'target' attribute in a multiversioned function}}
+// expected-note@+1 {{function multiversioning caused by this declaration}}
+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@+1 {{attribute 'target' multiversioning cannot be combined}}
+void __attribute__((target("default"), used)) 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__((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);
+
+int __attribute__((target("sse4.2"))) diff_cc(void);
+// expected-error@+1 {{multiversioned function declaration has a different calling convention}}
+__vectorcall int __attribute__((target("arch=sandybridge")))  diff_cc(void);
+
+int __attribute__((target("sse4.2"))) diff_ret(void);
+// expected-error@+1 {{multiversioned function declaration has a different return type}}
+short __attribute__((target("arch=sandybridge")))  diff_ret(void);
diff --git a/test/SemaCXX/attr-target-mv.cpp b/test/SemaCXX/attr-target-mv.cpp
new file mode 100644 (file)
index 0000000..18ec1bf
--- /dev/null
@@ -0,0 +1,178 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu  -fsyntax-only -verify -fexceptions -fcxx-exceptions %s -std=c++14
+void __attribute__((target("sse4.2"))) no_default(void);
+void __attribute__((target("arch=sandybridge")))  no_default(void);
+
+void use1(void){
+  // expected-error@+1 {{no matching function for call to 'no_default'}}
+  no_default();
+}
+constexpr int __attribute__((target("sse4.2"))) foo(void) { return 0; }
+constexpr int __attribute__((target("arch=sandybridge"))) foo(void);
+//expected-error@+1 {{multiversioned function declaration has a different constexpr specification}}
+int __attribute__((target("arch=ivybridge"))) foo(void) {return 1;}
+constexpr int __attribute__((target("default"))) foo(void) { return 2; }
+
+int __attribute__((target("sse4.2"))) foo2(void) { return 0; }
+//expected-error@+1 {{multiversioned function declaration has a different constexpr specification}}
+constexpr int __attribute__((target("arch=sandybridge"))) foo2(void);
+int __attribute__((target("arch=ivybridge"))) foo2(void) {return 1;}
+int __attribute__((target("default"))) foo2(void) { return 2; }
+
+static int __attribute__((target("sse4.2"))) bar(void) { return 0; }
+static int __attribute__((target("arch=sandybridge"))) bar(void);
+//expected-error@+1 {{multiversioned function declaration has a different storage class}}
+int __attribute__((target("arch=ivybridge"))) bar(void) {return 1;}
+static int __attribute__((target("default"))) bar(void) { return 2; }
+
+int __attribute__((target("sse4.2"))) bar2(void) { return 0; }
+//expected-error@+1 {{multiversioned function declaration has a different storage class}}
+static int __attribute__((target("arch=sandybridge"))) bar2(void);
+int __attribute__((target("arch=ivybridge"))) bar2(void) {return 1;}
+int __attribute__((target("default"))) bar2(void) { return 2; }
+
+
+inline int __attribute__((target("sse4.2"))) baz(void) { return 0; }
+inline int __attribute__((target("arch=sandybridge"))) baz(void);
+//expected-error@+1 {{multiversioned function declaration has a different inline specification}}
+int __attribute__((target("arch=ivybridge"))) baz(void) {return 1;}
+inline int __attribute__((target("default"))) baz(void) { return 2; }
+
+int __attribute__((target("sse4.2"))) baz2(void) { return 0; }
+//expected-error@+1 {{multiversioned function declaration has a different inline specification}}
+inline int __attribute__((target("arch=sandybridge"))) baz2(void);
+int __attribute__((target("arch=ivybridge"))) baz2(void) {return 1;}
+int __attribute__((target("default"))) baz2(void) { return 2; }
+
+float __attribute__((target("sse4.2"))) bock(void) { return 0; }
+//expected-error@+1 {{multiversioned function declaration has a different return type}}
+int __attribute__((target("arch=sandybridge"))) bock(void);
+//expected-error@+1 {{multiversioned function declaration has a different return type}}
+int __attribute__((target("arch=ivybridge"))) bock(void) {return 1;}
+//expected-error@+1 {{multiversioned function declaration has a different return type}}
+int __attribute__((target("default"))) bock(void) { return 2; }
+
+int __attribute__((target("sse4.2"))) bock2(void) { return 0; }
+//expected-error@+1 {{multiversioned function declaration has a different return type}}
+float __attribute__((target("arch=sandybridge"))) bock2(void);
+int __attribute__((target("arch=ivybridge"))) bock2(void) {return 1;}
+int __attribute__((target("default"))) bock2(void) { return 2; }
+
+auto __attribute__((target("sse4.2"))) bock3(void) -> int { return 0; }
+//expected-error@+1 {{multiversioned function declaration has a different return type}}
+auto __attribute__((target("arch=sandybridge"))) bock3(void) -> short { return (short)0;}
+
+int __attribute__((target("sse4.2"))) bock4(void) noexcept(false) { return 0; }
+//expected-error@+2 {{exception specification in declaration does not match previous declaration}}
+//expected-note@-2 {{previous declaration is here}}
+int __attribute__((target("arch=sandybridge"))) bock4(void) noexcept(true) { return 1;}
+
+// FIXME: Add support for templates and virtual functions!
+template<typename T>
+int __attribute__((target("sse4.2"))) foo(T) { return 0; }
+// expected-error@+2 {{multiversioned functions do not yet support function templates}}
+template<typename T>
+int __attribute__((target("arch=sandybridge"))) foo(T);
+
+// expected-error@+2 {{multiversioned functions do not yet support function templates}}
+template<typename T>
+int __attribute__((target("default"))) foo(T) { return 2; }
+
+struct S {
+  template<typename T>
+  int __attribute__((target("sse4.2"))) foo(T) { return 0; }
+  // expected-error@+2 {{multiversioned functions do not yet support function templates}}
+  template<typename T>
+  int __attribute__((target("arch=sandybridge"))) foo(T);
+
+  // expected-error@+2 {{multiversioned functions do not yet support function templates}}
+  template<typename T>
+  int __attribute__((target("default"))) foo(T) { return 2; }
+
+  // expected-error@+1 {{multiversioned functions do not yet support virtual functions}}
+  virtual void __attribute__((target("default"))) virt();
+};
+
+extern "C" {
+int __attribute__((target("sse4.2"))) diff_mangle(void) { return 0; }
+}
+//expected-error@+1 {{multiversioned function declaration has a different linkage}}
+int __attribute__((target("arch=sandybridge"))) diff_mangle(void) { return 0; }
+
+// expected-error@+1 {{multiversioned functions do not yet support deduced return types}}
+auto __attribute__((target("default"))) deduced_return(void) { return 0; }
+// expected-error@-1 {{cannot initialize return object of type 'auto' with an rvalue of type 'int'}}
+
+auto __attribute__((target("default"))) trailing_return(void)-> int { return 0; }
+
+__attribute__((target("default"))) void DiffDecl();
+namespace N {
+using ::DiffDecl;
+// expected-error@+3 {{declaration conflicts with target of using declaration already in scope}}
+// expected-note@-4 {{target of using declaration}}
+// expected-note@-3 {{using declaration}}
+__attribute__((target("arch=sandybridge"))) void DiffDecl();
+} // namespace N
+
+struct SpecialFuncs {
+  // expected-error@+1 {{multiversioned functions do not yet support constructors}}
+  __attribute__((target("default"))) SpecialFuncs();
+  // expected-error@+1 {{multiversioned functions do not yet support destructors}}
+  __attribute__((target("default"))) ~SpecialFuncs();
+
+  // expected-error@+1 {{multiversioned functions do not yet support defaulted functions}}
+  SpecialFuncs& __attribute__((target("default"))) operator=(const SpecialFuncs&) = default;
+  // expected-error@+1 {{multiversioned functions do not yet support deleted functions}}
+  SpecialFuncs& __attribute__((target("default"))) operator=(SpecialFuncs&&) = delete;
+};
+
+class Secret {
+  int i = 0;
+  __attribute__((target("default")))
+  friend int SecretAccessor(Secret &s);
+  __attribute__((target("arch=sandybridge")))
+  friend int SecretAccessor(Secret &s);
+};
+
+__attribute__((target("default")))
+int SecretAccessor(Secret &s) {
+  return s.i;
+}
+
+__attribute__((target("arch=sandybridge")))
+int SecretAccessor(Secret &s) {
+  return s.i + 2;
+}
+
+__attribute__((target("arch=ivybridge")))
+int SecretAccessor(Secret &s) {
+  //expected-error@+2{{'i' is a private member of 'Secret'}}
+  //expected-note@-20{{implicitly declared private here}}
+  return s.i + 3;
+}
+
+constexpr int __attribute__((target("sse4.2"))) constexpr_foo(void) {
+  return 0;
+}
+constexpr int __attribute__((target("arch=sandybridge"))) constexpr_foo(void);
+constexpr int __attribute__((target("arch=ivybridge"))) constexpr_foo(void) {
+  return 1;
+}
+constexpr int __attribute__((target("default"))) constexpr_foo(void) {
+  return 2;
+}
+
+void constexpr_test() {
+  static_assert(foo() == 2, "Should call 'default' in a constexpr context");
+}
+
+struct BadOutOfLine {
+  int __attribute__((target("sse4.2"))) foo(int);
+  int __attribute__((target("default"))) foo(int);
+};
+
+int __attribute__((target("sse4.2"))) BadOutOfLine::foo(int) { return 0; }
+int __attribute__((target("default"))) BadOutOfLine::foo(int) { return 1; }
+// expected-error@+3 {{out-of-line definition of 'foo' does not match any declaration in 'BadOutOfLine'}}
+// expected-note@-3 {{member declaration nearly matches}}
+// expected-note@-3 {{member declaration nearly matches}}
+int __attribute__((target("arch=atom"))) BadOutOfLine::foo(int) { return 1; }