diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/ASTContext.cpp | 17 | ||||
-rw-r--r-- | clang/lib/Basic/Targets/X86.cpp | 58 | ||||
-rw-r--r-- | clang/lib/Basic/Targets/X86.h | 7 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenFunction.cpp | 54 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenFunction.h | 24 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenModule.cpp | 199 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenModule.h | 12 | ||||
-rw-r--r-- | clang/lib/Sema/Sema.cpp | 6 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 357 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 30 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTReaderDecl.cpp | 16 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTWriterDecl.cpp | 2 |
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 |