Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -2209,6 +2209,13 @@ getCanonicalDecl()->IsMultiVersion = V; } + /// True if this function is a multiversioned dispatch function as a part of + /// the cpu_specific/cpu_dispatch functionality. + bool isCPUDispatchMultiVersion() const; + /// True if this function is a multiversioned processor specific function as a + /// part of the cpu_specific/cpu_dispatch functionality. + bool isCPUSpecificMultiVersion() const; + void setPreviousDeclaration(FunctionDecl * PrevDecl); FunctionDecl *getCanonicalDecl() override; Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -168,6 +168,7 @@ class VariadicUnsignedArgument : Argument; class VariadicExprArgument : Argument; class VariadicStringArgument : Argument; +class VariadicIdentifierArgument : Argument; // Like VariadicUnsignedArgument except values are ParamIdx. class VariadicParamIdxArgument : Argument; @@ -845,6 +846,27 @@ let Documentation = [Undocumented]; } +def CPUSpecific : InheritableAttr { + let Spellings = [Clang<"cpu_specific">]; + let Args = [VariadicIdentifierArgument<"Cpus">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [CPUSpecificCPUDispatchDocs]; + let AdditionalMembers = [{ + unsigned ActiveArgIndex = 0; + + IdentifierInfo *getCurCPUName() const { + return *(cpus_begin() + ActiveArgIndex); + } + }]; +} + +def CPUDispatch : InheritableAttr { + let Spellings = [Clang<"cpu_dispatch">]; + let Args = [VariadicIdentifierArgument<"Cpus">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [CPUSpecificCPUDispatchDocs]; +} + // CUDA attributes are spelled __attribute__((attr)) or __declspec(__attr__), // and they do not receive a [[]] spelling. def CUDAConstant : InheritableAttr { Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -191,6 +191,65 @@ }]; } +def CPUSpecificCPUDispatchDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``cpu_specific`` and ``cpu_dispatch`` attributes are used to define and +resolve multiversioned functions. This form of multiversioning provides a +mechanism for declaring versions across translation units and manually +specifying the resolved function list. A specified CPU defines a set of minimum +features that are required for the function to be called. The result of this is +that future processors execute the most restrictive version of the function the +new processor can execute. + +Function versions are defined with ``cpu_specific``, which takes one or more CPU +names as a parameter. For example: + +.. code-block:: c + + // Declares and defines the ivybridge version of single_cpu. + __attribute__((cpu_specific(ivybridge))) + void single_cpu(void){} + + // Declares and defines the atom version of single_cpu. + __attribute__((cpu_specific(atom))) + void single_cpu(void){} + + // Declares and defines both the ivybridge and atom version of multi_cpu. + __attribute__((cpu_specific(ivybridge, atom))) + void multi_cpu(void){} + +A dispatching (or resolving) function can be declared anywhere in a project's +source code with ``cpu_dispatch``. This attribute takes one or more CPU names +as a parameter (like ``cpu_specific``). Functions marked with ``cpu_dispatch`` +are not expected to be defined, only declared. If such a marked function has a +definition, any side effects of the function are ignored; trivial function +bodies are permissible for ICC compatibility. + +.. code-block:: c + + // Creates a resolver for single_cpu above. + __attribute__((cpu_dispatch(ivybridge, atom))) + void single_cpu(void){} + + // Creates a resolver for multi_cpu, but adds a 3rd version defined in another + // translation unit. + __attribute__((cpu_dispatch(ivybridge, atom, sandybridge))) + void multi_cpu(void){} + +Note that it is possible to have a resolving function that dispatches based on +more or fewer options than are present in the program. Specifying fewer will +result in the omitted options not being considered during resolution. Specifying +a version for resolution that isn't defined in the program will result in a +linking failure. + +It is also possible to specify a CPU name of ``generic`` which will be resolved +if the executing processor doesn't satisfy the features required in the CPU +name. The behavior of a program executing on a processor that doesn't satisfy +any option of a multiversioned function is undefined. + }]; +} + def C11NoReturnDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -1022,3 +1022,7 @@ // Warning for the experimental-isel options. def ExperimentalISel : DiagGroup<"experimental-isel">; + +// A warning group specifically for warnings related to function +// multiversioning. +def FunctionMultiVersioning : DiagGroup<"function-multiversion">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -608,6 +608,8 @@ def err_arm_invalid_specialreg : Error<"invalid special register for builtin">; def err_invalid_cpu_supports : Error<"invalid cpu feature string for builtin">; def err_invalid_cpu_is : Error<"invalid cpu name for builtin">; +def err_invalid_cpu_specific_dispatch_value : Error< +"invalid option '%0' for %select{cpu_specific|cpu_dispatch}1">; def err_builtin_needs_feature : Error<"%0 needs target feature %1">; def err_function_needs_feature : Error<"always_inline function %1 requires target feature '%2', but would " @@ -3788,8 +3790,8 @@ def err_ovl_no_oper : Error<"type %0 does not provide a %select{subscript|call}1 operator">; def err_ovl_unresolvable : Error< - "reference to overloaded function could not be resolved; " - "did you mean to call it%select{| with no arguments}0?">; + "reference to %select{overloaded|multiversioned}1 function could not be " + "resolved; did you mean to call it%select{| with no arguments}0?">; def err_bound_member_function : Error< "reference to non-static member function must be called" "%select{|; did you mean to call it with no arguments?}0">; @@ -9355,9 +9357,9 @@ 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 err_multiversion_required_in_redecl : Error< + "function declaration is missing %select{'target'|'cpu_specific' or " + "'cpu_dispatch'}0 attribute in a multiversioned function">; def note_multiversioning_caused_here : Note< "function multiversioning caused by this declaration">; def err_multiversion_after_used : Error< @@ -9371,20 +9373,33 @@ 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">; + "attribute '%select{target|cpu_specific|cpu_dispatch}0' multiversioning cannot be combined" + " with other attributes">; def err_multiversion_diff : Error< "multiversioned function declaration has a different %select{calling convention" "|return type|constexpr specification|inline specification|storage class|" "linkage}0">; def err_multiversion_doesnt_support : Error< - "multiversioned functions do not yet support %select{function templates|" - "virtual functions|deduced return types|constructors|destructors|" - "deleted functions|defaulted functions}0">; + "attribute '%select{target|cpu_specific|cpu_dispatch}0' multiversioned functions do not " + "yet support %select{function templates|virtual functions|" + "deduced return types|constructors|destructors|deleted functions|" + "defaulted functions|constexpr functions}1">; 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">; +def err_multiversion_types_mixed : Error< + "multiversioning attributes cannot be combined">; +def err_cpu_dispatch_mismatch : Error< + "'cpu_dispatch' function redeclared with different CPUs">; +def err_cpu_specific_multiple_defs : Error< + "multiple 'cpu_specific' functions cannot specify the same CPU: %0">; +def warn_multiversion_duplicate_entries : Warning< + "CPU list contains duplicate entries; attribute ignored">, + InGroup; +def warn_dispatch_body_ignored : Warning< + "body of cpu_dispatch function will be ignored">, + InGroup; // three-way comparison operator diagnostics def err_implied_comparison_category_type_not_found : Error< Index: include/clang/Basic/TargetInfo.h =================================================================== --- include/clang/Basic/TargetInfo.h +++ include/clang/Basic/TargetInfo.h @@ -1092,6 +1092,27 @@ // argument. virtual bool validateCpuIs(StringRef Name) const { return false; } + // Validate a cpu_dispatch/cpu_specific CPU option, which is a different list + // from cpu_is, since it checks via features rather than CPUs directly. + virtual bool validateCPUSpecificCPUDispatch(StringRef Name) const { + return false; + } + + // Get the character to be added for mangling purposes for cpu_specific. + virtual char CPUSpecificManglingCharacter(StringRef Name) const { + llvm_unreachable( + "cpu_specific Multiversioning not implemented on this target"); + } + + // Get a list of the features that make up the CPU option for + // cpu_specific/cpu_dispatch so that it can be passed to llvm as optimization + // options. + virtual void getCPUSpecificCPUDispatchFeatures( + StringRef Name, llvm::SmallVectorImpl &Features) const { + llvm_unreachable( + "cpu_specific Multiversioning not implemented on this target"); + } + // Returns maximal number of args passed in registers. unsigned getRegParmMax() const { assert(RegParmMax < 7 && "RegParmMax value is larger than AST can handle"); Index: include/clang/Basic/X86Target.def =================================================================== --- include/clang/Basic/X86Target.def +++ include/clang/Basic/X86Target.def @@ -29,6 +29,14 @@ #define FEATURE(ENUM) #endif +#ifndef CPU_SPECIFIC +#define CPU_SPECIFIC(NAME, MANGLING, FEATURES) +#endif + +#ifndef CPU_SPECIFIC_ALIAS +#define CPU_SPECIFIC_ALIAS(NEW_NAME, NAME) +#endif + #define PROC_64_BIT true #define PROC_32_BIT false @@ -276,6 +284,45 @@ FEATURE(FEATURE_AVX5124FMAPS) FEATURE(FEATURE_AVX512VPOPCNTDQ) + +// FIXME: When commented out features are supported in LLVM, enable them here. +CPU_SPECIFIC("generic", 'A', "") +CPU_SPECIFIC("pentium", 'B', "") +CPU_SPECIFIC("pentium_pro", 'C', "+cmov") +CPU_SPECIFIC("pentium_mmx", 'D', "+mmx") +CPU_SPECIFIC("pentium_ii", 'E', "+cmov,+mmx") +CPU_SPECIFIC("pentium_iii", 'H', "+cmov,+mmx,+sse") +CPU_SPECIFIC("pentium_iii_no_xmm_regs", 'H',"+cmov,+sse") +CPU_SPECIFIC("pentium_4", 'J', "+cmov,+mmx,+sse,+sse2") +CPU_SPECIFIC("pentium_m", 'K', "+cmov,+mmx,+sse,+sse2") +CPU_SPECIFIC("pentium_4_sse3", 'L', "+cmov,+mmx,+sse,+sse2,+sse3") +CPU_SPECIFIC("core_2_duo_ssse3", 'M', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3") +CPU_SPECIFIC("core_2_duo_sse4_1", 'N', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1") +CPU_SPECIFIC("atom", 'O', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+movbe") +CPU_SPECIFIC("atom_sse4_2", 'c', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt") +CPU_SPECIFIC("core_i7_sse4_2", 'P', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt") +CPU_SPECIFIC("core_aes_pclmulqdq", 'Q', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt") +CPU_SPECIFIC("atom_sse4_2_movbe", 'd', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+movbe,+popcnt") +CPU_SPECIFIC("goldmont", 'i', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+movbe,+popcnt") +CPU_SPECIFIC("sandybridge", 'R', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt,+avx") +CPU_SPECIFIC_ALIAS("core_2nd_gen_avx", "sandybridge") +CPU_SPECIFIC("ivybridge", 'S', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt,+f16c,+avx") +CPU_SPECIFIC_ALIAS("core_3rd_gen_avx", "ivybridge") +CPU_SPECIFIC("haswell", 'V', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+movbe,+popcnt,+f16c,+avx,+fma,+bmi,+lzcnt,+avx2") +CPU_SPECIFIC_ALIAS("core_4th_gen_avx", "haswell") +CPU_SPECIFIC("core_4th_gen_avx_tsx", 'W', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+movbe,+popcnt,+f16c,+avx,+fma,+bmi,+lzcnt,+avx2") +CPU_SPECIFIC("broadwell", 'X', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+movbe,+popcnt,+f16c,+avx,+fma,+bmi,+lzcnt,+avx2,+adx") +CPU_SPECIFIC_ALIAS("core_5th_gen_avx", "broadwell") +CPU_SPECIFIC("core_5th_gen_avx_tsx", 'Y', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+movbe,+popcnt,+f16c,+avx,+fma,+bmi,+lzcnt,+avx2,+adx") +CPU_SPECIFIC("knl", 'Z', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+movbe,+popcnt,+f16c,+avx,+fma,+bmi,+lzcnt,+avx2,+avx512f,+adx,+avx512er,+avx512pf,+avx512cd") +CPU_SPECIFIC_ALIAS("mic_avx512", "knl") +CPU_SPECIFIC("skylake", 'b', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+movbe,+popcnt,+f16c,+avx,+fma,+bmi,+lzcnt,+avx2,+adx,+mpx") +CPU_SPECIFIC( "skylake_avx512", 'a', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+movbe,+popcnt,+f16c,+avx,+fma,+bmi,+lzcnt,+avx2,+avx512dq,+avx512f,+adx,+avx512cd,+avx512bw,+avx512vl,+clwb") +CPU_SPECIFIC("cannonlake", 'e', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+movbe,+popcnt,+f16c,+avx,+fma,+bmi,+lzcnt,+avx2,+avx512dq,+avx512f,+adx,+avx512ifma,+avx512cd,+avx512bw,+avx512vl,+avx512vbmi") +CPU_SPECIFIC("knm", 'j', "+cmov,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+movbe,+popcnt,+f16c,+avx,+fma,+bmi,+lzcnt,+avx2,+avx512f,+adx,+avx512er,+avx512pf,+avx512cd,+avx5124fmaps,+avx5124vnniw,+avx512vpopcntdq") + +#undef CPU_SPECIFIC_ALIAS +#undef CPU_SPECIFIC #undef PROC_64_BIT #undef PROC_32_BIT #undef FEATURE Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -2873,6 +2873,14 @@ return false; } +bool FunctionDecl::isCPUDispatchMultiVersion() const { + return isMultiVersion() && hasAttr(); +} + +bool FunctionDecl::isCPUSpecificMultiVersion() const { + return isMultiVersion() && hasAttr(); +} + void FunctionDecl::setPreviousDeclaration(FunctionDecl *PrevDecl) { redeclarable_base::setPreviousDecl(PrevDecl); Index: lib/Basic/Targets/X86.h =================================================================== --- lib/Basic/Targets/X86.h +++ lib/Basic/Targets/X86.h @@ -150,6 +150,14 @@ bool validateCpuIs(StringRef Name) const override; + bool validateCPUSpecificCPUDispatch(StringRef Name) const override; + + char CPUSpecificManglingCharacter(StringRef Name) const override; + + void getCPUSpecificCPUDispatchFeatures( + StringRef Name, + llvm::SmallVectorImpl &Features) const override; + bool validateAsmConstraint(const char *&Name, TargetInfo::ConstraintInfo &info) const override; Index: lib/Basic/Targets/X86.cpp =================================================================== --- lib/Basic/Targets/X86.cpp +++ lib/Basic/Targets/X86.cpp @@ -1484,6 +1484,38 @@ return getFeaturePriority(getFeature(Name)) << 1; } +bool X86TargetInfo::validateCPUSpecificCPUDispatch(StringRef Name) const { + return llvm::StringSwitch(Name) +#define CPU_SPECIFIC(NAME, MANGLING, FEATURES) .Case(NAME, true) +#define CPU_SPECIFIC_ALIAS(NEW_NAME, NAME) .Case(NEW_NAME, true) +#include "clang/Basic/X86Target.def" + .Default(false); +} + +static StringRef CPUSpecificCPUDispatchNameDealias(StringRef Name) { + return llvm::StringSwitch(Name) +#define CPU_SPECIFIC_ALIAS(NEW_NAME, NAME) .Case(NEW_NAME, NAME) +#include "clang/Basic/X86Target.def" + .Default(Name); +} + +char X86TargetInfo::CPUSpecificManglingCharacter(StringRef Name) const { + return llvm::StringSwitch(CPUSpecificCPUDispatchNameDealias(Name)) +#define CPU_SPECIFIC(NAME, MANGLING, FEATURES) .Case(NAME, MANGLING) +#include "clang/Basic/X86Target.def" + .Default(0); +} + +void X86TargetInfo::getCPUSpecificCPUDispatchFeatures( + StringRef Name, llvm::SmallVectorImpl &Features) const { + StringRef WholeList = + llvm::StringSwitch(CPUSpecificCPUDispatchNameDealias(Name)) +#define CPU_SPECIFIC(NAME, MANGLING, FEATURES) .Case(NAME, FEATURES) +#include "clang/Basic/X86Target.def" + .Default(""); + WholeList.split(Features, ',', /*MaxSplit=*/-1, /*KeepEmpty=*/false); +} + std::string X86TargetInfo::getCPUKindCanonicalName(CPUKind Kind) const { switch (Kind) { case CK_Generic: Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -8904,11 +8904,10 @@ return EmitX86CpuSupports(FeatureStr); } -Value *CodeGenFunction::EmitX86CpuSupports(ArrayRef FeatureStrs) { +uint32_t +CodeGenFunction::GetX86CpuSupportsMask(ArrayRef FeatureStrs) { // Processor features and mapping to processor feature value. - uint32_t FeaturesMask = 0; - for (const StringRef &FeatureStr : FeatureStrs) { unsigned Feature = StringSwitch(FeatureStr) @@ -8917,7 +8916,14 @@ ; FeaturesMask |= (1U << Feature); } + return FeaturesMask; +} + +Value *CodeGenFunction::EmitX86CpuSupports(ArrayRef FeatureStrs) { + return EmitX86CpuSupports(GetX86CpuSupportsMask(FeatureStrs)); +} +llvm::Value *CodeGenFunction::EmitX86CpuSupports(uint32_t FeaturesMask) { // Matching the struct layout from the compiler-rt/libgcc structure that is // filled in: // unsigned int __cpu_vendor; Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -4113,12 +4113,13 @@ void EmitSanitizerStatReport(llvm::SanitizerStatKind SSK); - struct MultiVersionResolverOption { + struct TargetMultiVersionResolverOption { llvm::Function *Function; TargetAttr::ParsedTargetAttr ParsedAttribute; unsigned Priority; - MultiVersionResolverOption(const TargetInfo &TargInfo, llvm::Function *F, - const clang::TargetAttr::ParsedTargetAttr &PT) + TargetMultiVersionResolverOption( + 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, @@ -4129,12 +4130,30 @@ TargInfo.multiVersionSortPriority(PT.Architecture)); } - bool operator>(const MultiVersionResolverOption &Other) const { + bool operator>(const TargetMultiVersionResolverOption &Other) const { return Priority > Other.Priority; } }; - void EmitMultiVersionResolver(llvm::Function *Resolver, - ArrayRef Options); + void EmitTargetMultiVersionResolver( + llvm::Function *Resolver, + ArrayRef Options); + + struct CPUDispatchMultiVersionResolverOption { + llvm::Function *Function; + // Note: EmitX86CPUSupports only has 32 bits available, so we store the mask + // as 32 bits here. When 64-bit support is added to __builtin_cpu_supports, + // this can be extended to 64 bits. + uint32_t FeatureMask; + CPUDispatchMultiVersionResolverOption(llvm::Function *F, uint64_t Mask) + : Function(F), FeatureMask(static_cast(Mask)) {} + bool operator>(const CPUDispatchMultiVersionResolverOption &Other) const { + return FeatureMask > Other.FeatureMask; + } + }; + void EmitCPUDispatchMultiVersionResolver( + llvm::Function *Resolver, + ArrayRef Options); + static uint32_t GetX86CpuSupportsMask(ArrayRef FeatureStrs); private: QualType getVarArgType(const Expr *Arg); @@ -4151,8 +4170,10 @@ llvm::Value *EmitX86CpuIs(StringRef CPUStr); llvm::Value *EmitX86CpuSupports(const CallExpr *E); llvm::Value *EmitX86CpuSupports(ArrayRef FeatureStrs); + llvm::Value *EmitX86CpuSupports(uint32_t Mask); llvm::Value *EmitX86CpuInit(); - llvm::Value *FormResolverCondition(const MultiVersionResolverOption &RO); + llvm::Value * + FormResolverCondition(const TargetMultiVersionResolverOption &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 @@ -2323,7 +2323,8 @@ << TargetDecl->getDeclName() << CGM.getContext().BuiltinInfo.getRequiredFeatures(BuiltinID); - } else if (TargetDecl->hasAttr()) { + } else if (TargetDecl->hasAttr() || + TargetDecl->hasAttr()) { // Get the required features for the callee. const TargetAttr *TD = TargetDecl->getAttr(); @@ -2358,8 +2359,8 @@ CGM.getSanStats().create(IRB, SSK); } -llvm::Value * -CodeGenFunction::FormResolverCondition(const MultiVersionResolverOption &RO) { +llvm::Value *CodeGenFunction::FormResolverCondition( + const TargetMultiVersionResolverOption &RO) { llvm::Value *TrueCondition = nullptr; if (!RO.ParsedAttribute.Architecture.empty()) TrueCondition = EmitX86CpuIs(RO.ParsedAttribute.Architecture); @@ -2377,8 +2378,9 @@ return TrueCondition; } -void CodeGenFunction::EmitMultiVersionResolver( - llvm::Function *Resolver, ArrayRef Options) { +void CodeGenFunction::EmitTargetMultiVersionResolver( + llvm::Function *Resolver, + ArrayRef Options) { assert((getContext().getTargetInfo().getTriple().getArch() == llvm::Triple::x86 || getContext().getTargetInfo().getTriple().getArch() == @@ -2391,7 +2393,7 @@ EmitX86CpuInit(); llvm::Function *DefaultFunc = nullptr; - for (const MultiVersionResolverOption &RO : Options) { + for (const TargetMultiVersionResolverOption &RO : Options) { Builder.SetInsertPoint(CurBlock); llvm::Value *TrueCondition = FormResolverCondition(RO); @@ -2412,6 +2414,44 @@ Builder.CreateRet(DefaultFunc); } +void CodeGenFunction::EmitCPUDispatchMultiVersionResolver( + 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("resolver_entry", Resolver); + Builder.SetInsertPoint(CurBlock); + EmitX86CpuInit(); + + for (const CPUDispatchMultiVersionResolverOption &RO : Options) { + Builder.SetInsertPoint(CurBlock); + + // "generic" case should catch-all. + if (RO.FeatureMask == 0) { + Builder.CreateRet(RO.Function); + return; + } + llvm::BasicBlock *RetBlock = createBasicBlock("resolver_return", Resolver); + llvm::IRBuilder<> RetBuilder(RetBlock); + RetBuilder.CreateRet(RO.Function); + CurBlock = createBasicBlock("resolver_else", Resolver); + llvm::Value *TrueCondition = EmitX86CpuSupports(RO.FeatureMask); + Builder.CreateCondBr(TrueCondition, RetBlock, CurBlock); + } + + Builder.SetInsertPoint(CurBlock); + llvm::CallInst *TrapCall = EmitTrapCall(llvm::Intrinsic::trap); + TrapCall->setDoesNotReturn(); + TrapCall->setDoesNotThrow(); + Builder.CreateUnreachable(); + Builder.ClearInsertionPoint(); +} + 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 @@ -366,6 +366,13 @@ llvm::MapVector MangledDeclNames; llvm::StringMap Manglings; + // An ordered map of canonical GlobalDecls paired with the cpu-index for + // cpu-specific name manglings. + llvm::MapVector, StringRef> + CPUSpecificMangledDeclNames; + llvm::StringMap, llvm::BumpPtrAllocator> + CPUSpecificManglings; + /// Global annotations. std::vector Annotations; @@ -1283,7 +1290,6 @@ llvm::Constant *GetOrCreateMultiVersionIFunc(GlobalDecl GD, llvm::Type *DeclTy, - StringRef MangledName, const FunctionDecl *FD); void UpdateMultiVersionNames(GlobalDecl GD, const FunctionDecl *FD); @@ -1307,6 +1313,7 @@ void EmitGlobalVarDefinition(const VarDecl *D, bool IsTentative = false); void EmitAliasDefinition(GlobalDecl GD); void emitIFuncDefinition(GlobalDecl GD); + void emitCPUDispatchDefinition(GlobalDecl GD); void EmitObjCPropertyImplementations(const ObjCImplementationDecl *D); void EmitObjCIvarInitializations(ObjCImplementationDecl *D); Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -861,22 +861,38 @@ GV->setThreadLocalMode(TLM); } +static std::string getCPUSpecificMangling(const CodeGenModule &CGM, + StringRef Name) { + const TargetInfo &Target = CGM.getTarget(); + return (Twine('.') + Twine(Target.CPUSpecificManglingCharacter(Name))).str(); +} + +static void AppendCPUSpecificCPUDispatchMangling(const CodeGenModule &CGM, + const CPUSpecificAttr *Attr, + raw_ostream &Out) { + // cpu_specific gets the current name, dispatch gets the resolver. + if (Attr) + Out << getCPUSpecificMangling(CGM, Attr->getCurCPUName()->getName()); + else + Out << ".resolver"; +} + static void AppendTargetMangling(const CodeGenModule &CGM, const TargetAttr *Attr, raw_ostream &Out) { if (Attr->isDefaultVersion()) return; Out << '.'; - const auto &Target = CGM.getTarget(); + const TargetInfo &Target = CGM.getTarget(); TargetAttr::ParsedTargetAttr Info = Attr->parse([&Target](StringRef LHS, StringRef RHS) { - // Multiversioning doesn't allow "no-${feature}", so we can - // only have "+" prefixes here. - assert(LHS.startswith("+") && RHS.startswith("+") && - "Features should always have a prefix."); - return Target.multiVersionSortPriority(LHS.substr(1)) > - Target.multiVersionSortPriority(RHS.substr(1)); - }); + // Multiversioning doesn't allow "no-${feature}", so we can + // only have "+" prefixes here. + assert(LHS.startswith("+") && RHS.startswith("+") && + "Features should always have a prefix."); + return Target.multiVersionSortPriority(LHS.substr(1)) > + Target.multiVersionSortPriority(RHS.substr(1)); + }); bool IsFirst = true; @@ -895,7 +911,7 @@ static std::string getMangledNameImpl(const CodeGenModule &CGM, GlobalDecl GD, const NamedDecl *ND, - bool OmitTargetMangling = false) { + bool OmitMultiVersionMangling = false) { SmallString<256> Buffer; llvm::raw_svector_ostream Out(Buffer); MangleContext &MC = CGM.getCXXABI().getMangleContext(); @@ -922,8 +938,14 @@ } if (const auto *FD = dyn_cast(ND)) - if (FD->isMultiVersion() && !OmitTargetMangling) - AppendTargetMangling(CGM, FD->getAttr(), Out); + if (FD->isMultiVersion() && !OmitMultiVersionMangling) { + if (FD->isCPUDispatchMultiVersion() || FD->isCPUSpecificMultiVersion()) + AppendCPUSpecificCPUDispatchMangling( + CGM, FD->getAttr(), Out); + else + AppendTargetMangling(CGM, FD->getAttr(), Out); + } + return Out.str(); } @@ -936,7 +958,7 @@ // 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); + getMangledNameImpl(*this, GD, FD, /*OmitMultiVersionMangling=*/true); GlobalDecl OtherGD; if (lookupRepresentativeDecl(NonTargetName, OtherGD)) { assert(OtherGD.getCanonicalDecl() @@ -979,11 +1001,30 @@ } } + const auto *FD = dyn_cast(GD.getDecl()); + // Since CPUSpecific can require multiple emits per decl, store the manglings + // separately. + if (FD && + (FD->isCPUDispatchMultiVersion() || FD->isCPUSpecificMultiVersion())) { + const auto *SD = FD->getAttr(); + + std::pair SpecCanonicalGD{ + CanonicalGD, + SD ? SD->ActiveArgIndex : std::numeric_limits::max()}; + + auto FoundName = CPUSpecificMangledDeclNames.find(SpecCanonicalGD); + if (FoundName != CPUSpecificMangledDeclNames.end()) + return FoundName->second; + + auto Result = CPUSpecificManglings.insert( + std::make_pair(getMangledNameImpl(*this, GD, FD), SpecCanonicalGD)); + return CPUSpecificMangledDeclNames[SpecCanonicalGD] = Result.first->first(); + } + auto FoundName = MangledDeclNames.find(CanonicalGD); if (FoundName != MangledDeclNames.end()) return FoundName->second; - // Keep the first result in the case of a mangling collision. const auto *ND = cast(GD.getDecl()); auto Result = @@ -1321,8 +1362,9 @@ const auto *FD = dyn_cast_or_null(D); FD = FD ? FD->getMostRecentDecl() : FD; const auto *TD = FD ? FD->getAttr() : nullptr; + const auto *SD = FD ? FD->getAttr() : nullptr; bool AddedAttr = false; - if (TD) { + if (TD || SD) { llvm::StringMap FeatureMap; getFunctionFeatureMap(FeatureMap, FD); @@ -1334,10 +1376,12 @@ // While we populated the feature map above, we still need to // get and parse the target attribute so we can get the cpu for // the function. - TargetAttr::ParsedTargetAttr ParsedAttr = TD->parse(); - if (ParsedAttr.Architecture != "" && - getTarget().isValidCPUName(ParsedAttr.Architecture)) - TargetCPU = ParsedAttr.Architecture; + if (TD) { + TargetAttr::ParsedTargetAttr ParsedAttr = TD->parse(); + if (ParsedAttr.Architecture != "" && + getTarget().isValidCPUName(ParsedAttr.Architecture)) + TargetCPU = ParsedAttr.Architecture; + } } else { // Otherwise just add the existing target cpu and target features to the // function. @@ -2037,6 +2081,10 @@ if (Global->hasAttr()) return emitIFuncDefinition(GD); + // If this is a cpu_dispatch multiversion function, emit the resolver. + if (Global->hasAttr()) + return emitCPUDispatchDefinition(GD); + // If this is CUDA, be selective about which declarations we emit. if (LangOpts.CUDA) { if (LangOpts.CUDAIsDevice) { @@ -2355,7 +2403,7 @@ void CodeGenModule::emitMultiVersionFunctions() { for (GlobalDecl GD : MultiVersionFuncs) { - SmallVector Options; + SmallVector Options; const FunctionDecl *FD = cast(GD.getDecl()); getContext().forEachMultiversionedFunctionVersion( FD, [this, &GD, &Options](const FunctionDecl *CurFD) { @@ -2387,28 +2435,75 @@ getModule().getOrInsertComdat(ResolverFunc->getName())); std::stable_sort( Options.begin(), Options.end(), - std::greater()); + std::greater()); CodeGenFunction CGF(*this); - CGF.EmitMultiVersionResolver(ResolverFunc, Options); + CGF.EmitTargetMultiVersionResolver(ResolverFunc, Options); } } +void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) { + const auto *FD = cast(GD.getDecl()); + assert(FD && "Not a FunctionDecl?"); + const auto *DD = FD->getAttr(); + assert(DD && "Not a cpu_dispatch Function?"); + llvm::Type *DeclTy = getTypes().ConvertTypeForMem(FD->getType()); + + StringRef ResolverName = getMangledName(GD); + llvm::Type *ResolverType = llvm::FunctionType::get( + llvm::PointerType::get(DeclTy, + Context.getTargetAddressSpace(FD->getType())), + false); + auto *ResolverFunc = cast( + GetOrCreateLLVMFunction(ResolverName, ResolverType, GlobalDecl{}, + /*ForVTable=*/false)); + + SmallVector + Options; + const TargetInfo &Target = getTarget(); + for (const IdentifierInfo *II : DD->cpus()) { + // Get the name of the target function so we can look it up/create it. + std::string MangledName = getMangledNameImpl(*this, GD, FD, true) + + getCPUSpecificMangling(*this, II->getName()); + llvm::Constant *Func = GetOrCreateLLVMFunction( + MangledName, DeclTy, GD, /*ForVTable=*/false, /*DontDefer=*/false, + /*IsThunk=*/false, llvm::AttributeList(), ForDefinition); + llvm::SmallVector Features; + Target.getCPUSpecificCPUDispatchFeatures(II->getName(), Features); + llvm::transform(Features, Features.begin(), + [](StringRef Str) { return Str.substr(1); }); + Features.erase(std::remove_if( + Features.begin(), Features.end(), [&Target](StringRef Feat) { + return !Target.validateCpuSupports(Feat); + }), Features.end()); + Options.emplace_back(cast(Func), + CodeGenFunction::GetX86CpuSupportsMask(Features)); + } + + llvm::sort( + Options.begin(), Options.end(), + std::greater()); + CodeGenFunction CGF(*this); + CGF.EmitCPUDispatchMultiVersionResolver(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(); + std::string MangledName = + getMangledNameImpl(*this, GD, FD, /*OmitMultiVersionMangling=*/true); + std::string IFuncName = MangledName + ".ifunc"; 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); + // replaced later if necessary (target multiversioning only). + if (!FD->isCPUDispatchMultiVersion() && !FD->isCPUSpecificMultiVersion()) + MultiVersionFuncs.push_back(GD); - std::string ResolverName = (MangledName + ".resolver").str(); + std::string ResolverName = MangledName + ".resolver"; llvm::Type *ResolverType = llvm::FunctionType::get( llvm::PointerType::get(DeclTy, Context.getTargetAddressSpace(FD->getType())), @@ -2455,10 +2550,12 @@ addDeferredDeclToEmit(GDDef); } - if (FD->isMultiVersion() && FD->getAttr()->isDefaultVersion()) { - UpdateMultiVersionNames(GD, FD); + if (FD->isMultiVersion()) { + const auto *TA = FD->getAttr(); + if (TA && TA->isDefaultVersion()) + UpdateMultiVersionNames(GD, FD); if (!IsForDefinition) - return GetOrCreateMultiVersionIFunc(GD, Ty, MangledName, FD); + return GetOrCreateMultiVersionIFunc(GD, Ty, FD); } } @@ -3727,6 +3824,15 @@ AddGlobalDtor(Fn, DA->getPriority()); if (D->hasAttr()) AddGlobalAnnotations(D, Fn); + + if (D->isCPUSpecificMultiVersion()) { + auto *Spec = D->getAttr(); + // If there is another specific version we need to emit, do so here. + if (Spec->ActiveArgIndex + 1 < Spec->cpus_size()) { + ++Spec->ActiveArgIndex; + EmitGlobalFunctionDefinition(GD, nullptr); + } + } } void CodeGenModule::EmitAliasDefinition(GlobalDecl GD) { @@ -5107,6 +5213,12 @@ // the attribute. Target.initFeatureMap(FeatureMap, getDiags(), TargetCPU, ParsedAttr.Features); + } else if (const auto *SD = FD->getAttr()) { + llvm::SmallVector FeaturesTmp; + Target.getCPUSpecificCPUDispatchFeatures(SD->getCurCPUName()->getName(), + FeaturesTmp); + std::vector Features(FeaturesTmp.begin(), FeaturesTmp.end()); + Target.initFeatureMap(FeatureMap, getDiags(), TargetCPU, Features); } else { Target.initFeatureMap(FeatureMap, getDiags(), TargetCPU, Target.getTargetOpts().Features); Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -215,6 +215,15 @@ #undef CLANG_ATTR_IDENTIFIER_ARG_LIST } +/// Determine whether the given attribute has a variadic identifier argument. +static bool attributeHasVariadicIdentifierArg(const IdentifierInfo &II) { +#define CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST + return llvm::StringSwitch(normalizeAttrName(II.getName())) +#include "clang/Parse/AttrParserStringSwitches.inc" + .Default(false); +#undef CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST +} + /// Determine whether the given attribute parses a type argument. static bool attributeIsTypeArgAttr(const IdentifierInfo &II) { #define CLANG_ATTR_TYPE_ARG_LIST @@ -282,7 +291,8 @@ ArgsVector ArgExprs; if (Tok.is(tok::identifier)) { // If this attribute wants an 'identifier' argument, make it so. - bool IsIdentifierArg = attributeHasIdentifierArg(*AttrName); + bool IsIdentifierArg = attributeHasIdentifierArg(*AttrName) || + attributeHasVariadicIdentifierArg(*AttrName); ParsedAttr::Kind AttrKind = ParsedAttr::getKind(AttrName, ScopeName, Syntax); @@ -305,19 +315,25 @@ // Parse the non-empty comma-separated list of expressions. do { - bool Uneval = attributeParsedArgsUnevaluated(*AttrName); - EnterExpressionEvaluationContext Unevaluated( - Actions, - Uneval ? Sema::ExpressionEvaluationContext::Unevaluated - : Sema::ExpressionEvaluationContext::ConstantEvaluated); - - ExprResult ArgExpr( - Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression())); - if (ArgExpr.isInvalid()) { - SkipUntil(tok::r_paren, StopAtSemi); - return 0; + ExprResult ArgExpr; + if (Tok.is(tok::identifier) && + attributeHasVariadicIdentifierArg(*AttrName)) { + ArgExprs.push_back(ParseIdentifierLoc()); + } else { + bool Uneval = attributeParsedArgsUnevaluated(*AttrName); + EnterExpressionEvaluationContext Unevaluated( + Actions, + Uneval ? Sema::ExpressionEvaluationContext::Unevaluated + : Sema::ExpressionEvaluationContext::ConstantEvaluated); + + ExprResult ArgExpr( + Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression())); + if (ArgExpr.isInvalid()) { + SkipUntil(tok::r_paren, StopAtSemi); + return 0; + } + ArgExprs.push_back(ArgExpr.get()); } - ArgExprs.push_back(ArgExpr.get()); // Eat the comma, move to the next argument } while (TryConsumeToken(tok::comma)); } Index: lib/Sema/AnalysisBasedWarnings.cpp =================================================================== --- lib/Sema/AnalysisBasedWarnings.cpp +++ lib/Sema/AnalysisBasedWarnings.cpp @@ -658,6 +658,11 @@ else S.Diag(Loc, DiagID); }; + + // cpu_dispatch functions permit empty function bodies for ICC compatibility. + if (D->getAsFunction() && D->getAsFunction()->isCPUDispatchMultiVersion()) + return; + // Either in a function body compound statement, or a function-try-block. switch (CheckFallThrough(AC)) { case UnknownFallThrough: Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -1585,6 +1585,7 @@ } bool Ambiguous = false; + bool IsMV = false; if (Overloads) { for (OverloadExpr::decls_iterator it = Overloads->decls_begin(), @@ -1598,11 +1599,16 @@ if (const FunctionDecl *OverloadDecl = dyn_cast((*it)->getUnderlyingDecl())) { if (OverloadDecl->getMinRequiredArguments() == 0) { - if (!ZeroArgCallReturnTy.isNull() && !Ambiguous) { + if (!ZeroArgCallReturnTy.isNull() && !Ambiguous && + (!IsMV || !(OverloadDecl->isCPUDispatchMultiVersion() || + OverloadDecl->isCPUSpecificMultiVersion()))) { ZeroArgCallReturnTy = QualType(); Ambiguous = true; - } else + } else { ZeroArgCallReturnTy = OverloadDecl->getReturnType(); + IsMV = OverloadDecl->isCPUDispatchMultiVersion() || + OverloadDecl->isCPUSpecificMultiVersion(); + } } } } @@ -1683,7 +1689,7 @@ NamedDecl *Fn = (*It)->getUnderlyingDecl(); // Don't print overloads for non-default multiversioned functions. if (const auto *FD = Fn->getAsFunction()) { - if (FD->isMultiVersion() && + if (FD->isMultiVersion() && FD->hasAttr() && !FD->getAttr()->isDefaultVersion()) continue; } @@ -1725,6 +1731,21 @@ !isa(E)); } +static bool IsCPUDispatchCPUSpecificMultiVersion(const Expr *E) { + if (const auto *UO = dyn_cast(E)) + E = UO->getSubExpr(); + + if (const auto *ULE = dyn_cast(E)) { + if (ULE->getNumDecls() == 0) + return false; + + const NamedDecl *ND = *ULE->decls_begin(); + if (const auto *FD = dyn_cast(ND)) + return FD->isCPUDispatchMultiVersion() || FD->isCPUSpecificMultiVersion(); + } + return false; +} + bool Sema::tryToRecoverWithCall(ExprResult &E, const PartialDiagnostic &PD, bool ForceComplain, bool (*IsPlausibleResult)(QualType)) { @@ -1741,12 +1762,13 @@ // so we can emit a fixit and carry on pretending that E was // actually a CallExpr. SourceLocation ParenInsertionLoc = getLocForEndOfToken(Range.getEnd()); - Diag(Loc, PD) - << /*zero-arg*/ 1 << Range - << (IsCallableWithAppend(E.get()) - ? FixItHint::CreateInsertion(ParenInsertionLoc, "()") - : FixItHint()); - notePlausibleOverloads(*this, Loc, Overloads, IsPlausibleResult); + bool IsMV = IsCPUDispatchCPUSpecificMultiVersion(E.get()); + Diag(Loc, PD) << /*zero-arg*/ 1 << IsMV << Range + << (IsCallableWithAppend(E.get()) + ? FixItHint::CreateInsertion(ParenInsertionLoc, "()") + : FixItHint()); + if (!IsMV) + notePlausibleOverloads(*this, Loc, Overloads, IsPlausibleResult); // FIXME: Try this before emitting the fixit, and suppress diagnostics // while doing so. @@ -1757,8 +1779,10 @@ if (!ForceComplain) return false; - Diag(Loc, PD) << /*not zero-arg*/ 0 << Range; - notePlausibleOverloads(*this, Loc, Overloads, IsPlausibleResult); + bool IsMV = IsCPUDispatchCPUSpecificMultiVersion(E.get()); + Diag(Loc, PD) << /*not zero-arg*/ 0 << IsMV << Range; + if (!IsMV) + notePlausibleOverloads(*this, Loc, Overloads, IsPlausibleResult); E = ExprError(); return true; } Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -9275,6 +9275,20 @@ D->getFriendObjectKind() != Decl::FOK_None); } +namespace MultiVersioning { +enum Type { None, Target, CPUSpecific, CPUDispatch}; +} // MultiVersionType + +static MultiVersioning::Type +getMultiVersionType(const FunctionDecl *FD) { + if (FD->hasAttr()) + return MultiVersioning::Target; + if (FD->hasAttr()) + return MultiVersioning::CPUDispatch; + if (FD->hasAttr()) + return MultiVersioning::CPUSpecific; + return MultiVersioning::None; +} /// Check the target attribute of the function for MultiVersion /// validity. /// @@ -9313,7 +9327,8 @@ static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD, const FunctionDecl *NewFD, - bool CausesMV) { + bool CausesMV, + MultiVersioning::Type MVType) { enum DoesntSupport { FuncTemplates = 0, VirtFuncs = 1, @@ -9321,7 +9336,8 @@ Constructors = 3, Destructors = 4, DeletedFuncs = 5, - DefaultedFuncs = 6 + DefaultedFuncs = 6, + ConstexprFuncs = 7, }; enum Different { CallingConv = 0, @@ -9332,46 +9348,73 @@ Linkage = 5 }; + bool IsCPUSpecificCPUDispatchMVType = + MVType == MultiVersioning::CPUDispatch || + MVType == MultiVersioning::CPUSpecific; + + if (OldFD && !OldFD->getType()->getAs()) { + S.Diag(OldFD->getLocation(), diag::err_multiversion_noproto); + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + return true; + } + + if (!NewFD->getType()->getAs()) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_noproto); + + if (!S.getASTContext().getTargetInfo().supportsMultiVersioning()) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_not_supported); + if (OldFD) + S.Diag(OldFD->getLocation(), diag::note_previous_declaration); + return true; + } + // 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(OldFD->getLocation(), diag::err_multiversion_no_other_attrs) + << IsCPUSpecificCPUDispatchMVType; S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); return true; } if (std::distance(NewFD->attr_begin(), NewFD->attr_end()) != 1) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_no_other_attrs); + return S.Diag(NewFD->getLocation(), diag::err_multiversion_no_other_attrs) + << IsCPUSpecificCPUDispatchMVType; if (NewFD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate) return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) - << FuncTemplates; + << IsCPUSpecificCPUDispatchMVType << FuncTemplates; if (const auto *NewCXXFD = dyn_cast(NewFD)) { if (NewCXXFD->isVirtual()) return S.Diag(NewCXXFD->getLocation(), diag::err_multiversion_doesnt_support) - << VirtFuncs; + << IsCPUSpecificCPUDispatchMVType << VirtFuncs; if (const auto *NewCXXCtor = dyn_cast(NewFD)) return S.Diag(NewCXXCtor->getLocation(), diag::err_multiversion_doesnt_support) - << Constructors; + << IsCPUSpecificCPUDispatchMVType << Constructors; if (const auto *NewCXXDtor = dyn_cast(NewFD)) return S.Diag(NewCXXDtor->getLocation(), diag::err_multiversion_doesnt_support) - << Destructors; + << IsCPUSpecificCPUDispatchMVType << Destructors; } if (NewFD->isDeleted()) return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) - << DeletedFuncs; + << IsCPUSpecificCPUDispatchMVType << DeletedFuncs; if (NewFD->isDefaulted()) return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) - << DefaultedFuncs; + << IsCPUSpecificCPUDispatchMVType << DefaultedFuncs; + + if (NewFD->isConstexpr() && (MVType == MultiVersioning::CPUDispatch || + MVType == MultiVersioning::CPUSpecific)) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) + << IsCPUSpecificCPUDispatchMVType << ConstexprFuncs; QualType NewQType = S.getASTContext().getCanonicalType(NewFD->getType()); const auto *NewType = cast(NewQType); @@ -9379,7 +9422,7 @@ if (NewReturnType->isUndeducedType()) return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) - << DeducedReturn; + << IsCPUSpecificCPUDispatchMVType << DeducedReturn; // Only allow transition to MultiVersion if it hasn't been used. if (OldFD && CausesMV && OldFD->isUsed(false)) @@ -9426,138 +9469,133 @@ return false; } -/// Check the validity of a mulitversion function declaration. -/// Also sets the multiversion'ness' of the function itself. +/// Check the validity of a multiversion function declaration that is the +/// first of its kind. 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->setInvalidDecl(); - return true; - } +static bool CheckMultiVersionFirstFunction(Sema &S, FunctionDecl *FD, + MultiVersioning::Type MVType, + const TargetAttr *TA, + const CPUDispatchAttr *CPUDisp, + const CPUSpecificAttr *CPUSpec) { + assert(MVType != MultiVersioning::None && + "Function lacks multiversion attribute"); + + // Target only causes MV if it is default, otherwise this is a normal + // function. + if (MVType == MultiVersioning::Target && !TA->isDefaultVersion()) 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); - NewFD->setInvalidDecl(); - return true; - } + if (MVType == MultiVersioning::Target && CheckMultiVersionValue(S, FD)) { + FD->setInvalidDecl(); + return true; + } - NewFD->setIsMultiVersion(); - } - return false; + if (CheckMultiVersionAdditionalRules(S, nullptr, FD, true, MVType)) { + FD->setInvalidDecl(); + return true; } - if (OldDecl->getDeclContext()->getRedeclContext() != - NewFD->getDeclContext()->getRedeclContext()) - return false; + FD->setIsMultiVersion(); + 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; +static bool CheckTargetCausesMultiVersioning( + Sema &S, FunctionDecl *OldFD, FunctionDecl *NewFD, const TargetAttr *NewTA, + bool &Redeclaration, NamedDecl *&OldDecl, bool &MergeTypeWithPrevious, + LookupResult &Previous) { + const auto *OldTA = OldFD->getAttr(); + TargetAttr::ParsedTargetAttr NewParsed = NewTA->parse(); + // Sort order doesn't matter, it just needs to be consistent. + llvm::sort(NewParsed.Features.begin(), NewParsed.Features.end()); - if (!OldFD->isMultiVersion() && !NewTA) + // 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; - if (OldFD->isMultiVersion() && !NewTA) { - S.Diag(NewFD->getLocation(), diag::err_target_required_in_redecl); + // 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); NewFD->setInvalidDecl(); return true; } - TargetAttr::ParsedTargetAttr NewParsed = NewTA->parse(); - // Sort order doesn't matter, it just needs to be consistent. - llvm::sort(NewParsed.Features.begin(), NewParsed.Features.end()); + if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, true, + MultiVersioning::Target)) { + NewFD->setInvalidDecl(); + return true; + } - 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; + if (CheckMultiVersionValue(S, NewFD)) { + NewFD->setInvalidDecl(); + return true; + } - // 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); - NewFD->setInvalidDecl(); - return true; - } + if (CheckMultiVersionValue(S, OldFD)) { + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + NewFD->setInvalidDecl(); + 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; - } + TargetAttr::ParsedTargetAttr OldParsed = + OldTA->parse(std::less()); - if (CheckMultiVersionValue(S, NewFD)) { - NewFD->setInvalidDecl(); - return true; - } + if (OldParsed == NewParsed) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_duplicate); + S.Diag(OldFD->getLocation(), diag::note_previous_declaration); + NewFD->setInvalidDecl(); + return true; + } - if (CheckMultiVersionValue(S, OldFD)) { + for (const auto *FD : OldFD->redecls()) { + const auto *CurTA = FD->getAttr(); + if (!CurTA || CurTA->isInherited()) { + S.Diag(FD->getLocation(), diag::err_multiversion_required_in_redecl) + << 0; 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; - } - } + OldFD->setIsMultiVersion(); + NewFD->setIsMultiVersion(); + Redeclaration = false; + MergeTypeWithPrevious = false; + OldDecl = nullptr; + Previous.clear(); + return false; +} - if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, true)) { - NewFD->setInvalidDecl(); - return true; - } +/// Check the validity of a new function declaration being added to an existing +/// multiversioned declaration collection. +static bool CheckMultiVersionAdditionalDecl( + Sema &S, FunctionDecl *OldFD, FunctionDecl *NewFD, + MultiVersioning::Type NewMVType, const TargetAttr *NewTA, + const CPUDispatchAttr *NewCPUDisp, const CPUSpecificAttr *NewCPUSpec, + bool &Redeclaration, NamedDecl *&OldDecl, bool &MergeTypeWithPrevious, + LookupResult &Previous) { + + MultiVersioning::Type OldMVType = getMultiVersionType(OldFD); + // Disallow mixing of multiversioning types. + if ((OldMVType == MultiVersioning::Target && + NewMVType != MultiVersioning::Target) || + (NewMVType == MultiVersioning::Target && + OldMVType != MultiVersioning::Target)) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_types_mixed); + S.Diag(OldFD->getLocation(), diag::note_previous_declaration); + NewFD->setInvalidDecl(); + return true; + } - OldFD->setIsMultiVersion(); - NewFD->setIsMultiVersion(); - Redeclaration = false; - MergeTypeWithPrevious = false; - OldDecl = nullptr; - Previous.clear(); - return false; + TargetAttr::ParsedTargetAttr NewParsed; + if (NewTA) { + NewParsed = NewTA->parse(); + llvm::sort(NewParsed.Features.begin(), NewParsed.Features.end()); } bool UseMemberUsingDeclRules = @@ -9572,32 +9610,93 @@ 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; - } + if (NewMVType == MultiVersioning::Target) { + 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()); + 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 { + const auto *CurCPUSpec = CurFD->getAttr(); + const auto *CurCPUDisp = CurFD->getAttr(); + // Handle CPUDispatch/CPUSpecific versions. + // Only 1 CPUDispatch function is allowed, this will make it go through + // the redeclaration errors. + if (NewMVType == MultiVersioning::CPUDispatch && + CurFD->hasAttr()) { + if (CurCPUDisp->cpus_size() == NewCPUDisp->cpus_size() && + std::equal( + CurCPUDisp->cpus_begin(), CurCPUDisp->cpus_end(), + NewCPUDisp->cpus_begin(), + [](const IdentifierInfo *Cur, const IdentifierInfo *New) { + return Cur->getName() == New->getName(); + })) { + NewFD->setIsMultiVersion(); + Redeclaration = true; + OldDecl = ND; + return false; + } - if (CurParsed == NewParsed) { - S.Diag(NewFD->getLocation(), diag::err_multiversion_duplicate); - S.Diag(CurFD->getLocation(), diag::note_previous_declaration); - NewFD->setInvalidDecl(); - return true; + // If the declarations don't match, this is an error condition. + S.Diag(NewFD->getLocation(), diag::err_cpu_dispatch_mismatch); + S.Diag(CurFD->getLocation(), diag::note_previous_declaration); + NewFD->setInvalidDecl(); + return true; + } + if (NewMVType == MultiVersioning::CPUSpecific && CurCPUSpec) { + + if (CurCPUSpec->cpus_size() == NewCPUSpec->cpus_size() && + std::equal( + CurCPUSpec->cpus_begin(), CurCPUSpec->cpus_end(), + NewCPUSpec->cpus_begin(), + [](const IdentifierInfo *Cur, const IdentifierInfo *New) { + return Cur->getName() == New->getName(); + })) { + NewFD->setIsMultiVersion(); + Redeclaration = true; + OldDecl = ND; + return false; + } + + // Only 1 version of CPUSpecific is allowed for each CPU. + for (const IdentifierInfo *CurII : CurCPUSpec->cpus()) { + for (const IdentifierInfo *NewII : NewCPUSpec->cpus()) { + if (CurII == NewII) { + S.Diag(NewFD->getLocation(), diag::err_cpu_specific_multiple_defs) + << NewII; + S.Diag(CurFD->getLocation(), diag::note_previous_declaration); + NewFD->setInvalidDecl(); + return true; + } + } + } + } + // If the two decls aren't the same MVType, there is no possible error + // condition. } } - // Else, this is simply a non-redecl case. - if (CheckMultiVersionValue(S, NewFD)) { + // Else, this is simply a non-redecl case. Checking the 'value' is only + // necessary in the Target case, since The CPUSpecific/Dispatch cases are + // handled in the attribute adding step. + if (NewMVType == MultiVersioning::Target && + CheckMultiVersionValue(S, NewFD)) { NewFD->setInvalidDecl(); return true; } - if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, false)) { + if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, false, NewMVType)) { NewFD->setInvalidDecl(); return true; } @@ -9610,6 +9709,89 @@ return false; } + +/// 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(); + const auto *NewCPUDisp = NewFD->getAttr(); + const auto *NewCPUSpec = NewFD->getAttr(); + + // Mixing Multiversioning types is prohibited. + if ((NewTA && NewCPUDisp) || (NewTA && NewCPUSpec) || + (NewCPUDisp && NewCPUSpec)) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_types_mixed); + NewFD->setInvalidDecl(); + return true; + } + + MultiVersioning::Type MVType = getMultiVersionType(NewFD); + + // Main isn't allowed to become a multiversion function, however it IS + // permitted to have 'main' be marked with the 'target' optimization hint. + if (NewFD->isMain()) { + if ((MVType == MultiVersioning::Target && NewTA->isDefaultVersion()) || + MVType == MultiVersioning::CPUDispatch || + MVType == MultiVersioning::CPUSpecific) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_not_allowed_on_main); + NewFD->setInvalidDecl(); + return true; + } + return false; + } + + if (!OldDecl || !OldDecl->getAsFunction() || + OldDecl->getDeclContext()->getRedeclContext() != + NewFD->getDeclContext()->getRedeclContext()) { + // If there's no previous declaration, AND this isn't attempting to cause + // multiversioning, this isn't an error condition. + if (MVType == MultiVersioning::None) + return false; + return CheckMultiVersionFirstFunction(S, NewFD, MVType, NewTA, NewCPUDisp, + NewCPUSpec); + } + + FunctionDecl *OldFD = OldDecl->getAsFunction(); + + if (!OldFD->isMultiVersion() && MVType == MultiVersioning::None) + return false; + + if (OldFD->isMultiVersion() && MVType == MultiVersioning::None) { + S.Diag(NewFD->getLocation(), diag::err_multiversion_required_in_redecl) + << (getMultiVersionType(OldFD) != MultiVersioning::Target); + NewFD->setInvalidDecl(); + return true; + } + + // Handle the target potentially causes multiversioning case. + if (!OldFD->isMultiVersion() && MVType == MultiVersioning::Target) + return CheckTargetCausesMultiVersioning(S, OldFD, NewFD, NewTA, + Redeclaration, OldDecl, + MergeTypeWithPrevious, Previous); + // Previous declarations lack CPUDispatch/CPUSpecific. + if (!OldFD->isMultiVersion()) { + S.Diag(OldFD->getLocation(), diag::err_multiversion_required_in_redecl) + << 1; + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + NewFD->setInvalidDecl(); + return true; + } + + // At this point, we have a multiversion function decl (in OldFD) AND an + // appropriate attribute in the current function decl. Resolve that these are + // still compatible with previous declarations. + return CheckMultiVersionAdditionalDecl( + S, OldFD, NewFD, MVType, NewTA, NewCPUDisp, NewCPUSpec, Redeclaration, + OldDecl, MergeTypeWithPrevious, Previous); +} + /// Perform semantic checking of a new function declaration. /// /// Performs semantic analysis of the new function declaration @@ -12829,6 +13011,13 @@ } } + // Warn on CPUDispatch with an actual body. + if (FD->isMultiVersion() && FD->hasAttr() && Body) + if (const auto *CmpndBody = dyn_cast(Body)) + if (!CmpndBody->body_empty()) + Diag(CmpndBody->body_front()->getLocStart(), + diag::warn_dispatch_body_ignored); + if (auto *MD = dyn_cast(FD)) { const CXXMethodDecl *KeyFunction; if (MD->isOutOfLine() && (MD = MD->getCanonicalDecl()) && Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -1849,6 +1849,50 @@ << AL.getName() << getFunctionOrMethodResultSourceRange(D); } +static void handleCPUSpecificAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + FunctionDecl *FD = cast(D); + if (!checkAttributeAtLeastNumArgs(S, AL, 1)) + return; + + SmallVector CPUs; + for (unsigned ArgNo = 0; ArgNo < getNumAttributeArgs(AL); ++ArgNo) { + if (!AL.isArgIdent(ArgNo)) { + S.Diag(AL.getLoc(), diag::err_attribute_argument_type) + << AL.getName() << AANT_ArgumentIdentifier; + return; + } + + IdentifierLoc *CPUArg = AL.getArgAsIdent(ArgNo); + StringRef CPUName = CPUArg->Ident->getName().trim(); + + if (!S.Context.getTargetInfo().validateCPUSpecificCPUDispatch(CPUName)) { + S.Diag(CPUArg->Loc, diag::err_invalid_cpu_specific_dispatch_value) + << CPUName << (AL.getKind() == ParsedAttr::AT_CPUDispatch); + return; + } + + const TargetInfo &Target = S.Context.getTargetInfo(); + if (llvm::any_of(CPUs, [CPUName, &Target](const IdentifierInfo *Cur) { + return Target.CPUSpecificManglingCharacter(CPUName) == + Target.CPUSpecificManglingCharacter(Cur->getName()); + })) { + S.Diag(AL.getLoc(), diag::warn_multiversion_duplicate_entries); + return; + } + CPUs.push_back(CPUArg->Ident); + } + + FD->setIsMultiVersion(true); + if (AL.getKind() == ParsedAttr::AT_CPUSpecific) + D->addAttr(::new (S.Context) CPUSpecificAttr( + AL.getRange(), S.Context, CPUs.data(), CPUs.size(), + AL.getAttributeSpellingListIndex())); + else + D->addAttr(::new (S.Context) CPUDispatchAttr( + AL.getRange(), S.Context, CPUs.data(), CPUs.size(), + AL.getAttributeSpellingListIndex())); +} + static void handleCommonAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (S.LangOpts.CPlusPlus) { S.Diag(AL.getLoc(), diag::err_attribute_not_supported_in_lang) @@ -5967,6 +6011,10 @@ case ParsedAttr::AT_CarriesDependency: handleDependencyAttr(S, scope, D, AL); break; + case ParsedAttr::AT_CPUDispatch: + case ParsedAttr::AT_CPUSpecific: + handleCPUSpecificAttr(S, D, AL); + break; case ParsedAttr::AT_Common: handleCommonAttr(S, D, AL); break; Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -2728,12 +2728,23 @@ return false; } +// Certain multiversion types should be treated as overloaded even when there is +// only one result. +static bool ShouldLookupResultBeMultiVersionOverload(const LookupResult &R) { + assert(R.isSingleResult() && "Expected only a single result"); + const auto *FD = dyn_cast(R.getFoundDecl()); + return FD && + (FD->isCPUDispatchMultiVersion() || FD->isCPUSpecificMultiVersion()); +} + ExprResult Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS, LookupResult &R, bool NeedsADL, bool AcceptInvalidDecl) { // If this is a single, fully-resolved result and we don't need ADL, // just build an ordinary singleton decl ref. - if (!NeedsADL && R.isSingleResult() && !R.getAsSingle()) + if (!NeedsADL && R.isSingleResult() && + !R.getAsSingle() && + !ShouldLookupResultBeMultiVersionOverload(R)) return BuildDeclarationNameExpr(SS, R.getLookupNameInfo(), R.getFoundDecl(), R.getRepresentativeDecl(), nullptr, AcceptInvalidDecl); @@ -2741,7 +2752,7 @@ // We only need to check the declaration if there's exactly one // result, because in the overloaded case the results can only be // functions and function templates. - if (R.isSingleResult() && + if (R.isSingleResult() && !ShouldLookupResultBeMultiVersionOverload(R) && CheckDeclInExpr(*this, R.getNameLoc(), R.getFoundDecl())) return ExprError(); Index: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -5988,7 +5988,7 @@ Candidate.IgnoreObjectArgument = false; Candidate.ExplicitCallArguments = Args.size(); - if (Function->isMultiVersion() && + if (Function->isMultiVersion() && Function->hasAttr() && !Function->getAttr()->isDefaultVersion()) { Candidate.Viable = false; Candidate.FailureKind = ovl_non_default_multiversion_function; @@ -6623,7 +6623,7 @@ return; } - if (Method->isMultiVersion() && + if (Method->isMultiVersion() && Method->hasAttr() && !Method->getAttr()->isDefaultVersion()) { Candidate.Viable = false; Candidate.FailureKind = ovl_non_default_multiversion_function; @@ -7032,7 +7032,7 @@ return; } - if (Conversion->isMultiVersion() && + if (Conversion->isMultiVersion() && Conversion->hasAttr() && !Conversion->getAttr()->isDefaultVersion()) { Candidate.Viable = false; Candidate.FailureKind = ovl_non_default_multiversion_function; @@ -8987,6 +8987,47 @@ return Cand1I == Cand1Attrs.end() ? Comparison::Equal : Comparison::Better; } +static bool isBetterMultiversionCandidate(const OverloadCandidate &Cand1, + const OverloadCandidate &Cand2) { + if (!Cand1.Function || !Cand1.Function->isMultiVersion() || !Cand2.Function || + !Cand2.Function->isMultiVersion()) + return false; + + // If this is a cpu_dispatch/cpu_specific multiversion situation, prefer + // cpu_dispatch, else arbitrarily based on the identifiers. + bool Cand1CPUDisp = Cand1.Function->hasAttr(); + bool Cand2CPUDisp = Cand2.Function->hasAttr(); + const auto *Cand1CPUSpec = Cand1.Function->getAttr(); + const auto *Cand2CPUSpec = Cand2.Function->getAttr(); + + if (!Cand1CPUDisp && !Cand2CPUDisp && !Cand1CPUSpec && !Cand2CPUSpec) + return false; + + if (Cand1CPUDisp && !Cand2CPUDisp) + return true; + if (Cand2CPUDisp && !Cand1CPUDisp) + return false; + + if (Cand1CPUSpec && Cand2CPUSpec) { + if (Cand1CPUSpec->cpus_size() != Cand2CPUSpec->cpus_size()) + return Cand1CPUSpec->cpus_size() < Cand2CPUSpec->cpus_size(); + + std::pair + FirstDiff = std::mismatch( + Cand1CPUSpec->cpus_begin(), Cand1CPUSpec->cpus_end(), + Cand2CPUSpec->cpus_begin(), + [](const IdentifierInfo *LHS, const IdentifierInfo *RHS) { + return LHS->getName() == RHS->getName(); + }); + + assert(FirstDiff.first != Cand1CPUSpec->cpus_end() && + "Two different cpu-specific versions should not have the same " + "identifier list, otherwise they'd be the same decl!"); + return (*FirstDiff.first)->getName() < (*FirstDiff.second)->getName(); + } + llvm_unreachable("No way to get here unless both had cpu_dispatch"); +} + /// isBetterOverloadCandidate - Determines whether the first overload /// candidate is a better candidate than the second (C++ 13.3.3p1). bool clang::isBetterOverloadCandidate( @@ -9184,7 +9225,10 @@ functionHasPassObjectSizeParams(Cand1.Function); bool HasPS2 = Cand2.Function != nullptr && functionHasPassObjectSizeParams(Cand2.Function); - return HasPS1 != HasPS2 && HasPS1; + if (HasPS1 != HasPS2 && HasPS1) + return true; + + return isBetterMultiversionCandidate(Cand1, Cand2); } /// Determine whether two declarations are "equivalent" for the purposes of @@ -9503,7 +9547,8 @@ QualType DestType, bool TakingAddress) { if (TakingAddress && !checkAddressOfCandidateIsAvailable(*this, Fn)) return; - if (Fn->isMultiVersion() && !Fn->getAttr()->isDefaultVersion()) + if (Fn->isMultiVersion() && Fn->hasAttr() && + !Fn->getAttr()->isDefaultVersion()) return; std::string FnDesc; @@ -11056,8 +11101,7 @@ return false; if (FunDecl->isMultiVersion()) { const auto *TA = FunDecl->getAttr(); - assert(TA && "Multiversioned functions require a target attribute"); - if (!TA->isDefaultVersion()) + if (TA && !TA->isDefaultVersion()) return false; } @@ -11355,7 +11399,8 @@ DeclAccessPair DAP; FunctionDecl *Found = resolveAddressOfOnlyViableOverloadCandidate(E, DAP); - if (!Found) + if (!Found || Found->isCPUDispatchMultiVersion() || + Found->isCPUSpecificMultiVersion()) return false; // Emitting multiple diagnostics for a function that is both inaccessible and Index: test/CodeGen/attr-cpuspecific.c =================================================================== --- test/CodeGen/attr-cpuspecific.c +++ test/CodeGen/attr-cpuspecific.c @@ -0,0 +1,101 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s + + +// Each called version should have an IFunc. +// CHECK: @SingleVersion.ifunc = ifunc void (), void ()* ()* @SingleVersion.resolver +// CHECK: @TwoVersions.ifunc = ifunc void (), void ()* ()* @TwoVersions.resolver +// CHECK: @TwoVersionsSameAttr.ifunc = ifunc void (), void ()* ()* @TwoVersionsSameAttr.resolver +// CHECK: @ThreeVersionsSameAttr.ifunc = ifunc void (), void ()* ()* @ThreeVersionsSameAttr.resolver + +__attribute__((cpu_specific(ivybridge))) +void SingleVersion(void){} +// CHECK: define void @SingleVersion.S() #[[S:[0-9]+]] + +__attribute__((cpu_specific(ivybridge))) +void NotCalled(void){} +// CHECK: define void @NotCalled.S() #[[S]] + +// Done before any of the implementations. +__attribute__((cpu_dispatch(ivybridge, knl))) +void TwoVersions(void); +// CHECK: define void ()* @TwoVersions.resolver() +// CHECK: call void @__cpu_indicator_init +// CHECK: ret void ()* @TwoVersions.Z +// CHECK: ret void ()* @TwoVersions.S +// CHECK: call void @llvm.trap +// CHECK: unreachable + +__attribute__((cpu_specific(ivybridge))) +void TwoVersions(void){} +// CHECK: define void @TwoVersions.S() #[[S]] + +__attribute__((cpu_specific(knl))) +void TwoVersions(void){} +// CHECK: define void @TwoVersions.Z() #[[K:[0-9]+]] + +__attribute__((cpu_specific(ivybridge, knl))) +void TwoVersionsSameAttr(void){} +// CHECK: define void @TwoVersionsSameAttr.S() #[[S]] +// CHECK: define void @TwoVersionsSameAttr.Z() #[[K]] + +__attribute__((cpu_specific(atom, ivybridge, knl))) +void ThreeVersionsSameAttr(void){} +// CHECK: define void @ThreeVersionsSameAttr.O() #[[O:[0-9]+]] +// CHECK: define void @ThreeVersionsSameAttr.S() #[[S]] +// CHECK: define void @ThreeVersionsSameAttr.Z() #[[K]] + +void usages() { + SingleVersion(); + // CHECK: @SingleVersion.ifunc() + TwoVersions(); + // CHECK: @TwoVersions.ifunc() + TwoVersionsSameAttr(); + // CHECK: @TwoVersionsSameAttr.ifunc() + ThreeVersionsSameAttr(); + // CHECK: @ThreeVersionsSameAttr.ifunc() +} + +// has an extra config to emit! +__attribute__((cpu_dispatch(ivybridge, knl, atom))) +void TwoVersionsSameAttr(void); +// CHECK: define void ()* @TwoVersionsSameAttr.resolver() +// CHECK: ret void ()* @TwoVersionsSameAttr.Z +// CHECK: ret void ()* @TwoVersionsSameAttr.S +// CHECK: ret void ()* @TwoVersionsSameAttr.O +// CHECK: call void @llvm.trap +// CHECK: unreachable + +__attribute__((cpu_dispatch(atom, ivybridge, knl))) +void ThreeVersionsSameAttr(void){} +// CHECK: define void ()* @ThreeVersionsSameAttr.resolver() +// CHECK: call void @__cpu_indicator_init +// CHECK: ret void ()* @ThreeVersionsSameAttr.Z +// CHECK: ret void ()* @ThreeVersionsSameAttr.S +// CHECK: ret void ()* @ThreeVersionsSameAttr.O +// CHECK: call void @llvm.trap +// CHECK: unreachable + +// No Cpu Specific options. +__attribute__((cpu_dispatch(atom, ivybridge, knl))) +void NoSpecifics(void); +// CHECK: define void ()* @NoSpecifics.resolver() +// CHECK: call void @__cpu_indicator_init +// CHECK: ret void ()* @NoSpecifics.Z +// CHECK: ret void ()* @NoSpecifics.S +// CHECK: ret void ()* @NoSpecifics.O +// CHECK: call void @llvm.trap +// CHECK: unreachable + +__attribute__((cpu_dispatch(atom, generic, ivybridge, knl))) +void HasGeneric(void); +// CHECK: define void ()* @HasGeneric.resolver() +// CHECK: call void @__cpu_indicator_init +// CHECK: ret void ()* @HasGeneric.Z +// CHECK: ret void ()* @HasGeneric.S +// CHECK: ret void ()* @HasGeneric.O +// CHECK: ret void ()* @HasGeneric.A +// CHECK-NOT: call void @llvm.trap + +// CHECK: attributes #[[S]] = {{.*}}"target-features"="+avx,+cmov,+f16c,+mmx,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87,+xsave" +// CHECK: attributes #[[K]] = {{.*}}"target-features"="+adx,+avx,+avx2,+avx512cd,+avx512er,+avx512f,+avx512pf,+bmi,+cmov,+f16c,+fma,+lzcnt,+mmx,+movbe,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87,+xsave" +// CHECK: attributes #[[O]] = {{.*}}"target-features"="+cmov,+mmx,+movbe,+sse,+sse2,+sse3,+ssse3,+x87" Index: test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- test/Misc/pragma-attribute-supported-attributes-list.test +++ test/Misc/pragma-attribute-supported-attributes-list.test @@ -2,7 +2,7 @@ // The number of supported attributes should never go down! -// CHECK: #pragma clang attribute supports 70 attributes: +// CHECK: #pragma clang attribute supports 72 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -15,6 +15,8 @@ // CHECK-NEXT: AnyX86NoCfCheck (SubjectMatchRule_hasType_functionType) // CHECK-NEXT: AssumeAligned (SubjectMatchRule_objc_method, SubjectMatchRule_function) // CHECK-NEXT: Availability ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable)) +// CHECK-NEXT: CPUDispatch (SubjectMatchRule_function) +// CHECK-NEXT: CPUSpecific (SubjectMatchRule_function) // CHECK-NEXT: CXX11NoReturn (SubjectMatchRule_function) // CHECK-NEXT: CallableWhen (SubjectMatchRule_function_is_member) // CHECK-NEXT: CarriesDependency (SubjectMatchRule_variable_is_parameter, SubjectMatchRule_objc_method, SubjectMatchRule_function) Index: test/Sema/attr-cpuspecific.c =================================================================== --- test/Sema/attr-cpuspecific.c +++ test/Sema/attr-cpuspecific.c @@ -0,0 +1,96 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify %s + +void __attribute__((cpu_specific(ivybridge))) no_default(void); +void __attribute__((cpu_specific(sandybridge))) no_default(void); + +void use1(void){ + // Should be OK, default not a problem. + no_default(); +} + +int __attribute__((cpu_specific(atom))) addr_of(void); +int __attribute__((cpu_specific(ivybridge))) addr_of(void); +int __attribute__((cpu_specific(ivybridge))) addr_of2(void); + +void use2(void){ + addr_of(); + addr_of2(); + // expected-error@+1{{reference to multiversioned function could not be resolved; did you mean to call it with no arguments?}} + (void)+addr_of; + // expected-error@+1{{reference to multiversioned function could not be resolved; did you mean to call it with no arguments?}} + (void)+addr_of2; + // expected-error@+1{{reference to multiversioned function could not be resolved; did you mean to call it with no arguments?}} + (void)&addr_of; + // expected-error@+1{{reference to multiversioned function could not be resolved; did you mean to call it with no arguments?}} + (void)&addr_of2; +} + +// expected-error@+1 {{multiversioned function must have a prototype}} +int __attribute__((cpu_specific(atom))) no_proto(); + +int __attribute__((cpu_specific(atom))) redecl1(void); +int __attribute__((cpu_specific(atom))) redecl1(void) { return 1; } + +int __attribute__((cpu_dispatch(atom))) redecl2(void); +int __attribute__((cpu_dispatch(atom))) redecl2(void) { } +// expected-error@+2 {{redefinition of 'redecl2'}} +// expected-note@-2 {{previous definition is here}} +int __attribute__((cpu_dispatch(atom))) redecl2(void) { } + +int redecl3(void); +// expected-error@-1 {{function declaration is missing 'cpu_specific' or 'cpu_dispatch' attribute in a multiversioned function}} +// expected-note@+1 {{function multiversioning caused by this declaration}} +int __attribute__((cpu_dispatch(atom))) redecl3(void) {} + +int __attribute__((cpu_specific(atom))) redecl4(void); +// expected-error@+1 {{function declaration is missing 'cpu_specific' or 'cpu_dispatch' attribute in a multiversioned function}} +int redecl4(void); + +// expected-warning@+1 {{CPU list contains duplicate entries; attribute ignored}} +int __attribute__((cpu_specific(atom, atom))) dup_procs(void); + +int __attribute__((cpu_specific(ivybridge, atom))) dup_procs2(void); +// expected-error@+2 {{multiple 'cpu_specific' functions cannot specify the same CPU: 'atom'}} +// expected-note@-2 {{previous declaration is here}} +int __attribute__((cpu_specific(atom))) dup_procs2(void); + +int __attribute__((cpu_specific(ivybridge, atom))) dup_procs3(void); +// expected-error@+2 {{multiple 'cpu_specific' functions cannot specify the same CPU: 'ivybridge'}} +// expected-note@-2 {{previous declaration is here}} +int __attribute__((cpu_specific(atom, ivybridge))) dup_procs3(void); + +int __attribute__((cpu_specific(atom))) redef(void) { return 1; } +// expected-error@+2 {{redefinition of 'redef'}} +// expected-note@-2 {{previous definition is here}} +int __attribute__((cpu_specific(atom))) redef(void) { return 2; } + +int __attribute((cpu_dispatch(atom))) mult_dispatch(void) {} +// expected-error@+2 {{'cpu_dispatch' function redeclared with different CPUs}} +// expected-note@-2 {{previous declaration is here}} +int __attribute((cpu_dispatch(ivybridge))) mult_dispatch(void) {} + +// expected-error@+1 {{'cpu_dispatch' attribute takes at least 1 argument}} +int __attribute((cpu_dispatch())) no_dispatch(void) {} +// expected-error@+1 {{'cpu_specific' attribute takes at least 1 argument}} +int __attribute((cpu_specific())) no_specific(void) {} + +//expected-error@+1 {{attribute 'cpu_specific' multiversioning cannot be combined}} +void __attribute__((used,cpu_specific(sandybridge))) addtl_attrs(void); + +void __attribute__((target("default"))) addtl_attrs2(void); +// expected-error@+2 {{multiversioning attributes cannot be combined}} +// expected-note@-2 {{previous declaration is here}} +void __attribute__((cpu_specific(sandybridge))) addtl_attrs2(void); + +// expected-error@+2 {{multiversioning attributes cannot be combined}} +void __attribute((cpu_specific(sandybridge), cpu_dispatch(atom, sandybridge))) +combine_attrs(void); + +int __attribute__((cpu_dispatch(ivybridge))) diff_cc(void){} +// expected-error@+1 {{multiversioned function declaration has a different calling convention}} +__vectorcall int __attribute__((cpu_specific(sandybridge))) diff_cc(void); + +// expected-warning@+2 {{body of cpu_dispatch function will be ignored}} +int __attribute__((cpu_dispatch(atom))) disp_with_body(void) { + return 5; +} Index: test/SemaCXX/attr-cpuspecific.cpp =================================================================== --- test/SemaCXX/attr-cpuspecific.cpp +++ test/SemaCXX/attr-cpuspecific.cpp @@ -0,0 +1,111 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify -fexceptions -fcxx-exceptions %s -std=c++14 + +// expected-error@+1{{invalid option 'invalid' for cpu_dispatch}} +void __attribute__((cpu_dispatch(atom, invalid))) invalid_cpu(); + +void __attribute__((cpu_specific(atom))) no_default(void); +void __attribute__((cpu_specific(sandybridge))) no_default(void); + +struct MVReference { + int __attribute__((cpu_specific(sandybridge))) bar(void); + int __attribute__((cpu_specific(ivybridge))) bar(void); + int __attribute__((cpu_specific(sandybridge))) foo(void); +}; + +void use1(void){ + // OK, will fail in the linker, unless another TU provides the cpu_dispatch. + no_default(); + + // expected-error@+1 {{call to non-static member function without an object argument}} + +MVReference::bar; + // expected-error@+1 {{call to non-static member function without an object argument}} + +MVReference::foo; + // expected-error@+1 {{reference to multiversioned function could not be resolved; did you mean to call it?}} + &MVReference::bar; + // expected-error@+1 {{reference to multiversioned function could not be resolved; did you mean to call it?}} + &MVReference::foo; +} + +//expected-error@+1 {{attribute 'cpu_specific' multiversioned functions do not yet support constexpr functions}} +constexpr int __attribute__((cpu_specific(sandybridge))) foo(void); + +int __attribute__((cpu_specific(sandybridge))) foo2(void); +//expected-error@+1 {{attribute 'cpu_specific' multiversioned functions do not yet support constexpr functions}} +constexpr int __attribute__((cpu_specific(ivybridge))) foo2(void); + +static int __attribute__((cpu_specific(sandybridge))) bar(void); +//expected-error@+1 {{multiversioned function declaration has a different storage class}} +int __attribute__((cpu_dispatch(ivybridge))) bar(void) {} + +inline int __attribute__((cpu_specific(sandybridge))) baz(void); +//expected-error@+1 {{multiversioned function declaration has a different inline specification}} +int __attribute__((cpu_specific(ivybridge))) baz(void) {return 1;} + +void __attribute__((cpu_specific(atom))) diff_return(void); +//expected-error@+1 {{multiversioned function declaration has a different return type}} +int __attribute__((cpu_specific(sandybridge))) diff_return(void); + +int __attribute__((cpu_specific(atom))) diff_noexcept(void) noexcept(true); +//expected-error@+2 {{exception specification in declaration does not match previous declaration}} +//expected-note@-2 {{previous declaration is here}} +int __attribute__((cpu_specific(sandybridge))) diff_noexcept(void) noexcept(false); + +// FIXME: Add support for templates and virtual functions! +// expected-error@+2 {{multiversioned functions do not yet support function templates}} +template +int __attribute__((cpu_specific(atom))) foo(T) { return 0; } +// expected-error@+2 {{multiversioned functions do not yet support function templates}} +template +int __attribute__((cpu_specific(sandybridge))) foo2(T); + +struct S { + // expected-error@+2 {{multiversioned functions do not yet support function templates}} + template + int __attribute__((cpu_specific(atom))) foo(T) { return 0; } + + // expected-error@+2 {{multiversioned functions do not yet support function templates}} + template + int __attribute__((cpu_dispatch(ivybridge))) foo2(T) {} + + // expected-error@+1 {{multiversioned functions do not yet support virtual functions}} + virtual void __attribute__((cpu_specific(atom))) virt(); +}; + +extern "C" { +int __attribute__((cpu_specific(atom))) diff_mangle(void) { return 0; } +} +//expected-error@+1 {{multiversioned function declaration has a different linkage}} +int __attribute__((cpu_specific(sandybridge))) diff_mangle(void) { return 0; } + +__attribute__((cpu_specific(atom))) 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__((cpu_dispatch(atom))) void DiffDecl(); +} // namespace N + +struct SpecialFuncs { + // expected-error@+1 {{multiversioned functions do not yet support constructors}} + __attribute__((cpu_specific(atom))) SpecialFuncs(); + // expected-error@+1 {{multiversioned functions do not yet support destructors}} + __attribute__((cpu_specific(atom))) ~SpecialFuncs(); + + // expected-error@+1 {{multiversioned functions do not yet support defaulted functions}} + SpecialFuncs& __attribute__((cpu_specific(atom))) operator=(const SpecialFuncs&) = default; + // expected-error@+1 {{multiversioned functions do not yet support deleted functions}} + SpecialFuncs& __attribute__((cpu_specific(atom))) operator=(SpecialFuncs&&) = delete; +}; + +struct BadOutOfLine { + int __attribute__((cpu_specific(atom, ivybridge))) foo(int); +}; + +int __attribute__((cpu_specific(atom, ivybridge))) BadOutOfLine::foo(int) { return 0; } +// expected-error@+2 {{out-of-line definition of 'foo' does not match any declaration in 'BadOutOfLine'}} +// expected-note@-2 {{member declaration nearly matches}} +int __attribute__((cpu_specific(sandybridge))) BadOutOfLine::foo(int) { return 1; } + +// Ensure Cpp Spelling works. +[[clang::cpu_specific(ivybridge,atom)]] int CppSpelling(){} Index: utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- utils/TableGen/ClangAttrEmitter.cpp +++ utils/TableGen/ClangAttrEmitter.cpp @@ -1173,6 +1173,13 @@ } }; + class VariadicIdentifierArgument : public VariadicArgument { + public: + VariadicIdentifierArgument(const Record &Arg, StringRef Attr) + : VariadicArgument(Arg, Attr, "IdentifierInfo *") + {} + }; + class VariadicStringArgument : public VariadicArgument { public: VariadicStringArgument(const Record &Arg, StringRef Attr) @@ -1278,6 +1285,8 @@ Ptr = llvm::make_unique(Arg, Attr); else if (ArgName == "ParamIdxArgument") Ptr = llvm::make_unique(Arg, Attr, "ParamIdx"); + else if (ArgName == "VariadicIdentifierArgument") + Ptr = llvm::make_unique(Arg, Attr); else if (ArgName == "VersionArgument") Ptr = llvm::make_unique(Arg, Attr); @@ -2106,6 +2115,34 @@ .Default(false); } +static bool isVariadicIdentifierArgument(Record *Arg) { + return !Arg->getSuperClasses().empty() && + llvm::StringSwitch( + Arg->getSuperClasses().back().first->getName()) + .Case("VariadicIdentifierArgument", true) + .Default(false); +} + +static void emitClangAttrVariadicIdentifierArgList(RecordKeeper &Records, + raw_ostream &OS) { + OS << "#if defined(CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST)\n"; + std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); + for (const auto *A : Attrs) { + // Determine whether the first argument is a variadic identifier. + std::vector Args = A->getValueAsListOfDefs("Args"); + if (Args.empty() || !isVariadicIdentifierArgument(Args[0])) + continue; + + // All these spellings take an identifier argument. + forEachUniqueSpelling(*A, [&](const FlattenedSpelling &S) { + OS << ".Case(\"" << S.name() << "\", " + << "true" + << ")\n"; + }); + } + OS << "#endif // CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST\n\n"; +} + // Emits the first-argument-is-identifier property for attributes. static void emitClangAttrIdentifierArgList(RecordKeeper &Records, raw_ostream &OS) { OS << "#if defined(CLANG_ATTR_IDENTIFIER_ARG_LIST)\n"; @@ -3697,6 +3734,7 @@ emitSourceFileHeader("Parser-related llvm::StringSwitch cases", OS); emitClangAttrArgContextList(Records, OS); emitClangAttrIdentifierArgList(Records, OS); + emitClangAttrVariadicIdentifierArgList(Records, OS); emitClangAttrTypeArgList(Records, OS); emitClangAttrLateParsedList(Records, OS); }