summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
authorErich Keane <erich.keane@intel.com>2018-01-08 21:34:17 +0000
committerErich Keane <erich.keane@intel.com>2018-01-08 21:34:17 +0000
commit281d20b601c8a2a77b9eac458cafb0482fa94c88 (patch)
tree6553d6c2f54562465c8c423b48c2eab6bf0f4e24 /clang/lib
parent7ff91421b4d273e6b1589b45fb7542aef63d91d7 (diff)
downloadbcm5719-llvm-281d20b601c8a2a77b9eac458cafb0482fa94c88.tar.gz
bcm5719-llvm-281d20b601c8a2a77b9eac458cafb0482fa94c88.zip
Implement Attribute Target MultiVersioning
GCC's attribute 'target', in addition to being an optimization hint, also allows function multiversioning. We currently have the former implemented, this is the latter's implementation. This works by enabling functions with the same name/signature to coexist, so that they can all be emitted. Multiversion state is stored in the FunctionDecl itself, and SemaDecl manages the definitions. Note that it ends up having to permit redefinition of functions so that they can all be emitted. Additionally, all versions of the function must be emitted, so this also manages that. Note that this includes some additional rules that GCC does not, since defining something as a MultiVersion function after a usage has been made illegal. The only 'history rewriting' that happens is if a function is emitted before it has been converted to a multiversion'ed function, at which point its name needs to be changed. Function templates and virtual functions are NOT yet supported (not supported in GCC either). Additionally, constructors/destructors are disallowed, but the former is planned. llvm-svn: 322028
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/ASTContext.cpp17
-rw-r--r--clang/lib/Basic/Targets/X86.cpp58
-rw-r--r--clang/lib/Basic/Targets/X86.h7
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.cpp54
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.h24
-rw-r--r--clang/lib/CodeGen/CodeGenModule.cpp199
-rw-r--r--clang/lib/CodeGen/CodeGenModule.h12
-rw-r--r--clang/lib/Sema/Sema.cpp6
-rw-r--r--clang/lib/Sema/SemaDecl.cpp357
-rw-r--r--clang/lib/Sema/SemaOverload.cpp30
-rw-r--r--clang/lib/Serialization/ASTReaderDecl.cpp16
-rw-r--r--clang/lib/Serialization/ASTWriterDecl.cpp2
12 files changed, 758 insertions, 24 deletions
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 2e906247cc1..c81d5786402 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -9483,6 +9483,23 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
return false;
}
+void ASTContext::forEachMultiversionedFunctionVersion(
+ const FunctionDecl *FD,
+ llvm::function_ref<void(const FunctionDecl *)> Pred) const {
+ assert(FD->isMultiVersion() && "Only valid for multiversioned functions");
+ llvm::SmallDenseSet<const FunctionDecl*, 4> SeenDecls;
+ FD = FD->getCanonicalDecl();
+ for (auto *CurDecl :
+ FD->getDeclContext()->getRedeclContext()->lookup(FD->getDeclName())) {
+ FunctionDecl *CurFD = CurDecl->getAsFunction()->getCanonicalDecl();
+ if (CurFD && hasSameType(CurFD->getType(), FD->getType()) &&
+ std::end(SeenDecls) == llvm::find(SeenDecls, CurFD)) {
+ SeenDecls.insert(CurFD);
+ Pred(CurFD);
+ }
+ }
+}
+
CallingConv ASTContext::getDefaultCallingConvention(bool IsVariadic,
bool IsCXXMethod) const {
// Pass through to the C++ ABI object
diff --git a/clang/lib/Basic/Targets/X86.cpp b/clang/lib/Basic/Targets/X86.cpp
index 70433ff066f..5d4dfcb8441 100644
--- a/clang/lib/Basic/Targets/X86.cpp
+++ b/clang/lib/Basic/Targets/X86.cpp
@@ -17,6 +17,7 @@
#include "clang/Basic/TargetBuiltins.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/TargetParser.h"
namespace clang {
namespace targets {
@@ -1338,6 +1339,63 @@ bool X86TargetInfo::validateCpuSupports(StringRef FeatureStr) const {
.Default(false);
}
+static llvm::X86::ProcessorFeatures getFeature(StringRef Name) {
+ return llvm::StringSwitch<llvm::X86::ProcessorFeatures>(Name)
+#define X86_FEATURE_COMPAT(VAL, ENUM, STR) .Case(STR, llvm::X86::ENUM)
+#include "llvm/Support/X86TargetParser.def"
+ ;
+ // Note, this function should only be used after ensuring the value is
+ // correct, so it asserts if the value is out of range.
+}
+
+static unsigned getFeaturePriority(llvm::X86::ProcessorFeatures Feat) {
+ enum class FeatPriority {
+#define FEATURE(FEAT) FEAT,
+#include "clang/Basic/X86Target.def"
+ };
+ switch (Feat) {
+#define FEATURE(FEAT) \
+ case llvm::X86::FEAT: \
+ return static_cast<unsigned>(FeatPriority::FEAT);
+#include "clang/Basic/X86Target.def"
+ default:
+ llvm_unreachable("No Feature Priority for non-CPUSupports Features");
+ }
+}
+
+unsigned X86TargetInfo::multiVersionSortPriority(StringRef Name) const {
+ // Valid CPUs have a 'key feature' that compares just better than its key
+ // feature.
+ CPUKind Kind = getCPUKind(Name);
+ if (Kind != CK_Generic) {
+ switch (Kind) {
+ default:
+ llvm_unreachable(
+ "CPU Type without a key feature used in 'target' attribute");
+#define PROC_WITH_FEAT(ENUM, STR, IS64, KEY_FEAT) \
+ case CK_##ENUM: \
+ return (getFeaturePriority(llvm::X86::KEY_FEAT) << 1) + 1;
+#include "clang/Basic/X86Target.def"
+ }
+ }
+
+ // Now we know we have a feature, so get its priority and shift it a few so
+ // that we have sufficient room for the CPUs (above).
+ return getFeaturePriority(getFeature(Name)) << 1;
+}
+
+std::string X86TargetInfo::getCPUKindCanonicalName(CPUKind Kind) const {
+ switch (Kind) {
+ case CK_Generic:
+ return "";
+#define PROC(ENUM, STRING, IS64BIT) \
+ case CK_##ENUM: \
+ return STRING;
+#include "clang/Basic/X86Target.def"
+ }
+ llvm_unreachable("Invalid CPUKind");
+}
+
// We can't use a generic validation scheme for the cpus accepted here
// versus subtarget cpus accepted in the target attribute because the
// variables intitialized by the runtime only support the below currently
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index cbd6a2d24fb..58b7a09c9ab 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -111,6 +111,8 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {
CPUKind getCPUKind(StringRef CPU) const;
+ std::string getCPUKindCanonicalName(CPUKind Kind) const;
+
enum FPMathKind { FP_Default, FP_SSE, FP_387 } FPMath = FP_Default;
public:
@@ -256,6 +258,11 @@ public:
return checkCPUKind(CPU = getCPUKind(Name));
}
+ bool supportsMultiVersioning() const override {
+ return getTriple().isOSBinFormatELF();
+ }
+ unsigned multiVersionSortPriority(StringRef Name) const override;
+
bool setFPMath(StringRef Name) override;
CallingConvCheckResult checkCallingConvention(CallingConv CC) const override {
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 6fb21b15736..0cbe071d1b9 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -2305,6 +2305,60 @@ void CodeGenFunction::EmitSanitizerStatReport(llvm::SanitizerStatKind SSK) {
CGM.getSanStats().create(IRB, SSK);
}
+llvm::Value *
+CodeGenFunction::FormResolverCondition(const MultiVersionResolverOption &RO) {
+ llvm::Value *TrueCondition = nullptr;
+ if (!RO.ParsedAttribute.Architecture.empty())
+ TrueCondition = EmitX86CpuIs(RO.ParsedAttribute.Architecture);
+
+ if (!RO.ParsedAttribute.Features.empty()) {
+ SmallVector<StringRef, 8> FeatureList;
+ llvm::for_each(RO.ParsedAttribute.Features,
+ [&FeatureList](const std::string &Feature) {
+ FeatureList.push_back(StringRef{Feature}.substr(1));
+ });
+ llvm::Value *FeatureCmp = EmitX86CpuSupports(FeatureList);
+ TrueCondition = TrueCondition ? Builder.CreateAnd(TrueCondition, FeatureCmp)
+ : FeatureCmp;
+ }
+ return TrueCondition;
+}
+
+void CodeGenFunction::EmitMultiVersionResolver(
+ llvm::Function *Resolver, ArrayRef<MultiVersionResolverOption> Options) {
+ assert((getContext().getTargetInfo().getTriple().getArch() ==
+ llvm::Triple::x86 ||
+ getContext().getTargetInfo().getTriple().getArch() ==
+ llvm::Triple::x86_64) &&
+ "Only implemented for x86 targets");
+
+ // Main function's basic block.
+ llvm::BasicBlock *CurBlock = createBasicBlock("entry", Resolver);
+ Builder.SetInsertPoint(CurBlock);
+ EmitX86CpuInit();
+
+ llvm::Function *DefaultFunc = nullptr;
+ for (const MultiVersionResolverOption &RO : Options) {
+ Builder.SetInsertPoint(CurBlock);
+ llvm::Value *TrueCondition = FormResolverCondition(RO);
+
+ if (!TrueCondition) {
+ DefaultFunc = RO.Function;
+ } else {
+ llvm::BasicBlock *RetBlock = createBasicBlock("ro_ret", Resolver);
+ llvm::IRBuilder<> RetBuilder(RetBlock);
+ RetBuilder.CreateRet(RO.Function);
+ CurBlock = createBasicBlock("ro_else", Resolver);
+ Builder.CreateCondBr(TrueCondition, RetBlock, CurBlock);
+ }
+ }
+
+ assert(DefaultFunc && "No default version?");
+ // Emit return from the 'else-ist' block.
+ Builder.SetInsertPoint(CurBlock);
+ Builder.CreateRet(DefaultFunc);
+}
+
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 74750c4d28a..cedf327a480 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3988,6 +3988,29 @@ public:
void EmitSanitizerStatReport(llvm::SanitizerStatKind SSK);
+ struct MultiVersionResolverOption {
+ llvm::Function *Function;
+ TargetAttr::ParsedTargetAttr ParsedAttribute;
+ unsigned Priority;
+ MultiVersionResolverOption(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,
+ TargInfo.multiVersionSortPriority(Feat.substr(1)));
+
+ if (!PT.Architecture.empty())
+ Priority = std::max(Priority,
+ TargInfo.multiVersionSortPriority(PT.Architecture));
+ }
+
+ bool operator>(const MultiVersionResolverOption &Other) const {
+ return Priority > Other.Priority;
+ }
+ };
+ void EmitMultiVersionResolver(llvm::Function *Resolver,
+ ArrayRef<MultiVersionResolverOption> Options);
+
private:
QualType getVarArgType(const Expr *Arg);
@@ -4004,6 +4027,7 @@ private:
llvm::Value *EmitX86CpuSupports(const CallExpr *E);
llvm::Value *EmitX86CpuSupports(ArrayRef<StringRef> FeatureStrs);
llvm::Value *EmitX86CpuInit();
+ llvm::Value *FormResolverCondition(const MultiVersionResolverOption &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 df33fc3e820..85bd53741e1 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -395,6 +395,7 @@ void CodeGenModule::Release() {
applyGlobalValReplacements();
applyReplacements();
checkAliases();
+ emitMultiVersionFunctions();
EmitCXXGlobalInitFunc();
EmitCXXGlobalDtorFunc();
EmitCXXThreadLocalInitFunc();
@@ -725,36 +726,48 @@ void CodeGenModule::setTLSMode(llvm::GlobalValue *GV, const VarDecl &D) const {
GV->setThreadLocalMode(TLM);
}
-StringRef CodeGenModule::getMangledName(GlobalDecl GD) {
- GlobalDecl CanonicalGD = GD.getCanonicalDecl();
+static void AppendTargetMangling(const CodeGenModule &CGM,
+ const TargetAttr *Attr, raw_ostream &Out) {
+ if (Attr->isDefaultVersion())
+ return;
- // Some ABIs don't have constructor variants. Make sure that base and
- // complete constructors get mangled the same.
- if (const auto *CD = dyn_cast<CXXConstructorDecl>(CanonicalGD.getDecl())) {
- if (!getTarget().getCXXABI().hasConstructorVariants()) {
- CXXCtorType OrigCtorType = GD.getCtorType();
- assert(OrigCtorType == Ctor_Base || OrigCtorType == Ctor_Complete);
- if (OrigCtorType == Ctor_Base)
- CanonicalGD = GlobalDecl(CD, Ctor_Complete);
- }
+ Out << '.';
+ const auto &Target = CGM.getTarget();
+ TargetAttr::ParsedTargetAttr Info =
+ Attr->parse([&Target](StringRef LHS, StringRef RHS) {
+ return Target.multiVersionSortPriority(LHS) >
+ Target.multiVersionSortPriority(RHS);
+ });
+
+ bool IsFirst = true;
+
+ if (!Info.Architecture.empty()) {
+ IsFirst = false;
+ Out << "arch_" << Info.Architecture;
}
- auto FoundName = MangledDeclNames.find(CanonicalGD);
- if (FoundName != MangledDeclNames.end())
- return FoundName->second;
+ for (StringRef Feat : Info.Features) {
+ if (!IsFirst)
+ Out << '_';
+ IsFirst = false;
+ Out << Feat.substr(1);
+ }
+}
- const auto *ND = cast<NamedDecl>(GD.getDecl());
+static std::string getMangledNameImpl(const CodeGenModule &CGM, GlobalDecl GD,
+ const NamedDecl *ND,
+ bool OmitTargetMangling = false) {
SmallString<256> Buffer;
- StringRef Str;
- if (getCXXABI().getMangleContext().shouldMangleDeclName(ND)) {
+ llvm::raw_svector_ostream Out(Buffer);
+ MangleContext &MC = CGM.getCXXABI().getMangleContext();
+ if (MC.shouldMangleDeclName(ND)) {
llvm::raw_svector_ostream Out(Buffer);
if (const auto *D = dyn_cast<CXXConstructorDecl>(ND))
- getCXXABI().getMangleContext().mangleCXXCtor(D, GD.getCtorType(), Out);
+ MC.mangleCXXCtor(D, GD.getCtorType(), Out);
else if (const auto *D = dyn_cast<CXXDestructorDecl>(ND))
- getCXXABI().getMangleContext().mangleCXXDtor(D, GD.getDtorType(), Out);
+ MC.mangleCXXDtor(D, GD.getDtorType(), Out);
else
- getCXXABI().getMangleContext().mangleName(ND, Out);
- Str = Out.str();
+ MC.mangleName(ND, Out);
} else {
IdentifierInfo *II = ND->getIdentifier();
assert(II && "Attempt to mangle unnamed decl.");
@@ -764,14 +777,74 @@ StringRef CodeGenModule::getMangledName(GlobalDecl GD) {
FD->getType()->castAs<FunctionType>()->getCallConv() == CC_X86RegCall) {
llvm::raw_svector_ostream Out(Buffer);
Out << "__regcall3__" << II->getName();
- Str = Out.str();
} else {
- Str = II->getName();
+ Out << II->getName();
+ }
+ }
+
+ if (const auto *FD = dyn_cast<FunctionDecl>(ND))
+ if (FD->isMultiVersion() && !OmitTargetMangling)
+ AppendTargetMangling(CGM, FD->getAttr<TargetAttr>(), Out);
+ return Out.str();
+}
+
+void CodeGenModule::UpdateMultiVersionNames(GlobalDecl GD,
+ const FunctionDecl *FD) {
+ if (!FD->isMultiVersion())
+ return;
+
+ // Get the name of what this would be without the 'target' attribute. This
+ // 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);
+ GlobalDecl OtherGD;
+ if (lookupRepresentativeDecl(NonTargetName, OtherGD)) {
+ assert(OtherGD.getCanonicalDecl()
+ .getDecl()
+ ->getAsFunction()
+ ->isMultiVersion() &&
+ "Other GD should now be a multiversioned function");
+ // OtherFD is the version of this function that was mangled BEFORE
+ // becoming a MultiVersion function. It potentially needs to be updated.
+ const FunctionDecl *OtherFD =
+ OtherGD.getCanonicalDecl().getDecl()->getAsFunction();
+ std::string OtherName = getMangledNameImpl(*this, OtherGD, OtherFD);
+ // This is so that if the initial version was already the 'default'
+ // version, we don't try to update it.
+ if (OtherName != NonTargetName) {
+ Manglings.erase(NonTargetName);
+ auto Result = Manglings.insert(std::make_pair(OtherName, OtherGD));
+ MangledDeclNames[OtherGD.getCanonicalDecl()] = Result.first->first();
+ if (llvm::GlobalValue *Entry = GetGlobalValue(NonTargetName))
+ Entry->setName(OtherName);
}
}
+}
+
+StringRef CodeGenModule::getMangledName(GlobalDecl GD) {
+ GlobalDecl CanonicalGD = GD.getCanonicalDecl();
+
+ // Some ABIs don't have constructor variants. Make sure that base and
+ // complete constructors get mangled the same.
+ if (const auto *CD = dyn_cast<CXXConstructorDecl>(CanonicalGD.getDecl())) {
+ if (!getTarget().getCXXABI().hasConstructorVariants()) {
+ CXXCtorType OrigCtorType = GD.getCtorType();
+ assert(OrigCtorType == Ctor_Base || OrigCtorType == Ctor_Complete);
+ if (OrigCtorType == Ctor_Base)
+ CanonicalGD = GlobalDecl(CD, Ctor_Complete);
+ }
+ }
+
+ auto FoundName = MangledDeclNames.find(CanonicalGD);
+ if (FoundName != MangledDeclNames.end())
+ return FoundName->second;
+
// Keep the first result in the case of a mangling collision.
- auto Result = Manglings.insert(std::make_pair(Str, GD));
+ const auto *ND = cast<NamedDecl>(GD.getDecl());
+ auto Result =
+ Manglings.insert(std::make_pair(getMangledNameImpl(*this, GD, ND), GD));
return MangledDeclNames[CanonicalGD] = Result.first->first();
}
@@ -2061,6 +2134,74 @@ void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD, llvm::GlobalValue *GV) {
static void ReplaceUsesOfNonProtoTypeWithRealFunction(llvm::GlobalValue *Old,
llvm::Function *NewFn);
+void CodeGenModule::emitMultiVersionFunctions() {
+ for (GlobalDecl GD : MultiVersionFuncs) {
+ SmallVector<CodeGenFunction::MultiVersionResolverOption, 10> Options;
+ const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
+ getContext().forEachMultiversionedFunctionVersion(
+ FD, [this, &GD, &Options](const FunctionDecl *CurFD) {
+ GlobalDecl CurGD{
+ (CurFD->isDefined() ? CurFD->getDefinition() : CurFD)};
+ StringRef MangledName = getMangledName(CurGD);
+ llvm::Constant *Func = GetGlobalValue(MangledName);
+ if (!Func) {
+ if (CurFD->isDefined()) {
+ EmitGlobalFunctionDefinition(CurGD, nullptr);
+ Func = GetGlobalValue(MangledName);
+ } else {
+ const CGFunctionInfo &FI =
+ getTypes().arrangeGlobalDeclaration(GD);
+ llvm::FunctionType *Ty = getTypes().GetFunctionType(FI);
+ Func = GetAddrOfFunction(CurGD, Ty, /*ForVTable=*/false,
+ /*DontDefer=*/false, ForDefinition);
+ }
+ assert(Func && "This should have just been created");
+ }
+ Options.emplace_back(getTarget(), cast<llvm::Function>(Func),
+ CurFD->getAttr<TargetAttr>()->parse());
+ });
+
+ llvm::Function *ResolverFunc = cast<llvm::Function>(
+ GetGlobalValue((getMangledName(GD) + ".resolver").str()));
+ std::stable_sort(
+ Options.begin(), Options.end(),
+ std::greater<CodeGenFunction::MultiVersionResolverOption>());
+ CodeGenFunction CGF(*this);
+ CGF.EmitMultiVersionResolver(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();
+ 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);
+
+ std::string ResolverName = (MangledName + ".resolver").str();
+ llvm::Type *ResolverType = llvm::FunctionType::get(
+ llvm::PointerType::get(DeclTy,
+ Context.getTargetAddressSpace(FD->getType())),
+ false);
+ llvm::Constant *Resolver =
+ GetOrCreateLLVMFunction(ResolverName, ResolverType, GlobalDecl{},
+ /*ForVTable=*/false);
+ llvm::GlobalIFunc *GIF = llvm::GlobalIFunc::create(
+ DeclTy, 0, llvm::Function::ExternalLinkage, "", Resolver, &getModule());
+ GIF->setName(IFuncName);
+ SetCommonAttributes(FD, GIF);
+
+ return GIF;
+}
+
/// GetOrCreateLLVMFunction - If the specified mangled name is not in the
/// module, create and return an llvm Function with the specified type. If there
/// is something in the module with the specified name, return it potentially
@@ -2074,6 +2215,16 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction(
ForDefinition_t IsForDefinition) {
const Decl *D = GD.getDecl();
+ // Any attempts to use a MultiVersion function should result in retrieving
+ // the iFunc instead. Name Mangling will handle the rest of the changes.
+ if (const FunctionDecl *FD = cast_or_null<FunctionDecl>(D)) {
+ if (FD->isMultiVersion() && FD->getAttr<TargetAttr>()->isDefaultVersion()) {
+ UpdateMultiVersionNames(GD, FD);
+ if (!IsForDefinition)
+ return GetOrCreateMultiVersionIFunc(GD, Ty, MangledName, FD);
+ }
+ }
+
// Lookup the entry, lazily creating it if necessary.
llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
if (Entry) {
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 22c4463b2c8..e63fa287064 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -324,6 +324,10 @@ private:
/// is defined once we get to the end of the of the translation unit.
std::vector<GlobalDecl> Aliases;
+ /// List of multiversion functions that have to be emitted. Used to make sure
+ /// we properly emit the iFunc.
+ std::vector<GlobalDecl> MultiVersionFuncs;
+
typedef llvm::StringMap<llvm::TrackingVH<llvm::Constant> > ReplacementsTy;
ReplacementsTy Replacements;
@@ -1247,6 +1251,12 @@ private:
llvm::AttributeList ExtraAttrs = llvm::AttributeList(),
ForDefinition_t IsForDefinition = NotForDefinition);
+ llvm::Constant *GetOrCreateMultiVersionIFunc(GlobalDecl GD,
+ llvm::Type *DeclTy,
+ StringRef MangledName,
+ const FunctionDecl *FD);
+ void UpdateMultiVersionNames(GlobalDecl GD, const FunctionDecl *FD);
+
llvm::Constant *GetOrCreateLLVMGlobal(StringRef MangledName,
llvm::PointerType *PTy,
const VarDecl *D,
@@ -1319,6 +1329,8 @@ private:
void checkAliases();
+ void emitMultiVersionFunctions();
+
/// Emit any vtables which we deferred and still have a use for.
void EmitDeferredVTables();
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 4e57e5ef81c..e07f60989d8 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1647,6 +1647,12 @@ 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() &&
+ !FD->getAttr<TargetAttr>()->isDefaultVersion())
+ continue;
+ }
S.Diag(Fn->getLocation(), diag::note_possible_target_of_call);
++ShownOverloads;
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 2b2ab42427b..24ed78c8bfd 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -9157,6 +9157,359 @@ bool Sema::shouldLinkDependentDeclWithPrevious(Decl *D, Decl *PrevDecl) {
D->getFriendObjectKind() != Decl::FOK_None);
}
+/// \brief Check the target attribute of the function for MultiVersion
+/// validity.
+///
+/// Returns true if there was an error, false otherwise.
+static bool CheckMultiVersionValue(Sema &S, const FunctionDecl *FD) {
+ const auto *TA = FD->getAttr<TargetAttr>();
+ assert(TA && "MultiVersion Candidate requires a target attribute");
+ TargetAttr::ParsedTargetAttr ParseInfo = TA->parse();
+ const TargetInfo &TargetInfo = S.Context.getTargetInfo();
+ enum ErrType { Feature = 0, Architecture = 1 };
+
+ if (!ParseInfo.Architecture.empty() &&
+ !TargetInfo.validateCpuIs(ParseInfo.Architecture)) {
+ S.Diag(FD->getLocation(), diag::err_bad_multiversion_option)
+ << Architecture << ParseInfo.Architecture;
+ return true;
+ }
+
+ for (const auto &Feature : ParseInfo.Features) {
+ auto BareFeat = StringRef{Feature}.substr(1);
+ if (Feature[0] == '-') {
+ S.Diag(FD->getLocation(), diag::err_bad_multiversion_option)
+ << Feature << ("no-" + BareFeat).str();
+ return true;
+ }
+
+ if (!TargetInfo.validateCpuSupports(BareFeat) ||
+ !TargetInfo.isValidFeatureName(BareFeat)) {
+ S.Diag(FD->getLocation(), diag::err_bad_multiversion_option)
+ << Feature << BareFeat;
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD,
+ const FunctionDecl *NewFD,
+ bool CausesMV) {
+ enum DoesntSupport {
+ FuncTemplates = 0,
+ VirtFuncs = 1,
+ DeducedReturn = 2,
+ Constructors = 3,
+ Destructors = 4,
+ DeletedFuncs = 5,
+ DefaultedFuncs = 6
+ };
+ enum Different {
+ CallingConv = 0,
+ ReturnType = 1,
+ ConstexprSpec = 2,
+ InlineSpec = 3,
+ StorageClass = 4,
+ Linkage = 5
+ };
+
+ // 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(NewFD->getLocation(), diag::note_multiversioning_caused_here);
+ return true;
+ }
+
+ if (std::distance(NewFD->attr_begin(), NewFD->attr_end()) != 1) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_no_other_attrs);
+ return true;
+ }
+
+ if (NewFD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support)
+ << FuncTemplates;
+ return true;
+ }
+
+
+ if (const auto *NewCXXFD = dyn_cast<CXXMethodDecl>(NewFD)) {
+ if (NewCXXFD->isVirtual()) {
+ S.Diag(NewCXXFD->getLocation(), diag::err_multiversion_doesnt_support)
+ << VirtFuncs;
+ return true;
+ }
+
+ if (const auto *NewCXXCtor = dyn_cast<CXXConstructorDecl>(NewFD)) {
+ S.Diag(NewCXXCtor->getLocation(), diag::err_multiversion_doesnt_support)
+ << Constructors;
+ return true;
+ }
+
+ if (const auto *NewCXXDtor = dyn_cast<CXXDestructorDecl>(NewFD)) {
+ S.Diag(NewCXXDtor->getLocation(), diag::err_multiversion_doesnt_support)
+ << Destructors;
+ return true;
+ }
+ }
+
+ if (NewFD->isDeleted()) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support)
+ << DeletedFuncs;
+ }
+ if (NewFD->isDefaulted()) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support)
+ << DefaultedFuncs;
+ }
+
+ QualType NewQType = S.getASTContext().getCanonicalType(NewFD->getType());
+ const auto *NewType = cast<FunctionType>(NewQType);
+ QualType NewReturnType = NewType->getReturnType();
+
+ if (NewReturnType->isUndeducedType()) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support)
+ << DeducedReturn;
+ return true;
+ }
+
+ // Only allow transition to MultiVersion if it hasn't been used.
+ if (OldFD && CausesMV && OldFD->isUsed(false)) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_after_used);
+ return true;
+ }
+
+ // Ensure the return type is identical.
+ if (OldFD) {
+ QualType OldQType = S.getASTContext().getCanonicalType(OldFD->getType());
+ const auto *OldType = cast<FunctionType>(OldQType);
+ FunctionType::ExtInfo OldTypeInfo = OldType->getExtInfo();
+ FunctionType::ExtInfo NewTypeInfo = NewType->getExtInfo();
+
+ if (OldTypeInfo.getCC() != NewTypeInfo.getCC()) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << CallingConv;
+ return true;
+ }
+
+ QualType OldReturnType = OldType->getReturnType();
+
+ if (OldReturnType != NewReturnType) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << ReturnType;
+ return true;
+ }
+
+ if (OldFD->isConstexpr() != NewFD->isConstexpr()) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_diff)
+ << ConstexprSpec;
+ return true;
+ }
+
+ if (OldFD->isInlineSpecified() != NewFD->isInlineSpecified()) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << InlineSpec;
+ return true;
+ }
+
+ if (OldFD->getStorageClass() != NewFD->getStorageClass()) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << StorageClass;
+ return true;
+ }
+
+ if (OldFD->isExternC() != NewFD->isExternC()) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << Linkage;
+ return true;
+ }
+
+ if (S.CheckEquivalentExceptionSpec(
+ OldFD->getType()->getAs<FunctionProtoType>(), OldFD->getLocation(),
+ NewFD->getType()->getAs<FunctionProtoType>(), NewFD->getLocation()))
+ return true;
+ }
+ return false;
+}
+
+/// \brief 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>();
+ if (NewFD->isMain()) {
+ if (NewTA && NewTA->isDefaultVersion()) {
+ S.Diag(NewFD->getLocation(), diag::err_multiversion_not_allowed_on_main);
+ NewFD->isInvalidDecl();
+ return true;
+ }
+ 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);
+ return true;
+ }
+
+ NewFD->setIsMultiVersion();
+ }
+ return false;
+ }
+
+ if (OldDecl->getDeclContext()->getRedeclContext() !=
+ NewFD->getDeclContext()->getRedeclContext())
+ 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();
+ return true;
+ }
+
+ TargetAttr::ParsedTargetAttr NewParsed = NewTA->parse();
+ // Sort order doesn't matter, it just needs to be consistent.
+ std::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);
+ 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();
+ return false;
+ }
+
+ bool UseMemberUsingDeclRules =
+ S.CurContext->isRecord() && !NewFD->getFriendObjectKind();
+
+ // Next, check ALL non-overloads to see if this is a redeclaration of a
+ // previous member of the MultiVersion set.
+ for (NamedDecl *ND : Previous) {
+ FunctionDecl *CurFD = ND->getAsFunction();
+ if (!CurFD)
+ continue;
+ 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;
+ }
+
+ 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, this is simply a non-redecl case.
+ if (CheckMultiVersionValue(S, NewFD)) {
+ NewFD->setInvalidDecl();
+ return true;
+ }
+
+ if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, false)) {
+ NewFD->setInvalidDecl();
+ return true;
+ }
+
+ NewFD->setIsMultiVersion();
+ Redeclaration = false;
+ MergeTypeWithPrevious = false;
+ OldDecl = nullptr;
+ Previous.clear();
+ return false;
+}
+
/// \brief Perform semantic checking of a new function declaration.
///
/// Performs semantic analysis of the new function declaration
@@ -9244,6 +9597,10 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
}
}
+ if (CheckMultiVersionFunction(*this, NewFD, Redeclaration, OldDecl,
+ MergeTypeWithPrevious, Previous))
+ return Redeclaration;
+
// C++11 [dcl.constexpr]p8:
// A constexpr specifier for a non-static member function that is not
// a constructor declares that member function to be const.
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 05434903221..b760cdcd34e 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5957,6 +5957,13 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
Candidate.IgnoreObjectArgument = false;
Candidate.ExplicitCallArguments = Args.size();
+ if (Function->isMultiVersion() &&
+ !Function->getAttr<TargetAttr>()->isDefaultVersion()) {
+ Candidate.Viable = false;
+ Candidate.FailureKind = ovl_non_default_multiversion_function;
+ return;
+ }
+
if (Constructor) {
// C++ [class.copy]p3:
// A member function template is never instantiated to perform the copy
@@ -6581,6 +6588,12 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
+
+ if (Method->isMultiVersion() &&
+ !Method->getAttr<TargetAttr>()->isDefaultVersion()) {
+ Candidate.Viable = false;
+ Candidate.FailureKind = ovl_non_default_multiversion_function;
+ }
}
/// \brief Add a C++ member function template as a candidate to the candidate
@@ -6984,6 +6997,12 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion,
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
+
+ if (Conversion->isMultiVersion() &&
+ !Conversion->getAttr<TargetAttr>()->isDefaultVersion()) {
+ Candidate.Viable = false;
+ Candidate.FailureKind = ovl_non_default_multiversion_function;
+ }
}
/// \brief Adds a conversion function template specialization
@@ -9392,6 +9411,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())
+ return;
std::string FnDesc;
OverloadCandidateKind K = ClassifyOverloadCandidate(*this, Found, Fn, FnDesc);
@@ -10193,6 +10214,9 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
assert(!Available);
break;
}
+ case ovl_non_default_multiversion_function:
+ // Do nothing, these should simply be ignored.
+ break;
}
}
@@ -10929,6 +10953,12 @@ private:
if (FunctionDecl *Caller = dyn_cast<FunctionDecl>(S.CurContext))
if (!Caller->isImplicit() && !S.IsAllowedCUDACall(Caller, FunDecl))
return false;
+ if (FunDecl->isMultiVersion()) {
+ const auto *TA = FunDecl->getAttr<TargetAttr>();
+ assert(TA && "Multiversioned functions require a target attribute");
+ if (!TA->isDefaultVersion())
+ return false;
+ }
// If any candidate has a placeholder return type, trigger its deduction
// now.
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 0cf9afb0759..f3eab2e6d78 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -792,6 +792,7 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
FD->IsConstexpr = Record.readInt();
FD->UsesSEHTry = Record.readInt();
FD->HasSkippedBody = Record.readInt();
+ FD->IsMultiVersion = Record.readInt();
FD->IsLateTemplateParsed = Record.readInt();
FD->setCachedLinkage(Linkage(Record.readInt()));
FD->EndRangeLoc = ReadSourceLocation();
@@ -2818,6 +2819,21 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) {
CtorY->getInheritedConstructor().getConstructor()))
return false;
}
+
+ if (FuncX->isMultiVersion() != FuncY->isMultiVersion())
+ return false;
+
+ // Multiversioned functions with different feature strings are represented
+ // as separate declarations.
+ if (FuncX->isMultiVersion()) {
+ const auto *TAX = FuncX->getAttr<TargetAttr>();
+ const auto *TAY = FuncY->getAttr<TargetAttr>();
+ assert(TAX && TAY && "Multiversion Function without target attribute");
+
+ if (TAX->getFeaturesStr() != TAY->getFeaturesStr())
+ return false;
+ }
+
ASTContext &C = FuncX->getASTContext();
if (!C.hasSameType(FuncX->getType(), FuncY->getType())) {
// We can get functions with different types on the redecl chain in C++17
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 341f0e16075..c2c6fd44d78 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -534,6 +534,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
Record.push_back(D->IsConstexpr);
Record.push_back(D->UsesSEHTry);
Record.push_back(D->HasSkippedBody);
+ Record.push_back(D->IsMultiVersion);
Record.push_back(D->IsLateTemplateParsed);
Record.push_back(D->getLinkageInternal());
Record.AddSourceLocation(D->getLocEnd());
@@ -2072,6 +2073,7 @@ void ASTWriter::WriteDeclAbbrevs() {
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Constexpr
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // UsesSEHTry
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // SkippedBody
+ Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // MultiVersion
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // LateParsed
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LocEnd
OpenPOWER on IntegriCloud