Skip to content

Commit

Permalink
[FMV] Emit the resolver along with the default version definition.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
labrinea committed Mar 15, 2024
1 parent 8c4546f commit 2576857
Show file tree
Hide file tree
Showing 4 changed files with 628 additions and 238 deletions.
54 changes: 41 additions & 13 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3447,6 +3447,9 @@ bool CodeGenModule::MayBeEmittedEagerly(const ValueDecl *Global) {
// Implicit template instantiations may change linkage if they are later
// explicitly instantiated, so they should not be emitted eagerly.
return false;
// Defer until all versions have been semantically checked.
if (FD->hasAttr<TargetVersionAttr>() && !FD->isMultiVersion())
return false;
}
if (const auto *VD = dyn_cast<VarDecl>(Global)) {
if (Context.getInlineVariableDefinitionKind(VD) ==
Expand Down Expand Up @@ -3995,10 +3998,13 @@ void CodeGenModule::EmitMultiVersionFunctionDefinition(GlobalDecl GD,
EmitGlobalFunctionDefinition(GD.getWithMultiVersionIndex(I), nullptr);
// Ensure that the resolver function is also emitted.
GetOrCreateMultiVersionResolver(GD);
} else if (FD->hasAttr<TargetVersionAttr>()) {
GetOrCreateMultiVersionResolver(GD);
} else
EmitGlobalFunctionDefinition(GD, GV);

// Defer the resolver emission until we can reason whether the TU
// contains a default target version implementation.
if (FD->isTargetVersionMultiVersion())
AddDeferredMultiVersionResolverToEmit(GD);
}

void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD, llvm::GlobalValue *GV) {
Expand Down Expand Up @@ -4091,10 +4097,11 @@ void CodeGenModule::emitMultiVersionFunctions() {
const auto *FD = cast<FunctionDecl>(GD.getDecl());
assert(FD && "Expected a FunctionDecl");

bool EmitResolver = !FD->isTargetVersionMultiVersion();
SmallVector<CodeGenFunction::MultiVersionResolverOption, 10> Options;
if (FD->isTargetMultiVersion()) {
getContext().forEachMultiversionedFunctionVersion(
FD, [this, &GD, &Options](const FunctionDecl *CurFD) {
FD, [this, &GD, &Options, &EmitResolver](const FunctionDecl *CurFD) {
GlobalDecl CurGD{
(CurFD->isDefined() ? CurFD->getDefinition() : CurFD)};
StringRef MangledName = getMangledName(CurGD);
Expand All @@ -4120,6 +4127,9 @@ void CodeGenModule::emitMultiVersionFunctions() {
TA->getArchitecture(), Feats);
} else {
const auto *TVA = CurFD->getAttr<TargetVersionAttr>();
if (CurFD->isUsed() || (TVA->isDefaultVersion() &&
CurFD->doesThisDeclarationHaveABody()))
EmitResolver = true;
llvm::SmallVector<StringRef, 8> Feats;
TVA->getFeatures(Feats);
Options.emplace_back(cast<llvm::Function>(Func),
Expand Down Expand Up @@ -4175,22 +4185,27 @@ void CodeGenModule::emitMultiVersionFunctions() {
continue;
}

if (!EmitResolver)
continue;

llvm::Constant *ResolverConstant = GetOrCreateMultiVersionResolver(GD);
if (auto *IFunc = dyn_cast<llvm::GlobalIFunc>(ResolverConstant)) {
ResolverConstant = IFunc->getResolver();
if (FD->isTargetClonesMultiVersion() ||
FD->isTargetVersionMultiVersion()) {
const CGFunctionInfo &FI = getTypes().arrangeGlobalDeclaration(GD);
llvm::FunctionType *DeclTy = getTypes().GetFunctionType(FI);
std::string MangledName = getMangledNameImpl(
*this, GD, FD, /*OmitMultiVersionMangling=*/true);
// In prior versions of Clang, the mangling for ifuncs incorrectly
// included an .ifunc suffix. This alias is generated for backward
// compatibility. It is deprecated, and may be removed in the future.
auto *Alias = llvm::GlobalAlias::create(
DeclTy, 0, getMultiversionLinkage(*this, GD),
MangledName + ".ifunc", IFunc, &getModule());
SetCommonAttributes(FD, Alias);
if (!GetGlobalValue(MangledName + ".ifunc")) {
const CGFunctionInfo &FI = getTypes().arrangeGlobalDeclaration(GD);
llvm::FunctionType *DeclTy = getTypes().GetFunctionType(FI);
// In prior versions of Clang, the mangling for ifuncs incorrectly
// included an .ifunc suffix. This alias is generated for backward
// compatibility. It is deprecated, and may be removed in the future.
auto *Alias = llvm::GlobalAlias::create(
DeclTy, 0, getMultiversionLinkage(*this, GD),
MangledName + ".ifunc", IFunc, &getModule());
SetCommonAttributes(FD, Alias);
}
}
}
llvm::Function *ResolverFunc = cast<llvm::Function>(ResolverConstant);
Expand Down Expand Up @@ -4347,6 +4362,19 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) {
}
}

/// Adds a declaration to the list of multi version functions if not present.
void CodeGenModule::AddDeferredMultiVersionResolverToEmit(GlobalDecl GD) {
const auto *FD = cast<FunctionDecl>(GD.getDecl());
assert(FD && "Not a FunctionDecl?");

if (FD->isTargetVersionMultiVersion()) {
StringRef NamePrefix = getMangledName(GD).rsplit('.').first;
if (!DeferredResolversToEmit.insert(NamePrefix).second)
return;
}
MultiVersionFuncs.push_back(GD);
}

/// If a dispatcher for the specified mangled name is not in the module, create
/// and return an llvm Function with the specified type.
llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) {
Expand Down Expand Up @@ -4386,7 +4414,7 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) {
// The resolver needs to be created. For target and target_clones, defer
// creation until the end of the TU.
if (FD->isTargetMultiVersion() || FD->isTargetClonesMultiVersion())
MultiVersionFuncs.push_back(GD);
AddDeferredMultiVersionResolverToEmit(GD);

// For cpu_specific, don't create an ifunc yet because we don't know if the
// cpu_dispatch will be emitted in this translation unit.
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,8 @@ class CodeGenModule : public CodeGenTypeCache {
/// yet.
llvm::DenseMap<StringRef, GlobalDecl> DeferredDecls;

llvm::DenseSet<StringRef> DeferredResolversToEmit;

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

// Adds a declaration to the list of multi version functions if not present.
void AddDeferredMultiVersionResolverToEmit(GlobalDecl GD);

// References to multiversion functions are resolved through an implicitly
// defined resolver function. This function is responsible for creating
// the resolver symbol for the provided declaration. The value returned
Expand Down
Loading

0 comments on commit 2576857

Please sign in to comment.