Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -1751,6 +1751,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 @@ -1842,8 +1846,9 @@ IsExplicitlyDefaulted(false), HasImplicitReturnZero(false), IsLateTemplateParsed(false), IsConstexpr(isConstexprSpecified), InstantiationIsPending(false), UsesSEHTry(false), HasSkippedBody(false), - WillHaveBody(false), IsCopyDeductionCandidate(false), - EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) {} + WillHaveBody(false), IsMultiVersion(false), + IsCopyDeductionCandidate(false), EndRangeLoc(NameInfo.getEndLoc()), + DNLoc(NameInfo.getInfo()) {} using redeclarable_base = Redeclarable; @@ -2151,6 +2156,17 @@ 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 IsMultiVersion; } + + /// Sets the multiversion state for this declaration and all of its + /// redeclarations. + void setIsMultiVersion(bool V = true) { + IsMultiVersion = V; + for (FunctionDecl *FD : redecls()) + FD->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 @@ -1806,12 +1806,18 @@ std::vector Features; StringRef Architecture; bool DuplicateArchitecture = false; + bool operator ==(const ParsedTargetAttr &Other) { + return DuplicateArchitecture == Other.DuplicateArchitecture && + Architecture == Other.Architecture && Features == Other.Features; + } }; ParsedTargetAttr parse() const { return parse(getFeaturesStr()); } + 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 @@ -9313,4 +9313,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 multiversion 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 note_ovl_candidate_nondefault_multiversion : + Note<"candidate ignored: non-default multiversion function cannot be " + "called directly">; +def err_multiversion_duplicate : + Error<"multiversion function would have identical mangling to a previous " + "definition. Duplicate declarations must have identical target " + "attribute values">; +def err_multiversion_noproto : + Error<"multiversion 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<"multiversion function declaration has a different %select{calling " + "convention|return type|constexpr specification|inline specification|" + "storage class|access specifier|const qualifier}0">; +def err_multiversion_doesnt_support : + Error<"multiversion functions do not yet support %select{function templates|" + "virtual functions}0">; + } // end of sema component. Index: include/clang/Basic/TargetInfo.h =================================================================== --- include/clang/Basic/TargetInfo.h +++ include/clang/Basic/TargetInfo.h @@ -913,10 +913,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 false; + } + // \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,39 +108,39 @@ /// \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 @@ -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,43 @@ 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. +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 @@ -105,6 +105,8 @@ CPUKind getCPUKind(StringRef CPU) const; + std::string getCPUKindCanonicalName(CPUKind Kind) const; + enum FPMathKind { FP_Default, FP_SSE, FP_387 } FPMath = FP_Default; public: @@ -246,6 +248,9 @@ return checkCPUKind(CPU = getCPUKind(Name)); } + virtual bool supportsMultiVersioning() const { return true; } + virtual unsigned multiVersionSortPriority(StringRef Name) const; + 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 { @@ -1264,6 +1265,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/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -7670,7 +7670,7 @@ return Builder.CreateICmpNE(Bitset, llvm::ConstantInt::get(Int32Ty, 0)); } -Value *CodeGenFunction::EmitX86CpuInit() { +Value *CodeGenFunction::EmitX86CpuInit(CGBuilderTy &Builder) { llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, /*Variadic*/ false); llvm::Constant *Func = CGM.CreateRuntimeFunction(FTy, "__cpu_indicator_init"); @@ -7684,7 +7684,7 @@ if (BuiltinID == X86::BI__builtin_cpu_supports) return EmitX86CpuSupports(E); if (BuiltinID == X86::BI__builtin_cpu_init) - return EmitX86CpuInit(); + return EmitX86CpuInit(Builder); SmallVector Ops; Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -3929,6 +3929,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( + StringRef{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); @@ -3944,7 +3967,8 @@ llvm::Value *EmitX86CpuIs(StringRef CPUStr); llvm::Value *EmitX86CpuSupports(const CallExpr *E); llvm::Value *EmitX86CpuSupports(ArrayRef FeatureStrs); - llvm::Value *EmitX86CpuInit(); + llvm::Value *EmitX86CpuInit(CGBuilderTy &Builder); + 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 @@ -2296,6 +2296,61 @@ 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; + std::for_each(std::begin(RO.ParsedAttribute.Features), + std::end(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(Builder); + + 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 NamedDecl *ND); + 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 @@ -391,6 +391,7 @@ applyGlobalValReplacements(); applyReplacements(); checkAliases(); + emitMultiVersionFunctions(); EmitCXXGlobalInitFunc(); EmitCXXGlobalDtorFunc(); EmitCXXThreadLocalInitFunc(); @@ -721,36 +722,49 @@ 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->getFeaturesStr() == "default") + 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 << '.'; + TargetAttr::ParsedTargetAttr Info = Attr->parse(); + + 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; + const auto &Target = CGM.getTarget(); + auto CmpFunc = [&Target](StringRef LHS, StringRef RHS) { + return Target.multiVersionSortPriority(LHS) > + Target.multiVersionSortPriority(RHS); + }; + std::sort(Info.Features.begin(), Info.Features.end(), CmpFunc); + for (auto &&Feat : Info.Features) { + if (!IsFirst) + Out << '_'; + IsFirst = false; + Out << StringRef{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."); @@ -760,14 +774,76 @@ 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 NamedDecl *ND) { + const auto *FD = dyn_cast(ND); + if (!FD || !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, ND, /*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; + + const auto *ND = cast(GD.getDecl()); + UpdateMultiVersionNames(GD, ND); + // Keep the first result in the case of a mangling collision. - auto Result = Manglings.insert(std::make_pair(Str, GD)); + auto Result = + Manglings.insert(std::make_pair(getMangledNameImpl(*this, GD, ND), GD)); return MangledDeclNames[CanonicalGD] = Result.first->first(); } @@ -1978,6 +2054,8 @@ if (getFunctionLinkage(GD) != llvm::Function::AvailableExternallyLinkage) return true; const auto *F = cast(GD.getDecl()); + if (F->isMultiVersion()) + return true; if (CodeGenOpts.OptimizationLevel == 0 && !F->hasAttr()) return false; @@ -2054,6 +2132,78 @@ static void ReplaceUsesOfNonProtoTypeWithRealFunction(llvm::GlobalValue *Old, llvm::Function *NewFn); +void CodeGenModule::emitMultiVersionFunctions() { + for (GlobalDecl GD : MultiVersionFuncs) { + const FunctionDecl *FD = cast(GD.getDecl()); + + SmallVector Options; + + for (auto *CurDecl : FD->getDeclContext()->lookup(FD->getIdentifier())) { + if (const auto *CurFD = dyn_cast(CurDecl)) + if (getContext().hasSameType(CurFD->getType(), FD->getType())) { + StringRef MangledName = getMangledName(CurFD); + llvm::Constant *Func = GetGlobalValue(MangledName); + + if (!Func) { + GlobalDecl CurGD{CurFD}; + if (CurFD->isDefined()) { + EmitGlobalDefinition(CurGD); + 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(FD) + ".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 @@ -2067,6 +2217,12 @@ 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() && !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/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -9287,6 +9287,300 @@ 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 auto &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) { + + // 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_NonTemplate) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) << 0; + 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(NewFD->getLocation(), diag::err_multiversion_doesnt_support) << 1; + 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()); + QualType NewQType = S.getASTContext().getCanonicalType(NewFD->getType()); + const FunctionType *OldType = cast(OldQType); + const FunctionType *NewType = cast(NewQType); + + FunctionType::ExtInfo OldTypeInfo = OldType->getExtInfo(); + FunctionType::ExtInfo NewTypeInfo = NewType->getExtInfo(); + if (OldTypeInfo.getCC() != NewTypeInfo.getCC()) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << 0; + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + return true; + } + + QualType OldReturnType = OldType->getReturnType(); + QualType NewReturnType = NewType->getReturnType(); + if (OldReturnType != NewReturnType) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << 1; + 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) << 2; + 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) << 3; + 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) << 4; + 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 &MayNeedOverloadableChecks, + LookupResult &Previous) { + if (NewFD->isMain()) + return false; + const auto *NewTA = NewFD->getAttr(); + + // If there is no matching previous decl, than only 'default' can + // cause MultiVersioning. + if (!OldDecl) { + if (NewTA && NewTA->getFeaturesStr() == "default") { + 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; + } + NewFD->setIsMultiVersion(); + } + return false; + } + + auto *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->isMain()) + 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 (!NewFD->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::sort(OldParsed.Features.begin(), OldParsed.Features.end()); + if (OldParsed == NewParsed) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_duplicate); + S.Diag(OldFD->getLocation(), diag::note_previous_decl) << "previously"; + 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; + MayNeedOverloadableChecks = 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) { + auto *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::sort(CurParsed.Features.begin(), CurParsed.Features.end()); + + if (CurParsed == NewParsed) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_duplicate); + S.Diag(CurFD->getLocation(), diag::note_previous_decl) << "previously"; + 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; + MayNeedOverloadableChecks = false; + OldDecl = nullptr; + Previous.clear(); + return false; +} + /// \brief Perform semantic checking of a new function declaration. /// /// Performs semantic analysis of the new function declaration @@ -9374,6 +9668,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 @@ -5940,6 +5940,13 @@ Candidate.IgnoreObjectArgument = false; Candidate.ExplicitCallArguments = Args.size(); + if (Function->isMultiVersion() && + Function->getAttr()->getFeaturesStr() != "default") { + 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 @@ -6564,6 +6571,12 @@ Candidate.DeductionFailure.Data = FailedAttr; return; } + + if (Method->isMultiVersion() && + Method->getAttr()->getFeaturesStr() != "default") { + Candidate.Viable = false; + Candidate.FailureKind = ovl_non_default_multiversion_function; + } } /// \brief Add a C++ member function template as a candidate to the candidate @@ -10176,6 +10189,9 @@ assert(!Available); break; } + case ovl_non_default_multiversion_function: + S.Diag(Fn->getLocation(), diag::note_ovl_candidate_nondefault_multiversion); + break; } } 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-member-funcs.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/attr-target-mv-member-funcs.cpp @@ -0,0 +1,37 @@ +// 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; } +}; + +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 @_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: 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.c =================================================================== --- /dev/null +++ test/Sema/attr-target-mv.c @@ -0,0 +1,100 @@ +// 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-note@-4 {{candidate ignored: non-default multiversion function cannot be called directly}} + // expected-note@-4 {{candidate ignored: non-default multiversion function cannot be called directly}} + // expected-error@+1 {{no matching function for call to 'no_default'}} + no_default(); +} + +int __attribute__((target("sse4.2"))) no_proto(); +// expected-error@-1 {{multiversion function must have a prototype}} +// expected-note@+1 {{function multiversion 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 use2(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 {{multiversion function would have identical mangling to a previous}} +//expected-note@-2 {{previously declared 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 multiversion 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 multiversion 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 multiversion 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 multiversion 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 multiversion 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 {{multiversion function declaration has a different calling convention}} +// expected-note@+1 {{function multiversion caused by this declaration}} +__vectorcall int __attribute__((target("arch=sandybridge"))) diff_cc(void); + +int __attribute__((target("sse4.2"))) diff_ret(void); +// expected-error@+2 {{multiversion function declaration has a different return type}} +// expected-note@+1 {{function multiversion 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,99 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify -fexceptions -fcxx-exceptions %s -std=c++14 +constexpr int __attribute__((target("sse4.2"))) foo(void) { return 0; } +constexpr int __attribute__((target("arch=sandybridge"))) foo(void); +//expected-error@+1 {{multiversion 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 {{multiversion function declaration has a different constexpr specification}} +//expected-note@+1 {{function multiversion 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 {{multiversion 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 {{multiversion function declaration has a different storage class}} +//expected-note@+1 {{function multiversion 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 {{multiversion 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 {{multiversion function declaration has a different inline specification}} +//expected-note@+1 {{function multiversion 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 {{multiversion function declaration has a different return type}} +//expected-note@+1 {{function multiversion caused by this declaration}} +int __attribute__((target("arch=sandybridge"))) bock(void); +//expected-error@+2 {{multiversion function declaration has a different return type}} +//expected-note@+1 {{function multiversion caused by this declaration}} +int __attribute__((target("arch=ivybridge"))) bock(void) {return 1;} +//expected-error@+2 {{multiversion function declaration has a different return type}} +//expected-note@+1 {{function multiversion caused by this declaration}} +int __attribute__((target("default"))) bock(void) { return 2; } + +int __attribute__((target("sse4.2"))) bock2(void) { return 0; } +//expected-error@+2 {{multiversion function declaration has a different return type}} +//expected-note@+1 {{function multiversion 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 {{multiversion function declaration has a different return type}} +//expected-note@+1 {{function multiversion 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 {{multiversion functions do not yet support function templates}} +// expected-note@+2 {{function multiversion caused by this declaration}} +template +int __attribute__((target("arch=sandybridge"))) foo(T); + +// expected-error@+3 {{multiversion functions do not yet support function templates}} +// expected-note@+2 {{function multiversion 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 {{multiversion functions do not yet support function templates}} + // expected-note@+2 {{function multiversion caused by this declaration}} + template + int __attribute__((target("arch=sandybridge"))) foo(T); + + // expected-error@+3 {{multiversion functions do not yet support function templates}} + // expected-note@+2 {{function multiversion caused by this declaration}} + template + int __attribute__((target("default"))) foo(T) { return 2; } + + // expected-error@+2 {{multiversion functions do not yet support virtual functions}} + // expected-note@+1 {{function multiversion caused by this declaration}} + virtual void __attribute__((target("default"))) virt(); +};