Use comdats to avoid double initialization of weak data

Initializers of global data that can appear multiple TUs (static data
members of class templates or __declspec(selectany) data) are now in a
comdat group keyed on the global variable being initialized.  On
non-Windows platforms, this is a code size and startup time
optimization.  On Windows, this is necessary for ABI compatibility with
MSVC.

Fixes PR16959.

Reviewers: rsmith

Differential Revision: http://reviews.llvm.org/D3811

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@209555 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Reid Kleckner 2014-05-23 21:13:45 +00:00
parent 3ccca48c69
commit 8756959802
8 changed files with 78 additions and 51 deletions

View File

@ -294,10 +294,12 @@ CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,
// have unordered initialization.
//
// As a consequence, we can put them into their own llvm.global_ctors entry.
// This should allow GlobalOpt to fire more often, and allow us to implement
// the Microsoft C++ ABI, which uses COMDAT elimination to avoid double
// initializaiton.
AddGlobalCtor(Fn);
//
// In addition, put the initializer into a COMDAT group with the global
// being initialized. On most platforms, this is a minor startup time
// optimization. In the MS C++ ABI, there are no guard variables, so this
// COMDAT key is required for correctness.
AddGlobalCtor(Fn, 65535, Addr);
DelayedCXXInitPosition.erase(D);
} else {
llvm::DenseMap<const Decl *, unsigned>::iterator I =
@ -430,8 +432,7 @@ void CodeGenFunction::GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn,
// Use guarded initialization if the global variable is weak. This
// occurs for, e.g., instantiated static data members and
// definitions explicitly marked weak.
if (llvm::GlobalVariable::isWeakLinkage(Addr->getLinkage()) ||
llvm::GlobalVariable::isLinkOnceLinkage(Addr->getLinkage())) {
if (Addr->hasWeakLinkage() || Addr->hasLinkOnceLinkage()) {
EmitCXXGuardedInit(*D, Addr, PerformInit);
} else {
EmitCXXGlobalVarDeclInit(*D, Addr, PerformInit);

View File

@ -511,16 +511,17 @@ llvm::GlobalValue *CodeGenModule::GetGlobalValue(StringRef Name) {
/// AddGlobalCtor - Add a function to the list that will be called before
/// main() runs.
void CodeGenModule::AddGlobalCtor(llvm::Function * Ctor, int Priority) {
void CodeGenModule::AddGlobalCtor(llvm::Function *Ctor, int Priority,
llvm::Constant *AssociatedData) {
// FIXME: Type coercion of void()* types.
GlobalCtors.push_back(std::make_pair(Ctor, Priority));
GlobalCtors.push_back(Structor(Priority, Ctor, AssociatedData));
}
/// AddGlobalDtor - Add a function to the list that will be called
/// when the module is unloaded.
void CodeGenModule::AddGlobalDtor(llvm::Function * Dtor, int Priority) {
void CodeGenModule::AddGlobalDtor(llvm::Function *Dtor, int Priority) {
// FIXME: Type coercion of void()* types.
GlobalDtors.push_back(std::make_pair(Dtor, Priority));
GlobalDtors.push_back(Structor(Priority, Dtor, 0));
}
void CodeGenModule::EmitCtorList(const CtorList &Fns, const char *GlobalName) {
@ -528,16 +529,19 @@ void CodeGenModule::EmitCtorList(const CtorList &Fns, const char *GlobalName) {
llvm::FunctionType* CtorFTy = llvm::FunctionType::get(VoidTy, false);
llvm::Type *CtorPFTy = llvm::PointerType::getUnqual(CtorFTy);
// Get the type of a ctor entry, { i32, void ()* }.
llvm::StructType *CtorStructTy =
llvm::StructType::get(Int32Ty, llvm::PointerType::getUnqual(CtorFTy), NULL);
// Get the type of a ctor entry, { i32, void ()*, i8* }.
llvm::StructType *CtorStructTy = llvm::StructType::get(
Int32Ty, llvm::PointerType::getUnqual(CtorFTy), VoidPtrTy, NULL);
// Construct the constructor and destructor arrays.
SmallVector<llvm::Constant*, 8> Ctors;
for (CtorList::const_iterator I = Fns.begin(), E = Fns.end(); I != E; ++I) {
llvm::Constant *S[] = {
llvm::ConstantInt::get(Int32Ty, I->second, false),
llvm::ConstantExpr::getBitCast(I->first, CtorPFTy)
llvm::ConstantInt::get(Int32Ty, I->Priority, false),
llvm::ConstantExpr::getBitCast(I->Initializer, CtorPFTy),
(I->AssociatedData
? llvm::ConstantExpr::getBitCast(I->AssociatedData, VoidPtrTy)
: llvm::Constant::getNullValue(VoidPtrTy))
};
Ctors.push_back(llvm::ConstantStruct::get(CtorStructTy, S));
}

View File

@ -233,7 +233,18 @@ class CodeGenModule : public CodeGenTypeCache {
CodeGenModule(const CodeGenModule &) LLVM_DELETED_FUNCTION;
void operator=(const CodeGenModule &) LLVM_DELETED_FUNCTION;
typedef std::vector<std::pair<llvm::Constant*, int> > CtorList;
struct Structor {
Structor() : Priority(0), Initializer(nullptr), AssociatedData(nullptr) {}
Structor(int Priority, llvm::Constant *Initializer,
llvm::Constant *AssociatedData)
: Priority(Priority), Initializer(Initializer),
AssociatedData(AssociatedData) {}
int Priority;
llvm::Constant *Initializer;
llvm::Constant *AssociatedData;
};
typedef std::vector<Structor> CtorList;
ASTContext &Context;
const LangOptions &LangOpts;
@ -1081,8 +1092,9 @@ private:
bool PerformInit);
// FIXME: Hardcoding priority here is gross.
void AddGlobalCtor(llvm::Function *Ctor, int Priority=65535);
void AddGlobalDtor(llvm::Function *Dtor, int Priority=65535);
void AddGlobalCtor(llvm::Function *Ctor, int Priority = 65535,
llvm::Constant *AssociatedData = 0);
void AddGlobalDtor(llvm::Function *Dtor, int Priority = 65535);
/// Generates a global array of functions and priorities using the given list
/// and name. This array will have appending linkage and is suitable for use

View File

@ -1349,6 +1349,15 @@ llvm::Value* MicrosoftCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
void MicrosoftCXXABI::EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
llvm::GlobalVariable *GV,
bool PerformInit) {
// MSVC only uses guards for static locals.
if (!D.isStaticLocal()) {
assert(GV->hasWeakLinkage() || GV->hasLinkOnceLinkage());
// GlobalOpt is allowed to discard the initializer, so use linkonce_odr.
CGF.CurFn->setLinkage(llvm::GlobalValue::LinkOnceODRLinkage);
CGF.EmitCXXGlobalVarDeclInit(D, GV, PerformInit);
return;
}
// MSVC always uses an i32 bitfield to guard initialization, which is *not*
// threadsafe. Since the user may be linking in inline functions compiled by
// cl.exe, there's no reason to provide a false sense of security by using
@ -1362,11 +1371,7 @@ void MicrosoftCXXABI::EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
llvm::ConstantInt *Zero = llvm::ConstantInt::get(GuardTy, 0);
// Get the guard variable for this function if we have one already.
GuardInfo EmptyGuardInfo;
GuardInfo *GI = &EmptyGuardInfo;
if (isa<FunctionDecl>(D.getDeclContext())) {
GI = &GuardVariableMap[D.getDeclContext()];
}
GuardInfo *GI = &GuardVariableMap[D.getDeclContext()];
unsigned BitIndex;
if (D.isStaticLocal() && D.isExternallyVisible()) {

View File

@ -1,8 +1,8 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fno-use-cxa-atexit -fapple-kext -emit-llvm -o - %s | FileCheck %s
// CHECK: @_ZN5test01aE = global [[A:%.*]] zeroinitializer
// CHECK: @llvm.global_ctors = appending global {{.*}} { i32 65535, void ()* [[CTOR0:@.*]] }
// CHECK: @llvm.global_dtors = appending global {{.*}} { i32 65535, void ()* [[DTOR0:@.*]] }
// CHECK: @llvm.global_ctors = appending global {{.*}} { i32 65535, void ()* [[CTOR0:@.*]], i8* null }
// CHECK: @llvm.global_dtors = appending global {{.*}} { i32 65535, void ()* [[DTOR0:@.*]], i8* null }
// rdar://11241230
namespace test0 {

View File

@ -27,7 +27,10 @@ public:
A C::a = A();
// CHECK: @llvm.global_ctors = appending global [3 x { i32, void ()* }] [{ i32, void ()* } { i32 200, void ()* @_GLOBAL__I_000200 }, { i32, void ()* } { i32 300, void ()* @_GLOBAL__I_000300 }, { i32, void ()* } { i32 65535, void ()* @_GLOBAL__sub_I_init_priority_attr.cpp }]
// CHECK: @llvm.global_ctors = appending global [3 x { i32, void ()*, i8* }]
// CHECK: [{ i32, void ()*, i8* } { i32 200, void ()* @_GLOBAL__I_000200, i8* null },
// CHECK: { i32, void ()*, i8* } { i32 300, void ()* @_GLOBAL__I_000300, i8* null },
// CHECK: { i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I_init_priority_attr.cpp, i8* null }]
// CHECK: _GLOBAL__I_000200()
// CHECK: _Z3fooi(i32 3)

View File

@ -1,8 +1,9 @@
// RUN: %clang_cc1 -fms-extensions -emit-llvm %s -o - -mconstructor-aliases -triple=i386-pc-win32 | FileCheck %s
// CHECK: @llvm.global_ctors = appending global [2 x { i32, void ()* }]
// CHECK: [{ i32, void ()* } { i32 65535, void ()* @"\01??__Efoo@?$B@H@@2VA@@A@YAXXZ"
// CHECK: { i32, void ()* } { i32 65535, void ()* @_GLOBAL__sub_I_microsoft_abi_static_initializers.cpp }]
// CHECK: @llvm.global_ctors = appending global [2 x { i32, void ()*, i8* }]
// CHECK: [{ i32, void ()*, i8* } { i32 65535, void ()* @"\01??__Efoo@?$B@H@@2VA@@A@YAXXZ",
// CHECK: i8* bitcast (%class.A* @"\01?foo@?$B@H@@2VA@@A" to i8*) },
// CHECK: { i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I_microsoft_abi_static_initializers.cpp, i8* null }]
struct S {
S();
@ -11,12 +12,12 @@ struct S {
S s;
// CHECK: define internal void @"\01??__Es@@YAXXZ"() [[NUW:#[0-9]+]]
// CHECK: %{{[.0-9A-Z_a-z]+}} = call x86_thiscallcc %struct.S* @"\01??0S@@QAE@XZ"
// CHECK: define internal void @"\01??__Es@@YAXXZ"()
// CHECK: call x86_thiscallcc %struct.S* @"\01??0S@@QAE@XZ"
// CHECK: call i32 @atexit(void ()* @"\01??__Fs@@YAXXZ")
// CHECK: ret void
// CHECK: define internal void @"\01??__Fs@@YAXXZ"() [[NUW]] {
// CHECK: define internal void @"\01??__Fs@@YAXXZ"()
// CHECK: call x86_thiscallcc void @"\01??1S@@QAE@XZ"
// CHECK: ret void
@ -24,11 +25,13 @@ S s;
// the same global.
__declspec(selectany) S selectany1;
__declspec(selectany) S selectany2;
// CHECK: define internal void @"\01??__Eselectany1@@YAXXZ"() [[NUW:#[0-9]+]]
// CHECK: load i32* @"\01??_Bselectany1@@3US@@A@5"
// CHECK: define linkonce_odr void @"\01??__Eselectany1@@YAXXZ"()
// CHECK-NOT: @"\01??_Bselectany1
// CHECK: call x86_thiscallcc %struct.S* @"\01??0S@@QAE@XZ"
// CHECK: ret void
// CHECK: define internal void @"\01??__Eselectany2@@YAXXZ"() [[NUW:#[0-9]+]]
// CHECK: load i32* @"\01??_Bselectany2@@3US@@A@5"
// CHECK: define linkonce_odr void @"\01??__Eselectany2@@YAXXZ"()
// CHECK-NOT: @"\01??_Bselectany2
// CHECK: call x86_thiscallcc %struct.S* @"\01??0S@@QAE@XZ"
// CHECK: ret void
void StaticLocal() {
@ -96,6 +99,7 @@ class A {
public:
A() {}
~A() {}
int a;
};
template<typename T>
@ -145,10 +149,10 @@ void force_usage() {
(void)B<int>::foo; // (void) - force usage
}
// CHECK: define internal void @"\01??__Efoo@?$B@H@@2VA@@A@YAXXZ"() [[NUW]]
// CHECK: load i32* @"\01??_Bfoo@?$B@H@@2VA@@A@5"
// CHECK: store i32 {{.*}}, i32* @"\01??_Bfoo@?$B@H@@2VA@@A@5"
// CHECK: %{{[.0-9A-Z_a-z]+}} = call x86_thiscallcc %class.A* @"\01??0A@@QAE@XZ"
// CHECK: define linkonce_odr void @"\01??__Efoo@?$B@H@@2VA@@A@YAXXZ"()
// CHECK-NOT: and
// CHECK-NOT: ?_Bfoo@
// CHECK: call x86_thiscallcc %class.A* @"\01??0A@@QAE@XZ"
// CHECK: call i32 @atexit(void ()* @"\01??__Ffoo@?$B@H@@2VA@@A@YAXXZ")
// CHECK: ret void
@ -160,8 +164,6 @@ void force_usage() {
// CHECK: call x86_thiscallcc void @"\01??1A@@QAE@XZ"{{.*}}foo
// CHECK: ret void
// CHECK: define internal void @_GLOBAL__sub_I_microsoft_abi_static_initializers.cpp() [[NUW]] {
// CHECK: define internal void @_GLOBAL__sub_I_microsoft_abi_static_initializers.cpp()
// CHECK: call void @"\01??__Es@@YAXXZ"()
// CHECK: ret void
// CHECK: attributes [[NUW]] = { nounwind }

View File

@ -12,14 +12,14 @@ template<> int A<char>::a;
// CHECK: @_ZN1AIbE1aE = global i32 10
template<> int A<bool>::a = 10;
// CHECK: @llvm.global_ctors = appending global [7 x { i32, void ()* }]
// CHECK: [{ i32, void ()* } { i32 65535, void ()* @[[unordered1:[^ ]*]] },
// CHECK: { i32, void ()* } { i32 65535, void ()* @[[unordered2:[^ ]*]] },
// CHECK: { i32, void ()* } { i32 65535, void ()* @[[unordered3:[^ ]*]] },
// CHECK: { i32, void ()* } { i32 65535, void ()* @[[unordered4:[^ ]*]] },
// CHECK: { i32, void ()* } { i32 65535, void ()* @[[unordered5:[^ ]*]] },
// CHECK: { i32, void ()* } { i32 65535, void ()* @[[unordered6:[^ ]*]] },
// CHECK: { i32, void ()* } { i32 65535, void ()* @_GLOBAL__sub_I_static_member_variable_explicit_specialization.cpp }]
// CHECK: @llvm.global_ctors = appending global [7 x { i32, void ()*, i8* }]
// CHECK: [{ i32, void ()*, i8* } { i32 65535, void ()* @[[unordered1:[^,]*]], i8* bitcast (i32* @_ZN1AIsE1aE to i8*) },
// CHECK: { i32, void ()*, i8* } { i32 65535, void ()* @[[unordered2:[^,]*]], i8* bitcast (i16* @_Z1xIsE to i8*) },
// CHECK: { i32, void ()*, i8* } { i32 65535, void ()* @[[unordered3:[^,]*]], i8* bitcast (i32* @_ZN2ns1aIiE1iE to i8*) },
// CHECK: { i32, void ()*, i8* } { i32 65535, void ()* @[[unordered4:[^,]*]], i8* bitcast (i32* @_ZN2ns1b1iIiEE to i8*) },
// CHECK: { i32, void ()*, i8* } { i32 65535, void ()* @[[unordered5:[^,]*]], i8* bitcast (i32* @_ZN1AIvE1aE to i8*) },
// CHECK: { i32, void ()*, i8* } { i32 65535, void ()* @[[unordered6:[^,]*]], i8* @_Z1xIcE },
// CHECK: { i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I_static_member_variable_explicit_specialization.cpp, i8* null }]
template int A<short>::a; // Unordered
int b = foo();