Skip to content

Commit 63585c5

Browse files
committed
[FMV] Allow multi versioning without default declaration.
This was a limitation which has now been lifted upon request. Please read the thread below for more details: #84405 (comment) Basically it allows to separate versioned implementations across different TUs without having to share private header files which contain the default declaration. If a resolver is required (because there is a caller in the TU), then a default declaration is implicitly generated.
1 parent 6a49501 commit 63585c5

File tree

8 files changed

+336
-217
lines changed

8 files changed

+336
-217
lines changed

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 63 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4090,77 +4090,80 @@ llvm::GlobalValue::LinkageTypes getMultiversionLinkage(CodeGenModule &CGM,
40904090
return llvm::GlobalValue::WeakODRLinkage;
40914091
}
40924092

4093+
static FunctionDecl *createDefaultTargetVersionFrom(const FunctionDecl *FD) {
4094+
DeclContext *DeclCtx = FD->getASTContext().getTranslationUnitDecl();
4095+
TypeSourceInfo *TInfo = FD->getTypeSourceInfo();
4096+
StorageClass SC = FD->getStorageClass();
4097+
DeclarationName Name = FD->getNameInfo().getName();
4098+
4099+
FunctionDecl *NewDecl =
4100+
FunctionDecl::Create(FD->getASTContext(), DeclCtx, FD->getBeginLoc(),
4101+
FD->getEndLoc(), Name, TInfo->getType(), TInfo, SC);
4102+
4103+
NewDecl->setIsMultiVersion();
4104+
NewDecl->addAttr(TargetVersionAttr::CreateImplicit(
4105+
NewDecl->getASTContext(), "default", NewDecl->getSourceRange()));
4106+
4107+
return NewDecl;
4108+
}
4109+
40934110
void CodeGenModule::emitMultiVersionFunctions() {
40944111
std::vector<GlobalDecl> MVFuncsToEmit;
40954112
MultiVersionFuncs.swap(MVFuncsToEmit);
40964113
for (GlobalDecl GD : MVFuncsToEmit) {
40974114
const auto *FD = cast<FunctionDecl>(GD.getDecl());
40984115
assert(FD && "Expected a FunctionDecl");
40994116

4100-
bool EmitResolver = !FD->isTargetVersionMultiVersion();
4117+
auto createFunction = [&](const FunctionDecl *Decl, unsigned MVIdx = 0) {
4118+
GlobalDecl CurGD{Decl->isDefined() ? Decl->getDefinition() : Decl, MVIdx};
4119+
StringRef MangledName = getMangledName(CurGD);
4120+
llvm::Constant *Func = GetGlobalValue(MangledName);
4121+
if (!Func) {
4122+
if (Decl->isDefined()) {
4123+
EmitGlobalFunctionDefinition(CurGD, nullptr);
4124+
Func = GetGlobalValue(MangledName);
4125+
} else {
4126+
const CGFunctionInfo &FI = getTypes().arrangeGlobalDeclaration(CurGD);
4127+
llvm::FunctionType *Ty = getTypes().GetFunctionType(FI);
4128+
Func = GetAddrOfFunction(CurGD, Ty, /*ForVTable=*/false,
4129+
/*DontDefer=*/false, ForDefinition);
4130+
}
4131+
assert(Func && "This should have just been created");
4132+
}
4133+
return Func;
4134+
};
4135+
4136+
bool HasDefaultDecl = !FD->isTargetVersionMultiVersion();
4137+
bool ShouldEmitResolver = !FD->isTargetVersionMultiVersion();
41014138
SmallVector<CodeGenFunction::MultiVersionResolverOption, 10> Options;
41024139
if (FD->isTargetMultiVersion()) {
41034140
getContext().forEachMultiversionedFunctionVersion(
4104-
FD, [this, &GD, &Options, &EmitResolver](const FunctionDecl *CurFD) {
4105-
GlobalDecl CurGD{
4106-
(CurFD->isDefined() ? CurFD->getDefinition() : CurFD)};
4107-
StringRef MangledName = getMangledName(CurGD);
4108-
llvm::Constant *Func = GetGlobalValue(MangledName);
4109-
if (!Func) {
4110-
if (CurFD->isDefined()) {
4111-
EmitGlobalFunctionDefinition(CurGD, nullptr);
4112-
Func = GetGlobalValue(MangledName);
4113-
} else {
4114-
const CGFunctionInfo &FI =
4115-
getTypes().arrangeGlobalDeclaration(GD);
4116-
llvm::FunctionType *Ty = getTypes().GetFunctionType(FI);
4117-
Func = GetAddrOfFunction(CurGD, Ty, /*ForVTable=*/false,
4118-
/*DontDefer=*/false, ForDefinition);
4119-
}
4120-
assert(Func && "This should have just been created");
4121-
}
4122-
if (CurFD->getMultiVersionKind() == MultiVersionKind::Target) {
4123-
const auto *TA = CurFD->getAttr<TargetAttr>();
4124-
llvm::SmallVector<StringRef, 8> Feats;
4141+
FD, [&](const FunctionDecl *CurFD) {
4142+
llvm::SmallVector<StringRef, 8> Feats;
4143+
llvm::Constant *Func = createFunction(CurFD);
4144+
4145+
if (const auto *TA = CurFD->getAttr<TargetAttr>()) {
41254146
TA->getAddedFeatures(Feats);
41264147
Options.emplace_back(cast<llvm::Function>(Func),
41274148
TA->getArchitecture(), Feats);
4128-
} else {
4129-
const auto *TVA = CurFD->getAttr<TargetVersionAttr>();
4130-
if (CurFD->isUsed() || (TVA->isDefaultVersion() &&
4131-
CurFD->doesThisDeclarationHaveABody()))
4132-
EmitResolver = true;
4133-
llvm::SmallVector<StringRef, 8> Feats;
4149+
} else if (const auto *TVA = CurFD->getAttr<TargetVersionAttr>()) {
4150+
bool HasDefaultDef = TVA->isDefaultVersion() &&
4151+
CurFD->doesThisDeclarationHaveABody();
4152+
HasDefaultDecl |= TVA->isDefaultVersion();
4153+
ShouldEmitResolver |= (CurFD->isUsed() || HasDefaultDef);
41344154
TVA->getFeatures(Feats);
41354155
Options.emplace_back(cast<llvm::Function>(Func),
41364156
/*Architecture*/ "", Feats);
4137-
}
4157+
} else
4158+
llvm_unreachable("unexpected MultiVersionKind");
41384159
});
4139-
} else if (FD->isTargetClonesMultiVersion()) {
4140-
const auto *TC = FD->getAttr<TargetClonesAttr>();
4141-
for (unsigned VersionIndex = 0; VersionIndex < TC->featuresStrs_size();
4142-
++VersionIndex) {
4143-
if (!TC->isFirstOfVersion(VersionIndex))
4160+
} else if (const auto *TC = FD->getAttr<TargetClonesAttr>()) {
4161+
for (unsigned I = 0; I < TC->featuresStrs_size(); ++I) {
4162+
if (!TC->isFirstOfVersion(I))
41444163
continue;
4145-
GlobalDecl CurGD{(FD->isDefined() ? FD->getDefinition() : FD),
4146-
VersionIndex};
4147-
StringRef Version = TC->getFeatureStr(VersionIndex);
4148-
StringRef MangledName = getMangledName(CurGD);
4149-
llvm::Constant *Func = GetGlobalValue(MangledName);
4150-
if (!Func) {
4151-
if (FD->isDefined()) {
4152-
EmitGlobalFunctionDefinition(CurGD, nullptr);
4153-
Func = GetGlobalValue(MangledName);
4154-
} else {
4155-
const CGFunctionInfo &FI =
4156-
getTypes().arrangeGlobalDeclaration(CurGD);
4157-
llvm::FunctionType *Ty = getTypes().GetFunctionType(FI);
4158-
Func = GetAddrOfFunction(CurGD, Ty, /*ForVTable=*/false,
4159-
/*DontDefer=*/false, ForDefinition);
4160-
}
4161-
assert(Func && "This should have just been created");
4162-
}
41634164

4165+
llvm::Constant *Func = createFunction(FD, I);
4166+
StringRef Version = TC->getFeatureStr(I);
41644167
StringRef Architecture;
41654168
llvm::SmallVector<StringRef, 1> Feature;
41664169

@@ -4185,9 +4188,16 @@ void CodeGenModule::emitMultiVersionFunctions() {
41854188
continue;
41864189
}
41874190

4188-
if (!EmitResolver)
4191+
if (!ShouldEmitResolver)
41894192
continue;
41904193

4194+
if (!HasDefaultDecl) {
4195+
FunctionDecl *NewFD = createDefaultTargetVersionFrom(FD);
4196+
llvm::Constant *Func = createFunction(NewFD);
4197+
llvm::SmallVector<StringRef, 1> Feats;
4198+
Options.emplace_back(cast<llvm::Function>(Func), "", Feats);
4199+
}
4200+
41914201
llvm::Constant *ResolverConstant = GetOrCreateMultiVersionResolver(GD);
41924202
if (auto *IFunc = dyn_cast<llvm::GlobalIFunc>(ResolverConstant)) {
41934203
ResolverConstant = IFunc->getResolver();

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11447,9 +11447,9 @@ static bool CheckMultiVersionFirstFunction(Sema &S, FunctionDecl *FD) {
1144711447
"Function lacks multiversion attribute");
1144811448
const auto *TA = FD->getAttr<TargetAttr>();
1144911449
const auto *TVA = FD->getAttr<TargetVersionAttr>();
11450-
// Target and target_version only causes MV if it is default, otherwise this
11451-
// is a normal function.
11452-
if ((TA && !TA->isDefaultVersion()) || (TVA && !TVA->isDefaultVersion()))
11450+
// The target attribute only causes MV if this declaration is the default,
11451+
// otherwise it is treated as a normal function.
11452+
if (TA && !TA->isDefaultVersion())
1145311453
return false;
1145411454

1145511455
if ((TA || TVA) && CheckMultiVersionValue(S, FD)) {

clang/lib/Sema/SemaOverload.cpp

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6865,6 +6865,32 @@ static bool IsAcceptableNonMemberOperatorCandidate(ASTContext &Context,
68656865
return false;
68666866
}
68676867

6868+
static bool isNonViableMultiVersionOverload(FunctionDecl *FD) {
6869+
if (FD->isTargetMultiVersionDefault())
6870+
return false;
6871+
6872+
if (!FD->getASTContext().getTargetInfo().getTriple().isAArch64())
6873+
return FD->isTargetMultiVersion();
6874+
6875+
if (!FD->isMultiVersion())
6876+
return false;
6877+
6878+
// Among multiple target versions consider either the default,
6879+
// or the first non-default in the absence of default version.
6880+
unsigned SeenAt = 0;
6881+
unsigned I = 0;
6882+
bool HasDefault = false;
6883+
FD->getASTContext().forEachMultiversionedFunctionVersion(
6884+
FD, [&](const FunctionDecl *CurFD) {
6885+
if (FD == CurFD)
6886+
SeenAt = I;
6887+
else if (CurFD->isTargetMultiVersionDefault())
6888+
HasDefault = true;
6889+
++I;
6890+
});
6891+
return HasDefault || SeenAt != 0;
6892+
}
6893+
68686894
/// AddOverloadCandidate - Adds the given function to the set of
68696895
/// candidate functions, using the given function call arguments. If
68706896
/// @p SuppressUserConversions, then don't allow user-defined
@@ -6970,11 +6996,7 @@ void Sema::AddOverloadCandidate(
69706996
}
69716997
}
69726998

6973-
if (Function->isMultiVersion() &&
6974-
((Function->hasAttr<TargetAttr>() &&
6975-
!Function->getAttr<TargetAttr>()->isDefaultVersion()) ||
6976-
(Function->hasAttr<TargetVersionAttr>() &&
6977-
!Function->getAttr<TargetVersionAttr>()->isDefaultVersion()))) {
6999+
if (isNonViableMultiVersionOverload(Function)) {
69787000
Candidate.Viable = false;
69797001
Candidate.FailureKind = ovl_non_default_multiversion_function;
69807002
return;
@@ -7637,11 +7659,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
76377659
return;
76387660
}
76397661

7640-
if (Method->isMultiVersion() &&
7641-
((Method->hasAttr<TargetAttr>() &&
7642-
!Method->getAttr<TargetAttr>()->isDefaultVersion()) ||
7643-
(Method->hasAttr<TargetVersionAttr>() &&
7644-
!Method->getAttr<TargetVersionAttr>()->isDefaultVersion()))) {
7662+
if (isNonViableMultiVersionOverload(Method)) {
76457663
Candidate.Viable = false;
76467664
Candidate.FailureKind = ovl_non_default_multiversion_function;
76477665
}
@@ -8127,11 +8145,7 @@ void Sema::AddConversionCandidate(
81278145
return;
81288146
}
81298147

8130-
if (Conversion->isMultiVersion() &&
8131-
((Conversion->hasAttr<TargetAttr>() &&
8132-
!Conversion->getAttr<TargetAttr>()->isDefaultVersion()) ||
8133-
(Conversion->hasAttr<TargetVersionAttr>() &&
8134-
!Conversion->getAttr<TargetVersionAttr>()->isDefaultVersion()))) {
8148+
if (isNonViableMultiVersionOverload(Conversion)) {
81358149
Candidate.Viable = false;
81368150
Candidate.FailureKind = ovl_non_default_multiversion_function;
81378151
}

0 commit comments

Comments
 (0)