Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -2633,6 +2633,21 @@ /// it is not used. bool DeclMustBeEmitted(const Decl *D); + /// \brief Visits all versions of a multiversioned function with the passed + /// predicate. + template + void forEachMultiversionedFunctionVersion(const FunctionDecl *FD, + Pred P) const { + assert(FD->isMultiVersion() && "Only valid for multiversioned functions"); + FD = FD->getCanonicalDecl(); + for (auto *CurDecl : + FD->getDeclContext()->getRedeclContext()->lookup(FD->getDeclName())) { + FunctionDecl *CurFD = CurDecl->getAsFunction()->getCanonicalDecl(); + if (CurFD && hasSameType(CurFD->getType(), FD->getType())) + P(CurFD); + } + } + const CXXConstructorDecl * getCopyConstructorForExceptionObject(CXXRecordDecl *RD); Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -1750,6 +1750,10 @@ /// 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 @@ 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; @@ -2154,6 +2158,15 @@ 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: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1844,12 +1844,27 @@ std::vector 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 + 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 AttrFeatures; Features.split(AttrFeatures, ","); Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -9332,4 +9332,35 @@ InGroup, 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|destructors}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: include/clang/Basic/TargetInfo.h =================================================================== --- include/clang/Basic/TargetInfo.h +++ include/clang/Basic/TargetInfo.h @@ -920,10 +920,20 @@ 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: include/clang/Basic/X86Target.def =================================================================== --- include/clang/Basic/X86Target.def +++ include/clang/Basic/X86Target.def @@ -12,6 +12,10 @@ // //===----------------------------------------------------------------------===// +#ifndef PROC_WITH_FEAT +#define PROC_WITH_FEAT(ENUM, STRING, IS64BIT, KEYFEATURE) \ + PROC(ENUM, STRING, IS64BIT) +#endif #ifndef PROC #define PROC(ENUM, STRING, IS64BIT) @@ -21,6 +25,10 @@ #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 @@ /// \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 @@ /// \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 @@ /// \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 @@ /// \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_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(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: include/clang/Sema/Overload.h =================================================================== --- include/clang/Sema/Overload.h +++ include/clang/Sema/Overload.h @@ -613,6 +613,10 @@ /// 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: lib/Basic/Targets/X86.h =================================================================== --- lib/Basic/Targets/X86.h +++ lib/Basic/Targets/X86.h @@ -111,6 +111,8 @@ 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 @@ 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: lib/Basic/Targets/X86.cpp =================================================================== --- lib/Basic/Targets/X86.cpp +++ lib/Basic/Targets/X86.cpp @@ -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 @@ .Default(false); } +static llvm::X86::ProcessorFeatures getFeature(StringRef Name) { + return llvm::StringSwitch(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(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: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -3988,6 +3988,29 @@ 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 Options); + private: QualType getVarArgType(const Expr *Arg); @@ -4004,6 +4027,7 @@ llvm::Value *EmitX86CpuSupports(const CallExpr *E); llvm::Value *EmitX86CpuSupports(ArrayRef FeatureStrs); llvm::Value *EmitX86CpuInit(); + llvm::Value *FormResolverCondition(const MultiVersionResolverOption &RO); }; /// Helper class with most of the code for saving a value for a Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -2300,6 +2300,60 @@ 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 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 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: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -324,6 +324,10 @@ /// is defined once we get to the end of the of the translation unit. std::vector Aliases; + /// List of multiversion functions that have to be emitted. Used to make sure + /// we properly emit the iFunc. + std::vector MultiVersionFuncs; + typedef llvm::StringMap > ReplacementsTy; ReplacementsTy Replacements; @@ -1247,6 +1251,12 @@ 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 @@ void checkAliases(); + void emitMultiVersionFunctions(); + /// Emit any vtables which we deferred and still have a use for. void EmitDeferredVTables(); Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -395,6 +395,7 @@ applyGlobalValReplacements(); applyReplacements(); checkAliases(); + emitMultiVersionFunctions(); EmitCXXGlobalInitFunc(); EmitCXXGlobalDtorFunc(); EmitCXXThreadLocalInitFunc(); @@ -725,36 +726,48 @@ 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(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(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(ND)) - getCXXABI().getMangleContext().mangleCXXCtor(D, GD.getCtorType(), Out); + MC.mangleCXXCtor(D, GD.getCtorType(), Out); else if (const auto *D = dyn_cast(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 @@ FD->getType()->castAs()->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(ND)) + if (FD->isMultiVersion() && !OmitTargetMangling) + AppendTargetMangling(CGM, FD->getAttr(), 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(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(GD.getDecl()); + auto Result = + Manglings.insert(std::make_pair(getMangledNameImpl(*this, GD, ND), GD)); return MangledDeclNames[CanonicalGD] = Result.first->first(); } @@ -2061,6 +2134,74 @@ static void ReplaceUsesOfNonProtoTypeWithRealFunction(llvm::GlobalValue *Old, llvm::Function *NewFn); +void CodeGenModule::emitMultiVersionFunctions() { + for (GlobalDecl GD : MultiVersionFuncs) { + SmallVector Options; + const FunctionDecl *FD = cast(GD.getDecl()); + getContext().forEachMultiversionedFunctionVersion( + FD, [this, &GD, &Options](FunctionDecl *CurFD) { + GlobalDecl CurGD{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(Func), + CurFD->getAttr()->parse()); + }); + + + llvm::Function *ResolverFunc = cast( + GetGlobalValue((getMangledName(GD) + ".resolver").str())); + std::stable_sort( + Options.begin(), Options.end(), + std::greater()); + 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 @@ 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(D)) { + if (FD->isMultiVersion() && FD->getAttr()->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: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -1647,6 +1647,12 @@ } NamedDecl *Fn = (*It)->getUnderlyingDecl(); + // Don't print overloads for non-default multiversioned functions. + if (const auto *FD = Fn->getAsFunction()) { + if (FD->isMultiVersion() && + !FD->getAttr()->isDefaultVersion()) + continue; + } S.Diag(Fn->getLocation(), diag::note_possible_target_of_call); ++ShownOverloads; } Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -9157,6 +9157,388 @@ 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(); + 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); + if (CausesMV) + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + return true; + } + + if (NewFD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) + << FuncTemplates; + if (CausesMV) + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + return true; + } + + + if (const auto *NewCXXFD = dyn_cast(NewFD)) { + if (NewCXXFD->isVirtual()) { + S.Diag(NewCXXFD->getLocation(), diag::err_multiversion_doesnt_support) + << VirtFuncs; + if (CausesMV) + S.Diag(NewCXXFD->getLocation(), diag::note_multiversioning_caused_here); + return true; + } + + if (const auto *NewCXXCtor = dyn_cast(NewFD)) { + S.Diag(NewCXXCtor->getLocation(), diag::err_multiversion_doesnt_support) + << Constructors; + if (CausesMV) + S.Diag(NewCXXCtor->getLocation(), + diag::note_multiversioning_caused_here); + return true; + } + + if (const auto *NewCXXDtor = dyn_cast(NewFD)) { + S.Diag(NewCXXDtor->getLocation(), diag::err_multiversion_doesnt_support) + << Destructors; + if (CausesMV) + S.Diag(NewCXXDtor->getLocation(), + diag::note_multiversioning_caused_here); + return true; + } + } + + if (NewFD->isDeleted()) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) + << DeletedFuncs; + if (CausesMV) + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + } + if (NewFD->isDefaulted()) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) + << DefaultedFuncs; + if (CausesMV) + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + } + + QualType NewQType = S.getASTContext().getCanonicalType(NewFD->getType()); + const auto *NewType = cast(NewQType); + QualType NewReturnType = NewType->getReturnType(); + + if (NewReturnType->isUndeducedType()) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) + << DeducedReturn; + if (CausesMV) + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + 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(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; + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + return true; + } + + QualType OldReturnType = OldType->getReturnType(); + + if (OldReturnType != NewReturnType) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << ReturnType; + if (CausesMV) + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + return true; + } + + if (OldFD->isConstexpr() != NewFD->isConstexpr()) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) + << ConstexprSpec; + if (CausesMV) + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + return true; + } + + if (OldFD->isInlineSpecified() != NewFD->isInlineSpecified()) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << InlineSpec; + if (CausesMV) + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + return true; + } + + if (OldFD->getStorageClass() != NewFD->getStorageClass()) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << StorageClass; + if (CausesMV) + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + return true; + } + + if (OldFD->isExternC() != NewFD->isExternC()) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << Linkage; + if (CausesMV) + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + return true; + } + + if (S.CheckEquivalentExceptionSpec( + OldFD->getType()->getAs(), OldFD->getLocation(), + NewFD->getType()->getAs(), 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(); + 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()) { + 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(); + 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()) { + 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()); + + 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(); + 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(); + if (CurTA->getFeaturesStr() == NewTA->getFeaturesStr()) { + NewFD->setIsMultiVersion(); + Redeclaration = true; + OldDecl = ND; + return false; + } + + TargetAttr::ParsedTargetAttr CurParsed = + CurTA->parse(std::less()); + + 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 +9626,10 @@ } } + 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: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -5959,6 +5959,13 @@ Candidate.IgnoreObjectArgument = false; Candidate.ExplicitCallArguments = Args.size(); + if (Function->isMultiVersion() && + !Function->getAttr()->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 @@ -6583,6 +6590,12 @@ Candidate.DeductionFailure.Data = FailedAttr; return; } + + if (Method->isMultiVersion() && + !Method->getAttr()->isDefaultVersion()) { + Candidate.Viable = false; + Candidate.FailureKind = ovl_non_default_multiversion_function; + } } /// \brief Add a C++ member function template as a candidate to the candidate @@ -6986,6 +6999,12 @@ Candidate.DeductionFailure.Data = FailedAttr; return; } + + if (Conversion->isMultiVersion() && + !Conversion->getAttr()->isDefaultVersion()) { + Candidate.Viable = false; + Candidate.FailureKind = ovl_non_default_multiversion_function; + } } /// \brief Adds a conversion function template specialization @@ -9394,6 +9413,8 @@ QualType DestType, bool TakingAddress) { if (TakingAddress && !checkAddressOfCandidateIsAvailable(*this, Fn)) return; + if (Fn->isMultiVersion() && !Fn->getAttr()->isDefaultVersion()) + return; std::string FnDesc; OverloadCandidateKind K = ClassifyOverloadCandidate(*this, Found, Fn, FnDesc); @@ -10195,6 +10216,9 @@ assert(!Available); break; } + case ovl_non_default_multiversion_function: + // Do Nothing, these should simly be ignored. + break; } } @@ -10931,6 +10955,12 @@ if (FunctionDecl *Caller = dyn_cast(S.CurContext)) if (!Caller->isImplicit() && !S.IsAllowedCUDACall(Caller, FunDecl)) return false; + if (FunDecl->isMultiVersion()) { + const auto *TA = FunDecl->getAttr(); + 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: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -792,6 +792,7 @@ 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(); @@ -2817,6 +2818,16 @@ CtorY->getInheritedConstructor().getConstructor())) return false; } + // Multiversioned functions with different feature strings are represented + // as separate declarations. + if (FuncX->isMultiVersion()) { + const auto *TAX = FuncX->getAttr(); + const auto *TAY = FuncY->getAttr(); + assert(TAX && "Multiversion Function without target attribute"); + + if (!TAY || 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: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -534,6 +534,7 @@ 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()); @@ -2071,6 +2072,7 @@ 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 Index: test/CodeGen/attr-target-mv-func-ptrs.c =================================================================== --- /dev/null +++ test/CodeGen/attr-target-mv-func-ptrs.c @@ -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( Index: test/CodeGen/attr-target-mv-va-args.c =================================================================== --- /dev/null +++ test/CodeGen/attr-target-mv-va-args.c @@ -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, ...) Index: test/CodeGen/attr-target-mv.c =================================================================== --- /dev/null +++ test/CodeGen/attr-target-mv.c @@ -0,0 +1,61 @@ +// 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(); +} + +// CHECK: @foo.ifunc = ifunc i32 (), i32 ()* ()* @foo.resolver +// CHECK: @foo_inline.ifunc = ifunc i32 (), i32 ()* ()* @foo_inline.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: 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 + + Index: test/CodeGenCXX/attr-target-mv-constexpr.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/attr-target-mv-constexpr.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s + +constexpr int __attribute__((target("sse4.2"))) foo(void) { return 0; } +constexpr int __attribute__((target("arch=sandybridge"))) foo(void); +constexpr int __attribute__((target("arch=ivybridge"))) foo(void) {return 1;} +constexpr int __attribute__((target("default"))) foo(void) { return 2; } + +int bar() { + constexpr int i = foo(); + return i; +} + +// CHECK-NOT: foo +// CHECK: define i32 @_Z3barv() +// CHECK: ret i32 2 +// CHECK-NOT: foo Index: test/CodeGenCXX/attr-target-mv-diff-ns.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/attr-target-mv-diff-ns.cpp @@ -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) Index: test/CodeGenCXX/attr-target-mv-func-ptrs.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/attr-target-mv-func-ptrs.cpp @@ -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 Index: test/CodeGenCXX/attr-target-mv-member-funcs.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/attr-target-mv-member-funcs.cpp @@ -0,0 +1,112 @@ +// 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; + } + // TODO: + // befriending mv functions +}; + +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(C); + + return s.foo(0); +} + +template +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 a; + templ 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 +// 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 @_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 + Index: test/CodeGenCXX/attr-target-mv-out-of-line-defs.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/attr-target-mv-out-of-line-defs.cpp @@ -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) Index: test/CodeGenCXX/attr-target-mv-overloads.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/attr-target-mv-overloads.cpp @@ -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) Index: test/Sema/attr-target-mv-bad-target.c =================================================================== --- /dev/null +++ test/Sema/attr-target-mv-bad-target.c @@ -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;} Index: test/Sema/attr-target-mv.c =================================================================== --- /dev/null +++ test/Sema/attr-target-mv.c @@ -0,0 +1,107 @@ +// 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@+2 {{attribute 'target' multiversioning cannot be combined}} +//expected-note@+1 {{function multiversioning caused by this declaration}} +void __attribute__((used,target("arch=sandybridge"))) addtl_attrs(void); + +//expected-error@+2 {{attribute 'target' multiversioning cannot be combined}} +//expected-note@+1 {{function multiversioning caused by this declaration}} +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@+2 {{multiversioned function declaration has a different calling convention}} +// expected-note@+1 {{function multiversioning caused by this declaration}} +__vectorcall int __attribute__((target("arch=sandybridge"))) diff_cc(void); + +int __attribute__((target("sse4.2"))) diff_ret(void); +// expected-error@+2 {{multiversioned function declaration has a different return type}} +// expected-note@+1 {{function multiversioning caused by this declaration}} +short __attribute__((target("arch=sandybridge"))) diff_ret(void); Index: test/SemaCXX/attr-target-mv.cpp =================================================================== --- /dev/null +++ test/SemaCXX/attr-target-mv.cpp @@ -0,0 +1,145 @@ +// 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@+2 {{multiversioned function declaration has a different constexpr specification}} +//expected-note@+1 {{function multiversioning caused by this declaration}} +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@+2 {{multiversioned function declaration has a different storage class}} +//expected-note@+1 {{function multiversioning caused by this declaration}} +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@+2 {{multiversioned function declaration has a different inline specification}} +//expected-note@+1 {{function multiversioning caused by this declaration}} +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@+2 {{multiversioned function declaration has a different return type}} +//expected-note@+1 {{function multiversioning caused by this declaration}} +int __attribute__((target("arch=sandybridge"))) bock(void); +//expected-error@+2 {{multiversioned function declaration has a different return type}} +//expected-note@+1 {{function multiversioning caused by this declaration}} +int __attribute__((target("arch=ivybridge"))) bock(void) {return 1;} +//expected-error@+2 {{multiversioned function declaration has a different return type}} +//expected-note@+1 {{function multiversioning caused by this declaration}} +int __attribute__((target("default"))) bock(void) { return 2; } + +int __attribute__((target("sse4.2"))) bock2(void) { return 0; } +//expected-error@+2 {{multiversioned function declaration has a different return type}} +//expected-note@+1 {{function multiversioning caused by this declaration}} +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@+2 {{multiversioned function declaration has a different return type}} +//expected-note@+1 {{function multiversioning caused by this declaration}} +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 +int __attribute__((target("sse4.2"))) foo(T) { return 0; } +// expected-error@+3 {{multiversioned functions do not yet support function templates}} +// expected-note@+2 {{function multiversioning caused by this declaration}} +template +int __attribute__((target("arch=sandybridge"))) foo(T); + +// expected-error@+3 {{multiversioned functions do not yet support function templates}} +// expected-note@+2 {{function multiversioning caused by this declaration}} +template +int __attribute__((target("default"))) foo(T) { return 2; } + +struct S { + template + int __attribute__((target("sse4.2"))) foo(T) { return 0; } + // expected-error@+3 {{multiversioned functions do not yet support function templates}} + // expected-note@+2 {{function multiversioning caused by this declaration}} + template + int __attribute__((target("arch=sandybridge"))) foo(T); + + // expected-error@+3 {{multiversioned functions do not yet support function templates}} + // expected-note@+2 {{function multiversioning caused by this declaration}} + template + int __attribute__((target("default"))) foo(T) { return 2; } + + // expected-error@+2 {{multiversioned functions do not yet support virtual functions}} + // expected-note@+1 {{function multiversioning caused by this declaration}} + virtual void __attribute__((target("default"))) virt(); +}; + +extern "C" { +int __attribute__((target("sse4.2"))) diff_mangle(void) { return 0; } +} +//expected-error@+2 {{multiversioned function declaration has a different linkage}} +//expected-note@+1 {{function multiversioning caused by this declaration}} +int __attribute__((target("arch=sandybridge"))) diff_mangle(void) { return 0; } + +// expected-error@+2 {{multiversioned functions do not yet support deduced return types}} +// expected-note@+1 {{function multiversioning caused by this declaration}} +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@+2 {{multiversioned functions do not yet support constructors}} + // expected-note@+1 {{function multiversioning caused by this declaration}} + __attribute__((target("default"))) SpecialFuncs(); + // expected-error@+2 {{multiversioned functions do not yet support destructors}} + // expected-note@+1 {{function multiversioning caused by this declaration}} + __attribute__((target("default"))) ~SpecialFuncs(); + + // expected-error@+2 {{multiversioned functions do not yet support defaulted functions}} + // expected-note@+1 {{function multiversioning caused by this declaration}} + SpecialFuncs& __attribute__((target("default"))) operator=(const SpecialFuncs&) = default; + // expected-error@+2 {{multiversioned functions do not yet support deleted functions}} + // expected-note@+1 {{function multiversioning caused by this declaration}} + SpecialFuncs& __attribute__((target("default"))) operator=(SpecialFuncs&&) = delete; +};