forked from OSchip/llvm-project
CodeGen support for function-local static thread_local variables with
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. llvm-svn: 179496
This commit is contained in:
parent
e45f58d8a9
commit
dbf74baee5
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
Loading…
Reference in New Issue