forked from OSchip/llvm-project
Implement cpu_dispatch/cpu_specific Multiversioning
As documented here: https://software.intel.com/en-us/node/682969 and https://software.intel.com/en-us/node/523346. cpu_dispatch multiversioning is an ICC feature that provides for function multiversioning. This feature is implemented with two attributes: First, cpu_specific, which specifies the individual function versions. Second, cpu_dispatch, which specifies the location of the resolver function and the list of resolvable functions. This is valuable since it provides a mechanism where the resolver's TU can be specified in one location, and the individual implementions each in their own translation units. The goal of this patch is to be source-compatible with ICC, so this implementation diverges from the ICC implementation in a few ways: 1- Linux x86/64 only: This implementation uses ifuncs in order to properly dispatch functions. This is is a valuable performance benefit over the ICC implementation. A future patch will be provided to enable this feature on Windows, but it will obviously more closely fit ICC's implementation. 2- CPU Identification functions: ICC uses a set of custom functions to identify the feature list of the host processor. This patch uses the cpu_supports functionality in order to better align with 'target' multiversioning. 1- cpu_dispatch function def/decl: ICC's cpu_dispatch requires that the function marked cpu_dispatch be an empty definition. This patch supports that as well, however declarations are also permitted, since the linker will solve the issue of multiple emissions. Differential Revision: https://reviews.llvm.org/D47474 llvm-svn: 337552
This commit is contained in:
parent
f907e19b5e
commit
3efe00206f
|
@ -2209,6 +2209,13 @@ public:
|
|||
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;
|
||||
|
|
|
@ -168,6 +168,7 @@ class UnsignedArgument<string name, bit opt = 0> : Argument<name, opt>;
|
|||
class VariadicUnsignedArgument<string name> : Argument<name, 1>;
|
||||
class VariadicExprArgument<string name> : Argument<name, 1>;
|
||||
class VariadicStringArgument<string name> : Argument<name, 1>;
|
||||
class VariadicIdentifierArgument<string name> : Argument<name, 1>;
|
||||
|
||||
// Like VariadicUnsignedArgument except values are ParamIdx.
|
||||
class VariadicParamIdxArgument<string name> : Argument<name, 1>;
|
||||
|
@ -845,6 +846,27 @@ def Constructor : InheritableAttr {
|
|||
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 {
|
||||
|
|
|
@ -191,6 +191,65 @@ in generation of more efficient code.
|
|||
}];
|
||||
}
|
||||
|
||||
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 = [{
|
||||
|
|
|
@ -1022,3 +1022,7 @@ def SpirCompat : DiagGroup<"spir-compat">;
|
|||
|
||||
// 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">;
|
||||
|
|
|
@ -608,6 +608,8 @@ def err_builtin_redeclare : Error<"cannot redeclare builtin function %0">;
|
|||
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_viable_subscript :
|
|||
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 @@ def warn_shadow_field :
|
|||
InGroup<ShadowField>, DefaultIgnore;
|
||||
def note_shadow_field : Note<"declared here">;
|
||||
|
||||
def err_target_required_in_redecl : Error<
|
||||
"function declaration is missing 'target' attribute in a multiversioned "
|
||||
"function">;
|
||||
def 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_duplicate : Error<
|
|||
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<FunctionMultiVersioning>;
|
||||
def warn_dispatch_body_ignored : Warning<
|
||||
"body of cpu_dispatch function will be ignored">,
|
||||
InGroup<FunctionMultiVersioning>;
|
||||
|
||||
// three-way comparison operator diagnostics
|
||||
def err_implied_comparison_category_type_not_found : Error<
|
||||
|
|
|
@ -1092,6 +1092,27 @@ public:
|
|||
// 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<StringRef> &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");
|
||||
|
|
|
@ -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_AVX5124VNNIW)
|
|||
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
|
||||
|
|
|
@ -2873,6 +2873,14 @@ bool FunctionDecl::isNoReturn() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool FunctionDecl::isCPUDispatchMultiVersion() const {
|
||||
return isMultiVersion() && hasAttr<CPUDispatchAttr>();
|
||||
}
|
||||
|
||||
bool FunctionDecl::isCPUSpecificMultiVersion() const {
|
||||
return isMultiVersion() && hasAttr<CPUSpecificAttr>();
|
||||
}
|
||||
|
||||
void
|
||||
FunctionDecl::setPreviousDeclaration(FunctionDecl *PrevDecl) {
|
||||
redeclarable_base::setPreviousDecl(PrevDecl);
|
||||
|
|
|
@ -1484,6 +1484,38 @@ unsigned X86TargetInfo::multiVersionSortPriority(StringRef Name) const {
|
|||
return getFeaturePriority(getFeature(Name)) << 1;
|
||||
}
|
||||
|
||||
bool X86TargetInfo::validateCPUSpecificCPUDispatch(StringRef Name) const {
|
||||
return llvm::StringSwitch<bool>(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<StringRef>(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<char>(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<StringRef> &Features) const {
|
||||
StringRef WholeList =
|
||||
llvm::StringSwitch<StringRef>(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:
|
||||
|
|
|
@ -150,6 +150,14 @@ public:
|
|||
|
||||
bool validateCpuIs(StringRef Name) const override;
|
||||
|
||||
bool validateCPUSpecificCPUDispatch(StringRef Name) const override;
|
||||
|
||||
char CPUSpecificManglingCharacter(StringRef Name) const override;
|
||||
|
||||
void getCPUSpecificCPUDispatchFeatures(
|
||||
StringRef Name,
|
||||
llvm::SmallVectorImpl<StringRef> &Features) const override;
|
||||
|
||||
bool validateAsmConstraint(const char *&Name,
|
||||
TargetInfo::ConstraintInfo &info) const override;
|
||||
|
||||
|
|
|
@ -8904,11 +8904,10 @@ Value *CodeGenFunction::EmitX86CpuSupports(const CallExpr *E) {
|
|||
return EmitX86CpuSupports(FeatureStr);
|
||||
}
|
||||
|
||||
Value *CodeGenFunction::EmitX86CpuSupports(ArrayRef<StringRef> FeatureStrs) {
|
||||
uint32_t
|
||||
CodeGenFunction::GetX86CpuSupportsMask(ArrayRef<StringRef> FeatureStrs) {
|
||||
// Processor features and mapping to processor feature value.
|
||||
|
||||
uint32_t FeaturesMask = 0;
|
||||
|
||||
for (const StringRef &FeatureStr : FeatureStrs) {
|
||||
unsigned Feature =
|
||||
StringSwitch<unsigned>(FeatureStr)
|
||||
|
@ -8917,7 +8916,14 @@ Value *CodeGenFunction::EmitX86CpuSupports(ArrayRef<StringRef> FeatureStrs) {
|
|||
;
|
||||
FeaturesMask |= (1U << Feature);
|
||||
}
|
||||
return FeaturesMask;
|
||||
}
|
||||
|
||||
Value *CodeGenFunction::EmitX86CpuSupports(ArrayRef<StringRef> 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;
|
||||
|
|
|
@ -2323,7 +2323,8 @@ void CodeGenFunction::checkTargetFeatures(const CallExpr *E,
|
|||
<< TargetDecl->getDeclName()
|
||||
<< CGM.getContext().BuiltinInfo.getRequiredFeatures(BuiltinID);
|
||||
|
||||
} else if (TargetDecl->hasAttr<TargetAttr>()) {
|
||||
} else if (TargetDecl->hasAttr<TargetAttr>() ||
|
||||
TargetDecl->hasAttr<CPUSpecificAttr>()) {
|
||||
// Get the required features for the callee.
|
||||
|
||||
const TargetAttr *TD = TargetDecl->getAttr<TargetAttr>();
|
||||
|
@ -2358,8 +2359,8 @@ void CodeGenFunction::EmitSanitizerStatReport(llvm::SanitizerStatKind SSK) {
|
|||
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 @@ CodeGenFunction::FormResolverCondition(const MultiVersionResolverOption &RO) {
|
|||
return TrueCondition;
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitMultiVersionResolver(
|
||||
llvm::Function *Resolver, ArrayRef<MultiVersionResolverOption> Options) {
|
||||
void CodeGenFunction::EmitTargetMultiVersionResolver(
|
||||
llvm::Function *Resolver,
|
||||
ArrayRef<TargetMultiVersionResolverOption> Options) {
|
||||
assert((getContext().getTargetInfo().getTriple().getArch() ==
|
||||
llvm::Triple::x86 ||
|
||||
getContext().getTargetInfo().getTriple().getArch() ==
|
||||
|
@ -2391,7 +2393,7 @@ void CodeGenFunction::EmitMultiVersionResolver(
|
|||
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 @@ void CodeGenFunction::EmitMultiVersionResolver(
|
|||
Builder.CreateRet(DefaultFunc);
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitCPUDispatchMultiVersionResolver(
|
||||
llvm::Function *Resolver,
|
||||
ArrayRef<CPUDispatchMultiVersionResolverOption> 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);
|
||||
|
|
|
@ -4113,12 +4113,13 @@ public:
|
|||
|
||||
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 @@ public:
|
|||
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<MultiVersionResolverOption> Options);
|
||||
void EmitTargetMultiVersionResolver(
|
||||
llvm::Function *Resolver,
|
||||
ArrayRef<TargetMultiVersionResolverOption> 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<uint32_t>(Mask)) {}
|
||||
bool operator>(const CPUDispatchMultiVersionResolverOption &Other) const {
|
||||
return FeatureMask > Other.FeatureMask;
|
||||
}
|
||||
};
|
||||
void EmitCPUDispatchMultiVersionResolver(
|
||||
llvm::Function *Resolver,
|
||||
ArrayRef<CPUDispatchMultiVersionResolverOption> Options);
|
||||
static uint32_t GetX86CpuSupportsMask(ArrayRef<StringRef> FeatureStrs);
|
||||
|
||||
private:
|
||||
QualType getVarArgType(const Expr *Arg);
|
||||
|
@ -4151,8 +4170,10 @@ private:
|
|||
llvm::Value *EmitX86CpuIs(StringRef CPUStr);
|
||||
llvm::Value *EmitX86CpuSupports(const CallExpr *E);
|
||||
llvm::Value *EmitX86CpuSupports(ArrayRef<StringRef> 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
|
||||
|
|
|
@ -861,22 +861,38 @@ void CodeGenModule::setTLSMode(llvm::GlobalValue *GV, const VarDecl &D) const {
|
|||
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 void AppendTargetMangling(const CodeGenModule &CGM,
|
|||
|
||||
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 @@ static std::string getMangledNameImpl(const CodeGenModule &CGM, GlobalDecl GD,
|
|||
}
|
||||
|
||||
if (const auto *FD = dyn_cast<FunctionDecl>(ND))
|
||||
if (FD->isMultiVersion() && !OmitTargetMangling)
|
||||
AppendTargetMangling(CGM, FD->getAttr<TargetAttr>(), Out);
|
||||
if (FD->isMultiVersion() && !OmitMultiVersionMangling) {
|
||||
if (FD->isCPUDispatchMultiVersion() || FD->isCPUSpecificMultiVersion())
|
||||
AppendCPUSpecificCPUDispatchMangling(
|
||||
CGM, FD->getAttr<CPUSpecificAttr>(), Out);
|
||||
else
|
||||
AppendTargetMangling(CGM, FD->getAttr<TargetAttr>(), Out);
|
||||
}
|
||||
|
||||
return Out.str();
|
||||
}
|
||||
|
||||
|
@ -936,7 +958,7 @@ void CodeGenModule::UpdateMultiVersionNames(GlobalDecl GD,
|
|||
// 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 @@ StringRef CodeGenModule::getMangledName(GlobalDecl GD) {
|
|||
}
|
||||
}
|
||||
|
||||
const auto *FD = dyn_cast<FunctionDecl>(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<CPUSpecificAttr>();
|
||||
|
||||
std::pair<GlobalDecl, unsigned> SpecCanonicalGD{
|
||||
CanonicalGD,
|
||||
SD ? SD->ActiveArgIndex : std::numeric_limits<unsigned>::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<NamedDecl>(GD.getDecl());
|
||||
auto Result =
|
||||
|
@ -1321,8 +1362,9 @@ bool CodeGenModule::GetCPUAndFeaturesAttributes(const Decl *D,
|
|||
const auto *FD = dyn_cast_or_null<FunctionDecl>(D);
|
||||
FD = FD ? FD->getMostRecentDecl() : FD;
|
||||
const auto *TD = FD ? FD->getAttr<TargetAttr>() : nullptr;
|
||||
const auto *SD = FD ? FD->getAttr<CPUSpecificAttr>() : nullptr;
|
||||
bool AddedAttr = false;
|
||||
if (TD) {
|
||||
if (TD || SD) {
|
||||
llvm::StringMap<bool> FeatureMap;
|
||||
getFunctionFeatureMap(FeatureMap, FD);
|
||||
|
||||
|
@ -1334,10 +1376,12 @@ bool CodeGenModule::GetCPUAndFeaturesAttributes(const Decl *D,
|
|||
// 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 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
|
|||
if (Global->hasAttr<IFuncAttr>())
|
||||
return emitIFuncDefinition(GD);
|
||||
|
||||
// If this is a cpu_dispatch multiversion function, emit the resolver.
|
||||
if (Global->hasAttr<CPUDispatchAttr>())
|
||||
return emitCPUDispatchDefinition(GD);
|
||||
|
||||
// If this is CUDA, be selective about which declarations we emit.
|
||||
if (LangOpts.CUDA) {
|
||||
if (LangOpts.CUDAIsDevice) {
|
||||
|
@ -2355,7 +2403,7 @@ static void ReplaceUsesOfNonProtoTypeWithRealFunction(llvm::GlobalValue *Old,
|
|||
|
||||
void CodeGenModule::emitMultiVersionFunctions() {
|
||||
for (GlobalDecl GD : MultiVersionFuncs) {
|
||||
SmallVector<CodeGenFunction::MultiVersionResolverOption, 10> Options;
|
||||
SmallVector<CodeGenFunction::TargetMultiVersionResolverOption, 10> Options;
|
||||
const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
|
||||
getContext().forEachMultiversionedFunctionVersion(
|
||||
FD, [this, &GD, &Options](const FunctionDecl *CurFD) {
|
||||
|
@ -2387,28 +2435,75 @@ void CodeGenModule::emitMultiVersionFunctions() {
|
|||
getModule().getOrInsertComdat(ResolverFunc->getName()));
|
||||
std::stable_sort(
|
||||
Options.begin(), Options.end(),
|
||||
std::greater<CodeGenFunction::MultiVersionResolverOption>());
|
||||
std::greater<CodeGenFunction::TargetMultiVersionResolverOption>());
|
||||
CodeGenFunction CGF(*this);
|
||||
CGF.EmitMultiVersionResolver(ResolverFunc, Options);
|
||||
CGF.EmitTargetMultiVersionResolver(ResolverFunc, Options);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) {
|
||||
const auto *FD = cast<FunctionDecl>(GD.getDecl());
|
||||
assert(FD && "Not a FunctionDecl?");
|
||||
const auto *DD = FD->getAttr<CPUDispatchAttr>();
|
||||
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<llvm::Function>(
|
||||
GetOrCreateLLVMFunction(ResolverName, ResolverType, GlobalDecl{},
|
||||
/*ForVTable=*/false));
|
||||
|
||||
SmallVector<CodeGenFunction::CPUDispatchMultiVersionResolverOption, 10>
|
||||
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<StringRef, 32> 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<llvm::Function>(Func),
|
||||
CodeGenFunction::GetX86CpuSupportsMask(Features));
|
||||
}
|
||||
|
||||
llvm::sort(
|
||||
Options.begin(), Options.end(),
|
||||
std::greater<CodeGenFunction::CPUDispatchMultiVersionResolverOption>());
|
||||
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 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction(
|
|||
addDeferredDeclToEmit(GDDef);
|
||||
}
|
||||
|
||||
if (FD->isMultiVersion() && FD->getAttr<TargetAttr>()->isDefaultVersion()) {
|
||||
UpdateMultiVersionNames(GD, FD);
|
||||
if (FD->isMultiVersion()) {
|
||||
const auto *TA = FD->getAttr<TargetAttr>();
|
||||
if (TA && TA->isDefaultVersion())
|
||||
UpdateMultiVersionNames(GD, FD);
|
||||
if (!IsForDefinition)
|
||||
return GetOrCreateMultiVersionIFunc(GD, Ty, MangledName, FD);
|
||||
return GetOrCreateMultiVersionIFunc(GD, Ty, FD);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3727,6 +3824,15 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
|
|||
AddGlobalDtor(Fn, DA->getPriority());
|
||||
if (D->hasAttr<AnnotateAttr>())
|
||||
AddGlobalAnnotations(D, Fn);
|
||||
|
||||
if (D->isCPUSpecificMultiVersion()) {
|
||||
auto *Spec = D->getAttr<CPUSpecificAttr>();
|
||||
// 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 @@ void CodeGenModule::getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,
|
|||
// the attribute.
|
||||
Target.initFeatureMap(FeatureMap, getDiags(), TargetCPU,
|
||||
ParsedAttr.Features);
|
||||
} else if (const auto *SD = FD->getAttr<CPUSpecificAttr>()) {
|
||||
llvm::SmallVector<StringRef, 32> FeaturesTmp;
|
||||
Target.getCPUSpecificCPUDispatchFeatures(SD->getCurCPUName()->getName(),
|
||||
FeaturesTmp);
|
||||
std::vector<std::string> Features(FeaturesTmp.begin(), FeaturesTmp.end());
|
||||
Target.initFeatureMap(FeatureMap, getDiags(), TargetCPU, Features);
|
||||
} else {
|
||||
Target.initFeatureMap(FeatureMap, getDiags(), TargetCPU,
|
||||
Target.getTargetOpts().Features);
|
||||
|
|
|
@ -366,6 +366,13 @@ private:
|
|||
llvm::MapVector<GlobalDecl, StringRef> MangledDeclNames;
|
||||
llvm::StringMap<GlobalDecl, llvm::BumpPtrAllocator> Manglings;
|
||||
|
||||
// An ordered map of canonical GlobalDecls paired with the cpu-index for
|
||||
// cpu-specific name manglings.
|
||||
llvm::MapVector<std::pair<GlobalDecl, unsigned>, StringRef>
|
||||
CPUSpecificMangledDeclNames;
|
||||
llvm::StringMap<std::pair<GlobalDecl, unsigned>, llvm::BumpPtrAllocator>
|
||||
CPUSpecificManglings;
|
||||
|
||||
/// Global annotations.
|
||||
std::vector<llvm::Constant*> Annotations;
|
||||
|
||||
|
@ -1283,7 +1290,6 @@ private:
|
|||
|
||||
llvm::Constant *GetOrCreateMultiVersionIFunc(GlobalDecl GD,
|
||||
llvm::Type *DeclTy,
|
||||
StringRef MangledName,
|
||||
const FunctionDecl *FD);
|
||||
void UpdateMultiVersionNames(GlobalDecl GD, const FunctionDecl *FD);
|
||||
|
||||
|
@ -1307,6 +1313,7 @@ private:
|
|||
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);
|
||||
|
||||
|
|
|
@ -215,6 +215,15 @@ static bool attributeHasIdentifierArg(const IdentifierInfo &II) {
|
|||
#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<bool>(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 @@ unsigned Parser::ParseAttributeArgsCommon(
|
|||
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 @@ unsigned Parser::ParseAttributeArgsCommon(
|
|||
|
||||
// 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;
|
||||
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;
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -658,6 +658,11 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body,
|
|||
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:
|
||||
|
|
|
@ -1585,6 +1585,7 @@ bool Sema::tryExprAsCall(Expr &E, QualType &ZeroArgCallReturnTy,
|
|||
}
|
||||
|
||||
bool Ambiguous = false;
|
||||
bool IsMV = false;
|
||||
|
||||
if (Overloads) {
|
||||
for (OverloadExpr::decls_iterator it = Overloads->decls_begin(),
|
||||
|
@ -1598,11 +1599,16 @@ bool Sema::tryExprAsCall(Expr &E, QualType &ZeroArgCallReturnTy,
|
|||
if (const FunctionDecl *OverloadDecl
|
||||
= dyn_cast<FunctionDecl>((*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 @@ static void noteOverloads(Sema &S, const UnresolvedSetImpl &Overloads,
|
|||
NamedDecl *Fn = (*It)->getUnderlyingDecl();
|
||||
// Don't print overloads for non-default multiversioned functions.
|
||||
if (const auto *FD = Fn->getAsFunction()) {
|
||||
if (FD->isMultiVersion() &&
|
||||
if (FD->isMultiVersion() && FD->hasAttr<TargetAttr>() &&
|
||||
!FD->getAttr<TargetAttr>()->isDefaultVersion())
|
||||
continue;
|
||||
}
|
||||
|
@ -1725,6 +1731,21 @@ static bool IsCallableWithAppend(Expr *E) {
|
|||
!isa<CXXOperatorCallExpr>(E));
|
||||
}
|
||||
|
||||
static bool IsCPUDispatchCPUSpecificMultiVersion(const Expr *E) {
|
||||
if (const auto *UO = dyn_cast<UnaryOperator>(E))
|
||||
E = UO->getSubExpr();
|
||||
|
||||
if (const auto *ULE = dyn_cast<UnresolvedLookupExpr>(E)) {
|
||||
if (ULE->getNumDecls() == 0)
|
||||
return false;
|
||||
|
||||
const NamedDecl *ND = *ULE->decls_begin();
|
||||
if (const auto *FD = dyn_cast<FunctionDecl>(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 @@ bool Sema::tryToRecoverWithCall(ExprResult &E, const PartialDiagnostic &PD,
|
|||
// 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 @@ bool Sema::tryToRecoverWithCall(ExprResult &E, const PartialDiagnostic &PD,
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -9275,6 +9275,20 @@ bool Sema::shouldLinkDependentDeclWithPrevious(Decl *D, Decl *PrevDecl) {
|
|||
D->getFriendObjectKind() != Decl::FOK_None);
|
||||
}
|
||||
|
||||
namespace MultiVersioning {
|
||||
enum Type { None, Target, CPUSpecific, CPUDispatch};
|
||||
} // MultiVersionType
|
||||
|
||||
static MultiVersioning::Type
|
||||
getMultiVersionType(const FunctionDecl *FD) {
|
||||
if (FD->hasAttr<TargetAttr>())
|
||||
return MultiVersioning::Target;
|
||||
if (FD->hasAttr<CPUDispatchAttr>())
|
||||
return MultiVersioning::CPUDispatch;
|
||||
if (FD->hasAttr<CPUSpecificAttr>())
|
||||
return MultiVersioning::CPUSpecific;
|
||||
return MultiVersioning::None;
|
||||
}
|
||||
/// Check the target attribute of the function for MultiVersion
|
||||
/// validity.
|
||||
///
|
||||
|
@ -9313,7 +9327,8 @@ static bool CheckMultiVersionValue(Sema &S, const FunctionDecl *FD) {
|
|||
|
||||
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 @@ static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD,
|
|||
Constructors = 3,
|
||||
Destructors = 4,
|
||||
DeletedFuncs = 5,
|
||||
DefaultedFuncs = 6
|
||||
DefaultedFuncs = 6,
|
||||
ConstexprFuncs = 7,
|
||||
};
|
||||
enum Different {
|
||||
CallingConv = 0,
|
||||
|
@ -9332,46 +9348,73 @@ static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD,
|
|||
Linkage = 5
|
||||
};
|
||||
|
||||
bool IsCPUSpecificCPUDispatchMVType =
|
||||
MVType == MultiVersioning::CPUDispatch ||
|
||||
MVType == MultiVersioning::CPUSpecific;
|
||||
|
||||
if (OldFD && !OldFD->getType()->getAs<FunctionProtoType>()) {
|
||||
S.Diag(OldFD->getLocation(), diag::err_multiversion_noproto);
|
||||
S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!NewFD->getType()->getAs<FunctionProtoType>())
|
||||
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<CXXMethodDecl>(NewFD)) {
|
||||
if (NewCXXFD->isVirtual())
|
||||
return S.Diag(NewCXXFD->getLocation(),
|
||||
diag::err_multiversion_doesnt_support)
|
||||
<< VirtFuncs;
|
||||
<< IsCPUSpecificCPUDispatchMVType << VirtFuncs;
|
||||
|
||||
if (const auto *NewCXXCtor = dyn_cast<CXXConstructorDecl>(NewFD))
|
||||
return S.Diag(NewCXXCtor->getLocation(),
|
||||
diag::err_multiversion_doesnt_support)
|
||||
<< Constructors;
|
||||
<< IsCPUSpecificCPUDispatchMVType << Constructors;
|
||||
|
||||
if (const auto *NewCXXDtor = dyn_cast<CXXDestructorDecl>(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<FunctionType>(NewQType);
|
||||
|
@ -9379,7 +9422,7 @@ static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD,
|
|||
|
||||
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 @@ static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD,
|
|||
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<TargetAttr>();
|
||||
if (NewFD->isMain()) {
|
||||
if (NewTA && NewTA->isDefaultVersion()) {
|
||||
S.Diag(NewFD->getLocation(), diag::err_multiversion_not_allowed_on_main);
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
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");
|
||||
|
||||
// If there is no matching previous decl, only 'default' can
|
||||
// cause MultiVersioning.
|
||||
if (!OldDecl) {
|
||||
if (NewTA && NewTA->isDefaultVersion()) {
|
||||
if (!NewFD->getType()->getAs<FunctionProtoType>()) {
|
||||
S.Diag(NewFD->getLocation(), diag::err_multiversion_noproto);
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
if (CheckMultiVersionAdditionalRules(S, nullptr, NewFD, true)) {
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
if (!S.getASTContext().getTargetInfo().supportsMultiVersioning()) {
|
||||
S.Diag(NewFD->getLocation(), diag::err_multiversion_not_supported);
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
|
||||
NewFD->setIsMultiVersion();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OldDecl->getDeclContext()->getRedeclContext() !=
|
||||
NewFD->getDeclContext()->getRedeclContext())
|
||||
// Target only causes MV if it is default, otherwise this is a normal
|
||||
// function.
|
||||
if (MVType == MultiVersioning::Target && !TA->isDefaultVersion())
|
||||
return false;
|
||||
|
||||
FunctionDecl *OldFD = OldDecl->getAsFunction();
|
||||
// Unresolved 'using' statements (the other way OldDecl can be not a function)
|
||||
// likely cannot cause a problem here.
|
||||
if (!OldFD)
|
||||
return false;
|
||||
|
||||
if (!OldFD->isMultiVersion() && !NewTA)
|
||||
return false;
|
||||
|
||||
if (OldFD->isMultiVersion() && !NewTA) {
|
||||
S.Diag(NewFD->getLocation(), diag::err_target_required_in_redecl);
|
||||
NewFD->setInvalidDecl();
|
||||
if (MVType == MultiVersioning::Target && CheckMultiVersionValue(S, FD)) {
|
||||
FD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CheckMultiVersionAdditionalRules(S, nullptr, FD, true, MVType)) {
|
||||
FD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
|
||||
FD->setIsMultiVersion();
|
||||
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>();
|
||||
TargetAttr::ParsedTargetAttr NewParsed = NewTA->parse();
|
||||
// Sort order doesn't matter, it just needs to be consistent.
|
||||
llvm::sort(NewParsed.Features.begin(), NewParsed.Features.end());
|
||||
|
||||
const auto *OldTA = OldFD->getAttr<TargetAttr>();
|
||||
if (!OldFD->isMultiVersion()) {
|
||||
// If the old decl is NOT MultiVersioned yet, and we don't cause that
|
||||
// to change, this is a simple redeclaration.
|
||||
if (!OldTA || OldTA->getFeaturesStr() == NewTA->getFeaturesStr())
|
||||
return false;
|
||||
|
||||
// Otherwise, this decl causes MultiVersioning.
|
||||
if (!S.getASTContext().getTargetInfo().supportsMultiVersioning()) {
|
||||
S.Diag(NewFD->getLocation(), diag::err_multiversion_not_supported);
|
||||
S.Diag(OldFD->getLocation(), diag::note_previous_declaration);
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!OldFD->getType()->getAs<FunctionProtoType>()) {
|
||||
S.Diag(OldFD->getLocation(), diag::err_multiversion_noproto);
|
||||
S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here);
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CheckMultiVersionValue(S, NewFD)) {
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CheckMultiVersionValue(S, OldFD)) {
|
||||
S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here);
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
|
||||
TargetAttr::ParsedTargetAttr OldParsed =
|
||||
OldTA->parse(std::less<std::string>());
|
||||
|
||||
if (OldParsed == NewParsed) {
|
||||
S.Diag(NewFD->getLocation(), diag::err_multiversion_duplicate);
|
||||
S.Diag(OldFD->getLocation(), diag::note_previous_declaration);
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto *FD : OldFD->redecls()) {
|
||||
const auto *CurTA = FD->getAttr<TargetAttr>();
|
||||
if (!CurTA || CurTA->isInherited()) {
|
||||
S.Diag(FD->getLocation(), diag::err_target_required_in_redecl);
|
||||
S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here);
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, true)) {
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
|
||||
OldFD->setIsMultiVersion();
|
||||
NewFD->setIsMultiVersion();
|
||||
Redeclaration = false;
|
||||
MergeTypeWithPrevious = false;
|
||||
OldDecl = nullptr;
|
||||
Previous.clear();
|
||||
// If the old decl is NOT MultiVersioned yet, and we don't cause that
|
||||
// to change, this is a simple redeclaration.
|
||||
if (!OldTA || OldTA->getFeaturesStr() == NewTA->getFeaturesStr())
|
||||
return false;
|
||||
|
||||
// Otherwise, this decl causes MultiVersioning.
|
||||
if (!S.getASTContext().getTargetInfo().supportsMultiVersioning()) {
|
||||
S.Diag(NewFD->getLocation(), diag::err_multiversion_not_supported);
|
||||
S.Diag(OldFD->getLocation(), diag::note_previous_declaration);
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, true,
|
||||
MultiVersioning::Target)) {
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CheckMultiVersionValue(S, NewFD)) {
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CheckMultiVersionValue(S, OldFD)) {
|
||||
S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here);
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
|
||||
TargetAttr::ParsedTargetAttr OldParsed =
|
||||
OldTA->parse(std::less<std::string>());
|
||||
|
||||
if (OldParsed == NewParsed) {
|
||||
S.Diag(NewFD->getLocation(), diag::err_multiversion_duplicate);
|
||||
S.Diag(OldFD->getLocation(), diag::note_previous_declaration);
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto *FD : OldFD->redecls()) {
|
||||
const auto *CurTA = FD->getAttr<TargetAttr>();
|
||||
if (!CurTA || CurTA->isInherited()) {
|
||||
S.Diag(FD->getLocation(), diag::err_multiversion_required_in_redecl)
|
||||
<< 0;
|
||||
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;
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
TargetAttr::ParsedTargetAttr NewParsed;
|
||||
if (NewTA) {
|
||||
NewParsed = NewTA->parse();
|
||||
llvm::sort(NewParsed.Features.begin(), NewParsed.Features.end());
|
||||
}
|
||||
|
||||
bool UseMemberUsingDeclRules =
|
||||
|
@ -9572,32 +9610,93 @@ static bool CheckMultiVersionFunction(Sema &S, FunctionDecl *NewFD,
|
|||
if (S.IsOverload(NewFD, CurFD, UseMemberUsingDeclRules))
|
||||
continue;
|
||||
|
||||
const auto *CurTA = CurFD->getAttr<TargetAttr>();
|
||||
if (CurTA->getFeaturesStr() == NewTA->getFeaturesStr()) {
|
||||
NewFD->setIsMultiVersion();
|
||||
Redeclaration = true;
|
||||
OldDecl = ND;
|
||||
return false;
|
||||
}
|
||||
if (NewMVType == MultiVersioning::Target) {
|
||||
const auto *CurTA = CurFD->getAttr<TargetAttr>();
|
||||
if (CurTA->getFeaturesStr() == NewTA->getFeaturesStr()) {
|
||||
NewFD->setIsMultiVersion();
|
||||
Redeclaration = true;
|
||||
OldDecl = ND;
|
||||
return false;
|
||||
}
|
||||
|
||||
TargetAttr::ParsedTargetAttr CurParsed =
|
||||
CurTA->parse(std::less<std::string>());
|
||||
TargetAttr::ParsedTargetAttr CurParsed =
|
||||
CurTA->parse(std::less<std::string>());
|
||||
if (CurParsed == NewParsed) {
|
||||
S.Diag(NewFD->getLocation(), diag::err_multiversion_duplicate);
|
||||
S.Diag(CurFD->getLocation(), diag::note_previous_declaration);
|
||||
NewFD->setInvalidDecl();
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
const auto *CurCPUSpec = CurFD->getAttr<CPUSpecificAttr>();
|
||||
const auto *CurCPUDisp = CurFD->getAttr<CPUDispatchAttr>();
|
||||
// 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<CPUDispatchAttr>()) {
|
||||
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 @@ static bool CheckMultiVersionFunction(Sema &S, FunctionDecl *NewFD,
|
|||
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<TargetAttr>();
|
||||
const auto *NewCPUDisp = NewFD->getAttr<CPUDispatchAttr>();
|
||||
const auto *NewCPUSpec = NewFD->getAttr<CPUSpecificAttr>();
|
||||
|
||||
// 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 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
|
|||
}
|
||||
}
|
||||
|
||||
// Warn on CPUDispatch with an actual body.
|
||||
if (FD->isMultiVersion() && FD->hasAttr<CPUDispatchAttr>() && Body)
|
||||
if (const auto *CmpndBody = dyn_cast<CompoundStmt>(Body))
|
||||
if (!CmpndBody->body_empty())
|
||||
Diag(CmpndBody->body_front()->getLocStart(),
|
||||
diag::warn_dispatch_body_ignored);
|
||||
|
||||
if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
|
||||
const CXXMethodDecl *KeyFunction;
|
||||
if (MD->isOutOfLine() && (MD = MD->getCanonicalDecl()) &&
|
||||
|
|
|
@ -1849,6 +1849,50 @@ static void handleRestrictAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
|||
<< AL.getName() << getFunctionOrMethodResultSourceRange(D);
|
||||
}
|
||||
|
||||
static void handleCPUSpecificAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
FunctionDecl *FD = cast<FunctionDecl>(D);
|
||||
if (!checkAttributeAtLeastNumArgs(S, AL, 1))
|
||||
return;
|
||||
|
||||
SmallVector<IdentifierInfo *, 8> 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 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
|
|||
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;
|
||||
|
|
|
@ -2728,12 +2728,23 @@ static bool CheckDeclInExpr(Sema &S, SourceLocation Loc, NamedDecl *D) {
|
|||
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<FunctionDecl>(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<FunctionTemplateDecl>())
|
||||
if (!NeedsADL && R.isSingleResult() &&
|
||||
!R.getAsSingle<FunctionTemplateDecl>() &&
|
||||
!ShouldLookupResultBeMultiVersionOverload(R))
|
||||
return BuildDeclarationNameExpr(SS, R.getLookupNameInfo(), R.getFoundDecl(),
|
||||
R.getRepresentativeDecl(), nullptr,
|
||||
AcceptInvalidDecl);
|
||||
|
@ -2741,7 +2752,7 @@ ExprResult Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS,
|
|||
// 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();
|
||||
|
||||
|
|
|
@ -5988,7 +5988,7 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
|
|||
Candidate.IgnoreObjectArgument = false;
|
||||
Candidate.ExplicitCallArguments = Args.size();
|
||||
|
||||
if (Function->isMultiVersion() &&
|
||||
if (Function->isMultiVersion() && Function->hasAttr<TargetAttr>() &&
|
||||
!Function->getAttr<TargetAttr>()->isDefaultVersion()) {
|
||||
Candidate.Viable = false;
|
||||
Candidate.FailureKind = ovl_non_default_multiversion_function;
|
||||
|
@ -6623,7 +6623,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
|
|||
return;
|
||||
}
|
||||
|
||||
if (Method->isMultiVersion() &&
|
||||
if (Method->isMultiVersion() && Method->hasAttr<TargetAttr>() &&
|
||||
!Method->getAttr<TargetAttr>()->isDefaultVersion()) {
|
||||
Candidate.Viable = false;
|
||||
Candidate.FailureKind = ovl_non_default_multiversion_function;
|
||||
|
@ -7032,7 +7032,7 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion,
|
|||
return;
|
||||
}
|
||||
|
||||
if (Conversion->isMultiVersion() &&
|
||||
if (Conversion->isMultiVersion() && Conversion->hasAttr<TargetAttr>() &&
|
||||
!Conversion->getAttr<TargetAttr>()->isDefaultVersion()) {
|
||||
Candidate.Viable = false;
|
||||
Candidate.FailureKind = ovl_non_default_multiversion_function;
|
||||
|
@ -8987,6 +8987,47 @@ static Comparison compareEnableIfAttrs(const Sema &S, const FunctionDecl *Cand1,
|
|||
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<CPUDispatchAttr>();
|
||||
bool Cand2CPUDisp = Cand2.Function->hasAttr<CPUDispatchAttr>();
|
||||
const auto *Cand1CPUSpec = Cand1.Function->getAttr<CPUSpecificAttr>();
|
||||
const auto *Cand2CPUSpec = Cand2.Function->getAttr<CPUSpecificAttr>();
|
||||
|
||||
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<CPUSpecificAttr::cpus_iterator, CPUSpecificAttr::cpus_iterator>
|
||||
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 @@ bool clang::isBetterOverloadCandidate(
|
|||
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 @@ void Sema::NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,
|
|||
QualType DestType, bool TakingAddress) {
|
||||
if (TakingAddress && !checkAddressOfCandidateIsAvailable(*this, Fn))
|
||||
return;
|
||||
if (Fn->isMultiVersion() && !Fn->getAttr<TargetAttr>()->isDefaultVersion())
|
||||
if (Fn->isMultiVersion() && Fn->hasAttr<TargetAttr>() &&
|
||||
!Fn->getAttr<TargetAttr>()->isDefaultVersion())
|
||||
return;
|
||||
|
||||
std::string FnDesc;
|
||||
|
@ -11056,8 +11101,7 @@ private:
|
|||
return false;
|
||||
if (FunDecl->isMultiVersion()) {
|
||||
const auto *TA = FunDecl->getAttr<TargetAttr>();
|
||||
assert(TA && "Multiversioned functions require a target attribute");
|
||||
if (!TA->isDefaultVersion())
|
||||
if (TA && !TA->isDefaultVersion())
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -11355,7 +11399,8 @@ bool Sema::resolveAndFixAddressOfOnlyViableOverloadCandidate(
|
|||
|
||||
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
|
||||
|
|
|
@ -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"
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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<typename T>
|
||||
int __attribute__((cpu_specific(atom))) foo(T) { return 0; }
|
||||
// expected-error@+2 {{multiversioned functions do not yet support function templates}}
|
||||
template<typename T>
|
||||
int __attribute__((cpu_specific(sandybridge))) foo2(T);
|
||||
|
||||
struct S {
|
||||
// expected-error@+2 {{multiversioned functions do not yet support function templates}}
|
||||
template<typename T>
|
||||
int __attribute__((cpu_specific(atom))) foo(T) { return 0; }
|
||||
|
||||
// expected-error@+2 {{multiversioned functions do not yet support function templates}}
|
||||
template<typename T>
|
||||
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(){}
|
|
@ -1173,6 +1173,13 @@ namespace {
|
|||
}
|
||||
};
|
||||
|
||||
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 @@ createArgument(const Record &Arg, StringRef Attr,
|
|||
Ptr = llvm::make_unique<VariadicParamIdxArgument>(Arg, Attr);
|
||||
else if (ArgName == "ParamIdxArgument")
|
||||
Ptr = llvm::make_unique<SimpleArgument>(Arg, Attr, "ParamIdx");
|
||||
else if (ArgName == "VariadicIdentifierArgument")
|
||||
Ptr = llvm::make_unique<VariadicIdentifierArgument>(Arg, Attr);
|
||||
else if (ArgName == "VersionArgument")
|
||||
Ptr = llvm::make_unique<VersionArgument>(Arg, Attr);
|
||||
|
||||
|
@ -2106,6 +2115,34 @@ static bool isIdentifierArgument(Record *Arg) {
|
|||
.Default(false);
|
||||
}
|
||||
|
||||
static bool isVariadicIdentifierArgument(Record *Arg) {
|
||||
return !Arg->getSuperClasses().empty() &&
|
||||
llvm::StringSwitch<bool>(
|
||||
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<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
|
||||
for (const auto *A : Attrs) {
|
||||
// Determine whether the first argument is a variadic identifier.
|
||||
std::vector<Record *> 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 @@ void EmitClangAttrParserStringSwitches(RecordKeeper &Records,
|
|||
emitSourceFileHeader("Parser-related llvm::StringSwitch cases", OS);
|
||||
emitClangAttrArgContextList(Records, OS);
|
||||
emitClangAttrIdentifierArgList(Records, OS);
|
||||
emitClangAttrVariadicIdentifierArgList(Records, OS);
|
||||
emitClangAttrTypeArgList(Records, OS);
|
||||
emitClangAttrLateParsedList(Records, OS);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue