@@ -183,6 +183,7 @@ struct LoweringPreparePass
183183 guard.setInitialValueAttr (cir::IntAttr::get (guardTy, 0 ));
184184 guard.setDSOLocal (globalOp.getDsoLocal ());
185185 guard.setAlignment (guardAlignment.getAsAlign ().value ());
186+ guard.setTlsModel (globalOp.getTlsModel ());
186187
187188 // The ABI says: "It is suggested that it be emitted in the same COMDAT
188189 // group as the associated data object." In practice, this doesn't work
@@ -241,7 +242,7 @@ struct LoweringPreparePass
241242
242243 void emitGlobalGuardedDtorRegion (CIRBaseBuilderTy &builder,
243244 cir::GlobalOp global,
244- mlir::Region &dtorRegion,
245+ mlir::Region &dtorRegion, bool tls,
245246 mlir::Block &entryBB) {
246247 // Create a variable that binds the atexit to this shared object.
247248 builder.setInsertionPointToStart (&mlirModule.getBodyRegion ().front ());
@@ -266,6 +267,11 @@ struct LoweringPreparePass
266267 builder.getVoidFnTy ({voidFnPtrTy, voidPtrTy, handlePtrTy});
267268
268269 llvm::StringLiteral nameAtExit = " __cxa_atexit" ;
270+ if (tls)
271+ nameAtExit = astCtx->getTargetInfo ().getTriple ().isOSDarwin ()
272+ ? llvm::StringLiteral (" _tlv_atexit" )
273+ : llvm::StringLiteral (" __cxa_thread_atexit" );
274+
269275 cir::FuncOp fnAtExit = buildRuntimeFunction (builder, nameAtExit,
270276 global.getLoc (), fnAtExitType);
271277
@@ -317,6 +323,28 @@ struct LoweringPreparePass
317323 // initialization so that recursive accesses during initialization do not
318324 // restart initialization.
319325
326+ auto emitBody = [&]() {
327+ // Emit the initializer and add a global destructor if appropriate.
328+ mlir::Block *insertBlock = builder.getInsertionBlock ();
329+ if (!ctorRegion.empty ()) {
330+ assert (ctorRegion.hasOneBlock () && " Enforced by MaxSizedRegion<1>" );
331+
332+ mlir::Block &block = ctorRegion.front ();
333+ insertBlock->getOperations ().splice (
334+ insertBlock->end (), block.getOperations (), block.begin (),
335+ std::prev (block.end ()));
336+ }
337+
338+ if (!dtorRegion.empty ()) {
339+ assert (dtorRegion.hasOneBlock () && " Enforced by MaxSizedRegion<1>" );
340+
341+ emitGlobalGuardedDtorRegion (builder, globalOp, dtorRegion, !threadsafe,
342+ *insertBlock);
343+ }
344+ builder.setInsertionPointToEnd (insertBlock);
345+ ctorRegion.getBlocks ().clear ();
346+ };
347+
320348 // Variables used when coping with thread-safe statics and exceptions.
321349 if (threadsafe) {
322350 // Call __cxa_guard_acquire.
@@ -341,26 +369,7 @@ struct LoweringPreparePass
341369 // OG: CGF.EHStack.pushCleanup<CallGuardAbort>(EHCleanup, guard);
342370 assert (!cir::MissingFeatures::guardAbortOnException ());
343371
344- // Emit the initializer and add a global destructor if appropriate.
345- mlir::Block *insertBlock = builder.getInsertionBlock ();
346- if (!ctorRegion.empty ()) {
347- assert (ctorRegion.hasOneBlock () && " Enforced by MaxSizedRegion<1>" );
348-
349- mlir::Block &block = ctorRegion.front ();
350- insertBlock->getOperations ().splice (
351- insertBlock->end (), block.getOperations (), block.begin (),
352- std::prev (block.end ()));
353- }
354-
355- if (!dtorRegion.empty ()) {
356- assert (dtorRegion.hasOneBlock () && " Enforced by MaxSizedRegion<1>" );
357-
358- emitGlobalGuardedDtorRegion (builder, globalOp, dtorRegion,
359- *insertBlock);
360- }
361-
362- builder.setInsertionPointToEnd (insertBlock);
363- ctorRegion.getBlocks ().clear ();
372+ emitBody ();
364373
365374 // Pop the guard-abort cleanup if we pushed one.
366375 // OG: CGF.PopCleanupBlock();
@@ -380,11 +389,13 @@ struct LoweringPreparePass
380389 globalOp->emitError (" NYI: non-threadsafe init for non-local variables" );
381390 return ;
382391 } else {
392+ emitBody ();
383393 // For local variables, store 1 into the first byte of the guard variable
384394 // after the object initialization completes so that initialization is
385395 // retried if initialization is interrupted by an exception.
386- globalOp->emitError (" NYI: non-threadsafe init for local variables" );
387- return ;
396+ builder.createStore (
397+ loc, builder.getConstantInt (loc, guardPtrTy.getPointee (), 1 ),
398+ guardPtr);
388399 }
389400
390401 builder.createYield (loc); // Outermost IfOp
@@ -1063,7 +1074,8 @@ LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
10631074 assert (!cir::MissingFeatures::astVarDeclInterface ());
10641075 assert (!cir::MissingFeatures::opGlobalThreadLocal ());
10651076
1066- emitGlobalGuardedDtorRegion (builder, op, dtorRegion, *entryBB);
1077+ emitGlobalGuardedDtorRegion (builder, op, dtorRegion,
1078+ op.getTlsModel ().has_value (), *entryBB);
10671079 }
10681080
10691081 // Replace cir.yield with cir.return
@@ -1159,31 +1171,27 @@ void LoweringPreparePass::handleStaticLocal(cir::GlobalOp globalOp,
11591171 (varDecl.isLocalVarDecl () || nonTemplateInline) &&
11601172 !varDecl.getTLSKind ();
11611173
1162- // TLS variables need special handling - the guard must also be thread-local.
1163- if (varDecl.getTLSKind ()) {
1164- globalOp->emitError (" NYI: guarded initialization for thread-local statics" );
1165- return ;
1166- }
1167-
11681174 // If we have a global variable with internal linkage and thread-safe statics
11691175 // are disabled, we can just let the guard variable be of type i8.
11701176 bool useInt8GuardVariable = !threadsafe && globalOp.hasInternalLinkage ();
1171- if (useInt8GuardVariable) {
1172- globalOp->emitError (" NYI: int8 guard variables for non-threadsafe statics" );
1173- return ;
1174- }
1175-
1177+ cir::CIRDataLayout dataLayout (mlirModule);
1178+ cir::IntType guardTy;
1179+ clang::CharUnits guardAlignment;
11761180 // Guard variables are 64 bits in the generic ABI and size width on ARM
11771181 // (i.e. 32-bit on AArch32, 64-bit on AArch64).
1178- if (useARMGuardVarABI ()) {
1182+ if (useInt8GuardVariable) {
1183+ guardTy = cir::IntType::get (&getContext (), 8 , /* isSigned=*/ true );
1184+ guardAlignment = clang::CharUnits::One ();
1185+ } else if (useARMGuardVarABI ()) {
11791186 globalOp->emitError (" NYI: ARM-style guard variables for static locals" );
11801187 return ;
1188+ } else {
1189+ guardTy = cir::IntType::get (&getContext (), 64 , /* isSigned=*/ true );
1190+ guardAlignment =
1191+ clang::CharUnits::fromQuantity (dataLayout.getABITypeAlign (guardTy));
11811192 }
1182- cir::IntType guardTy =
1183- cir::IntType::get (&getContext (), 64 , /* isSigned=*/ true );
1184- cir::CIRDataLayout dataLayout (mlirModule);
1185- clang::CharUnits guardAlignment =
1186- clang::CharUnits::fromQuantity (dataLayout.getABITypeAlign (guardTy));
1193+ assert (guardTy && guardAlignment.getQuantity () != 0 );
1194+
11871195 auto guardPtrTy = cir::PointerType::get (guardTy);
11881196
11891197 // Create the guard variable if we don't already have it.
@@ -1195,7 +1203,7 @@ void LoweringPreparePass::handleStaticLocal(cir::GlobalOp globalOp,
11951203 return ;
11961204 }
11971205
1198- mlir::Value guardPtr = builder.createGetGlobal (guard, /* threadLocal */ false );
1206+ mlir::Value guardPtr = builder.createGetGlobal (guard, localInitOp. getTls () );
11991207
12001208 // Test whether the variable has completed initialization.
12011209 //
@@ -1292,10 +1300,6 @@ void LoweringPreparePass::handleStaticLocal(cir::GlobalOp globalOp,
12921300
12931301void LoweringPreparePass::lowerLocalInitOp (
12941302 cir::LocalInitOp initOp, mlir::SymbolTableCollection &symbolTables) {
1295- if (!initOp.getStaticLocal ()) {
1296- initOp->emitError (" NYI: Non-static-local in lower-init-local op" );
1297- return ;
1298- }
12991303
13001304 // If we don't actually need to initialize anything anymore, we're done here.
13011305 if (initOp.getCtorRegion ().empty () && initOp.getDtorRegion ().empty ()) {
0 commit comments