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:
Erich Keane 2018-07-20 14:13:28 +00:00
parent f907e19b5e
commit 3efe00206f
27 changed files with 1330 additions and 235 deletions

View File

@ -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;

View File

@ -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 {

View File

@ -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 = [{

View File

@ -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">;

View File

@ -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<

View File

@ -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");

View File

@ -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

View File

@ -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);

View File

@ -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:

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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));
}

View File

@ -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:

View File

@ -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;
}

View File

@ -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()) &&

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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"

View File

@ -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)

View File

@ -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;
}

View File

@ -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(){}

View File

@ -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);
}