Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -2209,6 +2209,9 @@ getCanonicalDecl()->IsMultiVersion = V; } + bool isCpuDispatchMultiVersion() const; + 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 = [GCC<"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 = [GCC<"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 your +compilation with ``cpu_dispatch``. This attribute takes one or more CPU names +as a parameter (like ``cpu_specific``). However, ``cpu_dispatch`` functions +may not have a body in its definition. An empty definition is permissible for +ICC compatibility, and all other definitions will have their body ignored. + +.. 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 is the +condition-less implementation, 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 @@ -1006,3 +1006,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 @@ -606,6 +606,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 " @@ -3883,8 +3885,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">; @@ -9382,9 +9384,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< @@ -9398,20 +9400,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 @@ -952,6 +952,35 @@ // 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 std::vector + getCpuSpecificCpuDispatchFeatures(StringRef Name) const { + llvm_unreachable( + "cpu_specific Multiversioning not implemented on this target"); + } + + // Get a bitmask of the features that make up the CPU option for + // cpu_specific/cpu_dispatch so that it can be checked against the value + // returned by a processor's feature list. + virtual uint64_t getCpuSpecificCpuDispatchFeatureMask(StringRef Name) 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,174 @@ FEATURE(FEATURE_AVX5124FMAPS) FEATURE(FEATURE_AVX512VPOPCNTDQ) + +// FIXME: When commented out features are supported in LLVM, enable them here. +CPU_SPECIFIC("generic", 'A', 0) +CPU_SPECIFIC("pentium", 'B', 0) +CPU_SPECIFIC("pentium_pro", 'C', (1ULL << FEATURE_CMOV)) +CPU_SPECIFIC("pentium_mmx", 'D', (1ULL << FEATURE_MMX)) +CPU_SPECIFIC("pentium_ii", 'E', (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX)) +CPU_SPECIFIC("pentium_iii", 'H', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE)) +CPU_SPECIFIC("pentium_iii_no_xmm_regs", 'H', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_SSE)) +CPU_SPECIFIC("pentium_4", 'J', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2)) +CPU_SPECIFIC("pentium_m", 'K', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2)) +CPU_SPECIFIC("pentium_4_sse3", 'L', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3)) +CPU_SPECIFIC("core_2_duo_ssse3", 'M', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3)) +CPU_SPECIFIC("core_2_duo_sse4_1", 'N', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1)) +CPU_SPECIFIC("atom", 'O', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_MOVBE)) +CPU_SPECIFIC("atom_sse4_2", 'c', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_POPCNT)) +CPU_SPECIFIC("core_i7_sse4_2", 'P', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_POPCNT)) +CPU_SPECIFIC("core_aes_pclmulqdq", 'Q', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_POPCNT)) +CPU_SPECIFIC("atom_sse4_2_movbe", 'd', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_MOVBE | + 1ULL << FEATURE_POPCNT)) +CPU_SPECIFIC("goldmont", 'i', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_MOVBE | + 1ULL << FEATURE_POPCNT)) +CPU_SPECIFIC("sandybridge", 'R', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_POPCNT | + 1ULL << FEATURE_AVX)) +CPU_SPECIFIC_ALIAS("core_2nd_gen_avx", "sandybridge") +CPU_SPECIFIC("ivybridge", 'S', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_POPCNT | + /*1ULL << FEATURE_F16C | */1ULL << FEATURE_AVX)) +CPU_SPECIFIC_ALIAS("core_3rd_gen_avx", "ivybridge") +CPU_SPECIFIC("haswell", 'V', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_MOVBE | + 1ULL << FEATURE_POPCNT | /*1ULL << FEATURE_F16C |*/ + 1ULL << FEATURE_AVX | 1ULL << FEATURE_FMA | 1ULL << FEATURE_BMI | + /*1ULL << FEATURE_LZCNT |*/ 1ULL << FEATURE_AVX2)) +CPU_SPECIFIC_ALIAS("core_4th_gen_avx", "haswell") +CPU_SPECIFIC("core_4th_gen_avx_tsx", 'W', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_MOVBE | + 1ULL << FEATURE_POPCNT | /*1ULL << FEATURE_F16C |*/ + 1ULL << FEATURE_AVX | 1ULL << FEATURE_FMA | 1ULL << FEATURE_BMI | + /*1ULL << FEATURE_LZCNT |*/ 1ULL << FEATURE_AVX2 /*| + 1ULL << FEATURE_TSX*/)) +CPU_SPECIFIC("broadwell", 'X', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_MOVBE | + 1ULL << FEATURE_POPCNT | /*1ULL << FEATURE_F16C |*/ + 1ULL << FEATURE_AVX | 1ULL << FEATURE_FMA | 1ULL << FEATURE_BMI | + /*1ULL << FEATURE_LZCNT |*/ 1ULL << FEATURE_AVX2 | + 1ULL << FEATURE_ADX)) +CPU_SPECIFIC_ALIAS("core_5th_gen_avx", "broadwell") +CPU_SPECIFIC("core_5th_gen_avx_tsx", 'Y', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_MOVBE | + 1ULL << FEATURE_POPCNT | /*1ULL << FEATURE_F16C |*/ + 1ULL << FEATURE_AVX | 1ULL << FEATURE_FMA | 1ULL << FEATURE_BMI | + /*1ULL << FEATURE_LZCNT |*/ 1ULL << FEATURE_AVX2 | + 1ULL << FEATURE_ADX /*| 1ULL << FEATURE_TSX*/)) +CPU_SPECIFIC("knl", 'Z', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_MOVBE | + 1ULL << FEATURE_POPCNT | /*1ULL << FEATURE_F16C |*/ + 1ULL << FEATURE_AVX | 1ULL << FEATURE_FMA | 1ULL << FEATURE_BMI | + /*1ULL << FEATURE_LZCNT |*/ 1ULL << FEATURE_AVX2 | + 1ULL << FEATURE_AVX512F | 1ULL << FEATURE_ADX | + 1ULL << FEATURE_AVX512ER | 1ULL << FEATURE_AVX512PF | + 1ULL << FEATURE_AVX512CD)) +CPU_SPECIFIC_ALIAS("mic_avx512", "knl") +CPU_SPECIFIC("skylake", 'b', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_MOVBE | + 1ULL << FEATURE_POPCNT | /*1ULL << FEATURE_F16C |*/ + 1ULL << FEATURE_AVX | 1ULL << FEATURE_FMA | 1ULL << FEATURE_BMI | + /*1ULL << FEATURE_LZCNT |*/ 1ULL << FEATURE_AVX2 | + 1ULL << FEATURE_ADX /*| 1ULL << FEATURE_MPX*/)) +CPU_SPECIFIC( + "skylake_avx512", 'a', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | 1ULL << FEATURE_SSSE3 | + 1ULL << FEATURE_SSE4_1 | 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_MOVBE | + 1ULL << FEATURE_POPCNT | /*1ULL << FEATURE_F16C |*/ 1ULL << FEATURE_AVX | + 1ULL << FEATURE_FMA | 1ULL << FEATURE_BMI | /*1ULL << FEATURE_LZCNT |*/ + 1ULL << FEATURE_AVX2 | 1ULL << FEATURE_AVX512DQ | 1ULL << FEATURE_AVX512F | + 1ULL << FEATURE_ADX | 1ULL << FEATURE_AVX512CD | 1ULL << FEATURE_AVX512BW | + 1ULL << FEATURE_AVX512VL /*| 1ULL << FEATURE_CLWB*/)) +CPU_SPECIFIC("cannonlake", 'e', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_MOVBE | + 1ULL << FEATURE_POPCNT | /*1ULL << FEATURE_F16C |*/ + 1ULL << FEATURE_AVX | 1ULL << FEATURE_FMA | 1ULL << FEATURE_BMI | + /*1ULL << FEATURE_LZCNT |*/ 1ULL << FEATURE_AVX2 | + 1ULL << FEATURE_AVX512DQ | 1ULL << FEATURE_AVX512F | + 1ULL << FEATURE_ADX | 1ULL << FEATURE_AVX512IFMA | + 1ULL << FEATURE_AVX512CD | 1ULL << FEATURE_AVX512BW | + 1ULL << FEATURE_AVX512VL | 1ULL << FEATURE_AVX512VBMI)) +CPU_SPECIFIC("knm", 'j', + (1ULL << FEATURE_CMOV | 1ULL << FEATURE_MMX | 1ULL << FEATURE_SSE | + 1ULL << FEATURE_SSE2 | 1ULL << FEATURE_SSE3 | + 1ULL << FEATURE_SSSE3 | 1ULL << FEATURE_SSE4_1 | + 1ULL << FEATURE_SSE4_2 | 1ULL << FEATURE_MOVBE | + 1ULL << FEATURE_POPCNT | /*1ULL << FEATURE_F16C |*/ + 1ULL << FEATURE_AVX | 1ULL << FEATURE_FMA | 1ULL << FEATURE_BMI | + /*1ULL << FEATURE_LZCNT |*/ 1ULL << FEATURE_AVX2 | + 1ULL << FEATURE_AVX512F | 1ULL << FEATURE_ADX | + 1ULL << FEATURE_AVX512ER | 1ULL << FEATURE_AVX512PF | + 1ULL << FEATURE_AVX512CD | 1ULL << FEATURE_AVX5124FMAPS | + 1ULL << FEATURE_AVX5124VNNIW | 1ULL << FEATURE_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,15 @@ bool validateCpuIs(StringRef Name) const override; + bool validateCpuSpecificCpuDispatch(StringRef Name) const override; + + char cpuSpecificManglingCharacter(StringRef Name) const override; + + std::vector + getCpuSpecificCpuDispatchFeatures(StringRef Name) const override; + + uint64_t getCpuSpecificCpuDispatchFeatureMask(StringRef Name) 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,49 @@ 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); +} + +std::vector +X86TargetInfo::getCpuSpecificCpuDispatchFeatures(StringRef Name) const { + using namespace llvm::X86; + std::vector Features; + uint64_t FeatInt = getCpuSpecificCpuDispatchFeatureMask(Name); +#define X86_FEATURE_COMPAT(VAL, ENUM, FEAT_STR) \ + if (FeatInt & (1 << ENUM)) \ + Features.emplace_back(FEAT_STR); +#include "llvm/Support/X86TargetParser.def" + return Features; +} + +uint64_t +X86TargetInfo::getCpuSpecificCpuDispatchFeatureMask(StringRef Name) const { + using namespace llvm::X86; + return llvm::StringSwitch(cpuSpecificCpuDispatchNameDealias(Name)) +#define CPU_SPECIFIC(NAME, MANGLING, FEATURES) .Case(NAME, FEATURES) +#include "clang/Basic/X86Target.def" + .Default(0); +} + 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 @@ -8545,6 +8545,10 @@ FeaturesMask |= (1U << Feature); } + return EmitX86CpuSupports(FeaturesMask); +} + +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 @@ -4096,12 +4096,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, @@ -4112,12 +4113,29 @@ 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); private: QualType getVarArgType(const Expr *Arg); @@ -4134,8 +4152,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 @@ -2328,7 +2328,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. SmallVector ReqFeatures; llvm::StringMap CalleeFeatureMap; @@ -2353,8 +2354,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); @@ -2372,8 +2373,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() == @@ -2386,7 +2388,7 @@ EmitX86CpuInit(); llvm::Function *DefaultFunc = nullptr; - for (const MultiVersionResolverOption &RO : Options) { + for (const TargetMultiVersionResolverOption &RO : Options) { Builder.SetInsertPoint(CurBlock); llvm::Value *TrueCondition = FormResolverCondition(RO); @@ -2407,6 +2409,43 @@ 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("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("ro_ret", Resolver); + llvm::IRBuilder<> RetBuilder(RetBlock); + RetBuilder.CreateRet(RO.Function); + CurBlock = createBasicBlock("ro_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; @@ -1266,7 +1273,6 @@ llvm::Constant *GetOrCreateMultiVersionIFunc(GlobalDecl GD, llvm::Type *DeclTy, - StringRef MangledName, const FunctionDecl *FD); void UpdateMultiVersionNames(GlobalDecl GD, const FunctionDecl *FD); @@ -1290,6 +1296,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 @@ -862,6 +862,22 @@ GV->setThreadLocalMode(TLM); } +static std::string getCpuSpecificMangling(const CodeGenModule &CGM, + StringRef Name) { + const auto &Target = CGM.getTarget(); + return std::string(".") + Target.cpuSpecificManglingCharacter(Name); +} + +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()) @@ -871,13 +887,13 @@ const auto &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; @@ -896,7 +912,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(); @@ -923,8 +939,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(); } @@ -937,7 +959,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() @@ -980,11 +1002,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 = @@ -1281,8 +1322,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); @@ -1294,10 +1336,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. @@ -1995,6 +2039,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) { @@ -2313,7 +2361,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) { @@ -2345,28 +2393,68 @@ 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()); + assert(isa(DeclTy) && "Not a Function Type?!"); + + StringRef ResolverName = getMangledName(GD); + llvm::Type *ResolverType = llvm::FunctionType::get( + llvm::PointerType::get(DeclTy, + Context.getTargetAddressSpace(FD->getType())), + false); + llvm::Function *ResolverFunc = cast( + GetOrCreateLLVMFunction(ResolverName, ResolverType, GlobalDecl{}, + /*ForVTable=*/false)); + + SmallVector + Options; + for (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); + Options.emplace_back( + cast(Func), + getTarget().getCpuSpecificCpuDispatchFeatureMask(II->getName())); } + + 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())), @@ -2413,10 +2501,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); } } @@ -3687,6 +3777,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) { @@ -5065,6 +5164,13 @@ // the attribute. Target.initFeatureMap(FeatureMap, getDiags(), TargetCPU, ParsedAttr.Features); + } else if (const auto *SD = FD->getAttr()) { + std::vector Features = + Target.getCpuSpecificCpuDispatchFeatures( + SD->getCurCpuName()->getName()); + llvm::transform(Features, Features.begin(), + [](const std::string &str) { return '+' + str; }); + 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 @@ -206,7 +206,7 @@ } } -/// Determine whether the given attribute has an identifier argument. +/// Determine whether the given attribute has a variadic identifier argument. static bool attributeHasIdentifierArg(const IdentifierInfo &II) { #define CLANG_ATTR_IDENTIFIER_ARG_LIST return llvm::StringSwitch(normalizeAttrName(II.getName())) @@ -215,6 +215,15 @@ #undef CLANG_ATTR_IDENTIFIER_ARG_LIST } +/// Determine whether the given attribute has an 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); AttributeList::Kind AttrKind = AttributeList::getKind(AttrName, ScopeName, Syntax); @@ -305,21 +315,27 @@ // Parse the non-empty comma-separated list of expressions. do { - bool Uneval = attributeParsedArgsUnevaluated(*AttrName); - EnterExpressionEvaluationContext Unevaluated( - Actions, - Uneval ? Sema::ExpressionEvaluationContext::Unevaluated - : Sema::ExpressionEvaluationContext::ConstantEvaluated, - /*LambdaContextDecl=*/nullptr, - /*IsDecltype=*/false); - - 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, + /*LambdaContextDecl=*/nullptr, + /*IsDecltype=*/false); + + 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 @@ -657,6 +657,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(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 @@ -9203,6 +9203,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; + else if (FD->hasAttr()) + return MultiVersioning::CpuDispatch; + else if (FD->hasAttr()) + return MultiVersioning::CpuSpecific; + return MultiVersioning::None; +} /// Check the target attribute of the function for MultiVersion /// validity. /// @@ -9241,7 +9255,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, @@ -9249,7 +9264,8 @@ Constructors = 3, Destructors = 4, DeletedFuncs = 5, - DefaultedFuncs = 6 + DefaultedFuncs = 6, + ConstexprFuncs = 7, }; enum Different { CallingConv = 0, @@ -9260,46 +9276,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); @@ -9307,7 +9350,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)) @@ -9354,138 +9397,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. + std::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(); + std::sort(NewParsed.Features.begin(), NewParsed.Features.end()); } bool UseMemberUsingDeclRules = @@ -9500,32 +9538,92 @@ 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; + } else 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; } @@ -9536,6 +9634,90 @@ OldDecl = nullptr; Previous.clear(); 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. + else 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. @@ -12800,6 +12982,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 @@ -1873,6 +1873,52 @@ << AL.getName() << getFunctionOrMethodResultSourceRange(D); } +static void handleCpuSpecificAttr(Sema &S, Decl *D, const AttributeList &AL) { + FunctionDecl *FD = D->getAsFunction(); + assert(FD && "CPU Specific/Dispatch only valid on a Function Decl"); + + 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() == AttributeList::AT_CpuDispatch); + return; + } + + const TargetInfo &Target = S.Context.getTargetInfo(); + if (llvm::find_if(Cpus, [CpuName, &Target](const IdentifierInfo *Cur) { + return Target.cpuSpecificManglingCharacter(CpuName) == + Target.cpuSpecificManglingCharacter(Cur->getName()); + }) != std::end(Cpus)) { + S.Diag(AL.getLoc(), diag::warn_multiversion_duplicate_entries); + return; + } + Cpus.push_back(CpuArg->Ident); + } + + FD->setIsMultiVersion(true); + if (AL.getKind() == AttributeList::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 AttributeList &AL) { if (S.LangOpts.CPlusPlus) { S.Diag(AL.getLoc(), diag::err_attribute_not_supported_in_lang) @@ -5910,6 +5956,10 @@ case AttributeList::AT_CarriesDependency: handleDependencyAttr(S, scope, D, AL); break; + case AttributeList::AT_CpuDispatch: + case AttributeList::AT_CpuSpecific: + handleCpuSpecificAttr(S, D, AL); + break; case AttributeList::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 @@ -5968,7 +5968,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; @@ -6605,7 +6605,7 @@ return; } - if (Method->isMultiVersion() && + if (Method->isMultiVersion() && Method->hasAttr() && !Method->getAttr()->isDefaultVersion()) { Candidate.Viable = false; Candidate.FailureKind = ovl_non_default_multiversion_function; @@ -7014,7 +7014,7 @@ return; } - if (Conversion->isMultiVersion() && + if (Conversion->isMultiVersion() && Conversion->hasAttr() && !Conversion->getAttr()->isDefaultVersion()) { Candidate.Viable = false; Candidate.FailureKind = ovl_non_default_multiversion_function; @@ -8969,6 +8969,46 @@ 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; + + assert(!Cand1CpuDisp && !Cand2CpuDisp && "Both cannot have cpu_dispatch"); + + 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(); + }); + + 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( @@ -9166,7 +9206,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 @@ -9474,7 +9517,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; @@ -11017,8 +11061,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; } @@ -11316,7 +11359,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 =================================================================== --- /dev/null +++ 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,+mmx,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87,+xsave" +// CHECK: attributes #[[K]] = {{.*}}"target-features"="+avx,+avx2,+avx512cd,+avx512er,+avx512f,+avx512pf,+bmi,+cmov,+f16c,+fma,+mmx,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87,+xsave" +// CHECK: attributes #[[O]] = {{.*}}"target-features"="+cmov,+mmx,+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 69 attributes: +// CHECK: #pragma clang attribute supports 71 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -20,6 +20,8 @@ // CHECK-NEXT: CarriesDependency (SubjectMatchRule_variable_is_parameter, SubjectMatchRule_objc_method, SubjectMatchRule_function) // CHECK-NEXT: Consumable (SubjectMatchRule_record) // CHECK-NEXT: Convergent (SubjectMatchRule_function) +// CHECK-NEXT: CpuDispatch (SubjectMatchRule_function) +// CHECK-NEXT: CpuSpecific (SubjectMatchRule_function) // CHECK-NEXT: DLLExport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface) // CHECK-NEXT: DLLImport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface) // CHECK-NEXT: DisableTailCalls (SubjectMatchRule_function, SubjectMatchRule_objc_method) Index: test/Sema/attr-cpuspecific.c =================================================================== --- /dev/null +++ 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 =================================================================== --- /dev/null +++ test/SemaCXX/attr-cpuspecific.cpp @@ -0,0 +1,108 @@ +// 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; } 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 *Attr : Attrs) { + // Determine whether the first argument is a variadic identifier. + std::vector Args = Attr->getValueAsListOfDefs("Args"); + if (Args.empty() || !isVariadicIdentifierArgument(Args[0])) + continue; + + // All these spellings take an identifier argument. + forEachUniqueSpelling(*Attr, [&](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"; @@ -3696,6 +3733,7 @@ emitSourceFileHeader("Parser-related llvm::StringSwitch cases", OS); emitClangAttrArgContextList(Records, OS); emitClangAttrIdentifierArgList(Records, OS); + emitClangAttrVariadicIdentifierArgList(Records, OS); emitClangAttrTypeArgList(Records, OS); emitClangAttrLateParsedList(Records, OS); }