mirror of
https://github.com/intel/llvm.git
synced 2026-02-03 02:26:27 +08:00
Implement CodeGen for C++11 thread_local, following the Itanium ABI specification as discussed on cxx-abi-dev.
llvm-svn: 179858
This commit is contained in:
@@ -141,6 +141,16 @@ public:
|
||||
raw_ostream &) {
|
||||
llvm_unreachable("Target does not support mangling guard variables");
|
||||
}
|
||||
// FIXME: Revisit this once we know what we need to do for MSVC compatibility.
|
||||
virtual void mangleItaniumThreadLocalInit(const VarDecl *D,
|
||||
raw_ostream &) {
|
||||
llvm_unreachable("Target does not support mangling thread_local variables");
|
||||
}
|
||||
virtual void mangleItaniumThreadLocalWrapper(const VarDecl *D,
|
||||
raw_ostream &) {
|
||||
llvm_unreachable("Target does not support mangling thread_local variables");
|
||||
}
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
|
||||
@@ -141,6 +141,8 @@ public:
|
||||
raw_ostream &);
|
||||
|
||||
void mangleItaniumGuardVariable(const VarDecl *D, raw_ostream &);
|
||||
void mangleItaniumThreadLocalInit(const VarDecl *D, raw_ostream &);
|
||||
void mangleItaniumThreadLocalWrapper(const VarDecl *D, raw_ostream &);
|
||||
|
||||
void mangleInitDiscriminator() {
|
||||
Discriminator = 0;
|
||||
@@ -3531,6 +3533,22 @@ void ItaniumMangleContext::mangleItaniumGuardVariable(const VarDecl *D,
|
||||
Mangler.mangleName(D);
|
||||
}
|
||||
|
||||
void ItaniumMangleContext::mangleItaniumThreadLocalInit(const VarDecl *D,
|
||||
raw_ostream &Out) {
|
||||
// <special-name> ::= TH <object name>
|
||||
CXXNameMangler Mangler(*this, Out);
|
||||
Mangler.getStream() << "_ZTH";
|
||||
Mangler.mangleName(D);
|
||||
}
|
||||
|
||||
void ItaniumMangleContext::mangleItaniumThreadLocalWrapper(const VarDecl *D,
|
||||
raw_ostream &Out) {
|
||||
// <special-name> ::= TW <object name>
|
||||
CXXNameMangler Mangler(*this, Out);
|
||||
Mangler.getStream() << "_ZTW";
|
||||
Mangler.mangleName(D);
|
||||
}
|
||||
|
||||
void ItaniumMangleContext::mangleReferenceTemporary(const VarDecl *D,
|
||||
raw_ostream &Out) {
|
||||
// We match the GCC mangling here.
|
||||
|
||||
@@ -259,3 +259,14 @@ llvm::BasicBlock *CGCXXABI::EmitCtorCompleteObjectHandler(
|
||||
ErrorUnsupportedABI(CGF, "complete object detection in ctor");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CGCXXABI::EmitThreadLocalInitFuncs(
|
||||
llvm::ArrayRef<std::pair<const VarDecl *, llvm::GlobalVariable *> > Decls,
|
||||
llvm::Function *InitFunc) {
|
||||
}
|
||||
|
||||
LValue CGCXXABI::EmitThreadLocalDeclRefExpr(CodeGenFunction &CGF,
|
||||
const DeclRefExpr *DRE) {
|
||||
ErrorUnsupportedABI(CGF, "odr-use of thread_local global");
|
||||
return LValue();
|
||||
}
|
||||
|
||||
@@ -353,6 +353,25 @@ public:
|
||||
/// \param addr - a pointer to pass to the destructor function.
|
||||
virtual void registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D,
|
||||
llvm::Constant *dtor, llvm::Constant *addr);
|
||||
|
||||
/*************************** thread_local initialization ********************/
|
||||
|
||||
/// Emits ABI-required functions necessary to initialize thread_local
|
||||
/// variables in this translation unit.
|
||||
///
|
||||
/// \param Decls The thread_local declarations in this translation unit.
|
||||
/// \param InitFunc If this translation unit contains any non-constant
|
||||
/// initialization or non-trivial destruction for thread_local
|
||||
/// variables, a function to perform the initialization. Otherwise, 0.
|
||||
virtual void EmitThreadLocalInitFuncs(
|
||||
llvm::ArrayRef<std::pair<const VarDecl *, llvm::GlobalVariable *> > Decls,
|
||||
llvm::Function *InitFunc);
|
||||
|
||||
/// Emit a reference to a non-local thread_local variable (including
|
||||
/// triggering the initialization of all thread_local variables in its
|
||||
/// translation unit).
|
||||
virtual LValue EmitThreadLocalDeclRefExpr(CodeGenFunction &CGF,
|
||||
const DeclRefExpr *DRE);
|
||||
};
|
||||
|
||||
// Create an instance of a C++ ABI class:
|
||||
|
||||
@@ -156,7 +156,8 @@ void CodeGenFunction::EmitCXXGlobalVarDeclInit(const VarDecl &D,
|
||||
static llvm::Function *
|
||||
CreateGlobalInitOrDestructFunction(CodeGenModule &CGM,
|
||||
llvm::FunctionType *ty,
|
||||
const Twine &name);
|
||||
const Twine &name,
|
||||
bool TLS = false);
|
||||
|
||||
/// Create a stub function, suitable for being passed to atexit,
|
||||
/// which passes the given address to the given destructor function.
|
||||
@@ -225,11 +226,11 @@ void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D,
|
||||
static llvm::Function *
|
||||
CreateGlobalInitOrDestructFunction(CodeGenModule &CGM,
|
||||
llvm::FunctionType *FTy,
|
||||
const Twine &Name) {
|
||||
const Twine &Name, bool TLS) {
|
||||
llvm::Function *Fn =
|
||||
llvm::Function::Create(FTy, llvm::GlobalValue::InternalLinkage,
|
||||
Name, &CGM.getModule());
|
||||
if (!CGM.getLangOpts().AppleKext) {
|
||||
if (!CGM.getLangOpts().AppleKext && !TLS) {
|
||||
// Set the section if needed.
|
||||
if (const char *Section =
|
||||
CGM.getTarget().getStaticInitSectionSpecifier())
|
||||
@@ -255,9 +256,6 @@ void
|
||||
CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,
|
||||
llvm::GlobalVariable *Addr,
|
||||
bool PerformInit) {
|
||||
if (D->getTLSKind())
|
||||
ErrorUnsupported(D->getInit(), "dynamic TLS initialization");
|
||||
|
||||
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
|
||||
|
||||
// Create a variable initialization function.
|
||||
@@ -267,12 +265,20 @@ CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,
|
||||
CodeGenFunction(*this).GenerateCXXGlobalVarDeclInitFunc(Fn, D, Addr,
|
||||
PerformInit);
|
||||
|
||||
if (D->hasAttr<InitPriorityAttr>()) {
|
||||
if (D->getTLSKind()) {
|
||||
// FIXME: Should we support init_priority for thread_local?
|
||||
// FIXME: Ideally, initialization of instantiated thread_local static data
|
||||
// members of class templates should not trigger initialization of other
|
||||
// entities in the TU.
|
||||
// FIXME: We only need to register one __cxa_thread_atexit function for the
|
||||
// entire TU.
|
||||
CXXThreadLocalInits.push_back(Fn);
|
||||
} else if (D->hasAttr<InitPriorityAttr>()) {
|
||||
unsigned int order = D->getAttr<InitPriorityAttr>()->getPriority();
|
||||
OrderGlobalInits Key(order, PrioritizedCXXGlobalInits.size());
|
||||
PrioritizedCXXGlobalInits.push_back(std::make_pair(Key, Fn));
|
||||
DelayedCXXInitPosition.erase(D);
|
||||
} else {
|
||||
} else {
|
||||
llvm::DenseMap<const Decl *, unsigned>::iterator I =
|
||||
DelayedCXXInitPosition.find(D);
|
||||
if (I == DelayedCXXInitPosition.end()) {
|
||||
@@ -285,6 +291,27 @@ CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,
|
||||
}
|
||||
}
|
||||
|
||||
void CodeGenModule::EmitCXXThreadLocalInitFunc() {
|
||||
llvm::Function *InitFn = 0;
|
||||
if (!CXXThreadLocalInits.empty()) {
|
||||
// Generate a guarded initialization function.
|
||||
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
|
||||
InitFn = CreateGlobalInitOrDestructFunction(
|
||||
*this, FTy, "__tls_init", /*TLS*/ true);
|
||||
llvm::GlobalVariable *Guard = new llvm::GlobalVariable(
|
||||
getModule(), Int8Ty, false, llvm::GlobalVariable::InternalLinkage,
|
||||
llvm::ConstantInt::get(Int8Ty, 0), "__tls_guard");
|
||||
Guard->setThreadLocal(true);
|
||||
CodeGenFunction(*this).GenerateCXXGlobalInitFunc(
|
||||
InitFn, CXXThreadLocalInits.data(), CXXThreadLocalInits.size(), Guard);
|
||||
}
|
||||
|
||||
getCXXABI().EmitThreadLocalInitFuncs(CXXThreadLocals, InitFn);
|
||||
|
||||
CXXThreadLocalInits.clear();
|
||||
CXXThreadLocals.clear();
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenModule::EmitCXXGlobalInitFunc() {
|
||||
while (!CXXGlobalInits.empty() && !CXXGlobalInits.back())
|
||||
@@ -385,7 +412,8 @@ void CodeGenFunction::GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn,
|
||||
|
||||
void CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn,
|
||||
llvm::Constant **Decls,
|
||||
unsigned NumDecls) {
|
||||
unsigned NumDecls,
|
||||
llvm::GlobalVariable *Guard) {
|
||||
// Initialize debug info if needed.
|
||||
maybeInitializeDebugInfo();
|
||||
|
||||
@@ -393,6 +421,22 @@ void CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn,
|
||||
getTypes().arrangeNullaryFunction(),
|
||||
FunctionArgList(), SourceLocation());
|
||||
|
||||
llvm::BasicBlock *ExitBlock = 0;
|
||||
if (Guard) {
|
||||
// If we have a guard variable, check whether we've already performed these
|
||||
// initializations. This happens for TLS initialization functions.
|
||||
llvm::Value *GuardVal = Builder.CreateLoad(Guard);
|
||||
llvm::Value *Uninit = Builder.CreateIsNull(GuardVal, "guard.uninitialized");
|
||||
// Mark as initialized before initializing anything else. If the
|
||||
// initializers use previously-initialized thread_local vars, that's
|
||||
// probably supposed to be OK, but the standard doesn't say.
|
||||
Builder.CreateStore(llvm::ConstantInt::get(GuardVal->getType(), 1), Guard);
|
||||
llvm::BasicBlock *InitBlock = createBasicBlock("init");
|
||||
ExitBlock = createBasicBlock("exit");
|
||||
Builder.CreateCondBr(Uninit, InitBlock, ExitBlock);
|
||||
EmitBlock(InitBlock);
|
||||
}
|
||||
|
||||
RunCleanupsScope Scope(*this);
|
||||
|
||||
// When building in Objective-C++ ARC mode, create an autorelease pool
|
||||
@@ -407,7 +451,12 @@ void CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn,
|
||||
EmitRuntimeCall(Decls[i]);
|
||||
|
||||
Scope.ForceCleanup();
|
||||
|
||||
|
||||
if (ExitBlock) {
|
||||
Builder.CreateBr(ExitBlock);
|
||||
EmitBlock(ExitBlock);
|
||||
}
|
||||
|
||||
FinishFunction();
|
||||
}
|
||||
|
||||
|
||||
@@ -1824,8 +1824,12 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
|
||||
|
||||
if (const VarDecl *VD = dyn_cast<VarDecl>(ND)) {
|
||||
// Check if this is a global variable.
|
||||
if (VD->hasLinkage() || VD->isStaticDataMember())
|
||||
if (VD->hasLinkage() || VD->isStaticDataMember()) {
|
||||
// If it's thread_local, emit a call to its wrapper function instead.
|
||||
if (VD->getTLSKind() == VarDecl::TLS_Dynamic)
|
||||
return CGM.getCXXABI().EmitThreadLocalDeclRefExpr(*this, E);
|
||||
return EmitGlobalVarDeclLValue(*this, E, VD);
|
||||
}
|
||||
|
||||
bool isBlockVariable = VD->hasAttr<BlocksAttr>();
|
||||
|
||||
|
||||
@@ -2628,7 +2628,8 @@ public:
|
||||
/// variables.
|
||||
void GenerateCXXGlobalInitFunc(llvm::Function *Fn,
|
||||
llvm::Constant **Decls,
|
||||
unsigned NumDecls);
|
||||
unsigned NumDecls,
|
||||
llvm::GlobalVariable *Guard = 0);
|
||||
|
||||
/// GenerateCXXGlobalDtorsFunc - Generates code for destroying global
|
||||
/// variables.
|
||||
|
||||
@@ -176,6 +176,7 @@ void CodeGenModule::Release() {
|
||||
EmitDeferred();
|
||||
EmitCXXGlobalInitFunc();
|
||||
EmitCXXGlobalDtorFunc();
|
||||
EmitCXXThreadLocalInitFunc();
|
||||
if (ObjCRuntime)
|
||||
if (llvm::Function *ObjCInitFunction = ObjCRuntime->ModuleInitFunction())
|
||||
AddGlobalCtor(ObjCInitFunction);
|
||||
@@ -1477,8 +1478,10 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName,
|
||||
GV->setVisibility(GetLLVMVisibility(LV.getVisibility()));
|
||||
}
|
||||
|
||||
if (D->getTLSKind())
|
||||
if (D->getTLSKind()) {
|
||||
CXXThreadLocals.push_back(std::make_pair(D, GV));
|
||||
setTLSMode(GV, *D);
|
||||
}
|
||||
}
|
||||
|
||||
if (AddrSpace != Ty->getAddressSpace())
|
||||
|
||||
@@ -317,6 +317,14 @@ class CodeGenModule : public CodeGenTypeCache {
|
||||
llvm::GlobalValue *> StaticExternCMap;
|
||||
StaticExternCMap StaticExternCValues;
|
||||
|
||||
/// \brief thread_local variables defined or used in this TU.
|
||||
std::vector<std::pair<const VarDecl *, llvm::GlobalVariable *> >
|
||||
CXXThreadLocals;
|
||||
|
||||
/// \brief thread_local variables with initializers that need to run
|
||||
/// before any thread_local variable in this TU is odr-used.
|
||||
std::vector<llvm::Constant*> CXXThreadLocalInits;
|
||||
|
||||
/// CXXGlobalInits - Global variables with initializers that need to run
|
||||
/// before main.
|
||||
std::vector<llvm::Constant*> CXXGlobalInits;
|
||||
@@ -1026,6 +1034,9 @@ private:
|
||||
/// a C++ destructor Decl.
|
||||
void EmitCXXDestructor(const CXXDestructorDecl *D, CXXDtorType Type);
|
||||
|
||||
/// \brief Emit the function that initializes C++ thread_local variables.
|
||||
void EmitCXXThreadLocalInitFunc();
|
||||
|
||||
/// EmitCXXGlobalInitFunc - Emit the function that initializes C++ globals.
|
||||
void EmitCXXGlobalInitFunc();
|
||||
|
||||
|
||||
@@ -146,6 +146,14 @@ public:
|
||||
llvm::GlobalVariable *DeclPtr, bool PerformInit);
|
||||
void registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D,
|
||||
llvm::Constant *dtor, llvm::Constant *addr);
|
||||
|
||||
llvm::Function *getOrCreateThreadLocalWrapper(const VarDecl *VD,
|
||||
llvm::GlobalVariable *Var);
|
||||
void EmitThreadLocalInitFuncs(
|
||||
llvm::ArrayRef<std::pair<const VarDecl *, llvm::GlobalVariable *> > Decls,
|
||||
llvm::Function *InitFunc);
|
||||
LValue EmitThreadLocalDeclRefExpr(CodeGenFunction &CGF,
|
||||
const DeclRefExpr *DRE);
|
||||
};
|
||||
|
||||
class ARMCXXABI : public ItaniumCXXABI {
|
||||
@@ -1249,3 +1257,138 @@ void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF,
|
||||
|
||||
CGF.registerGlobalDtorWithAtExit(dtor, addr);
|
||||
}
|
||||
|
||||
/// Get the appropriate linkage for the wrapper function. This is essentially
|
||||
/// the weak form of the variable's linkage; every translation unit which wneeds
|
||||
/// the wrapper emits a copy, and we want the linker to merge them.
|
||||
static llvm::GlobalValue::LinkageTypes getThreadLocalWrapperLinkage(
|
||||
llvm::GlobalValue::LinkageTypes VarLinkage) {
|
||||
if (llvm::GlobalValue::isLinkerPrivateLinkage(VarLinkage))
|
||||
return llvm::GlobalValue::LinkerPrivateWeakLinkage;
|
||||
// For internal linkage variables, we don't need an external or weak wrapper.
|
||||
if (llvm::GlobalValue::isLocalLinkage(VarLinkage))
|
||||
return VarLinkage;
|
||||
return llvm::GlobalValue::WeakODRLinkage;
|
||||
}
|
||||
|
||||
llvm::Function *
|
||||
ItaniumCXXABI::getOrCreateThreadLocalWrapper(const VarDecl *VD,
|
||||
llvm::GlobalVariable *Var) {
|
||||
// Mangle the name for the thread_local wrapper function.
|
||||
SmallString<256> WrapperName;
|
||||
{
|
||||
llvm::raw_svector_ostream Out(WrapperName);
|
||||
getMangleContext().mangleItaniumThreadLocalWrapper(VD, Out);
|
||||
Out.flush();
|
||||
}
|
||||
|
||||
if (llvm::Value *V = Var->getParent()->getNamedValue(WrapperName))
|
||||
return cast<llvm::Function>(V);
|
||||
|
||||
llvm::Type *RetTy = Var->getType();
|
||||
if (VD->getType()->isReferenceType())
|
||||
RetTy = RetTy->getPointerElementType();
|
||||
|
||||
llvm::FunctionType *FnTy = llvm::FunctionType::get(RetTy, false);
|
||||
llvm::Function *Wrapper = llvm::Function::Create(
|
||||
FnTy, getThreadLocalWrapperLinkage(Var->getLinkage()), WrapperName.str(),
|
||||
&CGM.getModule());
|
||||
// Always resolve references to the wrapper at link time.
|
||||
Wrapper->setVisibility(llvm::GlobalValue::HiddenVisibility);
|
||||
return Wrapper;
|
||||
}
|
||||
|
||||
void ItaniumCXXABI::EmitThreadLocalInitFuncs(
|
||||
llvm::ArrayRef<std::pair<const VarDecl *, llvm::GlobalVariable *> > Decls,
|
||||
llvm::Function *InitFunc) {
|
||||
for (unsigned I = 0, N = Decls.size(); I != N; ++I) {
|
||||
const VarDecl *VD = Decls[I].first;
|
||||
llvm::GlobalVariable *Var = Decls[I].second;
|
||||
|
||||
// Mangle the name for the thread_local initialization function.
|
||||
SmallString<256> InitFnName;
|
||||
{
|
||||
llvm::raw_svector_ostream Out(InitFnName);
|
||||
getMangleContext().mangleItaniumThreadLocalInit(VD, Out);
|
||||
Out.flush();
|
||||
}
|
||||
|
||||
// If we have a definition for the variable, emit the initialization
|
||||
// function as an alias to the global Init function (if any). Otherwise,
|
||||
// produce a declaration of the initialization function.
|
||||
llvm::GlobalValue *Init = 0;
|
||||
bool InitIsInitFunc = false;
|
||||
if (VD->hasDefinition()) {
|
||||
InitIsInitFunc = true;
|
||||
if (InitFunc)
|
||||
Init =
|
||||
new llvm::GlobalAlias(InitFunc->getType(), Var->getLinkage(),
|
||||
InitFnName.str(), InitFunc, &CGM.getModule());
|
||||
} else {
|
||||
// Emit a weak global function referring to the initialization function.
|
||||
// This function will not exist if the TU defining the thread_local
|
||||
// variable in question does not need any dynamic initialization for
|
||||
// its thread_local variables.
|
||||
llvm::FunctionType *FnTy = llvm::FunctionType::get(CGM.VoidTy, false);
|
||||
Init = llvm::Function::Create(
|
||||
FnTy, llvm::GlobalVariable::ExternalWeakLinkage, InitFnName.str(),
|
||||
&CGM.getModule());
|
||||
}
|
||||
|
||||
if (Init)
|
||||
Init->setVisibility(Var->getVisibility());
|
||||
|
||||
llvm::Function *Wrapper = getOrCreateThreadLocalWrapper(VD, Var);
|
||||
llvm::LLVMContext &Context = CGM.getModule().getContext();
|
||||
llvm::BasicBlock *Entry = llvm::BasicBlock::Create(Context, "", Wrapper);
|
||||
CGBuilderTy Builder(Entry);
|
||||
if (InitIsInitFunc) {
|
||||
if (Init)
|
||||
Builder.CreateCall(Init);
|
||||
} else {
|
||||
// Don't know whether we have an init function. Call it if it exists.
|
||||
llvm::Value *Have = Builder.CreateIsNotNull(Init);
|
||||
llvm::BasicBlock *InitBB = llvm::BasicBlock::Create(Context, "", Wrapper);
|
||||
llvm::BasicBlock *ExitBB = llvm::BasicBlock::Create(Context, "", Wrapper);
|
||||
Builder.CreateCondBr(Have, InitBB, ExitBB);
|
||||
|
||||
Builder.SetInsertPoint(InitBB);
|
||||
Builder.CreateCall(Init);
|
||||
Builder.CreateBr(ExitBB);
|
||||
|
||||
Builder.SetInsertPoint(ExitBB);
|
||||
}
|
||||
|
||||
// For a reference, the result of the wrapper function is a pointer to
|
||||
// the referenced object.
|
||||
llvm::Value *Val = Var;
|
||||
if (VD->getType()->isReferenceType()) {
|
||||
llvm::LoadInst *LI = Builder.CreateLoad(Val);
|
||||
LI->setAlignment(CGM.getContext().getDeclAlign(VD).getQuantity());
|
||||
Val = LI;
|
||||
}
|
||||
|
||||
Builder.CreateRet(Val);
|
||||
}
|
||||
}
|
||||
|
||||
LValue ItaniumCXXABI::EmitThreadLocalDeclRefExpr(CodeGenFunction &CGF,
|
||||
const DeclRefExpr *DRE) {
|
||||
const VarDecl *VD = cast<VarDecl>(DRE->getDecl());
|
||||
QualType T = VD->getType();
|
||||
llvm::Type *Ty = CGF.getTypes().ConvertTypeForMem(T);
|
||||
llvm::Value *Val = CGF.CGM.GetAddrOfGlobalVar(VD, Ty);
|
||||
llvm::Function *Wrapper =
|
||||
getOrCreateThreadLocalWrapper(VD, cast<llvm::GlobalVariable>(Val));
|
||||
|
||||
Val = CGF.Builder.CreateCall(Wrapper);
|
||||
|
||||
LValue LV;
|
||||
if (VD->getType()->isReferenceType())
|
||||
LV = CGF.MakeNaturalAlignAddrLValue(Val, T);
|
||||
else
|
||||
LV = CGF.MakeAddrLValue(Val, DRE->getType(),
|
||||
CGF.getContext().getDeclAlign(VD));
|
||||
// FIXME: need setObjCGCLValueClass?
|
||||
return LV;
|
||||
}
|
||||
|
||||
26
clang/test/CodeGenCXX/cxx11-thread-local-reference.cpp
Normal file
26
clang/test/CodeGenCXX/cxx11-thread-local-reference.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
|
||||
|
||||
int &f();
|
||||
|
||||
// CHECK: @r = thread_local global i32* null
|
||||
thread_local int &r = f();
|
||||
|
||||
// CHECK: @_ZTH1r = alias void ()* @__tls_init
|
||||
|
||||
int &g() { return r; }
|
||||
|
||||
// CHECK: define {{.*}} @[[R_INIT:.*]]()
|
||||
// CHECK: call i32* @_Z1fv()
|
||||
// CHECK: store i32* %{{.*}}, i32** @r, align 8
|
||||
|
||||
// CHECK: define i32* @_Z1gv()
|
||||
// CHECK: call i32* @_ZTW1r()
|
||||
// CHECK: ret i32* %{{.*}}
|
||||
|
||||
// CHECK: define weak_odr hidden i32* @_ZTW1r() {
|
||||
// CHECK: call void @_ZTH1r()
|
||||
// CHECK: load i32** @r, align 8
|
||||
// CHECK: ret i32* %{{.*}}
|
||||
|
||||
// CHECK: define internal void @__tls_init()
|
||||
// CHECK: call void @[[R_INIT]]()
|
||||
@@ -1,8 +1,30 @@
|
||||
// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
|
||||
|
||||
int f();
|
||||
int g();
|
||||
|
||||
// CHECK: @a = thread_local global i32 0
|
||||
thread_local int a = f();
|
||||
extern thread_local int b;
|
||||
// CHECK: @c = global i32 0
|
||||
int c = b;
|
||||
// CHECK: @_ZL1d = internal thread_local global i32 0
|
||||
static thread_local int d = g();
|
||||
|
||||
struct U { static thread_local int m; };
|
||||
// CHECK: @_ZN1U1mE = thread_local global i32 0
|
||||
thread_local int U::m = f();
|
||||
|
||||
template<typename T> struct V { static thread_local int m; };
|
||||
template<typename T> thread_local int V<T>::m = g();
|
||||
|
||||
// CHECK: @e = global i32 0
|
||||
int e = V<int>::m;
|
||||
|
||||
// CHECK: @_ZN1VIiE1mE = weak_odr thread_local global i32 0
|
||||
|
||||
// CHECK: @_ZZ1fvE1n = internal thread_local global i32 0
|
||||
|
||||
// CHECK: @_ZGVZ1fvE1n = internal thread_local global i8 0
|
||||
|
||||
// CHECK: @_ZZ8tls_dtorvE1s = internal thread_local global
|
||||
@@ -15,6 +37,24 @@ int g();
|
||||
// CHECK: @_ZGVZ8tls_dtorvE1u = internal thread_local global i8 0
|
||||
// CHECK: @_ZGRZ8tls_dtorvE1u = internal thread_local global
|
||||
|
||||
// CHECK: @_ZGVN1VIiE1mE = weak_odr thread_local global i64 0
|
||||
|
||||
// CHECK: @__tls_guard = internal thread_local global i8 0
|
||||
|
||||
// CHECK: @llvm.global_ctors = appending global {{.*}} @[[GLOBAL_INIT:[^ ]*]]
|
||||
|
||||
// CHECK: @_ZTH1a = alias void ()* @__tls_init
|
||||
// CHECK: @_ZTHL1d = alias internal void ()* @__tls_init
|
||||
// CHECK: @_ZTHN1U1mE = alias void ()* @__tls_init
|
||||
// CHECK: @_ZTHN1VIiE1mE = alias weak_odr void ()* @__tls_init
|
||||
|
||||
|
||||
// Individual variable initialization functions:
|
||||
|
||||
// CHECK: define {{.*}} @[[A_INIT:.*]]()
|
||||
// CHECK: call i32 @_Z1fv()
|
||||
// CHECK-NEXT: store i32 {{.*}}, i32* @a, align 4
|
||||
|
||||
// CHECK: define i32 @_Z1fv()
|
||||
int f() {
|
||||
// CHECK: %[[GUARD:.*]] = load i8* @_ZGVZ1fvE1n, align 1
|
||||
@@ -31,6 +71,37 @@ int f() {
|
||||
return n;
|
||||
}
|
||||
|
||||
// CHECK: define {{.*}} @[[C_INIT:.*]]()
|
||||
// CHECK: call i32* @_ZTW1b()
|
||||
// CHECK-NEXT: load i32* %{{.*}}, align 4
|
||||
// CHECK-NEXT: store i32 %{{.*}}, i32* @c, align 4
|
||||
|
||||
// CHECK: define weak_odr hidden i32* @_ZTW1b()
|
||||
// CHECK: br i1 icmp ne (void ()* @_ZTH1b, void ()* null),
|
||||
// not null:
|
||||
// CHECK: call void @_ZTH1b()
|
||||
// CHECK: br label
|
||||
// finally:
|
||||
// CHECK: ret i32* @b
|
||||
|
||||
// CHECK: define {{.*}} @[[D_INIT:.*]]()
|
||||
// CHECK: call i32 @_Z1gv()
|
||||
// CHECK-NEXT: store i32 %{{.*}}, i32* @_ZL1d, align 4
|
||||
|
||||
// CHECK: define {{.*}} @[[U_M_INIT:.*]]()
|
||||
// CHECK: call i32 @_Z1fv()
|
||||
// CHECK-NEXT: store i32 %{{.*}}, i32* @_ZN1U1mE, align 4
|
||||
|
||||
// CHECK: define {{.*}} @[[E_INIT:.*]]()
|
||||
// CHECK: call i32* @_ZTWN1VIiE1mE()
|
||||
// CHECK-NEXT: load i32* %{{.*}}, align 4
|
||||
// CHECK-NEXT: store i32 %{{.*}}, i32* @e, align 4
|
||||
|
||||
// CHECK: define weak_odr hidden i32* @_ZTWN1VIiE1mE()
|
||||
// CHECK: call void @_ZTHN1VIiE1mE()
|
||||
// CHECK: ret i32* @_ZN1VIiE1mE
|
||||
|
||||
|
||||
struct S { S(); ~S(); };
|
||||
struct T { ~T(); };
|
||||
|
||||
@@ -54,3 +125,49 @@ void tls_dtor() {
|
||||
// CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1u
|
||||
static thread_local const S &u = S();
|
||||
}
|
||||
|
||||
// CHECK: declare i32 @__cxa_thread_atexit(void (i8*)*, i8*, i8*)
|
||||
|
||||
// CHECK: define {{.*}} @[[V_M_INIT:.*]]()
|
||||
// CHECK: load i8* bitcast (i64* @_ZGVN1VIiE1mE to i8*)
|
||||
// CHECK: %[[V_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0
|
||||
// CHECK: br i1 %[[V_M_INITIALIZED]],
|
||||
// need init:
|
||||
// CHECK: call i32 @_Z1gv()
|
||||
// CHECK: store i32 %{{.*}}, i32* @_ZN1VIiE1mE, align 4
|
||||
// CHECK: store i64 1, i64* @_ZGVN1VIiE1mE
|
||||
// CHECK: br label
|
||||
|
||||
// CHECK: define {{.*}}@[[GLOBAL_INIT:.*]]()
|
||||
// CHECK: call void @[[C_INIT]]()
|
||||
// CHECK: call void @[[E_INIT]]()
|
||||
|
||||
|
||||
// CHECK: define {{.*}}@__tls_init()
|
||||
// CHECK: load i8* @__tls_guard
|
||||
// CHECK: %[[NEED_TLS_INIT:.*]] = icmp eq i8 %{{.*}}, 0
|
||||
// CHECK: store i8 1, i8* @__tls_guard
|
||||
// CHECK: br i1 %[[NEED_TLS_INIT]],
|
||||
// init:
|
||||
// CHECK: call void @[[A_INIT]]()
|
||||
// CHECK: call void @[[D_INIT]]()
|
||||
// CHECK: call void @[[U_M_INIT]]()
|
||||
// CHECK: call void @[[V_M_INIT]]()
|
||||
|
||||
|
||||
// CHECK: define weak_odr hidden i32* @_ZTW1a() {
|
||||
// CHECK: call void @_ZTH1a()
|
||||
// CHECK: ret i32* @a
|
||||
// CHECK: }
|
||||
|
||||
|
||||
// CHECK: declare extern_weak void @_ZTH1b()
|
||||
|
||||
|
||||
// CHECK: define internal hidden i32* @_ZTWL1d()
|
||||
// CHECK: call void @_ZTHL1d()
|
||||
// CHECK: ret i32* @_ZL1d
|
||||
|
||||
// CHECK: define weak_odr hidden i32* @_ZTWN1U1mE()
|
||||
// CHECK: call void @_ZTHN1U1mE()
|
||||
// CHECK: ret i32* @_ZN1U1mE
|
||||
|
||||
Reference in New Issue
Block a user