summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/Decl.cpp8
-rw-r--r--clang/lib/Basic/Targets/X86.cpp32
-rw-r--r--clang/lib/Basic/Targets/X86.h8
-rw-r--r--clang/lib/CodeGen/CGBuiltin.cpp12
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.cpp52
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.h35
-rw-r--r--clang/lib/CodeGen/CodeGenModule.cpp170
-rw-r--r--clang/lib/CodeGen/CodeGenModule.h9
-rw-r--r--clang/lib/Parse/ParseDecl.cpp42
-rw-r--r--clang/lib/Sema/AnalysisBasedWarnings.cpp5
-rw-r--r--clang/lib/Sema/Sema.cpp46
-rw-r--r--clang/lib/Sema/SemaDecl.cpp451
-rw-r--r--clang/lib/Sema/SemaDeclAttr.cpp48
-rw-r--r--clang/lib/Sema/SemaExpr.cpp15
-rw-r--r--clang/lib/Sema/SemaOverload.cpp61
15 files changed, 783 insertions, 211 deletions
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 7db105eded7..a23499f3fe8 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -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);
diff --git a/clang/lib/Basic/Targets/X86.cpp b/clang/lib/Basic/Targets/X86.cpp
index 43fbdc0329a..7ae0696ce7e 100644
--- a/clang/lib/Basic/Targets/X86.cpp
+++ b/clang/lib/Basic/Targets/X86.cpp
@@ -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:
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index 5b862face9b..dd04dee0871 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -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;
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index a256eb1bf09..b6d30a956d9 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -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;
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 99af1a18e81..3c582688e91 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -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);
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index d6deb655824..1fc445c66df 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -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
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 68868708888..627a33d8b55 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -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);
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index d2c7b327f98..ee64ed4f2ae 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -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);
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 9b552e10401..4f6bb08bdc6 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -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(
- Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
- if (ArgExpr.isInvalid()) {
- SkipUntil(tok::r_paren, StopAtSemi);
- return 0;
+ ExprResult ArgExpr;
+ if (Tok.is(tok::identifier) &&
+ attributeHasVariadicIdentifierArg(*AttrName)) {
+ ArgExprs.push_back(ParseIdentifierLoc());
+ } else {
+ bool Uneval = attributeParsedArgsUnevaluated(*AttrName);
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions,
+ Uneval ? Sema::ExpressionEvaluationContext::Unevaluated
+ : Sema::ExpressionEvaluationContext::ConstantEvaluated);
+
+ ExprResult ArgExpr(
+ Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
+ if (ArgExpr.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return 0;
+ }
+ ArgExprs.push_back(ArgExpr.get());
}
- ArgExprs.push_back(ArgExpr.get());
// Eat the comma, move to the next argument
} while (TryConsumeToken(tok::comma));
}
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 8938c341578..82d9df25d93 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -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:
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index f81a34c40bd..d57473c5616 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -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;
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index b6381dc1e4b..e91e9d3ac16 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -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;
- }
+static bool CheckMultiVersionFirstFunction(Sema &S, FunctionDecl *FD,
+ MultiVersioning::Type MVType,
+ const TargetAttr *TA,
+ const CPUDispatchAttr *CPUDisp,
+ const CPUSpecificAttr *CPUSpec) {
+ assert(MVType != MultiVersioning::None &&
+ "Function lacks multiversion attribute");
+
+ // Target only causes MV if it is default, otherwise this is a normal
+ // function.
+ if (MVType == MultiVersioning::Target && !TA->isDefaultVersion())
return false;
- }
- // If there is no matching previous decl, only 'default' can
- // cause MultiVersioning.
- if (!OldDecl) {
- if (NewTA && NewTA->isDefaultVersion()) {
- if (!NewFD->getType()->getAs<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;
- }
+ if (MVType == MultiVersioning::Target && CheckMultiVersionValue(S, FD)) {
+ FD->setInvalidDecl();
+ return true;
+ }
- NewFD->setIsMultiVersion();
- }
- return false;
+ if (CheckMultiVersionAdditionalRules(S, nullptr, FD, true, MVType)) {
+ FD->setInvalidDecl();
+ return true;
}
- if (OldDecl->getDeclContext()->getRedeclContext() !=
- NewFD->getDeclContext()->getRedeclContext())
- return false;
+ FD->setIsMultiVersion();
+ return false;
+}
- FunctionDecl *OldFD = OldDecl->getAsFunction();
- // Unresolved 'using' statements (the other way OldDecl can be not a function)
- // likely cannot cause a problem here.
- if (!OldFD)
- return false;
+static bool CheckTargetCausesMultiVersioning(
+ Sema &S, FunctionDecl *OldFD, FunctionDecl *NewFD, const TargetAttr *NewTA,
+ bool &Redeclaration, NamedDecl *&OldDecl, bool &MergeTypeWithPrevious,
+ LookupResult &Previous) {
+ const auto *OldTA = OldFD->getAttr<TargetAttr>();
+ TargetAttr::ParsedTargetAttr NewParsed = NewTA->parse();
+ // Sort order doesn't matter, it just needs to be consistent.
+ llvm::sort(NewParsed.Features.begin(), NewParsed.Features.end());
- if (!OldFD->isMultiVersion() && !NewTA)
+ // If the old decl is NOT MultiVersioned yet, and we don't cause that
+ // to change, this is a simple redeclaration.
+ if (!OldTA || OldTA->getFeaturesStr() == NewTA->getFeaturesStr())
return false;
- if (OldFD->isMultiVersion() && !NewTA) {
- S.Diag(NewFD->getLocation(), diag::err_target_required_in_redecl);
+ // Otherwise, this decl causes MultiVersioning.
+ if (!S.getASTContext().getTargetInfo().supportsMultiVersioning()) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_not_supported);
+ S.Diag(OldFD->getLocation(), diag::note_previous_declaration);
NewFD->setInvalidDecl();
return true;
}
- TargetAttr::ParsedTargetAttr NewParsed = NewTA->parse();
- // Sort order doesn't matter, it just needs to be consistent.
- llvm::sort(NewParsed.Features.begin(), NewParsed.Features.end());
+ if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, true,
+ MultiVersioning::Target)) {
+ NewFD->setInvalidDecl();
+ return true;
+ }
- const auto *OldTA = OldFD->getAttr<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;
+ if (CheckMultiVersionValue(S, NewFD)) {
+ NewFD->setInvalidDecl();
+ return true;
+ }
- // Otherwise, this decl causes MultiVersioning.
- if (!S.getASTContext().getTargetInfo().supportsMultiVersioning()) {
- S.Diag(NewFD->getLocation(), diag::err_multiversion_not_supported);
- S.Diag(OldFD->getLocation(), diag::note_previous_declaration);
- NewFD->setInvalidDecl();
- return true;
- }
+ if (CheckMultiVersionValue(S, OldFD)) {
+ S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here);
+ NewFD->setInvalidDecl();
+ return true;
+ }
- if (!OldFD->getType()->getAs<FunctionProtoType>()) {
- S.Diag(OldFD->getLocation(), diag::err_multiversion_noproto);
- S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here);
- NewFD->setInvalidDecl();
- return true;
- }
+ TargetAttr::ParsedTargetAttr OldParsed =
+ OldTA->parse(std::less<std::string>());
- if (CheckMultiVersionValue(S, NewFD)) {
- NewFD->setInvalidDecl();
- return true;
- }
+ if (OldParsed == NewParsed) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_duplicate);
+ S.Diag(OldFD->getLocation(), diag::note_previous_declaration);
+ NewFD->setInvalidDecl();
+ return true;
+ }
- if (CheckMultiVersionValue(S, OldFD)) {
+ for (const auto *FD : OldFD->redecls()) {
+ const auto *CurTA = FD->getAttr<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;
}
+ }
- 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;
- }
- }
+ OldFD->setIsMultiVersion();
+ NewFD->setIsMultiVersion();
+ Redeclaration = false;
+ MergeTypeWithPrevious = false;
+ OldDecl = nullptr;
+ Previous.clear();
+ return false;
+}
- if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, true)) {
- NewFD->setInvalidDecl();
- return true;
- }
+/// Check the validity of a new function declaration being added to an existing
+/// multiversioned declaration collection.
+static bool CheckMultiVersionAdditionalDecl(
+ Sema &S, FunctionDecl *OldFD, FunctionDecl *NewFD,
+ MultiVersioning::Type NewMVType, const TargetAttr *NewTA,
+ const CPUDispatchAttr *NewCPUDisp, const CPUSpecificAttr *NewCPUSpec,
+ bool &Redeclaration, NamedDecl *&OldDecl, bool &MergeTypeWithPrevious,
+ LookupResult &Previous) {
+
+ MultiVersioning::Type OldMVType = getMultiVersionType(OldFD);
+ // Disallow mixing of multiversioning types.
+ if ((OldMVType == MultiVersioning::Target &&
+ NewMVType != MultiVersioning::Target) ||
+ (NewMVType == MultiVersioning::Target &&
+ OldMVType != MultiVersioning::Target)) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_types_mixed);
+ S.Diag(OldFD->getLocation(), diag::note_previous_declaration);
+ NewFD->setInvalidDecl();
+ return true;
+ }
- OldFD->setIsMultiVersion();
- NewFD->setIsMultiVersion();
- Redeclaration = false;
- MergeTypeWithPrevious = false;
- OldDecl = nullptr;
- Previous.clear();
- return false;
+ TargetAttr::ParsedTargetAttr NewParsed;
+ if (NewTA) {
+ NewParsed = NewTA->parse();
+ llvm::sort(NewParsed.Features.begin(), NewParsed.Features.end());
}
bool UseMemberUsingDeclRules =
@@ -9572,32 +9610,93 @@ 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()) &&
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 55fb21af0d3..d1786f7682b 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -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;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 51c1c84c273..e350517946e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -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();
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 390782b35e6..a0b9498cdfa 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -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
OpenPOWER on IntegriCloud