Skip to content

Commit 072e2a7

Browse files
momo5502majnemer
authored andcommitted
[MS] Implement on-demand TLS initialization for Microsoft CXX ABI
TLS initializers, for example constructors of thread-local variables, don't necessarily get called. If a thread was created before a module is loaded, the module's TLS initializers are not executed for this particular thread. This is why Microsoft added support for dynamic TLS initialization. Before every use of thread-local variables, a check is added that runs the module's TLS initializers on-demand. To do this, the method `__dyn_tls_on_demand_init` gets called. Internally, it simply calls `__dyn_tls_init`. No additional TLS initializer that sets the guard needs to be emitted, as the guard always gets set by `__dyn_tls_init`. The guard is also checked again within `__dyn_tls_init`. This makes our check redundant, however, as Microsoft's compiler also emits this check, the behaviour is adopted here. Reviewed By: majnemer Differential Revision: https://reviews.llvm.org/D115456
1 parent 11067d7 commit 072e2a7

File tree

6 files changed

+180
-62
lines changed

6 files changed

+180
-62
lines changed

clang/include/clang/Basic/LangOptions.h

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ class LangOptions : public LangOptionsBase {
124124
MSVC2017_5 = 1912,
125125
MSVC2017_7 = 1914,
126126
MSVC2019 = 1920,
127+
MSVC2019_5 = 1925,
127128
MSVC2019_8 = 1928,
128129
};
129130

clang/lib/CodeGen/CGCXXABI.cpp

+45
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,51 @@ void CGCXXABI::setCXXABIThisValue(CodeGenFunction &CGF, llvm::Value *ThisPtr) {
154154
CGF.CXXABIThisValue = ThisPtr;
155155
}
156156

157+
bool CGCXXABI::mayNeedDestruction(const VarDecl *VD) const {
158+
if (VD->needsDestruction(getContext()))
159+
return true;
160+
161+
// If the variable has an incomplete class type (or array thereof), it
162+
// might need destruction.
163+
const Type *T = VD->getType()->getBaseElementTypeUnsafe();
164+
if (T->getAs<RecordType>() && T->isIncompleteType())
165+
return true;
166+
167+
return false;
168+
}
169+
170+
bool CGCXXABI::isEmittedWithConstantInitializer(
171+
const VarDecl *VD, bool InspectInitForWeakDef) const {
172+
VD = VD->getMostRecentDecl();
173+
if (VD->hasAttr<ConstInitAttr>())
174+
return true;
175+
176+
// All later checks examine the initializer specified on the variable. If
177+
// the variable is weak, such examination would not be correct.
178+
if (!InspectInitForWeakDef && (VD->isWeak() || VD->hasAttr<SelectAnyAttr>()))
179+
return false;
180+
181+
const VarDecl *InitDecl = VD->getInitializingDeclaration();
182+
if (!InitDecl)
183+
return false;
184+
185+
// If there's no initializer to run, this is constant initialization.
186+
if (!InitDecl->hasInit())
187+
return true;
188+
189+
// If we have the only definition, we don't need a thread wrapper if we
190+
// will emit the value as a constant.
191+
if (isUniqueGVALinkage(getContext().GetGVALinkageForVariable(VD)))
192+
return !mayNeedDestruction(VD) && InitDecl->evaluateValue();
193+
194+
// Otherwise, we need a thread wrapper unless we know that every
195+
// translation unit will emit the value as a constant. We rely on the
196+
// variable being constant-initialized in every translation unit if it's
197+
// constant-initialized in any translation unit, which isn't actually
198+
// guaranteed by the standard but is necessary for sanity.
199+
return InitDecl->hasConstantInitialization();
200+
}
201+
157202
void CGCXXABI::EmitReturnFromThunk(CodeGenFunction &CGF,
158203
RValue RV, QualType ResultType) {
159204
assert(!CGF.hasAggregateEvaluationKind(ResultType) &&

clang/lib/CodeGen/CGCXXABI.h

+12
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,18 @@ class CGCXXABI {
7979

8080
ASTContext &getContext() const { return CGM.getContext(); }
8181

82+
bool mayNeedDestruction(const VarDecl *VD) const;
83+
84+
/// Determine whether we will definitely emit this variable with a constant
85+
/// initializer, either because the language semantics demand it or because
86+
/// we know that the initializer is a constant.
87+
// For weak definitions, any initializer available in the current translation
88+
// is not necessarily reflective of the initializer used; such initializers
89+
// are ignored unless if InspectInitForWeakDef is true.
90+
bool
91+
isEmittedWithConstantInitializer(const VarDecl *VD,
92+
bool InspectInitForWeakDef = false) const;
93+
8294
virtual bool requiresArrayCookie(const CXXDeleteExpr *E, QualType eltType);
8395
virtual bool requiresArrayCookie(const CXXNewExpr *E);
8496

clang/lib/CodeGen/ItaniumCXXABI.cpp

-53
Original file line numberDiff line numberDiff line change
@@ -334,59 +334,6 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
334334
ArrayRef<llvm::Function *> CXXThreadLocalInits,
335335
ArrayRef<const VarDecl *> CXXThreadLocalInitVars) override;
336336

337-
bool mayNeedDestruction(const VarDecl *VD) const {
338-
if (VD->needsDestruction(getContext()))
339-
return true;
340-
341-
// If the variable has an incomplete class type (or array thereof), it
342-
// might need destruction.
343-
const Type *T = VD->getType()->getBaseElementTypeUnsafe();
344-
if (T->getAs<RecordType>() && T->isIncompleteType())
345-
return true;
346-
347-
return false;
348-
}
349-
350-
/// Determine whether we will definitely emit this variable with a constant
351-
/// initializer, either because the language semantics demand it or because
352-
/// we know that the initializer is a constant.
353-
// For weak definitions, any initializer available in the current translation
354-
// is not necessarily reflective of the initializer used; such initializers
355-
// are ignored unless if InspectInitForWeakDef is true.
356-
bool
357-
isEmittedWithConstantInitializer(const VarDecl *VD,
358-
bool InspectInitForWeakDef = false) const {
359-
VD = VD->getMostRecentDecl();
360-
if (VD->hasAttr<ConstInitAttr>())
361-
return true;
362-
363-
// All later checks examine the initializer specified on the variable. If
364-
// the variable is weak, such examination would not be correct.
365-
if (!InspectInitForWeakDef &&
366-
(VD->isWeak() || VD->hasAttr<SelectAnyAttr>()))
367-
return false;
368-
369-
const VarDecl *InitDecl = VD->getInitializingDeclaration();
370-
if (!InitDecl)
371-
return false;
372-
373-
// If there's no initializer to run, this is constant initialization.
374-
if (!InitDecl->hasInit())
375-
return true;
376-
377-
// If we have the only definition, we don't need a thread wrapper if we
378-
// will emit the value as a constant.
379-
if (isUniqueGVALinkage(getContext().GetGVALinkageForVariable(VD)))
380-
return !mayNeedDestruction(VD) && InitDecl->evaluateValue();
381-
382-
// Otherwise, we need a thread wrapper unless we know that every
383-
// translation unit will emit the value as a constant. We rely on the
384-
// variable being constant-initialized in every translation unit if it's
385-
// constant-initialized in any translation unit, which isn't actually
386-
// guaranteed by the standard but is necessary for sanity.
387-
return InitDecl->hasConstantInitialization();
388-
}
389-
390337
bool usesThreadWrapperFunction(const VarDecl *VD) const override {
391338
return !isEmittedWithConstantInitializer(VD) ||
392339
mayNeedDestruction(VD);

clang/lib/CodeGen/MicrosoftCXXABI.cpp

+91-3
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,9 @@ class MicrosoftCXXABI : public CGCXXABI {
401401
ArrayRef<const VarDecl *> CXXThreadLocalInitVars) override;
402402

403403
bool usesThreadWrapperFunction(const VarDecl *VD) const override {
404-
return false;
404+
return getContext().getLangOpts().isCompatibleWithMSVC(
405+
LangOptions::MSVC2019_5) &&
406+
(!isEmittedWithConstantInitializer(VD) || mayNeedDestruction(VD));
405407
}
406408
LValue EmitThreadLocalVarDeclLValue(CodeGenFunction &CGF, const VarDecl *VD,
407409
QualType LValType) override;
@@ -2397,11 +2399,97 @@ void MicrosoftCXXABI::EmitThreadLocalInitFuncs(
23972399
}
23982400
}
23992401

2402+
static llvm::GlobalValue *getTlsGuardVar(CodeGenModule &CGM) {
2403+
// __tls_guard comes from the MSVC runtime and reflects
2404+
// whether TLS has been initialized for a particular thread.
2405+
// It is set from within __dyn_tls_init by the runtime.
2406+
// Every library and executable has its own variable.
2407+
llvm::Type *VTy = llvm::Type::getInt8Ty(CGM.getLLVMContext());
2408+
llvm::Constant *TlsGuardConstant =
2409+
CGM.CreateRuntimeVariable(VTy, "__tls_guard");
2410+
llvm::GlobalValue *TlsGuard = cast<llvm::GlobalValue>(TlsGuardConstant);
2411+
2412+
TlsGuard->setThreadLocal(true);
2413+
2414+
return TlsGuard;
2415+
}
2416+
2417+
static llvm::FunctionCallee getDynTlsOnDemandInitFn(CodeGenModule &CGM) {
2418+
// __dyn_tls_on_demand_init comes from the MSVC runtime and triggers
2419+
// dynamic TLS initialization by calling __dyn_tls_init internally.
2420+
llvm::FunctionType *FTy =
2421+
llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), {},
2422+
/*isVarArg=*/false);
2423+
return CGM.CreateRuntimeFunction(
2424+
FTy, "__dyn_tls_on_demand_init",
2425+
llvm::AttributeList::get(CGM.getLLVMContext(),
2426+
llvm::AttributeList::FunctionIndex,
2427+
llvm::Attribute::NoUnwind),
2428+
/*Local=*/true);
2429+
}
2430+
2431+
static void emitTlsGuardCheck(CodeGenFunction &CGF, llvm::GlobalValue *TlsGuard,
2432+
llvm::BasicBlock *DynInitBB,
2433+
llvm::BasicBlock *ContinueBB) {
2434+
llvm::LoadInst *TlsGuardValue =
2435+
CGF.Builder.CreateLoad(Address(TlsGuard, CharUnits::One()));
2436+
llvm::Value *CmpResult =
2437+
CGF.Builder.CreateICmpEQ(TlsGuardValue, CGF.Builder.getInt8(0));
2438+
CGF.Builder.CreateCondBr(CmpResult, DynInitBB, ContinueBB);
2439+
}
2440+
2441+
static void emitDynamicTlsInitializationCall(CodeGenFunction &CGF,
2442+
llvm::GlobalValue *TlsGuard,
2443+
llvm::BasicBlock *ContinueBB) {
2444+
llvm::FunctionCallee Initializer = getDynTlsOnDemandInitFn(CGF.CGM);
2445+
llvm::Function *InitializerFunction =
2446+
cast<llvm::Function>(Initializer.getCallee());
2447+
llvm::CallInst *CallVal = CGF.Builder.CreateCall(InitializerFunction);
2448+
CallVal->setCallingConv(InitializerFunction->getCallingConv());
2449+
2450+
CGF.Builder.CreateBr(ContinueBB);
2451+
}
2452+
2453+
static void emitDynamicTlsInitialization(CodeGenFunction &CGF) {
2454+
llvm::BasicBlock *DynInitBB =
2455+
CGF.createBasicBlock("dyntls.dyn_init", CGF.CurFn);
2456+
llvm::BasicBlock *ContinueBB =
2457+
CGF.createBasicBlock("dyntls.continue", CGF.CurFn);
2458+
2459+
llvm::GlobalValue *TlsGuard = getTlsGuardVar(CGF.CGM);
2460+
2461+
emitTlsGuardCheck(CGF, TlsGuard, DynInitBB, ContinueBB);
2462+
CGF.Builder.SetInsertPoint(DynInitBB);
2463+
emitDynamicTlsInitializationCall(CGF, TlsGuard, ContinueBB);
2464+
CGF.Builder.SetInsertPoint(ContinueBB);
2465+
}
2466+
24002467
LValue MicrosoftCXXABI::EmitThreadLocalVarDeclLValue(CodeGenFunction &CGF,
24012468
const VarDecl *VD,
24022469
QualType LValType) {
2403-
CGF.CGM.ErrorUnsupported(VD, "thread wrappers");
2404-
return LValue();
2470+
// Dynamic TLS initialization works by checking the state of a
2471+
// guard variable (__tls_guard) to see whether TLS initialization
2472+
// for a thread has happend yet.
2473+
// If not, the initialization is triggered on-demand
2474+
// by calling __dyn_tls_on_demand_init.
2475+
emitDynamicTlsInitialization(CGF);
2476+
2477+
// Emit the variable just like any regular global variable.
2478+
2479+
llvm::Value *V = CGF.CGM.GetAddrOfGlobalVar(VD);
2480+
llvm::Type *RealVarTy = CGF.getTypes().ConvertTypeForMem(VD->getType());
2481+
2482+
unsigned AS = cast<llvm::PointerType>(V->getType())->getAddressSpace();
2483+
V = CGF.Builder.CreateBitCast(V, RealVarTy->getPointerTo(AS));
2484+
2485+
CharUnits Alignment = CGF.getContext().getDeclAlign(VD);
2486+
Address Addr(V, Alignment);
2487+
2488+
LValue LV = VD->getType()->isReferenceType()
2489+
? CGF.EmitLoadOfReferenceLValue(Addr, VD->getType(),
2490+
AlignmentSource::Decl)
2491+
: CGF.MakeAddrLValue(Addr, LValType, AlignmentSource::Decl);
2492+
return LV;
24052493
}
24062494

24072495
static ConstantAddress getInitThreadEpochPtr(CodeGenModule &CGM) {

clang/test/CodeGenCXX/ms-thread_local.cpp

+31-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
// RUN: %clang_cc1 %s -std=c++1y -triple=i686-pc-win32 -emit-llvm -o - | FileCheck %s
2-
// RUN: %clang_cc1 %s -std=c++1y -triple=i686-pc-win32 -ftls-model=local-dynamic -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-LD
1+
// RUN: %clang_cc1 %s -std=c++1y -triple=i686-pc-win32 -fms-compatibility-version=19.25 -emit-llvm -o - | FileCheck %s
2+
// RUN: %clang_cc1 %s -std=c++1y -triple=i686-pc-win32 -fms-compatibility-version=19.20 -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-LEGACY
3+
// RUN: %clang_cc1 %s -std=c++1y -triple=i686-pc-win32 -fms-compatibility-version=19.25 -ftls-model=local-dynamic -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-LD
34

45
struct A {
56
A();
@@ -17,14 +18,22 @@ thread_local A a = A();
1718

1819
// CHECK-DAG: @"?b@@3UA@@A" = dso_local thread_local global %struct.A zeroinitializer, align 1
1920
// CHECK-DAG: @"__tls_init$initializer$" = internal constant void ()* @__tls_init, section ".CRT$XDU"
21+
// CHECK-DAG: @__tls_guard = external dso_local thread_local global i8
2022
// CHECK-LD-DAG: @"?b@@3UA@@A" = dso_local thread_local(localdynamic) global %struct.A zeroinitializer, align 1
2123
// CHECK-LD-DAG: @"__tls_init$initializer$" = internal constant void ()* @__tls_init, section ".CRT$XDU"
24+
// CHECK-LD-DAG: @__tls_guard = external dso_local thread_local global i8
25+
// CHECK-LEGACY-NOT: @__tls_guard = external dso_local thread_local global i8
2226
thread_local A b;
2327

24-
// CHECK-LABEL: define internal void @__tls_init()
25-
// CHECK: call void @"??__Eb@@YAXXZ"
26-
// CHECK-LD-LABEL: define internal void @__tls_init()
27-
// CHECK-LD: call void @"??__Eb@@YAXXZ"
28+
// CHECK-LABEL: declare dso_local void @__dyn_tls_on_demand_init()
29+
// CHECK-LD-LABEL: declare dso_local void @__dyn_tls_on_demand_init()
30+
// CHECK-LEGACY-NOT: declare dso_local void @__dyn_tls_on_demand_init()
31+
32+
// CHECK-LABEL: define dso_local void @"?f@@YA?AUA@@XZ"(%struct.A* noalias sret(%struct.A) align 1 %agg.result)
33+
// CHECK: call void @__dyn_tls_on_demand_init()
34+
// CHECK-LD-LABEL: define dso_local void @"?f@@YA?AUA@@XZ"(%struct.A* noalias sret(%struct.A) align 1 %agg.result)
35+
// CHECK-LD: call void @__dyn_tls_on_demand_init()
36+
// CHECK-LEGACY-NOT: call void @__dyn_tls_on_demand_init()
2837

2938
thread_local A &c = b;
3039
thread_local A &d = c;
@@ -35,6 +44,22 @@ A f() {
3544
return c;
3645
}
3746

47+
// CHECK-LABEL: define dso_local i32 @"?g@@YAHXZ"()
48+
// CHECK-NOT: call void @__dyn_tls_on_demand_init()
49+
// CHECK-LD-LABEL: define dso_local i32 @"?g@@YAHXZ"()
50+
// CHECK-LD-NOT: call void @__dyn_tls_on_demand_init()
51+
52+
thread_local int e = 2;
53+
54+
int g() {
55+
return e;
56+
}
57+
58+
// CHECK-LABEL: define internal void @__tls_init()
59+
// CHECK: call void @"??__Eb@@YAXXZ"
60+
// CHECK-LD-LABEL: define internal void @__tls_init()
61+
// CHECK-LD: call void @"??__Eb@@YAXXZ"
62+
3863
// CHECK: !llvm.linker.options = !{![[dyn_tls_init:[0-9]+]]}
3964
// CHECK: ![[dyn_tls_init]] = !{!"/include:___dyn_tls_init@12"}
4065
// CHECK-LD: !llvm.linker.options = !{![[dyn_tls_init:[0-9]+]]}

0 commit comments

Comments
 (0)