Skip to content

Commit e6b5bd5

Browse files
authored
[FMV] Emit the resolver along with the default version definition. (#84405)
We would like the resolver to be generated eagerly, even if the versioned function is not called from the current translation unit. Fixes #81494. It further allows Multi Versioning to work even if the default target version attribute is omitted from function declarations.
1 parent 070d1e8 commit e6b5bd5

File tree

4 files changed

+629
-238
lines changed

4 files changed

+629
-238
lines changed

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3449,6 +3449,9 @@ bool CodeGenModule::MayBeEmittedEagerly(const ValueDecl *Global) {
34493449
// Implicit template instantiations may change linkage if they are later
34503450
// explicitly instantiated, so they should not be emitted eagerly.
34513451
return false;
3452+
// Defer until all versions have been semantically checked.
3453+
if (FD->hasAttr<TargetVersionAttr>() && !FD->isMultiVersion())
3454+
return false;
34523455
}
34533456
if (const auto *VD = dyn_cast<VarDecl>(Global)) {
34543457
if (Context.getInlineVariableDefinitionKind(VD) ==
@@ -3997,10 +4000,13 @@ void CodeGenModule::EmitMultiVersionFunctionDefinition(GlobalDecl GD,
39974000
EmitGlobalFunctionDefinition(GD.getWithMultiVersionIndex(I), nullptr);
39984001
// Ensure that the resolver function is also emitted.
39994002
GetOrCreateMultiVersionResolver(GD);
4000-
} else if (FD->hasAttr<TargetVersionAttr>()) {
4001-
GetOrCreateMultiVersionResolver(GD);
40024003
} else
40034004
EmitGlobalFunctionDefinition(GD, GV);
4005+
4006+
// Defer the resolver emission until we can reason whether the TU
4007+
// contains a default target version implementation.
4008+
if (FD->isTargetVersionMultiVersion())
4009+
AddDeferredMultiVersionResolverToEmit(GD);
40044010
}
40054011

40064012
void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD, llvm::GlobalValue *GV) {
@@ -4093,10 +4099,11 @@ void CodeGenModule::emitMultiVersionFunctions() {
40934099
const auto *FD = cast<FunctionDecl>(GD.getDecl());
40944100
assert(FD && "Expected a FunctionDecl");
40954101

4102+
bool EmitResolver = !FD->isTargetVersionMultiVersion();
40964103
SmallVector<CodeGenFunction::MultiVersionResolverOption, 10> Options;
40974104
if (FD->isTargetMultiVersion()) {
40984105
getContext().forEachMultiversionedFunctionVersion(
4099-
FD, [this, &GD, &Options](const FunctionDecl *CurFD) {
4106+
FD, [this, &GD, &Options, &EmitResolver](const FunctionDecl *CurFD) {
41004107
GlobalDecl CurGD{
41014108
(CurFD->isDefined() ? CurFD->getDefinition() : CurFD)};
41024109
StringRef MangledName = getMangledName(CurGD);
@@ -4122,6 +4129,9 @@ void CodeGenModule::emitMultiVersionFunctions() {
41224129
TA->getArchitecture(), Feats);
41234130
} else {
41244131
const auto *TVA = CurFD->getAttr<TargetVersionAttr>();
4132+
if (CurFD->isUsed() || (TVA->isDefaultVersion() &&
4133+
CurFD->doesThisDeclarationHaveABody()))
4134+
EmitResolver = true;
41254135
llvm::SmallVector<StringRef, 8> Feats;
41264136
TVA->getFeatures(Feats);
41274137
Options.emplace_back(cast<llvm::Function>(Func),
@@ -4177,22 +4187,27 @@ void CodeGenModule::emitMultiVersionFunctions() {
41774187
continue;
41784188
}
41794189

4190+
if (!EmitResolver)
4191+
continue;
4192+
41804193
llvm::Constant *ResolverConstant = GetOrCreateMultiVersionResolver(GD);
41814194
if (auto *IFunc = dyn_cast<llvm::GlobalIFunc>(ResolverConstant)) {
41824195
ResolverConstant = IFunc->getResolver();
41834196
if (FD->isTargetClonesMultiVersion() ||
41844197
FD->isTargetVersionMultiVersion()) {
4185-
const CGFunctionInfo &FI = getTypes().arrangeGlobalDeclaration(GD);
4186-
llvm::FunctionType *DeclTy = getTypes().GetFunctionType(FI);
41874198
std::string MangledName = getMangledNameImpl(
41884199
*this, GD, FD, /*OmitMultiVersionMangling=*/true);
4189-
// In prior versions of Clang, the mangling for ifuncs incorrectly
4190-
// included an .ifunc suffix. This alias is generated for backward
4191-
// compatibility. It is deprecated, and may be removed in the future.
4192-
auto *Alias = llvm::GlobalAlias::create(
4193-
DeclTy, 0, getMultiversionLinkage(*this, GD),
4194-
MangledName + ".ifunc", IFunc, &getModule());
4195-
SetCommonAttributes(FD, Alias);
4200+
if (!GetGlobalValue(MangledName + ".ifunc")) {
4201+
const CGFunctionInfo &FI = getTypes().arrangeGlobalDeclaration(GD);
4202+
llvm::FunctionType *DeclTy = getTypes().GetFunctionType(FI);
4203+
// In prior versions of Clang, the mangling for ifuncs incorrectly
4204+
// included an .ifunc suffix. This alias is generated for backward
4205+
// compatibility. It is deprecated, and may be removed in the future.
4206+
auto *Alias = llvm::GlobalAlias::create(
4207+
DeclTy, 0, getMultiversionLinkage(*this, GD),
4208+
MangledName + ".ifunc", IFunc, &getModule());
4209+
SetCommonAttributes(FD, Alias);
4210+
}
41964211
}
41974212
}
41984213
llvm::Function *ResolverFunc = cast<llvm::Function>(ResolverConstant);
@@ -4349,6 +4364,20 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) {
43494364
}
43504365
}
43514366

4367+
/// Adds a declaration to the list of multi version functions if not present.
4368+
void CodeGenModule::AddDeferredMultiVersionResolverToEmit(GlobalDecl GD) {
4369+
const auto *FD = cast<FunctionDecl>(GD.getDecl());
4370+
assert(FD && "Not a FunctionDecl?");
4371+
4372+
if (FD->isTargetVersionMultiVersion()) {
4373+
std::string MangledName =
4374+
getMangledNameImpl(*this, GD, FD, /*OmitMultiVersionMangling=*/true);
4375+
if (!DeferredResolversToEmit.insert(MangledName).second)
4376+
return;
4377+
}
4378+
MultiVersionFuncs.push_back(GD);
4379+
}
4380+
43524381
/// If a dispatcher for the specified mangled name is not in the module, create
43534382
/// and return an llvm Function with the specified type.
43544383
llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) {
@@ -4388,7 +4417,7 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) {
43884417
// The resolver needs to be created. For target and target_clones, defer
43894418
// creation until the end of the TU.
43904419
if (FD->isTargetMultiVersion() || FD->isTargetClonesMultiVersion())
4391-
MultiVersionFuncs.push_back(GD);
4420+
AddDeferredMultiVersionResolverToEmit(GD);
43924421

43934422
// For cpu_specific, don't create an ifunc yet because we don't know if the
43944423
// cpu_dispatch will be emitted in this translation unit.

clang/lib/CodeGen/CodeGenModule.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,8 @@ class CodeGenModule : public CodeGenTypeCache {
348348
/// yet.
349349
llvm::DenseMap<StringRef, GlobalDecl> DeferredDecls;
350350

351+
llvm::StringSet<llvm::BumpPtrAllocator> DeferredResolversToEmit;
352+
351353
/// This is a list of deferred decls which we have seen that *are* actually
352354
/// referenced. These get code generated when the module is done.
353355
std::vector<GlobalDecl> DeferredDeclsToEmit;
@@ -1588,6 +1590,9 @@ class CodeGenModule : public CodeGenTypeCache {
15881590
llvm::AttributeList ExtraAttrs = llvm::AttributeList(),
15891591
ForDefinition_t IsForDefinition = NotForDefinition);
15901592

1593+
// Adds a declaration to the list of multi version functions if not present.
1594+
void AddDeferredMultiVersionResolverToEmit(GlobalDecl GD);
1595+
15911596
// References to multiversion functions are resolved through an implicitly
15921597
// defined resolver function. This function is responsible for creating
15931598
// the resolver symbol for the provided declaration. The value returned

0 commit comments

Comments
 (0)