]> granicus.if.org Git - clang/commitdiff
CodeGen support for function-local static thread_local variables with
authorRichard Smith <richard-llvm@metafoo.co.uk>
Sun, 14 Apr 2013 23:01:42 +0000 (23:01 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Sun, 14 Apr 2013 23:01:42 +0000 (23:01 +0000)
non-constant constructors or non-trivial destructors. Plus bugfixes for
thread_local references bound to temporaries (the temporaries themselves are
lifetime-extended to become thread_local), and the corresponding case for
std::initializer_list.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@179496 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGCXXABI.cpp
lib/CodeGen/CGCXXABI.h
lib/CodeGen/CGDeclCXX.cpp
lib/CodeGen/CGExpr.cpp
lib/CodeGen/CodeGenModule.cpp
lib/CodeGen/ItaniumCXXABI.cpp
lib/CodeGen/MicrosoftCXXABI.cpp
test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp
test/CodeGenCXX/cxx11-thread-local.cpp [new file with mode: 0644]

index 0c0a76f346a5baa41cc9cf3c394192026c1b7121..3bf64802983ca1a55c64f1f11c71849946709a08 100644 (file)
@@ -220,8 +220,12 @@ void CGCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
 }
 
 void CGCXXABI::registerGlobalDtor(CodeGenFunction &CGF,
+                                  const VarDecl &D,
                                   llvm::Constant *dtor,
                                   llvm::Constant *addr) {
+  if (D.getTLSKind())
+    CGM.ErrorUnsupported(&D, "non-trivial TLS destruction");
+
   // The default behavior is to use atexit.
   CGF.registerGlobalDtorWithAtExit(dtor, addr);
 }
index 702e59b71a72419106514de10aa134d57d225665..1f46f1212c8c5b96044f2277847555df65d43fb5 100644 (file)
@@ -330,8 +330,8 @@ public:
   ///
   /// \param dtor - a function taking a single pointer argument
   /// \param addr - a pointer to pass to the destructor function.
-  virtual void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor,
-                                  llvm::Constant *addr);
+  virtual void registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D,
+                                  llvm::Constant *dtor, llvm::Constant *addr);
 };
 
 // Create an instance of a C++ ABI class:
index 609f6aa7c6e6ee92b75b1bca63c6453185aaec70..fd766bf3deb13f2ef82c295a4c083cc5adcc104a 100644 (file)
@@ -80,6 +80,7 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D,
   case QualType::DK_objc_strong_lifetime:
   case QualType::DK_objc_weak_lifetime:
     // We don't care about releasing objects during process teardown.
+    assert(!D.getTLSKind() && "should have rejected this");
     return;
   }
 
@@ -105,7 +106,7 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D,
     argument = llvm::Constant::getNullValue(CGF.Int8PtrTy);
   }
 
-  CGM.getCXXABI().registerGlobalDtor(CGF, function, argument);
+  CGM.getCXXABI().registerGlobalDtor(CGF, D, function, argument);
 }
 
 /// Emit code to cause the variable at the given address to be considered as
@@ -218,9 +219,6 @@ void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D,
               "this initialization requires a guard variable, which "
               "the kernel does not support");
 
-  if (D.getTLSKind())
-    CGM.ErrorUnsupported(D.getInit(), "dynamic TLS initialization");
-
   CGM.getCXXABI().EmitGuardedInit(*this, D, DeclPtr, PerformInit);
 }
 
index c515508875836835226687145b90bcb246bb9f98..1c06b7d2d982282f7e381b8bd8a5bc99e8aae16b 100644 (file)
@@ -184,12 +184,16 @@ CreateReferenceTemporary(CodeGenFunction &CGF, QualType Type,
       llvm::Type *RefTempTy = CGF.ConvertTypeForMem(Type);
   
       // Create the reference temporary.
-      llvm::GlobalValue *RefTemp =
+      llvm::GlobalVariable *RefTemp =
         new llvm::GlobalVariable(CGF.CGM.getModule(), 
                                  RefTempTy, /*isConstant=*/false,
                                  llvm::GlobalValue::InternalLinkage,
                                  llvm::Constant::getNullValue(RefTempTy),
                                  Name.str());
+      // If we're binding to a thread_local variable, the temporary is also
+      // thread local.
+      if (VD->getTLSKind())
+        CGF.CGM.setTLSMode(RefTemp, *VD);
       return RefTemp;
     }
   }
@@ -434,12 +438,15 @@ CodeGenFunction::EmitReferenceBindingToExpr(const Expr *E,
           CGM.GetAddrOfCXXDestructor(ReferenceTemporaryDtor, Dtor_Complete);
         CleanupArg = cast<llvm::Constant>(ReferenceTemporary);
       }
-      CGM.getCXXABI().registerGlobalDtor(*this, CleanupFn, CleanupArg);
+      CGM.getCXXABI().registerGlobalDtor(*this, *VD, CleanupFn, CleanupArg);
     } else if (ReferenceInitializerList) {
+      // FIXME: This is wrong. We need to register a global destructor to clean
+      // up the initializer_list object, rather than adding it as a local
+      // cleanup.
       EmitStdInitializerListCleanup(ReferenceTemporary,
                                     ReferenceInitializerList);
     } else {
-      assert(!ObjCARCReferenceLifetimeType.isNull());
+      assert(!ObjCARCReferenceLifetimeType.isNull() && !VD->getTLSKind());
       // Note: We intentionally do not register a global "destructor" to
       // release the object.
     }
index 20532d68c9eb24f22d04e9c7e855e7e82417aaca..f8627b2b70df68af25ee6736b9cb0120173b7c21 100644 (file)
@@ -1627,6 +1627,7 @@ CodeGenModule::MaybeEmitGlobalStdInitializerListInitializer(const VarDecl *D,
                                           D->getLocStart(), D->getLocation(),
                                           name, arrayType, sourceInfo,
                                           SC_Static);
+  backingArray->setTLSKind(D->getTLSKind());
 
   // Now clone the InitListExpr to initialize the array instead.
   // Incredible hack: we want to use the existing InitListExpr here, so we need
index e25d422d23676c687df5030d235c9cd126434448..25cd5f58f8f61224d27116da1cd3e2c2cac68c1c 100644 (file)
@@ -130,8 +130,8 @@ public:
 
   void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
                        llvm::GlobalVariable *DeclPtr, bool PerformInit);
-  void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor,
-                          llvm::Constant *addr);
+  void registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D,
+                          llvm::Constant *dtor, llvm::Constant *addr);
 };
 
 class ARMCXXABI : public ItaniumCXXABI {
@@ -1042,10 +1042,10 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
                                     bool shouldPerformInit) {
   CGBuilderTy &Builder = CGF.Builder;
 
-  // We only need to use thread-safe statics for local variables;
+  // We only need to use thread-safe statics for local non-TLS variables;
   // global initialization is always single-threaded.
-  bool threadsafe =
-    (getContext().getLangOpts().ThreadsafeStatics && D.isLocalVarDecl());
+  bool threadsafe = getContext().getLangOpts().ThreadsafeStatics &&
+                    D.isLocalVarDecl() && !D.getTLSKind();
 
   // If we have a global variable with internal linkage and thread-safe statics
   // are disabled, we can just let the guard variable be of type i8.
@@ -1080,6 +1080,8 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
                                      llvm::ConstantInt::get(guardTy, 0),
                                      guardName.str());
     guard->setVisibility(var->getVisibility());
+    // If the variable is thread-local, so is its guard variable.
+    guard->setThreadLocalMode(var->getThreadLocalMode());
 
     CGM.setStaticLocalDeclGuardAddress(&D, guard);
   }
@@ -1180,7 +1182,10 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
 /// Register a global destructor using __cxa_atexit.
 static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF,
                                         llvm::Constant *dtor,
-                                        llvm::Constant *addr) {
+                                        llvm::Constant *addr,
+                                        bool TLS) {
+  const char *Name = TLS ? "__cxa_thread_atexit" : "__cxa_atexit";
+
   // We're assuming that the destructor function is something we can
   // reasonably call with the default CC.  Go ahead and cast it to the
   // right prototype.
@@ -1193,8 +1198,7 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF,
     llvm::FunctionType::get(CGF.IntTy, paramTys, false);
 
   // Fetch the actual function.
-  llvm::Constant *atexit =
-    CGF.CGM.CreateRuntimeFunction(atexitTy, "__cxa_atexit");
+  llvm::Constant *atexit = CGF.CGM.CreateRuntimeFunction(atexitTy, Name);
   if (llvm::Function *fn = dyn_cast<llvm::Function>(atexit))
     fn->setDoesNotThrow();
 
@@ -1212,12 +1216,15 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF,
 
 /// Register a global destructor as best as we know how.
 void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF,
+                                       const VarDecl &D,
                                        llvm::Constant *dtor,
                                        llvm::Constant *addr) {
   // Use __cxa_atexit if available.
-  if (CGM.getCodeGenOpts().CXAAtExit) {
-    return emitGlobalDtorWithCXAAtExit(CGF, dtor, addr);
-  }
+  if (CGM.getCodeGenOpts().CXAAtExit)
+    return emitGlobalDtorWithCXAAtExit(CGF, dtor, addr, D.getTLSKind());
+
+  if (D.getTLSKind())
+    CGM.ErrorUnsupported(&D, "non-trivial TLS destruction");
 
   // In Apple kexts, we want to add a global destructor entry.
   // FIXME: shouldn't this be guarded by some variable?
index 2c7f89aa508ef4e47a1d89ab2b84873c9bbf6174..a0e4f4d38478d17081369500f758a283178958aa 100644 (file)
@@ -392,6 +392,9 @@ void MicrosoftCXXABI::EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
   // Not sure whether we want thread-safe static local variables as VS
   // doesn't make them thread-safe.
 
+  if (D.getTLSKind())
+    CGM.ErrorUnsupported(&D, "dynamic TLS initialization");
+
   // Emit the initializer and add a global destructor if appropriate.
   CGF.EmitCXXGlobalVarDeclInit(D, DeclPtr, PerformInit);
 }
index fc908804c0734055efafc860238e466589983e69..d683493a83d812fc4ed3168703c5d0cc993d4060 100644 (file)
@@ -51,6 +51,12 @@ struct wantslist1 {
 // CHECK: @globalInitList1 = global %{{[^ ]+}} { i32* getelementptr inbounds ([3 x i32]* @_ZL25globalInitList1__initlist, i32 0, i32 0), i{{32|64}} 3 }
 std::initializer_list<int> globalInitList1 = {1, 2, 3};
 
+namespace thread_local_global_array {
+  // CHECK: @_ZN25thread_local_global_arrayL11x__initlistE = internal thread_local global [4 x i32] [i32 1, i32 2, i32 3, i32 4]
+  // CHECK: @_ZN25thread_local_global_array1xE = thread_local global {{.*}} @_ZN25thread_local_global_arrayL11x__initlistE, {{.*}} i64 4
+  std::initializer_list<int> thread_local x = { 1, 2, 3, 4 };
+}
+
 // CHECK: @_ZL25globalInitList2__initlist = internal global [2 x %{{[^ ]*}}] zeroinitializer
 // CHECK: @globalInitList2 = global %{{[^ ]+}} { %[[WITHARG:[^ *]+]]* getelementptr inbounds ([2 x
 // CHECK: appending global
diff --git a/test/CodeGenCXX/cxx11-thread-local.cpp b/test/CodeGenCXX/cxx11-thread-local.cpp
new file mode 100644 (file)
index 0000000..5ed582d
--- /dev/null
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
+
+int g();
+
+// CHECK: @_ZZ1fvE1n = internal thread_local global i32 0
+// CHECK: @_ZGVZ1fvE1n = internal thread_local global i8 0
+
+// CHECK: @_ZZ8tls_dtorvE1s = internal thread_local global
+// CHECK: @_ZGVZ8tls_dtorvE1s = internal thread_local global i8 0
+
+// CHECK: @_ZZ8tls_dtorvE1t = internal thread_local global
+// CHECK: @_ZGVZ8tls_dtorvE1t = internal thread_local global i8 0
+
+// CHECK: @_ZZ8tls_dtorvE1u = internal thread_local global
+// CHECK: @_ZGVZ8tls_dtorvE1u = internal thread_local global i8 0
+// CHECK: @_ZGRZ8tls_dtorvE1u = internal thread_local global
+
+// CHECK: define i32 @_Z1fv()
+int f() {
+  // CHECK: %[[GUARD:.*]] = load i8* @_ZGVZ1fvE1n, align 1
+  // CHECK: %[[NEED_INIT:.*]] = icmp eq i8 %[[GUARD]], 0
+  // CHECK: br i1 %[[NEED_INIT]]
+
+  // CHECK: %[[CALL:.*]] = call i32 @_Z1gv()
+  // CHECK: store i32 %[[CALL]], i32* @_ZZ1fvE1n, align 4
+  // CHECK: store i8 1, i8* @_ZGVZ1fvE1n
+  // CHECK: br label
+  static thread_local int n = g();
+
+  // CHECK: load i32* @_ZZ1fvE1n, align 4
+  return n;
+}
+
+struct S { S(); ~S(); };
+struct T { ~T(); };
+
+// CHECK: define void @_Z8tls_dtorv()
+void tls_dtor() {
+  // CHECK: load i8* @_ZGVZ8tls_dtorvE1s
+  // CHECK: call void @_ZN1SC1Ev(%struct.S* @_ZZ8tls_dtorvE1s)
+  // CHECK: call i32 @__cxa_thread_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZZ8tls_dtorvE1s{{.*}} @__dso_handle
+  // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1s
+  static thread_local S s;
+
+  // CHECK: load i8* @_ZGVZ8tls_dtorvE1t
+  // CHECK-NOT: _ZN1T
+  // CHECK: call i32 @__cxa_thread_atexit({{.*}}@_ZN1TD1Ev {{.*}}@_ZZ8tls_dtorvE1t{{.*}} @__dso_handle
+  // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1t
+  static thread_local T t;
+
+  // CHECK: load i8* @_ZGVZ8tls_dtorvE1u
+  // CHECK: call void @_ZN1SC1Ev(%struct.S* @_ZGRZ8tls_dtorvE1u)
+  // CHECK: call i32 @__cxa_thread_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZGRZ8tls_dtorvE1u{{.*}} @__dso_handle
+  // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1u
+  static thread_local const S &u = S();
+}