From e3b10525b489b604d6a1e540be78bda80afb5868 Mon Sep 17 00:00:00 2001 From: Erich Keane Date: Thu, 16 Sep 2021 09:57:54 -0700 Subject: [PATCH] Make multiversioning work with internal linkage We previously made all multiversioning resolvers/ifuncs have weak ODR linkage in IR, since we NEED to emit the whole resolver every time we see a call, but it is not necessarily the place where all the definitions live. HOWEVER, when doing so, we neglected the case where the versions have internal linkage. This patch ensures we do this, so you don't get weird behavior with static functions. --- clang/lib/CodeGen/CodeGenModule.cpp | 28 +++++++++---- .../test/CodeGen/unique-internal-linkage-names.cpp | 4 +- .../multi-versioning-internal-linkage.cpp | 49 ++++++++++++++++++++++ 3 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 clang/test/CodeGenCXX/multi-versioning-internal-linkage.cpp diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index b274edb..9715657 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3258,6 +3258,19 @@ TargetMVPriority(const TargetInfo &TI, return Priority; } +// Multiversion functions should be at most 'WeakODRLinkage' so that a different +// TU can forward declare the function without causing problems. Particularly +// in the cases of CPUDispatch, this causes issues. This also makes sure we +// work with internal linkage functions, so that the same function name can be +// used with internal linkage in multiple TUs. +llvm::GlobalValue::LinkageTypes getMultiversionLinkage(CodeGenModule &CGM, + GlobalDecl GD) { + const FunctionDecl *FD = cast(GD.getDecl()); + if (FD->getFormalLinkage() == InternalLinkage) + return llvm::GlobalValue::InternalLinkage; + return llvm::GlobalValue::WeakODRLinkage; +} + void CodeGenModule::emitMultiVersionFunctions() { std::vector MVFuncsToEmit; MultiVersionFuncs.swap(MVFuncsToEmit); @@ -3298,7 +3311,7 @@ void CodeGenModule::emitMultiVersionFunctions() { if (TI.supportsIFunc() || FD->isTargetMultiVersion()) { ResolverFunc = cast( GetGlobalValue((getMangledName(GD) + ".resolver").str())); - ResolverFunc->setLinkage(llvm::Function::WeakODRLinkage); + ResolverFunc->setLinkage(getMultiversionLinkage(*this, GD)); } else { ResolverFunc = cast(GetGlobalValue(getMangledName(GD))); } @@ -3356,7 +3369,7 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) { auto *ResolverFunc = cast(GetOrCreateLLVMFunction( ResolverName, ResolverType, ResolverGD, /*ForVTable=*/false)); - ResolverFunc->setLinkage(llvm::Function::WeakODRLinkage); + ResolverFunc->setLinkage(getMultiversionLinkage(*this, GD)); if (supportsCOMDAT()) ResolverFunc->setComdat( getModule().getOrInsertComdat(ResolverFunc->getName())); @@ -3433,9 +3446,9 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) { auto *IFunc = cast(GetOrCreateLLVMFunction( AliasName, DeclTy, GD, /*ForVTable=*/false, /*DontDefer=*/true, /*IsThunk=*/false, llvm::AttributeList(), NotForDefinition)); - auto *GA = llvm::GlobalAlias::create( - DeclTy, 0, getFunctionLinkage(GD), AliasName, IFunc, &getModule()); - GA->setLinkage(llvm::Function::WeakODRLinkage); + auto *GA = llvm::GlobalAlias::create(DeclTy, 0, + getMultiversionLinkage(*this, GD), + AliasName, IFunc, &getModule()); SetCommonAttributes(GD, GA); } } @@ -3474,8 +3487,9 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver( llvm::Constant *Resolver = GetOrCreateLLVMFunction( MangledName + ".resolver", ResolverType, GlobalDecl{}, /*ForVTable=*/false); - llvm::GlobalIFunc *GIF = llvm::GlobalIFunc::create( - DeclTy, 0, llvm::Function::WeakODRLinkage, "", Resolver, &getModule()); + llvm::GlobalIFunc *GIF = + llvm::GlobalIFunc::create(DeclTy, 0, getMultiversionLinkage(*this, GD), + "", Resolver, &getModule()); GIF->setName(ResolverName); SetCommonAttributes(FD, GIF); diff --git a/clang/test/CodeGen/unique-internal-linkage-names.cpp b/clang/test/CodeGen/unique-internal-linkage-names.cpp index 95591de..65069c0 100644 --- a/clang/test/CodeGen/unique-internal-linkage-names.cpp +++ b/clang/test/CodeGen/unique-internal-linkage-names.cpp @@ -59,7 +59,7 @@ void test() { // PLAIN: @_ZN12_GLOBAL__N_16anon_mE = internal global // PLAIN: define internal i32 @_ZL3foov() // PLAIN: define internal i32 @_ZN12_GLOBAL__N_14getMEv -// PLAIN: define weak_odr i32 ()* @_ZL4mverv.resolver() +// PLAIN: define internal i32 ()* @_ZL4mverv.resolver() // PLAIN: define internal void @_ZN12_GLOBAL__N_11AC1Ev // PLAIN: define internal void @_ZN12_GLOBAL__N_11AD1Ev // PLAIN: define internal i32 @_ZL4mverv() @@ -70,7 +70,7 @@ void test() { // UNIQUE: @_ZN12_GLOBAL__N_16anon_mE = internal global // UNIQUE: define internal i32 @_ZL3foov.[[MODHASH:__uniq.[0-9]+]]() #[[#ATTR:]] { // UNIQUE: define internal i32 @_ZN12_GLOBAL__N_14getMEv.[[MODHASH]] -// UNIQUE: define weak_odr i32 ()* @_ZL4mverv.[[MODHASH]].resolver() +// UNIQUE: define internal i32 ()* @_ZL4mverv.[[MODHASH]].resolver() // UNIQUE: define internal void @_ZN12_GLOBAL__N_11AC1Ev.__uniq.68358509610070717889884130747296293671 // UNIQUE: define internal void @_ZN12_GLOBAL__N_11AD1Ev.__uniq.68358509610070717889884130747296293671 // UNIQUE: define internal i32 @_ZL4mverv.[[MODHASH]]() diff --git a/clang/test/CodeGenCXX/multi-versioning-internal-linkage.cpp b/clang/test/CodeGenCXX/multi-versioning-internal-linkage.cpp new file mode 100644 index 0000000..246599a --- /dev/null +++ b/clang/test/CodeGenCXX/multi-versioning-internal-linkage.cpp @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=LINUX +// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows-pc -emit-llvm %s -o - | FileCheck %s --check-prefix=WINDOWS + +__attribute__((target("default"))) static int static_target() { + return 0; +} +__attribute__((target("arch=sandybridge"))) static int static_target() { + return 1; +} + +__attribute__((cpu_dispatch(atom, generic))) static int static_dispatch() {} + +namespace { +int __attribute__((target("default"))) anon_target() { + return 0; +} +int __attribute__((target("arch=sandybridge"))) anon_target() { + return 1; +} +__attribute__((cpu_dispatch(atom, generic))) static int anon_dispatch() {} +} + + + +int usage() { + return static_target() + anon_target() + static_dispatch() + anon_dispatch(); +} + +// Ensure that these are all 'internal'. Windows doesn't have ifuncs or aliases, +// since Windows doesn't support ifuncs. +// aliases: +// LINUX: @_ZL15static_dispatchv = internal alias i32 (), i32 ()* @_ZL15static_dispatchv.ifunc +// LINUX: @_ZN12_GLOBAL__N_113anon_dispatchEv = internal alias i32 (), i32 ()* @_ZN12_GLOBAL__N_113anon_dispatchEv.ifunc + +// ifuncs: +// LINUX: @_ZL15static_dispatchv.ifunc = internal ifunc i32 (), i32 ()* ()* @_ZL15static_dispatchv.resolver +// LINUX: @_ZN12_GLOBAL__N_113anon_dispatchEv.ifunc = internal ifunc i32 (), i32 ()* ()* @_ZN12_GLOBAL__N_113anon_dispatchEv.resolver +// LINUX: @_ZL13static_targetv.ifunc = internal ifunc i32 (), i32 ()* ()* @_ZL13static_targetv.resolver +// LINUX: @_ZN12_GLOBAL__N_111anon_targetEv.ifunc = internal ifunc i32 (), i32 ()* ()* @_ZN12_GLOBAL__N_111anon_targetEv.resolver + +// resolvers: +// LINUX: define internal i32 ()* @_ZL15static_dispatchv.resolver() +// WINDOWS: define internal i32 @"?static_dispatch@@YAHXZ"() +// LINUX: define internal i32 ()* @_ZN12_GLOBAL__N_113anon_dispatchEv.resolver() +// WINDOWS: define internal i32 @"?anon_dispatch@?A0x7F72A7FB@@YAHXZ"() +// LINUX: define internal i32 ()* @_ZL13static_targetv.resolver() +// WINDOWS: define internal i32 @"?static_target@@YAHXZ.resolver"() +// LINUX: define internal i32 ()* @_ZN12_GLOBAL__N_111anon_targetEv.resolver() +// WINDOWS: define internal i32 @"?anon_target@?A0x7F72A7FB@@YAHXZ.resolver"() -- 2.7.4