Ensure target-multiversioning emits deferred declarations

As reported in PR50025, sometimes we would end up not emitting functions
needed by inline multiversioned variants. This is because we typically
use the 'deferred decl' mechanism to emit these.  However, the variants
are emitted after that typically happens.  This fixes that by ensuring
we re-run deferred decls after this happens. Also, the multiversion
emission is done recursively to ensure that MV functions that require
other MV functions to be emitted get emitted.
This commit is contained in:
Erich Keane 2021-04-20 07:35:57 -07:00
parent 83a25a1010
commit 0ed613612c
2 changed files with 37 additions and 1 deletions

View File

@ -3189,7 +3189,9 @@ TargetMVPriority(const TargetInfo &TI,
}
void CodeGenModule::emitMultiVersionFunctions() {
for (GlobalDecl GD : MultiVersionFuncs) {
std::vector<GlobalDecl> MVFuncsToEmit;
MultiVersionFuncs.swap(MVFuncsToEmit);
for (GlobalDecl GD : MVFuncsToEmit) {
SmallVector<CodeGenFunction::MultiVersionResolverOption, 10> Options;
const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
getContext().forEachMultiversionedFunctionVersion(
@ -3243,6 +3245,17 @@ void CodeGenModule::emitMultiVersionFunctions() {
CodeGenFunction CGF(*this);
CGF.EmitMultiVersionResolver(ResolverFunc, Options);
}
// Ensure that any additions to the deferred decls list caused by emitting a
// variant are emitted. This can happen when the variant itself is inline and
// calls a function without linkage.
if (!MVFuncsToEmit.empty())
EmitDeferred();
// Ensure that any additions to the multiversion funcs list from either the
// deferred decls or the multiversion functions themselves are emitted.
if (!MultiVersionFuncs.empty())
emitMultiVersionFunctions();
}
void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) {

View File

@ -66,6 +66,16 @@ __attribute__((target("avx,sse4.2"))) inline void foo_used(int i, double d) {}
__attribute__((target("default"))) inline void foo_used2(int i, double d) {}
__attribute__((target("avx,sse4.2"), used)) inline void foo_used2(int i, double d) {}
// PR50025:
static void must_be_emitted(void) {}
inline __attribute__((target("default"))) void pr50025(void) { must_be_emitted(); }
void calls_pr50025() { pr50025(); }
// Also need to make sure we get other multiversion functions.
inline __attribute__((target("default"))) void pr50025b(void) { must_be_emitted(); }
inline __attribute__((target("default"))) void pr50025c(void) { pr50025b(); }
void calls_pr50025c() { pr50025c(); }
// LINUX: @llvm.compiler.used = appending global [2 x i8*] [i8* bitcast (void (i32, double)* @foo_used to i8*), i8* bitcast (void (i32, double)* @foo_used2.avx_sse4.2 to i8*)], section "llvm.metadata"
// WINDOWS: @llvm.used = appending global [2 x i8*] [i8* bitcast (void (i32, double)* @foo_used to i8*), i8* bitcast (void (i32, double)* @foo_used2.avx_sse4.2 to i8*)], section "llvm.metadata"
@ -300,3 +310,16 @@ __attribute__((target("avx,sse4.2"), used)) inline void foo_used2(int i, double
// WINDOWS: define linkonce_odr dso_local void @foo_multi.avx_sse4.2(i32 %{{[^,]+}}, double %{{[^\)]+}})
// WINDOWS: define linkonce_odr dso_local void @foo_multi.fma4_sse4.2(i32 %{{[^,]+}}, double %{{[^\)]+}})
// WINDOWS: define linkonce_odr dso_local void @foo_multi.arch_ivybridge_fma4_sse4.2(i32 %{{[^,]+}}, double %{{[^\)]+}})
// Ensure that we emit the 'static' function here.
// LINUX: define linkonce void @pr50025()
// LINUX: call void @must_be_emitted
// LINUX: define internal void @must_be_emitted()
// WINDOWS: define linkonce_odr dso_local void @pr50025()
// WINDOWS: call void @must_be_emitted
// WINDOWS: define internal void @must_be_emitted()
// LINUX: define linkonce void @pr50025c()
// LINUX: define linkonce void @pr50025b()
// WINDOWS: define linkonce_odr dso_local void @pr50025c()
// WINDOWS: define linkonce_odr dso_local void @pr50025b()