diff options
Diffstat (limited to 'lib/CodeGen')
-rw-r--r-- | lib/CodeGen/CGCXXABI.cpp | 11 | ||||
-rw-r--r-- | lib/CodeGen/CGCXXABI.h | 19 | ||||
-rw-r--r-- | lib/CodeGen/CGDeclCXX.cpp | 69 | ||||
-rw-r--r-- | lib/CodeGen/CGExpr.cpp | 6 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenFunction.h | 3 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenModule.cpp | 5 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenModule.h | 11 | ||||
-rw-r--r-- | lib/CodeGen/ItaniumCXXABI.cpp | 143 |
8 files changed, 254 insertions, 13 deletions
diff --git a/lib/CodeGen/CGCXXABI.cpp b/lib/CodeGen/CGCXXABI.cpp index 3bf6480298..68fecb2d0c 100644 --- a/lib/CodeGen/CGCXXABI.cpp +++ b/lib/CodeGen/CGCXXABI.cpp @@ -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(); +} diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h index b736d9437e..1e4da631d6 100644 --- a/lib/CodeGen/CGCXXABI.h +++ b/lib/CodeGen/CGCXXABI.h @@ -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: diff --git a/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp index b18fe45296..d406227301 100644 --- a/lib/CodeGen/CGDeclCXX.cpp +++ b/lib/CodeGen/CGDeclCXX.cpp @@ -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(); } diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 1c06b7d2d9..a0725b5f9c 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -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>(); diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index f4ac0866c8..6e2d62760f 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -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. diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 42be779afb..fdd3568472 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -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()) diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 1a803f38c0..0f4fe8ae51 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -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(); diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 382aa47957..2714c9e728 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -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; +} |