Simplify the logic for emitting guard variables for template static

data members by delaying the emission of the initializer until after
linkage and visibility have been set on the global.  Also, don't
emit a guard unless the variable actually ends up with vague linkage,
and don't use thread-safe statics in any case.

llvm-svn: 118336
This commit is contained in:
John McCall 2010-11-06 09:44:32 +00:00
parent 8b0a71fc31
commit cdf7ef5437
9 changed files with 112 additions and 58 deletions

View File

@ -468,8 +468,8 @@ void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
CookieSize = CharUnits::Zero(); CookieSize = CharUnits::Zero();
} }
void CGCXXABI::EmitStaticLocalInit(CodeGenFunction &CGF, void CGCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
const VarDecl &D, const VarDecl &D,
llvm::GlobalVariable *GV) { llvm::GlobalVariable *GV) {
ErrorUnsupportedABI(CGF, "static local variable initialization"); ErrorUnsupportedABI(CGF, "static local variable initialization");
} }

View File

@ -221,11 +221,14 @@ public:
/*************************** Static local guards ****************************/ /*************************** Static local guards ****************************/
/// Emits the initializer and destructor setup for the given static /// Emits the guarded initializer and destructor setup for the given
/// local variable, given that it's reachable and couldn't be /// variable, given that it couldn't be emitted as a constant.
/// emitted as a constant. ///
virtual void EmitStaticLocalInit(CodeGenFunction &CGF, const VarDecl &D, /// The variable may be:
llvm::GlobalVariable *DeclPtr); /// - a static local variable
/// - a static data member of a class template instantiation
virtual void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
llvm::GlobalVariable *DeclPtr);
}; };

View File

@ -196,7 +196,7 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
// be constant. // be constant.
GV->setConstant(false); GV->setConstant(false);
EmitCXXStaticLocalInit(D, GV); EmitCXXGuardedInit(D, GV);
} }
return GV; return GV;
} }

View File

@ -140,9 +140,9 @@ CodeGenFunction::EmitCXXGlobalDtorRegistration(llvm::Constant *DtorFn,
Builder.CreateCall(AtExitFn, &Args[0], llvm::array_endof(Args)); Builder.CreateCall(AtExitFn, &Args[0], llvm::array_endof(Args));
} }
void CodeGenFunction::EmitCXXStaticLocalInit(const VarDecl &D, void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D,
llvm::GlobalVariable *DeclPtr) { llvm::GlobalVariable *DeclPtr) {
CGM.getCXXABI().EmitStaticLocalInit(*this, D, DeclPtr); CGM.getCXXABI().EmitGuardedInit(*this, D, DeclPtr);
} }
static llvm::Function * static llvm::Function *
@ -165,7 +165,8 @@ CreateGlobalInitOrDestructFunction(CodeGenModule &CGM,
} }
void void
CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D) { CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,
llvm::GlobalVariable *Addr) {
const llvm::FunctionType *FTy const llvm::FunctionType *FTy
= llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), = llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext),
false); false);
@ -174,7 +175,7 @@ CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D) {
llvm::Function *Fn = llvm::Function *Fn =
CreateGlobalInitOrDestructFunction(*this, FTy, "__cxx_global_var_init"); CreateGlobalInitOrDestructFunction(*this, FTy, "__cxx_global_var_init");
CodeGenFunction(*this).GenerateCXXGlobalVarDeclInitFunc(Fn, D); CodeGenFunction(*this).GenerateCXXGlobalVarDeclInitFunc(Fn, D, Addr);
if (D->hasAttr<InitPriorityAttr>()) { if (D->hasAttr<InitPriorityAttr>()) {
unsigned int order = D->getAttr<InitPriorityAttr>()->getPriority(); unsigned int order = D->getAttr<InitPriorityAttr>()->getPriority();
@ -247,26 +248,20 @@ void CodeGenModule::EmitCXXGlobalDtorFunc() {
AddGlobalDtor(Fn); AddGlobalDtor(Fn);
} }
/// Emit the code necessary to initialize the given global variable.
void CodeGenFunction::GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn, void CodeGenFunction::GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn,
const VarDecl *D) { const VarDecl *D,
llvm::GlobalVariable *Addr) {
StartFunction(GlobalDecl(), getContext().VoidTy, Fn, FunctionArgList(), StartFunction(GlobalDecl(), getContext().VoidTy, Fn, FunctionArgList(),
SourceLocation()); SourceLocation());
llvm::Constant *DeclPtr = CGM.GetAddrOfGlobalVar(D); // Use guarded initialization if the global variable is weak due to
if (D->isStaticDataMember() && // being a class template's static data member.
D->getInstantiatedFromStaticDataMember() && D->getInit()){ if (Addr->hasWeakLinkage() && D->getInstantiatedFromStaticDataMember()) {
llvm::GlobalVariable *GV = dyn_cast<llvm::GlobalVariable>(DeclPtr); EmitCXXGuardedInit(*D, Addr);
assert(GV && "GenerateCXXGlobalVarDeclInitFunc - GV is null"); } else {
llvm::GlobalValue::LinkageTypes Linkage = EmitCXXGlobalVarDeclInit(*D, Addr);
CGM.GetLLVMLinkageVarDefinition(D, GV);
if (Linkage == llvm::GlobalVariable::WeakAnyLinkage) {
GV->setConstant(false);
EmitCXXStaticLocalInit(*D, GV);
FinishFunction();
return;
}
} }
EmitCXXGlobalVarDeclInit(*D, DeclPtr);
FinishFunction(); FinishFunction();
} }

View File

@ -1628,7 +1628,12 @@ public:
void EmitCXXGlobalDtorRegistration(llvm::Constant *DtorFn, void EmitCXXGlobalDtorRegistration(llvm::Constant *DtorFn,
llvm::Constant *DeclPtr); llvm::Constant *DeclPtr);
void EmitCXXStaticLocalInit(const VarDecl &D, llvm::GlobalVariable *DeclPtr); /// Emit code in this function to perform a guarded variable
/// initialization. Guarded initializations are used when it's not
/// possible to prove that an initialization will be done exactly
/// once, e.g. with a static local variable or a static data member
/// of a class template.
void EmitCXXGuardedInit(const VarDecl &D, llvm::GlobalVariable *DeclPtr);
/// GenerateCXXGlobalInitFunc - Generates code for initializing global /// GenerateCXXGlobalInitFunc - Generates code for initializing global
/// variables. /// variables.
@ -1642,7 +1647,8 @@ public:
const std::vector<std::pair<llvm::WeakVH, const std::vector<std::pair<llvm::WeakVH,
llvm::Constant*> > &DtorsAndObjects); llvm::Constant*> > &DtorsAndObjects);
void GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn, const VarDecl *D); void GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn, const VarDecl *D,
llvm::GlobalVariable *Addr);
void EmitCXXConstructExpr(const CXXConstructExpr *E, AggValueSlot Dest); void EmitCXXConstructExpr(const CXXConstructExpr *E, AggValueSlot Dest);

View File

@ -1102,7 +1102,6 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
T = D->getType(); T = D->getType();
if (getLangOptions().CPlusPlus) { if (getLangOptions().CPlusPlus) {
EmitCXXGlobalVarDeclInitFunc(D);
Init = EmitNullConstant(T); Init = EmitNullConstant(T);
NonConstInit = true; NonConstInit = true;
} else { } else {
@ -1184,6 +1183,10 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
SetCommonAttributes(D, GV); SetCommonAttributes(D, GV);
// Emit the initializer function if necessary.
if (NonConstInit)
EmitCXXGlobalVarDeclInitFunc(D, GV);
// Emit global variable debug information. // Emit global variable debug information.
if (CGDebugInfo *DI = getDebugInfo()) { if (CGDebugInfo *DI = getDebugInfo()) {
DI->setLocation(D->getLocation()); DI->setLocation(D->getLocation());

View File

@ -590,7 +590,8 @@ private:
/// EmitCXXGlobalDtorFunc - Emit the function that destroys C++ globals. /// EmitCXXGlobalDtorFunc - Emit the function that destroys C++ globals.
void EmitCXXGlobalDtorFunc(); void EmitCXXGlobalDtorFunc();
void EmitCXXGlobalVarDeclInitFunc(const VarDecl *D); void EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,
llvm::GlobalVariable *Addr);
// FIXME: Hardcoding priority here is gross. // FIXME: Hardcoding priority here is gross.
void AddGlobalCtor(llvm::Function *Ctor, int Priority=65535); void AddGlobalCtor(llvm::Function *Ctor, int Priority=65535);

View File

@ -120,8 +120,8 @@ public:
QualType ElementType, llvm::Value *&NumElements, QualType ElementType, llvm::Value *&NumElements,
llvm::Value *&AllocPtr, CharUnits &CookieSize); llvm::Value *&AllocPtr, CharUnits &CookieSize);
void EmitStaticLocalInit(CodeGenFunction &CGF, const VarDecl &D, void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
llvm::GlobalVariable *DeclPtr); llvm::GlobalVariable *DeclPtr);
}; };
class ARMCXXABI : public ItaniumCXXABI { class ARMCXXABI : public ItaniumCXXABI {
@ -1078,11 +1078,15 @@ namespace {
/// The ARM code here follows the Itanium code closely enough that we /// The ARM code here follows the Itanium code closely enough that we
/// just special-case it at particular places. /// just special-case it at particular places.
void ItaniumCXXABI::EmitStaticLocalInit(CodeGenFunction &CGF, void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
const VarDecl &D, const VarDecl &D,
llvm::GlobalVariable *GV) { llvm::GlobalVariable *GV) {
CGBuilderTy &Builder = CGF.Builder; CGBuilderTy &Builder = CGF.Builder;
bool ThreadsafeStatics = getContext().getLangOptions().ThreadsafeStatics;
// We only need to use thread-safe statics for local variables;
// global initialization is always single-threaded.
bool ThreadsafeStatics = (getContext().getLangOptions().ThreadsafeStatics &&
D.isLocalVarDecl());
// Guard variables are 64 bits in the generic ABI and 32 bits on ARM. // Guard variables are 64 bits in the generic ABI and 32 bits on ARM.
const llvm::IntegerType *GuardTy const llvm::IntegerType *GuardTy
@ -1093,16 +1097,10 @@ void ItaniumCXXABI::EmitStaticLocalInit(CodeGenFunction &CGF,
llvm::SmallString<256> GuardVName; llvm::SmallString<256> GuardVName;
getMangleContext().mangleItaniumGuardVariable(&D, GuardVName); getMangleContext().mangleItaniumGuardVariable(&D, GuardVName);
// FIXME: we should just absorb linkage and visibility from the // Just absorb linkage and visibility from the variable.
// variable, but that's not always set up properly just yet.
llvm::GlobalValue::LinkageTypes Linkage = GV->getLinkage();
if (D.isStaticDataMember() &&
D.getInstantiatedFromStaticDataMember())
Linkage = llvm::GlobalVariable::WeakAnyLinkage;
llvm::GlobalVariable *GuardVariable = llvm::GlobalVariable *GuardVariable =
new llvm::GlobalVariable(CGM.getModule(), GuardTy, new llvm::GlobalVariable(CGM.getModule(), GuardTy,
false, Linkage, false, GV->getLinkage(),
llvm::ConstantInt::get(GuardTy, 0), llvm::ConstantInt::get(GuardTy, 0),
GuardVName.str()); GuardVName.str());
GuardVariable->setVisibility(GV->getVisibility()); GuardVariable->setVisibility(GV->getVisibility());

View File

@ -1,18 +1,66 @@
// RUN: %clang_cc1 -emit-llvm -o - %s // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s
// CHECK: @_ZN1A1aE = constant i32 10 // CHECK: @_ZN5test11A1aE = constant i32 10, align 4
// CHECK: @_ZN5test212_GLOBAL__N_11AIiE1xE = internal global i32 0, align 4
// CHECK: @_ZN5test31AIiE1xE = weak global i32 0, align 4
// CHECK: @_ZGVN5test31AIiE1xE = weak global i64 0
// PR5564. // PR5564.
struct A { namespace test1 {
static const int a = 10; struct A {
}; static const int a = 10;
};
const int A::a; const int A::a;
struct S { struct S {
static int i; static int i;
}; };
void f() { void f() {
int a = S::i; int a = S::i;
}
}
// Test that we don't use guards for initializing template static data
// members with internal linkage.
namespace test2 {
int foo();
namespace {
template <class T> struct A {
static int x;
};
template <class T> int A<T>::x = foo();
template struct A<int>;
}
// CHECK: define internal void @__cxx_global_var_init()
// CHECK: [[TMP:%.*]] = call i32 @_ZN5test23fooEv()
// CHECK-NEXT: store i32 [[TMP]], i32* @_ZN5test212_GLOBAL__N_11AIiE1xE, align 4
// CHECK-NEXT: ret void
}
// Test that we don't use threadsafe statics when initializing
// template static data members.
namespace test3 {
int foo();
template <class T> struct A {
static int x;
};
template <class T> int A<T>::x = foo();
template struct A<int>;
// CHECK: define internal void @__cxx_global_var_init1()
// CHECK: [[GUARDBYTE:%.*]] = load i8* bitcast (i64* @_ZGVN5test31AIiE1xE to i8*)
// CHECK-NEXT: [[UNINITIALIZED:%.*]] = icmp eq i8 [[GUARDBYTE]], 0
// CHECK-NEXT: br i1 [[UNINITIALIZED]]
// CHECK: [[TMP:%.*]] = call i32 @_ZN5test33fooEv()
// CHECK-NEXT: store i32 [[TMP]], i32* @_ZN5test31AIiE1xE, align 4
// CHECK-NEXT: store i64 1, i64* @_ZGVN5test31AIiE1xE
// CHECK-NEXT: br label
// CHECK: ret void
} }