58 changes: 58 additions & 0 deletions clang/lib/Basic/Targets/X86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Basic/Targets/X86.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -256,6 +258,11 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {
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 {
Expand Down
54 changes: 54 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
24 changes: 24 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -3988,6 +3988,29 @@ class CodeGenFunction : public CodeGenTypeCache {

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

Expand All @@ -4004,6 +4027,7 @@ class CodeGenFunction : public CodeGenTypeCache {
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
Expand Down
199 changes: 175 additions & 24 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ void CodeGenModule::Release() {
applyGlobalValReplacements();
applyReplacements();
checkAliases();
emitMultiVersionFunctions();
EmitCXXGlobalInitFunc();
EmitCXXGlobalDtorFunc();
EmitCXXThreadLocalInitFunc();
Expand Down Expand Up @@ -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.");
Expand All @@ -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();
}

Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ class CodeGenModule : public CodeGenTypeCache {
/// 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;

Expand Down Expand Up @@ -1247,6 +1251,12 @@ class CodeGenModule : public CodeGenTypeCache {
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,
Expand Down Expand Up @@ -1319,6 +1329,8 @@ class CodeGenModule : public CodeGenTypeCache {

void checkAliases();

void emitMultiVersionFunctions();

/// Emit any vtables which we deferred and still have a use for.
void EmitDeferredVTables();

Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
357 changes: 357 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
30 changes: 30 additions & 0 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -10929,6 +10953,12 @@ class AddressOfFunctionResolver {
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.
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Serialization/ASTWriterDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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
Expand Down
32 changes: 32 additions & 0 deletions clang/test/CodeGen/attr-target-mv-func-ptrs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
int __attribute__((target("sse4.2"))) foo(int i) { return 0; }
int __attribute__((target("arch=sandybridge"))) foo(int);
int __attribute__((target("arch=ivybridge"))) foo(int i) {return 1;}
int __attribute__((target("default"))) foo(int i) { return 2; }

typedef int (*FuncPtr)(int);
void func(FuncPtr);

int bar() {
func(foo);
FuncPtr Free = &foo;
FuncPtr Free2 = foo;

return 0;
return Free(1) + Free(2);
}

// CHECK: @foo.ifunc = ifunc i32 (i32), i32 (i32)* ()* @foo.resolver
// CHECK: define i32 @foo.sse4.2(
// CHECK: ret i32 0
// CHECK: define i32 @foo.arch_ivybridge(
// CHECK: ret i32 1
// CHECK: define i32 @foo(
// CHECK: ret i32 2

// CHECK: define i32 @bar()
// CHECK: call void @func(i32 (i32)* @foo.ifunc)
// CHECK: store i32 (i32)* @foo.ifunc
// CHECK: store i32 (i32)* @foo.ifunc

// CHECK: declare i32 @foo.arch_sandybridge(
26 changes: 26 additions & 0 deletions clang/test/CodeGen/attr-target-mv-va-args.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
int __attribute__((target("sse4.2"))) foo(int i, ...) { return 0; }
int __attribute__((target("arch=sandybridge"))) foo(int i, ...);
int __attribute__((target("arch=ivybridge"))) foo(int i, ...) {return 1;}
int __attribute__((target("default"))) foo(int i, ...) { return 2; }

int bar() {
return foo(1, 'a', 1.1) + foo(2, 2.2, "asdf");
}

// CHECK: @foo.ifunc = ifunc i32 (i32, ...), i32 (i32, ...)* ()* @foo.resolver
// CHECK: define i32 @foo.sse4.2(i32 %i, ...)
// CHECK: ret i32 0
// CHECK: define i32 @foo.arch_ivybridge(i32 %i, ...)
// CHECK: ret i32 1
// CHECK: define i32 @foo(i32 %i, ...)
// CHECK: ret i32 2
// CHECK: define i32 @bar()
// CHECK: call i32 (i32, ...) @foo.ifunc(i32 1, i32 97, double
// CHECK: call i32 (i32, ...) @foo.ifunc(i32 2, double 2.2{{[0-9Ee+]+}}, i8* getelementptr inbounds
// CHECK: define i32 (i32, ...)* @foo.resolver()
// CHECK: ret i32 (i32, ...)* @foo.arch_sandybridge
// CHECK: ret i32 (i32, ...)* @foo.arch_ivybridge
// CHECK: ret i32 (i32, ...)* @foo.sse4.2
// CHECK: ret i32 (i32, ...)* @foo
// CHECK: declare i32 @foo.arch_sandybridge(i32, ...)
79 changes: 79 additions & 0 deletions clang/test/CodeGen/attr-target-mv.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
int __attribute__((target("sse4.2"))) foo(void) { return 0; }
int __attribute__((target("arch=sandybridge"))) foo(void);
int __attribute__((target("arch=ivybridge"))) foo(void) {return 1;}
int __attribute__((target("default"))) foo(void) { return 2; }

int bar() {
return foo();
}

inline int __attribute__((target("sse4.2"))) foo_inline(void) { return 0; }
inline int __attribute__((target("arch=sandybridge"))) foo_inline(void);
inline int __attribute__((target("arch=ivybridge"))) foo_inline(void) {return 1;}
inline int __attribute__((target("default"))) foo_inline(void) { return 2; }

int bar2() {
return foo_inline();
}

inline __attribute__((target("default"))) void foo_decls(void);
inline __attribute__((target("sse4.2"))) void foo_decls(void);
void bar3() {
foo_decls();
}
inline __attribute__((target("default"))) void foo_decls(void) {}
inline __attribute__((target("sse4.2"))) void foo_decls(void) {}

// CHECK: @foo.ifunc = ifunc i32 (), i32 ()* ()* @foo.resolver
// CHECK: @foo_inline.ifunc = ifunc i32 (), i32 ()* ()* @foo_inline.resolver
// CHECK: @foo_decls.ifunc = ifunc void (), void ()* ()* @foo_decls.resolver

// CHECK: define i32 @foo.sse4.2()
// CHECK: ret i32 0
// CHECK: define i32 @foo.arch_ivybridge()
// CHECK: ret i32 1
// CHECK: define i32 @foo()
// CHECK: ret i32 2
// CHECK: define i32 @bar()
// CHECK: call i32 @foo.ifunc()

// CHECK: define i32 ()* @foo.resolver()
// CHECK: call void @__cpu_indicator_init()
// CHECK: ret i32 ()* @foo.arch_sandybridge
// CHECK: ret i32 ()* @foo.arch_ivybridge
// CHECK: ret i32 ()* @foo.sse4.2
// CHECK: ret i32 ()* @foo

// CHECK: define i32 @bar2()
// CHECK: call i32 @foo_inline.ifunc()

// CHECK: define i32 ()* @foo_inline.resolver()
// CHECK: call void @__cpu_indicator_init()
// CHECK: ret i32 ()* @foo_inline.arch_sandybridge
// CHECK: ret i32 ()* @foo_inline.arch_ivybridge
// CHECK: ret i32 ()* @foo_inline.sse4.2
// CHECK: ret i32 ()* @foo_inline

// CHECK: define void @bar3()
// CHECK: call void @foo_decls.ifunc()

// CHECK: define void ()* @foo_decls.resolver()
// CHECK: ret void ()* @foo_decls.sse4.2
// CHECK: ret void ()* @foo_decls

// CHECK: declare i32 @foo.arch_sandybridge()

// CHECK: define available_externally i32 @foo_inline.sse4.2()
// CHECK: ret i32 0

// CHECK: declare i32 @foo_inline.arch_sandybridge()
//
// CHECK: define available_externally i32 @foo_inline.arch_ivybridge()
// CHECK: ret i32 1
// CHECK: define available_externally i32 @foo_inline()
// CHECK: ret i32 2

// CHECK: define available_externally void @foo_decls()
// CHECK: define available_externally void @foo_decls.sse4.2()

54 changes: 54 additions & 0 deletions clang/test/CodeGenCXX/attr-target-mv-diff-ns.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
// Test ensures that this properly differentiates between types in different
// namespaces.
int __attribute__((target("sse4.2"))) foo(int) { return 0; }
int __attribute__((target("arch=sandybridge"))) foo(int);
int __attribute__((target("arch=ivybridge"))) foo(int) {return 1;}
int __attribute__((target("default"))) foo(int) { return 2; }

namespace ns {
int __attribute__((target("sse4.2"))) foo(int) { return 0; }
int __attribute__((target("arch=sandybridge"))) foo(int);
int __attribute__((target("arch=ivybridge"))) foo(int) {return 1;}
int __attribute__((target("default"))) foo(int) { return 2; }
}

int bar() {
return foo(1) + ns::foo(2);
}

// CHECK: @_Z3fooi.ifunc = ifunc i32 (i32), i32 (i32)* ()* @_Z3fooi.resolver
// CHECK: @_ZN2ns3fooEi.ifunc = ifunc i32 (i32), i32 (i32)* ()* @_ZN2ns3fooEi.resolver

// CHECK: define i32 @_Z3fooi.sse4.2(i32)
// CHECK: ret i32 0
// CHECK: define i32 @_Z3fooi.arch_ivybridge(i32)
// CHECK: ret i32 1
// CHECK: define i32 @_Z3fooi(i32)
// CHECK: ret i32 2

// CHECK: define i32 @_ZN2ns3fooEi.sse4.2(i32)
// CHECK: ret i32 0
// CHECK: define i32 @_ZN2ns3fooEi.arch_ivybridge(i32)
// CHECK: ret i32 1
// CHECK: define i32 @_ZN2ns3fooEi(i32)
// CHECK: ret i32 2

// CHECK: define i32 @_Z3barv()
// CHECK: call i32 @_Z3fooi.ifunc(i32 1)
// CHECK: call i32 @_ZN2ns3fooEi.ifunc(i32 2)

// CHECK: define i32 (i32)* @_Z3fooi.resolver()
// CHECK: ret i32 (i32)* @_Z3fooi.arch_sandybridge
// CHECK: ret i32 (i32)* @_Z3fooi.arch_ivybridge
// CHECK: ret i32 (i32)* @_Z3fooi.sse4.2
// CHECK: ret i32 (i32)* @_Z3fooi
//
// CHECK: define i32 (i32)* @_ZN2ns3fooEi.resolver()
// CHECK: ret i32 (i32)* @_ZN2ns3fooEi.arch_sandybridge
// CHECK: ret i32 (i32)* @_ZN2ns3fooEi.arch_ivybridge
// CHECK: ret i32 (i32)* @_ZN2ns3fooEi.sse4.2
// CHECK: ret i32 (i32)* @_ZN2ns3fooEi

// CHECK: declare i32 @_Z3fooi.arch_sandybridge(i32)
// CHECK: declare i32 @_ZN2ns3fooEi.arch_sandybridge(i32)
45 changes: 45 additions & 0 deletions clang/test/CodeGenCXX/attr-target-mv-func-ptrs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
void temp();
void temp(int);
using FP = void(*)(int);
void b() {
FP f = temp;
}

int __attribute__((target("sse4.2"))) foo(int) { return 0; }
int __attribute__((target("arch=sandybridge"))) foo(int);
int __attribute__((target("arch=ivybridge"))) foo(int) {return 1;}
int __attribute__((target("default"))) foo(int) { return 2; }

struct S {
int __attribute__((target("sse4.2"))) foo(int) { return 0; }
int __attribute__((target("arch=sandybridge"))) foo(int);
int __attribute__((target("arch=ivybridge"))) foo(int) {return 1;}
int __attribute__((target("default"))) foo(int) { return 2; }
};

using FuncPtr = int (*)(int);
using MemFuncPtr = int (S::*)(int);

void f(FuncPtr, MemFuncPtr);

int bar() {
FuncPtr Free = &foo;
MemFuncPtr Member = &S::foo;
S s;
f(foo, &S::foo);
return Free(1) + (s.*Member)(2);
}


// CHECK: @_Z3fooi.ifunc
// CHECK: @_ZN1S3fooEi.ifunc

// CHECK: define i32 @_Z3barv()
// Store to Free of ifunc
// CHECK: store i32 (i32)* @_Z3fooi.ifunc
// Store to Member of ifunc
// CHECK: store { i64, i64 } { i64 ptrtoint (i32 (%struct.S*, i32)* @_ZN1S3fooEi.ifunc to i64), i64 0 }, { i64, i64 }* [[MEMBER:%[a-z]+]]

// Call to 'f' with the ifunc
// CHECK: call void @_Z1fPFiiEM1SFiiE(i32 (i32)* @_Z3fooi.ifunc
137 changes: 137 additions & 0 deletions clang/test/CodeGenCXX/attr-target-mv-member-funcs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s

struct S {
int __attribute__((target("sse4.2"))) foo(int) { return 0; }
int __attribute__((target("arch=sandybridge"))) foo(int);
int __attribute__((target("arch=ivybridge"))) foo(int) { return 1; }
int __attribute__((target("default"))) foo(int) { return 2; }

S &__attribute__((target("arch=ivybridge"))) operator=(const S &) {
return *this;
}
S &__attribute__((target("default"))) operator=(const S &) {
return *this;
}
};

struct ConvertTo {
__attribute__((target("arch=ivybridge"))) operator S() const {
return S{};
}
__attribute__((target("default"))) operator S() const {
return S{};
}
};

int bar() {
S s;
S s2;
s2 = s;

ConvertTo C;
s2 = static_cast<S>(C);

return s.foo(0);
}

struct S2 {
int __attribute__((target("sse4.2"))) foo(int);
int __attribute__((target("arch=sandybridge"))) foo(int);
int __attribute__((target("arch=ivybridge"))) foo(int);
int __attribute__((target("default"))) foo(int);
};

int bar2() {
S2 s;
return s.foo(0);
}

int __attribute__((target("sse4.2"))) S2::foo(int) { return 0; }
int __attribute__((target("arch=ivybridge"))) S2::foo(int) { return 1; }
int __attribute__((target("default"))) S2::foo(int) { return 2; }

template<typename T>
struct templ {
int __attribute__((target("sse4.2"))) foo(int) { return 0; }
int __attribute__((target("arch=sandybridge"))) foo(int);
int __attribute__((target("arch=ivybridge"))) foo(int) { return 1; }
int __attribute__((target("default"))) foo(int) { return 2; }
};

int templ_use() {
templ<int> a;
templ<double> b;
return a.foo(1) + b.foo(2);
}

// CHECK: @_ZN1SaSERKS_.ifunc = ifunc %struct.S* (%struct.S*, %struct.S*), %struct.S* (%struct.S*, %struct.S*)* ()* @_ZN1SaSERKS_.resolver
// CHECK: @_ZNK9ConvertTocv1SEv.ifunc = ifunc void (%struct.ConvertTo*), void (%struct.ConvertTo*)* ()* @_ZNK9ConvertTocv1SEv.resolver
// CHECK: @_ZN1S3fooEi.ifunc = ifunc i32 (%struct.S*, i32), i32 (%struct.S*, i32)* ()* @_ZN1S3fooEi.resolver
// CHECK: @_ZN2S23fooEi.ifunc = ifunc i32 (%struct.S2*, i32), i32 (%struct.S2*, i32)* ()* @_ZN2S23fooEi.resolver
// Templates:
// CHECK: @_ZN5templIiE3fooEi.ifunc = ifunc i32 (%struct.templ*, i32), i32 (%struct.templ*, i32)* ()* @_ZN5templIiE3fooEi.resolver
// CHECK: @_ZN5templIdE3fooEi.ifunc = ifunc i32 (%struct.templ.0*, i32), i32 (%struct.templ.0*, i32)* ()* @_ZN5templIdE3fooEi.resolver

// CHECK: define i32 @_Z3barv()
// CHECK: %s = alloca %struct.S, align 1
// CHECK: %s2 = alloca %struct.S, align 1
// CHECK: %C = alloca %struct.ConvertTo, align 1
// CHECK: call dereferenceable(1) %struct.S* @_ZN1SaSERKS_.ifunc(%struct.S* %s2
// CHECK: call void @_ZNK9ConvertTocv1SEv.ifunc(%struct.ConvertTo* %C)
// CHECK: call dereferenceable(1) %struct.S* @_ZN1SaSERKS_.ifunc(%struct.S* %s2
// CHECK: call i32 @_ZN1S3fooEi.ifunc(%struct.S* %s, i32 0)

// CHECK: define %struct.S* (%struct.S*, %struct.S*)* @_ZN1SaSERKS_.resolver()
// CHECK: ret %struct.S* (%struct.S*, %struct.S*)* @_ZN1SaSERKS_.arch_ivybridge
// CHECK: ret %struct.S* (%struct.S*, %struct.S*)* @_ZN1SaSERKS_

// CHECK: define void (%struct.ConvertTo*)* @_ZNK9ConvertTocv1SEv.resolver()
// CHECK: ret void (%struct.ConvertTo*)* @_ZNK9ConvertTocv1SEv.arch_ivybridge
// CHECK: ret void (%struct.ConvertTo*)* @_ZNK9ConvertTocv1SEv

// CHECK: define i32 (%struct.S*, i32)* @_ZN1S3fooEi.resolver()
// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.arch_sandybridge
// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.arch_ivybridge
// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.sse4.2
// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi

// CHECK: define i32 @_Z4bar2v()
// CHECK:call i32 @_ZN2S23fooEi.ifunc
// define i32 (%struct.S2*, i32)* @_ZN2S23fooEi.resolver()
// CHECK: ret i32 (%struct.S2*, i32)* @_ZN2S23fooEi.arch_sandybridge
// CHECK: ret i32 (%struct.S2*, i32)* @_ZN2S23fooEi.arch_ivybridge
// CHECK: ret i32 (%struct.S2*, i32)* @_ZN2S23fooEi.sse4.2
// CHECK: ret i32 (%struct.S2*, i32)* @_ZN2S23fooEi

// CHECK: define i32 @_ZN2S23fooEi.sse4.2(%struct.S2* %this, i32)
// CHECK: define i32 @_ZN2S23fooEi.arch_ivybridge(%struct.S2* %this, i32)
// CHECK: define i32 @_ZN2S23fooEi(%struct.S2* %this, i32)

// CHECK: define i32 @_Z9templ_usev()
// CHECK: call i32 @_ZN5templIiE3fooEi.ifunc
// CHECK: call i32 @_ZN5templIdE3fooEi.ifunc


// CHECK: define i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi.resolver()
// CHECK: ret i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi.arch_sandybridge
// CHECK: ret i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi.arch_ivybridge
// CHECK: ret i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi.sse4.2
// CHECK: ret i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi
//
// CHECK: define i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi.resolver()
// CHECK: ret i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi.arch_sandybridge
// CHECK: ret i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi.arch_ivybridge
// CHECK: ret i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi.sse4.2
// CHECK: ret i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi

// CHECK: define linkonce_odr i32 @_ZN1S3fooEi.sse4.2(%struct.S* %this, i32)
// CHECK: ret i32 0

// CHECK: declare i32 @_ZN1S3fooEi.arch_sandybridge(%struct.S*, i32)

// CHECK: define linkonce_odr i32 @_ZN1S3fooEi.arch_ivybridge(%struct.S* %this, i32)
// CHECK: ret i32 1

// CHECK: define linkonce_odr i32 @_ZN1S3fooEi(%struct.S* %this, i32)
// CHECK: ret i32 2

29 changes: 29 additions & 0 deletions clang/test/CodeGenCXX/attr-target-mv-modules.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -fmodules -emit-llvm %s -o - | FileCheck %s
#pragma clang module build A
module A {}
#pragma clang module contents
#pragma clang module begin A
__attribute__((target("default"))) void f();
__attribute__((target("sse4.2"))) void f();
#pragma clang module end
#pragma clang module endbuild

#pragma clang module build B
module B {}
#pragma clang module contents
#pragma clang module begin B
__attribute__((target("default"))) void f();
__attribute__((target("sse4.2"))) void f();
#pragma clang module end
#pragma clang module endbuild

#pragma clang module import A
#pragma clang module import B
void g() { f(); }

// Negative tests to validate that the resolver only calls each 1x.
// CHECK: define void ()* @_Z1fv.resolver
// CHECK: ret void ()* @_Z1fv.sse4.2
// CHECK-NOT: ret void ()* @_Z1fv.sse4.2
// CHECK: ret void ()* @_Z1fv
// CHECK-NOT: ret void ()* @_Z1fv
39 changes: 39 additions & 0 deletions clang/test/CodeGenCXX/attr-target-mv-out-of-line-defs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
struct S {
int __attribute__((target("sse4.2"))) foo(int);
int __attribute__((target("arch=sandybridge"))) foo(int);
int __attribute__((target("arch=ivybridge"))) foo(int);
int __attribute__((target("default"))) foo(int);
};

int __attribute__((target("default"))) S::foo(int) { return 2; }
int __attribute__((target("sse4.2"))) S::foo(int) { return 0; }
int __attribute__((target("arch=ivybridge"))) S::foo(int) { return 1; }

int bar() {
S s;
return s.foo(0);
}

// CHECK: @_ZN1S3fooEi.ifunc = ifunc i32 (%struct.S*, i32), i32 (%struct.S*, i32)* ()* @_ZN1S3fooEi.resolver

// CHECK: define i32 @_ZN1S3fooEi(%struct.S* %this, i32)
// CHECK: ret i32 2

// CHECK: define i32 @_ZN1S3fooEi.sse4.2(%struct.S* %this, i32)
// CHECK: ret i32 0

// CHECK: define i32 @_ZN1S3fooEi.arch_ivybridge(%struct.S* %this, i32)
// CHECK: ret i32 1

// CHECK: define i32 @_Z3barv()
// CHECK: %s = alloca %struct.S, align 1
// CHECK: %call = call i32 @_ZN1S3fooEi.ifunc(%struct.S* %s, i32 0)

// CHECK: define i32 (%struct.S*, i32)* @_ZN1S3fooEi.resolver()
// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.arch_sandybridge
// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.arch_ivybridge
// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.sse4.2
// CHECK: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi

// CHECK: declare i32 @_ZN1S3fooEi.arch_sandybridge(%struct.S*, i32)
50 changes: 50 additions & 0 deletions clang/test/CodeGenCXX/attr-target-mv-overloads.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s

int __attribute__((target("sse4.2"))) foo_overload(int) { return 0; }
int __attribute__((target("arch=sandybridge"))) foo_overload(int);
int __attribute__((target("arch=ivybridge"))) foo_overload(int) {return 1;}
int __attribute__((target("default"))) foo_overload(int) { return 2; }
int __attribute__((target("sse4.2"))) foo_overload(void) { return 0; }
int __attribute__((target("arch=sandybridge"))) foo_overload(void);
int __attribute__((target("arch=ivybridge"))) foo_overload(void) {return 1;}
int __attribute__((target("default"))) foo_overload(void) { return 2; }

int bar2() {
return foo_overload() + foo_overload(1);
}

// CHECK: @_Z12foo_overloadv.ifunc = ifunc i32 (), i32 ()* ()* @_Z12foo_overloadv.resolver
// CHECK: @_Z12foo_overloadi.ifunc = ifunc i32 (i32), i32 (i32)* ()* @_Z12foo_overloadi.resolver


// CHECK: define i32 @_Z12foo_overloadi.sse4.2(i32)
// CHECK: ret i32 0
// CHECK: define i32 @_Z12foo_overloadi.arch_ivybridge(i32)
// CHECK: ret i32 1
// CHECK: define i32 @_Z12foo_overloadi(i32)
// CHECK: ret i32 2
// CHECK: define i32 @_Z12foo_overloadv.sse4.2()
// CHECK: ret i32 0
// CHECK: define i32 @_Z12foo_overloadv.arch_ivybridge()
// CHECK: ret i32 1
// CHECK: define i32 @_Z12foo_overloadv()
// CHECK: ret i32 2

// CHECK: define i32 @_Z4bar2v()
// CHECK: call i32 @_Z12foo_overloadv.ifunc()
// CHECK: call i32 @_Z12foo_overloadi.ifunc(i32 1)

// CHECK: define i32 ()* @_Z12foo_overloadv.resolver()
// CHECK: ret i32 ()* @_Z12foo_overloadv.arch_sandybridge
// CHECK: ret i32 ()* @_Z12foo_overloadv.arch_ivybridge
// CHECK: ret i32 ()* @_Z12foo_overloadv.sse4.2
// CHECK: ret i32 ()* @_Z12foo_overloadv

// CHECK: define i32 (i32)* @_Z12foo_overloadi.resolver()
// CHECK: ret i32 (i32)* @_Z12foo_overloadi.arch_sandybridge
// CHECK: ret i32 (i32)* @_Z12foo_overloadi.arch_ivybridge
// CHECK: ret i32 (i32)* @_Z12foo_overloadi.sse4.2
// CHECK: ret i32 (i32)* @_Z12foo_overloadi

// CHECK: declare i32 @_Z12foo_overloadv.arch_sandybridge()
// CHECK: declare i32 @_Z12foo_overloadi.arch_sandybridge(i32)
10 changes: 10 additions & 0 deletions clang/test/Sema/attr-target-mv-bad-target.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// RUN: %clang_cc1 -triple x86_64-windows-pc -fsyntax-only -verify %s
// RUN: %clang_cc1 -triple arm-none-eabi -fsyntax-only -verify %s

int __attribute__((target("sse4.2"))) redecl1(void) { return 1; }
//expected-error@+2 {{function multiversioning is not supported on the current target}}
//expected-note@-2 {{previous declaration is here}}
int __attribute__((target("avx"))) redecl1(void) { return 2; }

//expected-error@+1 {{function multiversioning is not supported on the current target}}
int __attribute__((target("default"))) with_def(void) { return 1;}
103 changes: 103 additions & 0 deletions clang/test/Sema/attr-target-mv.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify %s

void __attribute__((target("sse4.2"))) no_default(void);
void __attribute__((target("arch=sandybridge"))) no_default(void);

void use1(void){
// expected-error@+1 {{no matching function for call to 'no_default'}}
no_default();
}

void __attribute__((target("sse4.2"))) has_def(void);
void __attribute__((target("default"))) has_def(void);

void use2(void){
// expected-error@+2 {{reference to overloaded function could not be resolved; did you mean to call it?}}
// expected-note@-4 {{possible target for call}}
+has_def;
}

int __attribute__((target("sse4.2"))) no_proto();
// expected-error@-1 {{multiversioned function must have a prototype}}
// expected-note@+1 {{function multiversioning caused by this declaration}}
int __attribute__((target("arch=sandybridge"))) no_proto();

// The following should all be legal, since they are just redeclarations.
int __attribute__((target("sse4.2"))) redecl1(void);
int __attribute__((target("sse4.2"))) redecl1(void) { return 1; }
int __attribute__((target("arch=sandybridge"))) redecl1(void) { return 2; }

int __attribute__((target("sse4.2"))) redecl2(void) { return 1; }
int __attribute__((target("sse4.2"))) redecl2(void);
int __attribute__((target("arch=sandybridge"))) redecl2(void) { return 2; }

int __attribute__((target("sse4.2"))) redecl3(void) { return 0; }
int __attribute__((target("arch=ivybridge"))) redecl3(void) { return 1; }
int __attribute__((target("arch=sandybridge"))) redecl3(void);
int __attribute__((target("arch=sandybridge"))) redecl3(void) { return 2; }

int __attribute__((target("sse4.2"))) redecl4(void) { return 1; }
int __attribute__((target("arch=sandybridge"))) redecl4(void) { return 2; }
int __attribute__((target("arch=sandybridge"))) redecl4(void);

int __attribute__((target("sse4.2"))) redef(void) { return 1; }
int __attribute__((target("arch=ivybridge"))) redef(void) { return 1; }
int __attribute__((target("arch=sandybridge"))) redef(void) { return 2; }
// expected-error@+2 {{redefinition of 'redef'}}
// expected-note@-2 {{previous definition is here}}
int __attribute__((target("arch=sandybridge"))) redef(void) { return 2; }

int __attribute__((target("default"))) redef2(void) { return 1;}
// expected-error@+2 {{redefinition of 'redef2'}}
// expected-note@-2 {{previous definition is here}}
int __attribute__((target("default"))) redef2(void) { return 1;}

int __attribute__((target("sse4.2"))) mv_after_use(void) { return 1; }
int use3(void) {
return mv_after_use();
}

// expected-error@+1 {{function declaration cannot become a multiversioned function after first usage}}
int __attribute__((target("arch=sandybridge"))) mv_after_use(void) { return 2; }

int __attribute__((target("sse4.2,arch=sandybridge"))) mangle(void) { return 1; }
//expected-error@+2 {{multiversioned function redeclarations require identical target attributes}}
//expected-note@-2 {{previous declaration is here}}
int __attribute__((target("arch=sandybridge,sse4.2"))) mangle(void) { return 2; }

int prev_no_target(void);
int __attribute__((target("arch=sandybridge"))) prev_no_target(void) { return 2; }
// expected-error@-2 {{function declaration is missing 'target' attribute in a multiversioned function}}
// expected-note@+1 {{function multiversioning caused by this declaration}}
int __attribute__((target("arch=ivybridge"))) prev_no_target(void) { return 2; }

int __attribute__((target("arch=sandybridge"))) prev_no_target2(void);
int prev_no_target2(void);
// expected-error@-1 {{function declaration is missing 'target' attribute in a multiversioned function}}
// expected-note@+1 {{function multiversioning caused by this declaration}}
int __attribute__((target("arch=ivybridge"))) prev_no_target2(void);

void __attribute__((target("sse4.2"))) addtl_attrs(void);
//expected-error@+1 {{attribute 'target' multiversioning cannot be combined}}
void __attribute__((used,target("arch=sandybridge"))) addtl_attrs(void);

//expected-error@+1 {{attribute 'target' multiversioning cannot be combined}}
void __attribute__((target("default"), used)) addtl_attrs2(void);

//expected-error@+2 {{attribute 'target' multiversioning cannot be combined}}
//expected-note@+2 {{function multiversioning caused by this declaration}}
void __attribute__((used,target("sse4.2"))) addtl_attrs3(void);
void __attribute__((target("arch=sandybridge"))) addtl_attrs3(void);

void __attribute__((target("sse4.2"))) addtl_attrs4(void);
void __attribute__((target("arch=sandybridge"))) addtl_attrs4(void);
//expected-error@+1 {{attribute 'target' multiversioning cannot be combined}}
void __attribute__((used,target("arch=ivybridge"))) addtl_attrs4(void);

int __attribute__((target("sse4.2"))) diff_cc(void);
// expected-error@+1 {{multiversioned function declaration has a different calling convention}}
__vectorcall int __attribute__((target("arch=sandybridge"))) diff_cc(void);

int __attribute__((target("sse4.2"))) diff_ret(void);
// expected-error@+1 {{multiversioned function declaration has a different return type}}
short __attribute__((target("arch=sandybridge"))) diff_ret(void);
178 changes: 178 additions & 0 deletions clang/test/SemaCXX/attr-target-mv.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify -fexceptions -fcxx-exceptions %s -std=c++14
void __attribute__((target("sse4.2"))) no_default(void);
void __attribute__((target("arch=sandybridge"))) no_default(void);

void use1(void){
// expected-error@+1 {{no matching function for call to 'no_default'}}
no_default();
}
constexpr int __attribute__((target("sse4.2"))) foo(void) { return 0; }
constexpr int __attribute__((target("arch=sandybridge"))) foo(void);
//expected-error@+1 {{multiversioned function declaration has a different constexpr specification}}
int __attribute__((target("arch=ivybridge"))) foo(void) {return 1;}
constexpr int __attribute__((target("default"))) foo(void) { return 2; }

int __attribute__((target("sse4.2"))) foo2(void) { return 0; }
//expected-error@+1 {{multiversioned function declaration has a different constexpr specification}}
constexpr int __attribute__((target("arch=sandybridge"))) foo2(void);
int __attribute__((target("arch=ivybridge"))) foo2(void) {return 1;}
int __attribute__((target("default"))) foo2(void) { return 2; }

static int __attribute__((target("sse4.2"))) bar(void) { return 0; }
static int __attribute__((target("arch=sandybridge"))) bar(void);
//expected-error@+1 {{multiversioned function declaration has a different storage class}}
int __attribute__((target("arch=ivybridge"))) bar(void) {return 1;}
static int __attribute__((target("default"))) bar(void) { return 2; }

int __attribute__((target("sse4.2"))) bar2(void) { return 0; }
//expected-error@+1 {{multiversioned function declaration has a different storage class}}
static int __attribute__((target("arch=sandybridge"))) bar2(void);
int __attribute__((target("arch=ivybridge"))) bar2(void) {return 1;}
int __attribute__((target("default"))) bar2(void) { return 2; }


inline int __attribute__((target("sse4.2"))) baz(void) { return 0; }
inline int __attribute__((target("arch=sandybridge"))) baz(void);
//expected-error@+1 {{multiversioned function declaration has a different inline specification}}
int __attribute__((target("arch=ivybridge"))) baz(void) {return 1;}
inline int __attribute__((target("default"))) baz(void) { return 2; }

int __attribute__((target("sse4.2"))) baz2(void) { return 0; }
//expected-error@+1 {{multiversioned function declaration has a different inline specification}}
inline int __attribute__((target("arch=sandybridge"))) baz2(void);
int __attribute__((target("arch=ivybridge"))) baz2(void) {return 1;}
int __attribute__((target("default"))) baz2(void) { return 2; }

float __attribute__((target("sse4.2"))) bock(void) { return 0; }
//expected-error@+1 {{multiversioned function declaration has a different return type}}
int __attribute__((target("arch=sandybridge"))) bock(void);
//expected-error@+1 {{multiversioned function declaration has a different return type}}
int __attribute__((target("arch=ivybridge"))) bock(void) {return 1;}
//expected-error@+1 {{multiversioned function declaration has a different return type}}
int __attribute__((target("default"))) bock(void) { return 2; }

int __attribute__((target("sse4.2"))) bock2(void) { return 0; }
//expected-error@+1 {{multiversioned function declaration has a different return type}}
float __attribute__((target("arch=sandybridge"))) bock2(void);
int __attribute__((target("arch=ivybridge"))) bock2(void) {return 1;}
int __attribute__((target("default"))) bock2(void) { return 2; }

auto __attribute__((target("sse4.2"))) bock3(void) -> int { return 0; }
//expected-error@+1 {{multiversioned function declaration has a different return type}}
auto __attribute__((target("arch=sandybridge"))) bock3(void) -> short { return (short)0;}

int __attribute__((target("sse4.2"))) bock4(void) noexcept(false) { return 0; }
//expected-error@+2 {{exception specification in declaration does not match previous declaration}}
//expected-note@-2 {{previous declaration is here}}
int __attribute__((target("arch=sandybridge"))) bock4(void) noexcept(true) { return 1;}

// FIXME: Add support for templates and virtual functions!
template<typename T>
int __attribute__((target("sse4.2"))) foo(T) { return 0; }
// expected-error@+2 {{multiversioned functions do not yet support function templates}}
template<typename T>
int __attribute__((target("arch=sandybridge"))) foo(T);

// expected-error@+2 {{multiversioned functions do not yet support function templates}}
template<typename T>
int __attribute__((target("default"))) foo(T) { return 2; }

struct S {
template<typename T>
int __attribute__((target("sse4.2"))) foo(T) { return 0; }
// expected-error@+2 {{multiversioned functions do not yet support function templates}}
template<typename T>
int __attribute__((target("arch=sandybridge"))) foo(T);

// expected-error@+2 {{multiversioned functions do not yet support function templates}}
template<typename T>
int __attribute__((target("default"))) foo(T) { return 2; }

// expected-error@+1 {{multiversioned functions do not yet support virtual functions}}
virtual void __attribute__((target("default"))) virt();
};

extern "C" {
int __attribute__((target("sse4.2"))) diff_mangle(void) { return 0; }
}
//expected-error@+1 {{multiversioned function declaration has a different linkage}}
int __attribute__((target("arch=sandybridge"))) diff_mangle(void) { return 0; }

// expected-error@+1 {{multiversioned functions do not yet support deduced return types}}
auto __attribute__((target("default"))) deduced_return(void) { return 0; }
// expected-error@-1 {{cannot initialize return object of type 'auto' with an rvalue of type 'int'}}

auto __attribute__((target("default"))) trailing_return(void)-> int { return 0; }

__attribute__((target("default"))) void DiffDecl();
namespace N {
using ::DiffDecl;
// expected-error@+3 {{declaration conflicts with target of using declaration already in scope}}
// expected-note@-4 {{target of using declaration}}
// expected-note@-3 {{using declaration}}
__attribute__((target("arch=sandybridge"))) void DiffDecl();
} // namespace N

struct SpecialFuncs {
// expected-error@+1 {{multiversioned functions do not yet support constructors}}
__attribute__((target("default"))) SpecialFuncs();
// expected-error@+1 {{multiversioned functions do not yet support destructors}}
__attribute__((target("default"))) ~SpecialFuncs();

// expected-error@+1 {{multiversioned functions do not yet support defaulted functions}}
SpecialFuncs& __attribute__((target("default"))) operator=(const SpecialFuncs&) = default;
// expected-error@+1 {{multiversioned functions do not yet support deleted functions}}
SpecialFuncs& __attribute__((target("default"))) operator=(SpecialFuncs&&) = delete;
};

class Secret {
int i = 0;
__attribute__((target("default")))
friend int SecretAccessor(Secret &s);
__attribute__((target("arch=sandybridge")))
friend int SecretAccessor(Secret &s);
};

__attribute__((target("default")))
int SecretAccessor(Secret &s) {
return s.i;
}

__attribute__((target("arch=sandybridge")))
int SecretAccessor(Secret &s) {
return s.i + 2;
}

__attribute__((target("arch=ivybridge")))
int SecretAccessor(Secret &s) {
//expected-error@+2{{'i' is a private member of 'Secret'}}
//expected-note@-20{{implicitly declared private here}}
return s.i + 3;
}

constexpr int __attribute__((target("sse4.2"))) constexpr_foo(void) {
return 0;
}
constexpr int __attribute__((target("arch=sandybridge"))) constexpr_foo(void);
constexpr int __attribute__((target("arch=ivybridge"))) constexpr_foo(void) {
return 1;
}
constexpr int __attribute__((target("default"))) constexpr_foo(void) {
return 2;
}

void constexpr_test() {
static_assert(foo() == 2, "Should call 'default' in a constexpr context");
}

struct BadOutOfLine {
int __attribute__((target("sse4.2"))) foo(int);
int __attribute__((target("default"))) foo(int);
};

int __attribute__((target("sse4.2"))) BadOutOfLine::foo(int) { return 0; }
int __attribute__((target("default"))) BadOutOfLine::foo(int) { return 1; }
// expected-error@+3 {{out-of-line definition of 'foo' does not match any declaration in 'BadOutOfLine'}}
// expected-note@-3 {{member declaration nearly matches}}
// expected-note@-3 {{member declaration nearly matches}}
int __attribute__((target("arch=atom"))) BadOutOfLine::foo(int) { return 1; }