mirror of https://github.com/microsoft/clang.git
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:
parent
3ccca48c69
commit
8756959802
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue