https://github.com/erichkeane updated 
https://github.com/llvm/llvm-project/pull/197265

>From dea6fc94de4fbbcc2fcf0dd8f4601c00b1d88ca0 Mon Sep 17 00:00:00 2001
From: erichkeane <[email protected]>
Date: Mon, 11 May 2026 14:04:08 -0700
Subject: [PATCH 1/3] [CIR] Global-TLS Wrapper/init func lowering-

This patch does most of the rest of the Global-TLS lowering prepare
feature.  It makes sure the individual 'init' functions are generated,
  and properly generates the 'wrapper' to call the 'init'.

It is missing(to come in the followup patch):
1- Generation of the actual 'call all inits' function.
2- Emitting the 'guard' functionality in unordered inits.
---
 .../Dialect/Transforms/LoweringPrepare.cpp    | 202 ++++++++++++++----
 .../test/CIR/CodeGen/global-tls-dyn-init.cpp  | 114 ++++++++--
 .../CIR/CodeGen/global-tls-simple-init.cpp    | 145 +++++++++----
 .../test/CIR/CodeGen/global-tls-templates.cpp | 108 ++++++++--
 4 files changed, 442 insertions(+), 127 deletions(-)

diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index dc1a872bd8c8a..e041ad984c995 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -123,10 +123,12 @@ struct LoweringPreparePass
   /// When looking at the 'global' op, create the wrapper function.
   void defineGlobalThreadLocalWrapper(cir::GlobalOp op, cir::FuncOp initAlias,
                                       bool isVarDefinition);
+  /// Create an initialization alias for a thread-local variable.
+  cir::FuncOp defineGlobalThreadLocalInitAlias(cir::GlobalOp op,
+                                               cir::FuncOp aliasee);
   /// Get the declaration for the 'wrapper' function for a global-TLS variable.
   cir::FuncOp getOrCreateThreadLocalWrapper(CIRBaseBuilderTy &builder,
                                             cir::GlobalOp op);
-
   /// Handle the dtor region by registering destructor with __cxa_atexit
   cir::FuncOp getOrCreateDtorFunc(CIRBaseBuilderTy &builder, cir::GlobalOp op,
                                   mlir::Region &dtorRegion,
@@ -134,6 +136,9 @@ struct LoweringPreparePass
 
   /// Build a module init function that calls all the dynamic initializers.
   void buildCXXGlobalInitFunc();
+  // Build a init function for all of the ordered global thread local storage
+  // variables.
+ void buildCXXGlobalTlsFunc();
 
   /// Materialize global ctor/dtor list
   void buildGlobalCtorDtorList();
@@ -173,6 +178,9 @@ struct LoweringPreparePass
   /// Get or create __cxa_guard_release function.
   cir::FuncOp getGuardReleaseFn(cir::PointerType guardPtrTy);
 
+  /// Get or create the __init_tls function.
+  cir::FuncOp getTlsInitFn();
+
   /// Create a guard global variable for a static local.
   cir::GlobalOp createGuardGlobalOp(CIRBaseBuilderTy &builder,
                                     mlir::Location loc, llvm::StringRef name,
@@ -195,16 +203,35 @@ struct LoweringPreparePass
 
   /// Get or create the guard variable for a static local declaration.
   cir::GlobalOp getOrCreateStaticLocalDeclGuardAddress(
-      CIRBaseBuilderTy &builder, cir::GlobalOp globalOp,
-      cir::ASTVarDeclInterface varDecl, cir::IntType guardTy,
-      clang::CharUnits guardAlignment) {
+      CIRBaseBuilderTy &builder, cir::GlobalOp globalOp, StringRef guardName,
+      bool isLocalVarDecl, bool useInt8GuardVariable) {
+
+    cir::CIRDataLayout dataLayout(mlirModule);
+    cir::IntType guardTy;
+    clang::CharUnits guardAlignment;
+    // Guard variables are 64 bits in the generic ABI and size width on ARM
+    // (i.e. 32-bit on AArch32, 64-bit on AArch64).
+    if (useInt8GuardVariable) {
+      guardTy = cir::IntType::get(&getContext(), 8, /*isSigned=*/true);
+      guardAlignment = clang::CharUnits::One();
+    } else if (useARMGuardVarABI()) {
+      // Guard variables are size width on ARM (32-bit AArch32, 64-bit 
AArch64).
+      const unsigned sizeTypeSize =
+          astCtx->getTypeSize(astCtx->getSignedSizeType());
+      guardTy =
+          cir::IntType::get(&getContext(), sizeTypeSize, /*isSigned=*/true);
+      guardAlignment =
+          clang::CharUnits::fromQuantity(dataLayout.getABITypeAlign(guardTy));
+    } else {
+      guardTy = cir::IntType::get(&getContext(), 64, /*isSigned=*/true);
+      guardAlignment =
+          clang::CharUnits::fromQuantity(dataLayout.getABITypeAlign(guardTy));
+    }
+    assert(guardTy && guardAlignment.getQuantity() != 0);
+
     llvm::StringRef globalSymName = globalOp.getSymName();
     cir::GlobalOp guard = getStaticLocalDeclGuardAddress(globalSymName);
     if (!guard) {
-      // Get the guard name from the static_local attribute.
-      llvm::StringRef guardName =
-          globalOp.getStaticLocalGuard()->getName().getValue();
-
       // Create the guard variable with a zero-initializer.
       guard = createGuardGlobalOp(builder, globalOp->getLoc(), guardName,
                                   guardTy, globalOp.getLinkage());
@@ -219,10 +246,12 @@ struct LoweringPreparePass
       // Wasm.
       bool hasComdat = globalOp.getComdat();
       const llvm::Triple &triple = astCtx->getTargetInfo().getTriple();
-      if (!varDecl.isLocalVarDecl() && hasComdat &&
+      // TODO(cir): for now, we're just setting comdat to true, but it should
+      // contain a comdat reference name here instead.
+      if (!isLocalVarDecl && hasComdat &&
           (triple.isOSBinFormatELF() || triple.isOSBinFormatWasm())) {
-        globalOp->emitError("NYI: guard COMDAT for non-local variables");
-        return {};
+        // This should be a comdat for the variable.
+        guard.setComdat(true);
       } else if (hasComdat && globalOp.isWeakForLinker()) {
         guard.setComdat(true);
       }
@@ -264,7 +293,9 @@ struct LoweringPreparePass
   /// Tracks existing dynamic initializers.
   llvm::StringMap<uint32_t> dynamicInitializerNames;
   llvm::SmallVector<cir::FuncOp> dynamicInitializers;
+  llvm::SmallVector<cir::FuncOp> globalThreadLocalInitializers;
   llvm::StringMap<cir::FuncOp> threadLocalWrappers;
+  llvm::StringMap<cir::FuncOp> threadLocalInitAliases;
 
   /// Tracks guard variables for static locals (keyed by global symbol name).
   llvm::StringMap<cir::GlobalOp> staticLocalDeclGuardMap;
@@ -988,7 +1019,6 @@ cir::FuncOp 
LoweringPreparePass::getOrCreateDtorFunc(CIRBaseBuilderTy &builder,
                                                      cir::CallOp &dtorCall) {
   mlir::OpBuilder::InsertionGuard guard(builder);
   assert(!cir::MissingFeatures::astVarDeclInterface());
-  assert(!cir::MissingFeatures::opGlobalThreadLocal());
 
   cir::VoidType voidTy = builder.getVoidTy();
   auto voidPtrTy = cir::PointerType::get(voidTy);
@@ -1170,6 +1200,18 @@ LoweringPreparePass::getGuardReleaseFn(cir::PointerType 
guardPtrTy) {
   return buildRuntimeFunction(builder, "__cxa_guard_release", loc, fnType);
 }
 
+cir::FuncOp LoweringPreparePass::getTlsInitFn() {
+  // void __tls_init(void);
+  CIRBaseBuilderTy builder(getContext());
+  mlir::OpBuilder::InsertionGuard ipGuard{builder};
+  builder.setInsertionPointToStart(mlirModule.getBody());
+  mlir::Location loc = mlirModule.getLoc();
+  cir::VoidType voidTy = cir::VoidType::get(&getContext());
+  auto fnType = cir::FuncType::get({}, voidTy);
+  return buildRuntimeFunction(builder, "__tls_init", loc, fnType,
+                              cir::GlobalLinkageKind::InternalLinkage);
+}
+
 cir::GlobalOp LoweringPreparePass::createGuardGlobalOp(
     CIRBaseBuilderTy &builder, mlir::Location loc, llvm::StringRef name,
     cir::IntType guardTy, cir::GlobalLinkageKind linkage) {
@@ -1225,33 +1267,11 @@ void 
LoweringPreparePass::handleStaticLocal(cir::GlobalOp globalOp,
   // 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.
   bool useInt8GuardVariable = !threadsafe && globalOp.hasInternalLinkage();
-  cir::CIRDataLayout dataLayout(mlirModule);
-  cir::IntType guardTy;
-  clang::CharUnits guardAlignment;
-  // Guard variables are 64 bits in the generic ABI and size width on ARM
-  // (i.e. 32-bit on AArch32, 64-bit on AArch64).
-  if (useInt8GuardVariable) {
-    guardTy = cir::IntType::get(&getContext(), 8, /*isSigned=*/true);
-    guardAlignment = clang::CharUnits::One();
-  } else if (useARMGuardVarABI()) {
-    // Guard variables are size width on ARM (32-bit AArch32, 64-bit AArch64).
-    const unsigned sizeTypeSize =
-        astCtx->getTypeSize(astCtx->getSignedSizeType());
-    guardTy = cir::IntType::get(&getContext(), sizeTypeSize, 
/*isSigned=*/true);
-    guardAlignment =
-        clang::CharUnits::fromQuantity(dataLayout.getABITypeAlign(guardTy));
-  } else {
-    guardTy = cir::IntType::get(&getContext(), 64, /*isSigned=*/true);
-    guardAlignment =
-        clang::CharUnits::fromQuantity(dataLayout.getABITypeAlign(guardTy));
-  }
-  assert(guardTy && guardAlignment.getQuantity() != 0);
-
-  auto guardPtrTy = cir::PointerType::get(guardTy);
 
   // Create the guard variable if we don't already have it.
   cir::GlobalOp guard = getOrCreateStaticLocalDeclGuardAddress(
-      builder, globalOp, varDecl, guardTy, guardAlignment);
+      builder, globalOp, globalOp.getStaticLocalGuard()->getName().getValue(),
+      varDecl.isLocalVarDecl(), useInt8GuardVariable);
   if (!guard) {
     // Error was already emitted, just restore the terminator and return.
     localInitBlock->push_back(ret);
@@ -1288,7 +1308,7 @@ void LoweringPreparePass::handleStaticLocal(cir::GlobalOp 
globalOp,
     auto bytePtrTy = cir::PointerType::get(builder.getSIntNTy(8));
     mlir::Value bytePtr = builder.createBitcast(guardPtr, bytePtrTy);
     mlir::Value guardLoad = builder.createAlignedLoad(
-        localInitOp.getLoc(), bytePtr, guardAlignment.getAsAlign().value());
+        localInitOp.getLoc(), bytePtr, *guard.getAlignment());
 
     // Itanium ABI:
     //   An implementation supporting thread-safety on multiprocessor
@@ -1341,7 +1361,8 @@ void LoweringPreparePass::handleStaticLocal(cir::GlobalOp 
globalOp,
         /*withElseRegion=*/false, [&](mlir::OpBuilder &, mlir::Location) {
           emitCXXGuardedInitIf(builder, globalOp, localInitOp.getCtorRegion(),
                                localInitOp.getDtorRegion(), varDecl, guardPtr,
-                               guardPtrTy, threadsafe);
+                               builder.getPointerTo(guard.getSymType()),
+                               threadsafe);
         });
   } else {
     // Threadsafe statics without inline atomics - call __cxa_guard_acquire
@@ -1447,12 +1468,64 @@ void 
LoweringPreparePass::defineGlobalThreadLocalWrapper(cir::GlobalOp op,
   // If we are a situation where we have/need one, emit a call to the init
   // function.
   if (initAlias) {
-    op->emitError("not yet implemented, wrapper with an init alias");
+    if (!isVarDefinition) {
+      // If this isn't a definition, we have to check that the alias exists.
+      mlir::Value funcLoad = cir::GetGlobalOp::create(
+          builder, initAlias.getLoc(),
+          cir::PointerType::get(initAlias.getFunctionType()),
+          initAlias.getSymName());
+      mlir::Value nullCheck =
+          builder.getNullValue(funcLoad.getType(), initAlias.getLoc());
+      mlir::Value cmp = cir::CmpOp::create(
+          builder, initAlias.getLoc(), cir::CmpOpKind::ne, funcLoad, 
nullCheck);
+      cir::IfOp::create(
+          builder, initAlias.getLoc(), cmp, /*withElseRegion=*/false,
+          [&](mlir::OpBuilder &, mlir::Location loc) {
+            builder.createCallOp(initAlias.getLoc(), initAlias, {});
+            cir::YieldOp::create(builder, initAlias.getLoc());
+          });
+    } else {
+      // If this IS a definition, we know the alias exists, so we can just emit
+      // a call to it.
+      builder.createCallOp(initAlias.getLoc(), initAlias, {});
+    }
   }
   auto get = builder.createGetGlobal(op, /*tls=*/true);
   cir::ReturnOp::create(builder, op.getLoc(), {get});
 }
 
+cir::FuncOp
+LoweringPreparePass::defineGlobalThreadLocalInitAlias(cir::GlobalOp op,
+                                                      cir::FuncOp aliasee) {
+  CIRBaseBuilderTy builder(getContext());
+  mlir::OpBuilder::InsertionGuard insertGuard(builder);
+  builder.setInsertionPointToStart(&mlirModule.getBodyRegion().front());
+  mlir::StringAttr aliasName = op.getDynTlsRefs()->getInitName();
+  auto existingAliasIter = threadLocalInitAliases.find(aliasName.getValue());
+
+  if (existingAliasIter != threadLocalInitAliases.end())
+    return existingAliasIter->second;
+
+  auto funcType = cir::FuncType::get({}, builder.getVoidTy());
+  cir::FuncOp alias =
+      cir::FuncOp::create(builder, op.getLoc(), aliasName, funcType);
+  alias.setLinkageAttr(op.getLinkageAttr());
+
+  if (aliasee) {
+    alias.setAliasee(aliasee.getSymName());
+  } else {
+    // If we don't have anything to alias (because this isn't a variable
+    // definition!), we set this as just a function definition with no alias,
+    // and extern-weak.
+    alias.setLinkage(cir::GlobalLinkageKind::ExternalWeakLinkage);
+    mlir::SymbolTable::setSymbolVisibility(
+        alias, mlir::SymbolTable::Visibility::Private);
+  }
+
+  threadLocalInitAliases.insert({aliasName.getValue(), alias});
+  return alias;
+}
+
 void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
   // Static locals are handled separately via guard variables.
   if (op.getStaticLocalGuard())
@@ -1460,7 +1533,6 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
 
   mlir::Region &ctorRegion = op.getCtorRegion();
   mlir::Region &dtorRegion = op.getDtorRegion();
-  // TODO(cir): Implement the initialization of this.
   cir::FuncOp initAlias;
 
   if (!ctorRegion.empty() || !dtorRegion.empty()) {
@@ -1473,8 +1545,39 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
     dtorRegion.getBlocks().clear();
 
     assert(!cir::MissingFeatures::astVarDeclInterface());
-    dynamicInitializers.push_back(f);
-  }
+    if (op.getTlsModel() == TLS_Model::GeneralDynamic &&
+        !op.getStaticLocalGuard().has_value()) {
+      // There are two types of global TLS variables: 'ordered' and 
'unordered'.
+      // 'ordered' are the common case. A call to any of them causes all of the
+      // initializers for all other 'ordered' ones to be called, via a
+      // `__tls_init` function. So the 'init alias' that gets called in the
+      // wrapper for these goes directly to `__tls_init`.
+
+      // 'Unordered' values are the case for variable templates. In this case,
+      // their init alias goes directly to their init function. The FE 
generates
+      // a guard variable for them (since they cannot use the global guard), so
+      // we differentiate them that way.
+
+      if (op.getDynTlsRefs()->getGuardName()) {
+        // Unordered: the alias is the function we just generated.
+        initAlias = defineGlobalThreadLocalInitAlias(op, f);
+      } else {
+        // Ordered: Get the __tls_init, and make the alias to that.
+        initAlias = defineGlobalThreadLocalInitAlias(op, getTlsInitFn());
+        // Ordered inits also need to get called from the __tls_init function,
+        // so we add the init function to the list, so that we can add them to
+        // it later.
+        globalThreadLocalInitializers.push_back(f);
+      }
+    } else {
+      dynamicInitializers.push_back(f);
+    }
+  } else if (op.getTlsModel() == TLS_Model::GeneralDynamic &&
+             op.getDynTlsRefs() && op.isDeclaration()) {
+    // If this is a declaration and has no init function, we probably DO have 
to
+    // create an alias that needs checking, so create it as extern-weak.
+    initAlias = defineGlobalThreadLocalInitAlias(op, {});
+   }
 
   // We need a wrapper for TLS globals that MIGHT have a non-constant
   // initialization. The FE will have generated the DynTlsRefs for any with
@@ -1609,6 +1712,20 @@ void LoweringPreparePass::buildGlobalCtorDtorList() {
   }
 }
 
+void LoweringPreparePass::buildCXXGlobalTlsFunc() {
+  if (globalThreadLocalInitializers.empty())
+    return;
+  // The global-ordered-init function for TLS variables just calls each of the
+  // init-functions in order after doing a guard.
+
+  cir::FuncOp tlsInit = getTlsInitFn();
+  mlir::Location loc = tlsInit.getLoc();
+  CIRBaseBuilderTy builder(getContext());
+  mlir::Block *entryBB = tlsInit.addEntryBlock();
+  builder.setInsertionPointToStart(entryBB);
+  // Note: a followup patch will emit the body here correctly.
+  cir::ReturnOp::create(builder, loc);
+}
 void LoweringPreparePass::buildCXXGlobalInitFunc() {
   if (dynamicInitializers.empty())
     return;
@@ -2584,6 +2701,7 @@ void LoweringPreparePass::runOnOperation() {
     runOnOp(o);
 
   buildCXXGlobalInitFunc();
+  buildCXXGlobalTlsFunc();
   if (astCtx->getLangOpts().CUDA && !astCtx->getLangOpts().CUDAIsDevice)
     buildCUDAModuleCtor();
 
diff --git a/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp 
b/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp
index b974f61131353..f471d586aa850 100644
--- a/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp
+++ b/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp
@@ -10,51 +10,70 @@ struct CtorDtor {
     int i;
 };
 
-// Wrappers:
+// LLVM-BOTH-DAG: @__dso_handle = external hidden global i8
+// LLVM-BOTH-DAG: @tls_cd = thread_local global %struct.CtorDtor { i32 5 }, 
align 4
+// LLVM-BOTH-DAG: @tls_cd_dyn = thread_local global %struct.CtorDtor 
zeroinitializer, align 4
+// LLVM-BOTH-DAG: @tls_cd_ref = thread_local global ptr null, align 8
+// LLVM-BOTH-DAG: @tls_cd_dyn_not_used = thread_local global %struct.CtorDtor 
zeroinitializer, align 4
+//
+// LLVM-BOTH-DAG: @_ZTH19tls_cd_dyn_not_used = alias void (), ptr @__tls_init
+// LLVM-BOTH-DAG: @_ZTH10tls_cd_ref = alias void (), ptr @__tls_init
+// LLVM-BOTH-DAG: @_ZTH10tls_cd_dyn = alias void (), ptr @__tls_init
+// LLVM-BOTH-DAG: @_ZTH6tls_cd = alias void (), ptr @__tls_init
+
+// Wrappers & aliases.
 // CIR-LABEL: cir.func comdat weak_odr private hidden 
@_ZTW19tls_cd_dyn_not_used() -> !cir.ptr<!rec_CtorDtor> {
-// CIR-NOT: cir.call @_ZTH19tls_cd_dyn_not_used() : () -> ()
+// CIR: cir.call @_ZTH19tls_cd_dyn_not_used() : () -> ()
 // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_dyn_not_used : 
!cir.ptr<!rec_CtorDtor>
 // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!rec_CtorDtor>
+// CIR: cir.func @_ZTH19tls_cd_dyn_not_used() alias(@__tls_init)
 
 // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW10tls_cd_ref() -> 
!cir.ptr<!cir.ptr<!rec_CtorDtor>> {
-// CIR-NOT: cir.call @_ZTH10tls_cd_ref() : () -> ()
+// CIR: cir.call @_ZTH10tls_cd_ref() : () -> ()
 // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_ref : 
!cir.ptr<!cir.ptr<!rec_CtorDtor>>
 // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!cir.ptr<!rec_CtorDtor>>
+// CIR: cir.func @_ZTH10tls_cd_ref() alias(@__tls_init)
 
 // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW10tls_cd_dyn() -> 
!cir.ptr<!rec_CtorDtor> {
-// CIR-NOT: cir.call @_ZTH10tls_cd_dyn() : () -> ()
+// CIR: cir.call @_ZTH10tls_cd_dyn() : () -> ()
 // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_dyn : 
!cir.ptr<!rec_CtorDtor>
 // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!rec_CtorDtor> 
+// CIR: cir.func @_ZTH10tls_cd_dyn() alias(@__tls_init)
 
 // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW6tls_cd() -> 
!cir.ptr<!rec_CtorDtor> {
-// CIR-NOT: cir.call @_ZTH6tls_cd() : () -> ()
+// CIR: cir.call @_ZTH6tls_cd() : () -> ()
 // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd : 
!cir.ptr<!rec_CtorDtor>
 // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!rec_CtorDtor>
+// CIR: cir.func @_ZTH6tls_cd() alias(@__tls_init)
+
+// CIR-LABEL: cir.func internal private @__tls_init() {
+// CIR:  cir.return
 
 // LLVM: define weak_odr hidden ptr @_ZTW19tls_cd_dyn_not_used() {
-// LLVM-NOT:   call void @_ZTH19tls_cd_dyn_not_used()
+// LLVM:   call void @_ZTH19tls_cd_dyn_not_used()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_cd_dyn_not_used)
 // LLVM:   ret ptr %[[GET_GLOB]]
 // LLVM: }
 //
 // LLVM: define weak_odr hidden ptr @_ZTW10tls_cd_ref() {
-// LLVM-NOT:   call void @_ZTH10tls_cd_ref()
+// LLVM:   call void @_ZTH10tls_cd_ref()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_cd_ref)
 // LLVM:   ret ptr %[[GET_GLOB]]
 // LLVM: }
 //
 // LLVM: define weak_odr hidden ptr @_ZTW10tls_cd_dyn() {
-// LLVM-NOT:   call void @_ZTH10tls_cd_dyn()
+// LLVM:   call void @_ZTH10tls_cd_dyn()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_cd_dyn)
 // LLVM:   ret ptr %[[GET_GLOB]]
 // LLVM: }
 //
 // LLVM: define weak_odr hidden ptr @_ZTW6tls_cd() {
-// LLVM-NOT:   call void @_ZTH6tls_cd()
+// LLVM:   call void @_ZTH6tls_cd()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_cd)
 // LLVM:   ret ptr %[[GET_GLOB]]
 // LLVM: }
 //
+// LLVM: define internal void @__tls_init() {
 
 thread_local CtorDtor tls_cd = 5;
 // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = <"_ZTW6tls_cd", 
"_ZTH6tls_cd"> @tls_cd = #cir.const_record<{#cir.int<5> : !s32i}> : 
!rec_CtorDtor dtor {
@@ -62,10 +81,21 @@ thread_local CtorDtor tls_cd = 5;
 // CIR-BEFORE-LPP:   cir.call @_ZN8CtorDtorD1Ev(%[[GET_GLOB]]) : 
(!cir.ptr<!rec_CtorDtor>) -> ()
 // CIR-BEFORE-LPP: }
 // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW6tls_cd", 
"_ZTH6tls_cd"> @tls_cd = #cir.const_record<{#cir.int<5> : !s32i}> : 
!rec_CtorDtor
-
+// CIR: cir.func internal private @[[TLS_CD_INIT:.*]]() {
+// CIR:   %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd : 
!cir.ptr<!rec_CtorDtor>
+// CIR:   %[[GET_DTOR:.*]] = cir.get_global @_ZN8CtorDtorD1Ev : 
!cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>>
+// CIR:   %[[DTOR_DECAY:.*]] = cir.cast bitcast %[[GET_DTOR]] : 
!cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>> -> 
!cir.ptr<!cir.func<(!cir.ptr<!void>)>>
+// CIR:   %[[GLOB_DECAY:.*]] = cir.cast bitcast %[[GET_GLOB]] : 
!cir.ptr<!rec_CtorDtor> -> !cir.ptr<!void>
+// CIR:   %[[DSOHANDLE:.*]] = cir.get_global @__dso_handle : !cir.ptr<i8>
+// CIR:   cir.call @__cxa_thread_atexit(%[[DTOR_DECAY]], %[[GLOB_DECAY]], 
%[[DSOHANDLE]]) : (!cir.ptr<!cir.func<(!cir.ptr<!void>)>>, !cir.ptr<!void>, 
!cir.ptr<i8>) -> ()
+// CIR:   cir.return
+//
+// LLVM: define internal void @[[TLS_CD_INIT:.*]]() {
 // OGCG: define internal void @[[TLS_CD_INIT:.*]]() {{.*}}{
+// LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_cd)
+// LLVM:   call void @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr 
%[[GET_GLOB]], ptr @__dso_handle)
 // OGCG:   call i32 @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr @tls_cd, 
ptr @__dso_handle)
-// OGCG:   ret void
+// LLVM-BOTH:   ret void
 
 thread_local CtorDtor tls_cd_dyn = get_i();
 // CIR-BEFORE-LPP:  cir.global external tls_dyn dyn_tls_refs = 
<"_ZTW10tls_cd_dyn", "_ZTH10tls_cd_dyn"> @tls_cd_dyn = ctor : !rec_CtorDtor {
@@ -77,12 +107,28 @@ thread_local CtorDtor tls_cd_dyn = get_i();
 // CIR-BEFORE-LPP:    cir.call @_ZN8CtorDtorD1Ev(%[[GET_GLOB]]) : 
(!cir.ptr<!rec_CtorDtor>) -> ()
 // CIR-BEFORE-LPP:  }
 // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW10tls_cd_dyn", 
"_ZTH10tls_cd_dyn"> @tls_cd_dyn = #cir.zero : !rec_CtorDtor
-
+// CIR: cir.func internal private @[[TLS_CD_DYN_INIT:.*]]() {
+// CIR:   %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_dyn : 
!cir.ptr<!rec_CtorDtor>
+// CIR:   %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i {llvm.noundef})
+// CIR:   cir.call @_ZN8CtorDtorC1Ei(%[[GET_GLOB]], %[[CALL]])
+// CIR:   %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_dyn : 
!cir.ptr<!rec_CtorDtor>
+// CIR:   %[[GET_DTOR:.*]] = cir.get_global @_ZN8CtorDtorD1Ev : 
!cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>>
+// CIR:   %[[DTOR_DECAY:.*]] = cir.cast bitcast %[[GET_DTOR]] : 
!cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>> -> 
!cir.ptr<!cir.func<(!cir.ptr<!void>)>>
+// CIR:   %[[GLOB_DECAY:.*]] = cir.cast bitcast %[[GET_GLOB]] : 
!cir.ptr<!rec_CtorDtor> -> !cir.ptr<!void>
+// CIR:   %[[DSOHANDLE:.*]] = cir.get_global @__dso_handle : !cir.ptr<i8>
+// CIR:   cir.call @__cxa_thread_atexit(%[[DTOR_DECAY]], %[[GLOB_DECAY]], 
%[[DSOHANDLE]]) : (!cir.ptr<!cir.func<(!cir.ptr<!void>)>>, !cir.ptr<!void>, 
!cir.ptr<i8>) -> ()
+// CIR:   cir.return
+//
+// LLVM: define internal void @[[TLS_CD_DYN_INIT:.*]]() {
 // OGCG: define internal void @[[TLS_CD_DYN_INIT:.*]]() {{.*}} {
-// OGCG:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
+// LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_cd_dyn)
+// LLVM-BOTH:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
+// LLVM:   call void @_ZN8CtorDtorC1Ei(ptr {{.*}}%[[GET_GLOB]], i32 
{{.*}}%[[CALL]])
 // OGCG:   call void @_ZN8CtorDtorC1Ei(ptr {{.*}}@tls_cd_dyn, i32 
{{.*}}%[[CALL]])
+// LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_cd_dyn)
+// LLVM:   call void @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr 
%[[GET_GLOB]], ptr @__dso_handle)
 // OGCG:   call i32 @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr 
@tls_cd_dyn, ptr @__dso_handle)
-// OGCG:   ret void
+// LLVM-BOTH:   ret void
 
 thread_local CtorDtor &tls_cd_ref = tls_cd_dyn;
 // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = 
<"_ZTW10tls_cd_ref", "_ZTH10tls_cd_ref"> @tls_cd_ref = ctor : 
!cir.ptr<!rec_CtorDtor> {
@@ -91,13 +137,20 @@ thread_local CtorDtor &tls_cd_ref = tls_cd_dyn;
 // CIR-BEFORE-LPP:   cir.store {{.*}}%[[CALL]], %[[GET_GLOB]] : 
!cir.ptr<!rec_CtorDtor>, !cir.ptr<!cir.ptr<!rec_CtorDtor>>
 // CIR-BEFORE-LPP: }
 // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW10tls_cd_ref", 
"_ZTH10tls_cd_ref"> @tls_cd_ref = #cir.ptr<null> : !cir.ptr<!rec_CtorDtor>
-
+// CIR: cir.func internal private @[[TLS_CD_REF_INIT:.*]]() {
+// CIR:   %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_ref : 
!cir.ptr<!cir.ptr<!rec_CtorDtor>>
+// CIR:   %[[GET_DYN:.*]] = cir.call @_ZTW10tls_cd_dyn() : () -> 
!cir.ptr<!rec_CtorDtor>
+// CIR:   cir.store align(8) %[[GET_DYN]], %[[GET_GLOB]] : 
!cir.ptr<!rec_CtorDtor>, !cir.ptr<!cir.ptr<!rec_CtorDtor>>
+// CIR:   cir.return
+//
+// LLVM: define internal void @[[TLS_CD_REF_INIT:.*]]() {
 // OGCG: define internal void @[[TLS_CD_REF_INIT:.*]]() {{.*}} {
-// OGCG:   %[[CALL:.*]] = call ptr @_ZTW10tls_cd_dyn()
+// LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_cd_ref)
+// LLVM-BOTH:   %[[CALL:.*]] = call ptr @_ZTW10tls_cd_dyn()
 // OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@tls_cd_ref)
-// OGCG:   store ptr %[[CALL]], ptr %[[GET_GLOB]], align 8
-// OGCG:   ret void
-
+// LLVM-BOTH:   store ptr %[[CALL]], ptr %[[GET_GLOB]], align 8
+// LLVM-BOTH:   ret void
+//
 // OGCG: define weak_odr hidden noundef ptr @_ZTW10tls_cd_dyn() {{.*}} comdat {
 // OGCG:   call void @_ZTH10tls_cd_dyn()
 // OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@tls_cd_dyn)
@@ -114,12 +167,28 @@ thread_local CtorDtor tls_cd_dyn_not_used = get_i();
 // CIR-BEFORE-LPP:   cir.call @_ZN8CtorDtorD1Ev(%[[GET_GLOB]]) : 
(!cir.ptr<!rec_CtorDtor>) -> ()
 // CIR-BEFORE-LPP: }
 // CIR: cir.global external tls_dyn dyn_tls_refs = 
<"_ZTW19tls_cd_dyn_not_used", "_ZTH19tls_cd_dyn_not_used"> @tls_cd_dyn_not_used 
= #cir.zero : !rec_CtorDtor
-
+// CIR: cir.func internal private @[[TLS_CD_DYN_NOT_USED_INIT:.*]]() {
+// CIR:   %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_dyn_not_used 
: !cir.ptr<!rec_CtorDtor>
+// CIR:   %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i {llvm.noundef})
+// CIR:   cir.call @_ZN8CtorDtorC1Ei(%[[GET_GLOB]], %[[CALL]])
+// CIR:   %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_dyn_not_used 
: !cir.ptr<!rec_CtorDtor>
+// CIR:   %[[GET_DTOR:.*]] = cir.get_global @_ZN8CtorDtorD1Ev : 
!cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>>
+// CIR:   %[[DTOR_DECAY:.*]] = cir.cast bitcast %[[GET_DTOR]] : 
!cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>> -> 
!cir.ptr<!cir.func<(!cir.ptr<!void>)>>
+// CIR:   %[[GLOB_DECAY:.*]] = cir.cast bitcast %[[GET_GLOB]] : 
!cir.ptr<!rec_CtorDtor> -> !cir.ptr<!void>
+// CIR:   %[[DSOHANDLE:.*]] = cir.get_global @__dso_handle : !cir.ptr<i8>
+// CIR:   cir.call @__cxa_thread_atexit(%[[DTOR_DECAY]], %[[GLOB_DECAY]], 
%[[DSOHANDLE]]) : (!cir.ptr<!cir.func<(!cir.ptr<!void>)>>, !cir.ptr<!void>, 
!cir.ptr<i8>) -> ()
+// CIR:   cir.return
+//
+// LLVM: define internal void @[[TLS_CD_DYN_NOT_USED_INIT:.*]]() {
 // OGCG: define internal void @[[TLS_CD_DYN_NOT_USED_INIT:.*]]() {{.*}} {
-// OGCG:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
+// LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_cd_dyn_not_used)
+// LLVM-BOTH:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
+// LLVM:   call void @_ZN8CtorDtorC1Ei(ptr {{.*}}%[[GET_GLOB]], i32 
{{.*}}%[[CALL]])
 // OGCG:   call void @_ZN8CtorDtorC1Ei(ptr {{.*}}@tls_cd_dyn_not_used, i32 
{{.*}}%[[CALL]])
+// LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_cd_dyn_not_used)
+// LLVM:   call void @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr 
%[[GET_GLOB]], ptr @__dso_handle)
 // OGCG:   call i32 @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr 
@tls_cd_dyn_not_used, ptr @__dso_handle)
-// OGCG:   ret void
+// LLVM-BOTH:   ret void
 
 void uses() {
   auto a = tls_cd;
@@ -165,3 +234,4 @@ void uses() {
 // OGCG:   %[[GET_GLOB:.*]] = call {{.*}} ptr @llvm.threadlocal.address.p0(ptr 
{{.*}} @tls_cd_dyn_not_used)
 // OGCG:   ret ptr %[[GET_GLOB]]
 // OGCG: }
+//
diff --git a/clang/test/CIR/CodeGen/global-tls-simple-init.cpp 
b/clang/test/CIR/CodeGen/global-tls-simple-init.cpp
index b9030d6518222..c14c21358ebff 100644
--- a/clang/test/CIR/CodeGen/global-tls-simple-init.cpp
+++ b/clang/test/CIR/CodeGen/global-tls-simple-init.cpp
@@ -9,58 +9,80 @@ struct CtorDtor {
   ~CtorDtor(){}
     int i;
 };
-
-// Wrappers:
+// Wrappers & Aliases: 
 // CIR-LABEL: cir.func comdat linkonce_odr private hidden 
@_ZTW12maybe_inited() -> !cir.ptr<!s32i> {
-// CIR-NOT: %[[GET_INIT_FUNC:.*]] = cir.get_global @_ZTH12maybe_inited : 
!cir.ptr<!cir.func<()>>
-// Note: The following intentionally disabled since they have matchers in them.
-// CIRX-NOT: %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!cir.func<()>>
-// CIRX-NOT: %[[IS_VALID:.*]] = cir.cmp ne %[[GET_INIT_FUNC]], %[[NULL]] : 
!cir.ptr<!cir.func<()>>
-// CIRX-NOT: cir.if %[[IS_VALID]] {
-// CIR-NOT: cir.call @_ZTH12maybe_inited() : () -> ()
-// CIRX-NOT: }
+// CIR: %[[GET_INIT_FUNC:.*]] = cir.get_global @_ZTH12maybe_inited : 
!cir.ptr<!cir.func<()>>
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!cir.func<()>>
+// CIR: %[[IS_VALID:.*]] = cir.cmp ne %[[GET_INIT_FUNC]], %[[NULL]] : 
!cir.ptr<!cir.func<()>>
+// CIR: cir.if %[[IS_VALID]] {
+// CIR: cir.call @_ZTH12maybe_inited() : () -> ()
+// CIR: }
 // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @maybe_inited : 
!cir.ptr<!s32i>
 // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!s32i>
 
 // CIR-LABEL: cir.func comdat weak_odr private hidden 
@_ZTW21definitely_inited_dyn() -> !cir.ptr<!s32i> {
-// CIR-NOT: cir.call @_ZTH21definitely_inited_dyn() : () -> ()
+// CIR: cir.call @_ZTH21definitely_inited_dyn() : () -> ()
 // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @definitely_inited_dyn 
: !cir.ptr<!s32i>
 // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!s32i>
 
+// CIR: cir.func @_ZTH21definitely_inited_dyn() alias(@__tls_init)
+
 // CIR: cir.func comdat weak_odr private hidden @_ZTW17definitely_inited() -> 
!cir.ptr<!s32i> {
-// CIR-NEXT:   %[[GET_GLOB:.*]] = cir.get_global thread_local 
@definitely_inited : !cir.ptr<!s32i>
+// CIR:   %[[GET_GLOB:.*]] = cir.get_global thread_local @definitely_inited : 
!cir.ptr<!s32i>
 // CIR:   cir.return %[[GET_GLOB]] : !cir.ptr<!s32i>
 
+// CIR: cir.func @_ZTH17tls_int_self_init() alias(@__tls_init)
+
 // CIR-LABEL: cir.func comdat weak_odr private hidden 
@_ZTW17tls_int_self_init() -> !cir.ptr<!s32i> {
-// CIR-NOT: cir.call @_ZTH17tls_int_self_init() : () -> ()
+// CIR: cir.call @_ZTH17tls_int_self_init() : () -> ()
 // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_int_self_init : 
!cir.ptr<!s32i>
 // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!s32i>
 
 // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW11tls_int_ref() -> 
!cir.ptr<!cir.ptr<!s32i>> {
-// CIR-NOT: cir.call @_ZTH11tls_int_ref() : () -> ()
+// CIR: cir.call @_ZTH11tls_int_ref() : () -> ()
 // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_int_ref : 
!cir.ptr<!cir.ptr<!s32i>>
 // CIR: cir.return %0 : !cir.ptr<!cir.ptr<!s32i>>
 
+// CIR: cir.func @_ZTH11tls_int_ref() alias(@__tls_init)
+
 // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW11tls_int_dyn() -> 
!cir.ptr<!s32i> {
-// CIR-NOT: cir.call @_ZTH11tls_int_dyn() : () -> ()
+// CIR: cir.call @_ZTH11tls_int_dyn() : () -> ()
 // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_int_dyn : 
!cir.ptr<!s32i>
 // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!s32i>
 
+// CIR: cir.func @_ZTH11tls_int_dyn() alias(@__tls_init)
+
+// Full init of all variables (func names below).
+// CIR-LABEL: cir.func internal private @__tls_init() {
+// CIR: cir.return
+
 // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW7tls_int() -> 
!cir.ptr<!s32i> {
 // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_int : 
!cir.ptr<!s32i>
 // CIR: cir.return %[[GET_GLOB]]
 
+// LLVM-BOTH-DAG: @tls_int = thread_local global i32 5, align 4
+// LLVM-BOTH-DAG: @tls_int_dyn = thread_local global i32 0, align 4
+// LLVM-BOTH-DAG: @tls_int_ref = thread_local global ptr null, align 8
+// LLVM-BOTH-DAG: @tls_int_self_init = thread_local global i32 0, align 4
+// LLVM-BOTH-DAG: @definitely_inited = thread_local global i32 5, align 4
+// LLVM-BOTH-DAG: @definitely_inited_dyn = thread_local global i32 0, align 4
+// LLVM-BOTH-DAG: @maybe_inited = external thread_local global i32, align 4
+//
+// LLVM-BOTH-DAG: @_ZTH21definitely_inited_dyn = alias void (), ptr @__tls_init
+// LLVM-BOTH-DAG: @_ZTH17tls_int_self_init = alias void (), ptr @__tls_init
+// LLVM-BOTH-DAG: @_ZTH11tls_int_ref = alias void (), ptr @__tls_init
+// LLVM-BOTH-DAG: @_ZTH11tls_int_dyn = alias void (), ptr @__tls_init
+
 // Wrappers: 
 // LLVM: define linkonce_odr hidden ptr @_ZTW12maybe_inited() {
-// Intentionally disabled until we implement this.
-// LLVMX:   %[[HAS_INIT_FUNC:.*]] = icmp ne ptr @_ZTH12maybe_inited, null
-// LLVMX:   br i1 %[[HAS_INIT_FUNC]]
-// LLVM-NOT:   call void @_ZTH12maybe_inited()
+// LLVM:   %[[HAS_INIT_FUNC:.*]] = icmp ne ptr @_ZTH12maybe_inited, null
+// LLVM:   br i1 %[[HAS_INIT_FUNC]]
+// LLVM:   call void @_ZTH12maybe_inited()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@maybe_inited)
 // LLVM:   ret ptr %[[GET_GLOB]]
 //
 // LLVM: define weak_odr hidden ptr @_ZTW21definitely_inited_dyn() {
-// LLVM-NOT:   call void @_ZTH21definitely_inited_dyn()
+// LLVM:   call void @_ZTH21definitely_inited_dyn()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@definitely_inited_dyn)
 // LLVM:   ret ptr %[[GET_GLOB]]
 //
@@ -70,25 +92,29 @@ struct CtorDtor {
 // LLVM: }
 //
 // LLVM: define weak_odr hidden ptr @_ZTW17tls_int_self_init() {
-// LLVM-NOT:   call void @_ZTH17tls_int_self_init()
+// LLVM:   call void @_ZTH17tls_int_self_init()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_int_self_init)
 // LLVM:   ret ptr %[[GET_GLOB]]
 //
 // LLVM: define weak_odr hidden ptr @_ZTW11tls_int_ref() {
-// LLVM-NOT:   call void @_ZTH11tls_int_ref()
+// LLVM:   call void @_ZTH11tls_int_ref()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_int_ref)
 // LLVM:   ret ptr %[[GET_GLOB]]
 //
 // LLVM: define weak_odr hidden ptr @_ZTW11tls_int_dyn() {
-// LLVM-NOT:   call void @_ZTH11tls_int_dyn()
+// LLVM:   call void @_ZTH11tls_int_dyn()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_int_dyn)
 // LLVM:   ret ptr %[[GET_GLOB]]
 
+// LLVM: define internal void @__tls_init() {
+// LLVM:   ret void
+
 // LLVM: define weak_odr hidden ptr @_ZTW7tls_int() {
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_int)
 // LLVM:   ret ptr %[[GET_GLOB]]
 // LLVM: }
 
+
 thread_local int tls_int = 5;
 // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = <"_ZTW7tls_int", 
"_ZTH7tls_int"> @tls_int = #cir.int<5> : !s32i
 // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW7tls_int", 
"_ZTH7tls_int"> @tls_int = #cir.int<5> : !s32i
@@ -100,12 +126,18 @@ thread_local int tls_int_dyn = get_i();
 // CIR-BEFORE-LPP:   cir.store {{.*}}%[[CALL]], %[[GET_GLOB]] : !s32i, 
!cir.ptr<!s32i>
 // CIR-BEFORE-LPP: }
 // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW11tls_int_dyn", 
"_ZTH11tls_int_dyn"> @tls_int_dyn = #cir.int<0> : !s32i 
-
+// CIR: cir.func internal private @[[TLS_INT_DYN_INIT:.*]]() {
+// CIR:   %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_int_dyn : 
!cir.ptr<!s32i>
+// CIR:   %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i {llvm.noundef})
+// CIR:   cir.store {{.*}}%[[CALL]], %[[GET_GLOB]] : !s32i, !cir.ptr<!s32i>
+// CIR:   cir.return
+// LLVM: define internal void @[[TLS_INT_DYN_INIT:.*]]() {
 // OGCG: define internal void @[[TLS_INT_DYN_INIT:.*]]()
 // OGCG:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
-// OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@tls_int_dyn)
-// OGCG:   store i32 %[[CALL]], ptr %[[GET_GLOB]], align 4
-// OGCG:   ret void
+// LLVM-BOTH:   %[[GET_GLOB:.*]] = call {{.*}}ptr 
@llvm.threadlocal.address.p0(ptr {{.*}}@tls_int_dyn)
+// LLVM:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
+// LLVM-BOTH:   store i32 %[[CALL]], ptr %[[GET_GLOB]], align 4
+// LLVM-BOTH:   ret void
 
 thread_local int &tls_int_ref = tls_int_dyn;
 // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = 
<"_ZTW11tls_int_ref", "_ZTH11tls_int_ref"> @tls_int_ref = ctor : 
!cir.ptr<!s32i> {
@@ -114,12 +146,18 @@ thread_local int &tls_int_ref = tls_int_dyn;
 // CIR-BEFORE-LPP:   cir.store {{.*}}%[[GET_OTHER]], %[[GET_GLOB]] : 
!cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>
 // CIR-BEFORE-LPP: }
 // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW11tls_int_ref", 
"_ZTH11tls_int_ref"> @tls_int_ref = #cir.ptr<null> : !cir.ptr<!s32i>
-
+// CIR: cir.func internal private @[[TLS_INT_REF_INIT:.*]]() {
+// CIR:   %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_int_ref : 
!cir.ptr<!cir.ptr<!s32i>>
+// CIR:   %[[GET_REF:.*]] = cir.call @_ZTW11tls_int_dyn() : () -> 
!cir.ptr<!s32i>
+// CIR:   cir.store {{.*}}%[[GET_REF]], %[[GET_GLOB]] : !cir.ptr<!s32i>, 
!cir.ptr<!cir.ptr<!s32i>>
+// CIR:   cir.return
+// LLVM: define internal void @[[TLS_INT_REF_INIT:.*]]() {
 // OGCG: define internal void @[[TLS_INT_REF_INIT:.*]]()
 // OGCG:   %[[GET_REF:.*]] = call ptr @_ZTW11tls_int_dyn()
-// OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@tls_int_ref)
-// OGCG:   store ptr %[[GET_REF]], ptr %[[GET_GLOB]], align 8
-// OGCG:   ret void
+// LLVM-BOTH:   %[[GET_GLOB:.*]] = call {{.*}}ptr 
@llvm.threadlocal.address.p0(ptr {{.*}}@tls_int_ref)
+// LLVM:   %[[GET_REF:.*]] = call ptr @_ZTW11tls_int_dyn()
+// LLVM-BOTH:   store ptr %[[GET_REF]], ptr %[[GET_GLOB]], align 8
+// LLVM-BOTH:   ret void
 
 // OGCG: define weak_odr hidden noundef ptr @_ZTW11tls_int_dyn() {{.*}} comdat 
{
 // OGCG:   call void @_ZTH11tls_int_dyn()
@@ -136,16 +174,25 @@ thread_local int tls_int_self_init = tls_int_self_init + 
get_i();
 // CIR-BEFORE-LPP:    cir.store {{.*}}%[[ADD]], %[[GET_GLOB]] : !s32i, 
!cir.ptr<!s32i>
 // CIR-BEFORE-LPP:  }
 // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW17tls_int_self_init", 
"_ZTH17tls_int_self_init"> @tls_int_self_init = #cir.int<0> : !s32i
-
+// CIR: cir.func internal private @[[TLS_INT_SELF_REF_INIT:.*]]() {
+// CIR:   %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_int_self_init : 
!cir.ptr<!s32i>
+// CIR:   %[[GET_SELF_FROM_WRAPPER:.*]] = cir.call @_ZTW17tls_int_self_init() 
: () -> !cir.ptr<!s32i>
+// CIR:   %[[SELF_LOAD:.*]] = cir.load {{.*}}%[[GET_SELF_FROM_WRAPPER]] : 
!cir.ptr<!s32i>, !s32i
+// CIR:   %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i {llvm.noundef})
+// CIR:   %[[ADD:.*]] = cir.add nsw %[[SELF_LOAD]], %[[CALL]] : !s32i
+// CIR:   cir.store{{.*}} %[[ADD]], %[[GET_GLOB]] : !s32i, !cir.ptr<!s32i>
+// CIR:   cir.return
+// LLVM: define internal void @[[TLS_INT_SELF_REF_INIT:.*]]() {
 // OGCG: define internal void @[[TLS_INT_SELF_REF_INIT:.*]]()
-// OGCG:   %[[GET_SELF_FROM_WRAPPER:.*]] = call ptr @_ZTW17tls_int_self_init()
-// OGCG:   %[[SELF_LOAD:.*]] = load i32, ptr %[[GET_SELF_FROM_WRAPPER]], align 
4
-// OGCG:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
-// OGCG:   %[[ADD:.*]] = add nsw i32 %[[SELF_LOAD]], %[[CALL]]
+// LLVM:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@tls_int_self_init)
+// LLVM-BOTH:   %[[GET_SELF_FROM_WRAPPER:.*]] = call ptr 
@_ZTW17tls_int_self_init()
+// LLVM-BOTH:   %[[SELF_LOAD:.*]] = load i32, ptr %[[GET_SELF_FROM_WRAPPER]], 
align 4
+// LLVM-BOTH:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
+// LLVM-BOTH:   %[[ADD:.*]] = add nsw i32 %[[SELF_LOAD]], %[[CALL]]
 // OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@tls_int_self_init)
-// OGCG:   store i32 %[[ADD]], ptr %[[GET_GLOB]], align 4
-// OGCG:   ret void
-
+// LLVM-BOTH:   store i32 %[[ADD]], ptr %[[GET_GLOB]], align 4
+// LLVM-BOTH:   ret void
+//
 // OGCG: define weak_odr hidden noundef ptr @_ZTW17tls_int_self_init() {{.*}} 
comdat {
 // OGCG:   call void @_ZTH17tls_int_self_init()
 // OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@tls_int_self_init)
@@ -155,12 +202,6 @@ extern thread_local int definitely_inited = 5;
 // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = 
<"_ZTW17definitely_inited", "_ZTH17definitely_inited"> @definitely_inited = 
#cir.int<5> : !s32i
 // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW17definitely_inited", 
"_ZTH17definitely_inited"> @definitely_inited = #cir.int<5> : !s32i
 
-// OGCG: define internal void @[[DEF_INITED_DYN:.*]]()
-// OGCG:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
-// OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@definitely_inited_dyn)
-// OGCG:   store i32 %[[CALL]], ptr %[[GET_GLOB]], align 4
-// OGCG:   ret void
-
 extern thread_local int definitely_inited_dyn = get_i();
 // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = 
<"_ZTW21definitely_inited_dyn", "_ZTH21definitely_inited_dyn"> 
@definitely_inited_dyn = ctor : !s32i {
 // CIR-BEFORE-LPP:   %[[GET_GLOB:.*]] = cir.get_global thread_local 
@definitely_inited_dyn : !cir.ptr<!s32i>
@@ -168,9 +209,22 @@ extern thread_local int definitely_inited_dyn = get_i();
 // CIR-BEFORE-LPP:   cir.store {{.*}}%[[CALL]], %[[GET_GLOB]] : !s32i, 
!cir.ptr<!s32i>
 // CIR-BEFORE-LPP: }
 // CIR: cir.global external tls_dyn dyn_tls_refs = 
<"_ZTW21definitely_inited_dyn", "_ZTH21definitely_inited_dyn"> 
@definitely_inited_dyn = #cir.int<0> : !s32i
+// CIR: cir.func internal private @[[DEF_INITED_DYN:.*]]() {
+// CIR:   %[[GET_GLOB:.*]] = cir.get_global thread_local 
@definitely_inited_dyn : !cir.ptr<!s32i>
+// CIR:   %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i {llvm.noundef})
+// CIR:   cir.store align(4) %[[CALL]], %[[GET_GLOB]] : !s32i, !cir.ptr<!s32i>
+// CIR:   cir.return
+// LLVM: define internal void @[[DEF_INITED_DYN:.*]]() {
+// OGCG: define internal void @[[DEF_INITED_DYN:.*]]()
+// LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@definitely_inited_dyn)
+// LLVM-BOTH:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
+// OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@definitely_inited_dyn)
+// LLVM-BOTH:   store i32 %[[CALL]], ptr %[[GET_GLOB]], align 4
+// LLVM-BOTH:   ret void
 
 extern thread_local int maybe_inited;
 // CIR-BEFORE-LPP: cir.global "private" external tls_dyn dyn_tls_refs = 
<"_ZTW12maybe_inited", "_ZTH12maybe_inited"> @maybe_inited : !s32i
+// CIR: cir.global "private" external tls_dyn dyn_tls_refs = 
<"_ZTW12maybe_inited", "_ZTH12maybe_inited"> @maybe_inited : !s32i
 
 void uses() {
   auto a = tls_int;
@@ -189,7 +243,6 @@ void uses() {
 // CIR-BEFORE-LPP: cir.get_global thread_local @tls_int_ref : 
!cir.ptr<!cir.ptr<!s32i>>
 // CIR: cir.call @_ZTW11tls_int_ref() : () -> !cir.ptr<!cir.ptr<!s32i>>
 // LLVM-BOTH: call ptr @_ZTW11tls_int_ref()
-
   auto d = tls_int_self_init;
 // CIR-BEFORE-LPP: cir.get_global thread_local @tls_int_self_init : 
!cir.ptr<!s32i>
 // CIR: cir.call @_ZTW17tls_int_self_init() : () -> !cir.ptr<!s32i>
@@ -198,7 +251,6 @@ void uses() {
 // CIR-BEFORE-LPP: cir.get_global thread_local @maybe_inited : !cir.ptr<!s32i>
 // CIR: cir.call @_ZTW12maybe_inited() : () -> !cir.ptr<!s32i>
 // LLVM-BOTH: call ptr @_ZTW12maybe_inited()
-
   auto f = definitely_inited;
 // CIR-BEFORE-LPP: cir.get_global thread_local @definitely_inited : 
!cir.ptr<!s32i>
 // CIR: cir.call @_ZTW17definitely_inited() : () -> !cir.ptr<!s32i>
@@ -212,6 +264,7 @@ void uses() {
 // CIR: cir.call @_ZTW21definitely_inited_dyn() : () -> !cir.ptr<!s32i>
 // LLVM-BOTH: call ptr @_ZTW21definitely_inited_dyn()
 }
+
 // OGCG Wrappers: For some reason this puts them at the end, otherwise they are
 // basically identical (return val has a noundef?). Note some are above because
 // they are referenced up there.
diff --git a/clang/test/CIR/CodeGen/global-tls-templates.cpp 
b/clang/test/CIR/CodeGen/global-tls-templates.cpp
index 36086f45e6543..95cf1a26069e9 100644
--- a/clang/test/CIR/CodeGen/global-tls-templates.cpp
+++ b/clang/test/CIR/CodeGen/global-tls-templates.cpp
@@ -30,60 +30,134 @@ thread_local T tls_templ = {get_i()};
 
 // Wrapper: Ctor/Dtor
 // CIR-LABEL: cir.func comdat weak_odr private hidden 
@_ZTW9tls_templI8CtorDtorE() -> !cir.ptr<!rec_CtorDtor> {
-// CIR-NOT:  cir.call @_ZTH9tls_templI8CtorDtorE() : () -> ()
+// CIR:  cir.call @_ZTH9tls_templI8CtorDtorE() : () -> ()
 // CIR:  %[[GET_GLOB:.*]] = cir.get_global thread_local 
@_Z9tls_templI8CtorDtorE : !cir.ptr<!rec_CtorDtor>
 // CIR:  cir.return %[[GET_GLOB]] : !cir.ptr<!rec_CtorDtor>
 // CIR:}
 
+// Alias: Ctor/Dtor: 
+// CIR: cir.func linkonce_odr @_ZTH9tls_templI8CtorDtorE() 
alias(@[[CTOR_DTOR_INIT:[^)]*]])
+// TLS Guard: Ctor/Dtor:
+
 // Wrapper: int
 // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW9tls_templIiE() -> 
!cir.ptr<!s32i>
-// CIR-NOT:   cir.call @_ZTH9tls_templIiE() : () -> () 
+// CIR:   cir.call @_ZTH9tls_templIiE() : () -> () 
 // CIR:   %[[GET_GLOB:.*]] = cir.get_global thread_local @_Z9tls_templIiE : 
!cir.ptr<!s32i>
 // CIR:   cir.return %[[GET_GLOB]] : !cir.ptr<!s32i>
 // CIR: }
 
+// Alias: int
+// CIR: cir.func linkonce_odr @_ZTH9tls_templIiE() alias(@[[INT_INIT:[^)]*]])
+
 // Global: int
 // CIR: cir.global linkonce_odr comdat tls_dyn dyn_tls_refs = 
<"_ZTW9tls_templIiE", "_ZTH9tls_templIiE", "_ZGV9tls_templIiE"> 
@_Z9tls_templIiE = #cir.int<0> : !s32i
+
+// Init Func: int
+// CIR:  cir.func internal private @[[INT_INIT]]() {
+// CIR:      %[[GET_GLOB:.*]] = cir.get_global thread_local @_Z9tls_templIiE : 
!cir.ptr<!s32i>
+// CIR:      %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i 
{llvm.noundef})
+// CIR:      cir.store {{.*}}%[[CALL]], %[[GET_GLOB]] : !s32i, !cir.ptr<!s32i>
+
+
 // Global: Ctor/Dotr:
 // CIR: cir.global linkonce_odr comdat tls_dyn dyn_tls_refs = 
<"_ZTW9tls_templI8CtorDtorE", "_ZTH9tls_templI8CtorDtorE", 
"_ZGV9tls_templI8CtorDtorE"> @_Z9tls_templI8CtorDtorE = #cir.zero : 
!rec_CtorDtor
 
+// Init Func: Ctor/Dtor:
+// CIR: cir.func internal private @[[CTOR_DTOR_INIT]]() {
+// CIR:     %[[GET_GLOB:.*]] = cir.get_global thread_local 
@_Z9tls_templI8CtorDtorE : !cir.ptr<!rec_CtorDtor>
+// CIR:     %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i {llvm.noundef})
+// CIR:     cir.call @_ZN8CtorDtorC1Ei(%[[GET_GLOB]], %[[CALL]]) : 
(!cir.ptr<!rec_CtorDtor> {{.*}}, !s32i {llvm.noundef}) -> ()
+// CIR:     %[[GET_GLOB:.*]] = cir.get_global thread_local 
@_Z9tls_templI8CtorDtorE : !cir.ptr<!rec_CtorDtor>
+// CIR:     %[[GET_DTOR:.*]] = cir.get_global @_ZN8CtorDtorD1Ev : 
!cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>>
+// CIR:     %[[DTOR_FPTR:.*]] = cir.cast bitcast %[[GET_DTOR]] : 
!cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>> -> 
!cir.ptr<!cir.func<(!cir.ptr<!void>)>>
+// CIR:     %[[GLOB_DECAY:.*]] = cir.cast bitcast %[[GET_GLOB:.*]] : 
!cir.ptr<!rec_CtorDtor> -> !cir.ptr<!void>
+// CIR:     %[[DSO_HANDLE:.*]] = cir.get_global @__dso_handle : !cir.ptr<i8>
+// CIR:     cir.call @__cxa_thread_atexit(%[[DTOR_FPTR]], %[[GLOB_DECAY]], 
%[[DSO_HANDLE]]) : (!cir.ptr<!cir.func<(!cir.ptr<!void>)>>, !cir.ptr<!void>, 
!cir.ptr<i8>) -> ()
+
+// FIXME: These have inconsistent COMDAT with classic codegen, but we don't
+// currently specify 'comdat' with a name.
 // Globals:
 // LLVM-BOTH-DAG: @_Z9tls_templIiE = linkonce_odr thread_local global i32 0, 
comdat, align 4
 // LLVM-BOTH-DAG: @_Z9tls_templI8CtorDtorE = linkonce_odr thread_local global 
%struct.CtorDtor zeroinitializer, comdat, align 4
 
+// Aliases:
+// LLVM-BOTH-DAG: @_ZTH9tls_templI8CtorDtorE = linkonce_odr alias void (), ptr 
@[[CTOR_DTOR_INIT:.*]]
+// LLVM-BOTH-DAG: @_ZTH9tls_templIiE = linkonce_odr alias void (), ptr 
@[[INT_INIT:.*]]
+
+// OGCG Has this first, same check lines as LLVM.
+// OGCG-LABEL: define dso_local void @_Z4usesv()
+// OGCG: call ptr @_ZTW9tls_templIiE()
+// OGCG: call ptr @_ZTW9tls_templI8CtorDtorE()
+
 // Wrappers: Just opposite ordering, same check lines as LLVM.
 // FIXME: OGCG has these set as 'comdat'. However, CIR doesn't lower comdat to
 // LLVM, so it doesn't show up in the IR here.
 // LLVM-LABEL: define weak_odr hidden {{.*}}ptr @_ZTW9tls_templI8CtorDtorE() {
-// LLVM-NOT:   call void @_ZTH9tls_templI8CtorDtorE()
+// LLVM:   call void @_ZTH9tls_templI8CtorDtorE()
 // LLVM:   call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@_Z9tls_templI8CtorDtorE)
 // LLVM: }
 
 // LLVM-LABEL: define weak_odr hidden {{.*}}ptr @_ZTW9tls_templIiE() {
-// LLVM-NOT:   call void @_ZTH9tls_templIiE()
+// LLVM:   call void @_ZTH9tls_templIiE()
 // LLVM:   call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@_Z9tls_templIiE)
 // LLVM: }
 
+// OGCG-LABEL: define weak_odr hidden {{.*}}ptr @_ZTW9tls_templIiE() {{.*}} 
comdat {
+// OGCG:   call void @_ZTH9tls_templIiE()
+// OGCG:   call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@_Z9tls_templIiE)
+// OGCG: }
+
+// OGCG-LABEL: define weak_odr hidden {{.*}}ptr 
@_ZTW9tls_templI8CtorDtorE(){{.*}} comdat {
+// OGCG:   call void @_ZTH9tls_templI8CtorDtorE()
+// OGCG:   call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@_Z9tls_templI8CtorDtorE)
+// OGCG: }
+
+
+// Inits: 
+// Note: the differences here are mostly ordering, however there are a few 
diferences:
+// 1- OGCG skipps the llvm.threadlocal call.  We've seen this elsewhere with 
thread local,
+//    so I don't think it is problematic, as it just seems like an early opt.
+// 2- For some reason OGCG generates guards as i64/i8 depending on platform 
(like with static-local),
+//    but ALWAYS treats the load/stores as i8.  This is likely a 'bug' in 
OGCG, but one that
+//    doesn't really matter at all.
+// LLVM-BOTH: define internal void @[[INT_INIT]]()
+// OGCG:   %[[LOAD_GUARD:.*]] = load i8, ptr @_ZGV9tls_templIiE, align 8
+// OGCG:   %[[ISUNINIT:.*]] = icmp eq i{{.*}} %[[LOAD_GUARD]], 0
+// OGCG:   br i1 %[[ISUNINIT]]
+//
+// OGCG:   store i8 1, ptr @_ZGV9tls_templIiE, align 8
+// LLVM:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@_Z9tls_templIiE)
+// LLVM:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
+// OGCG:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
+// OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@_Z9tls_templIiE)
+// LLVM-BOTH:   store i32 %[[CALL]], ptr %[[GET_GLOB]]
+
+// LLVM-BOTH: define internal void @[[CTOR_DTOR_INIT]]()
+// OGCG:   %[[LOAD_GUARD:.*]] = load i8, ptr @_ZGV9tls_templI8CtorDtorE, align 
8
+// OGCG:   %[[ISUNINIT:.*]] = icmp eq i{{.*}} %[[LOAD_GUARD]], 0
+// OGCG:   br i1 %[[ISUNINIT]]
+//
+// OGCG:  store i8 1, ptr @_ZGV9tls_templI8CtorDtorE, align 8
+//
+// LLVM:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@_Z9tls_templI8CtorDtorE)
+// LLVM-BOTH:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
+// LLVM:   call void @_ZN8CtorDtorC1Ei(ptr {{.*}}%[[GET_GLOB]], i32 
{{.*}}%[[CALL]])
+// OGCG:   call void @_ZN8CtorDtorC1Ei(ptr {{.*}}@_Z9tls_templI8CtorDtorE, i32 
{{.*}}%[[CALL]])
+//
+// LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@_Z9tls_templI8CtorDtorE)
+// LLVM:   call void @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr 
%[[GET_GLOB]], ptr @__dso_handle)
+// OGCG:   call i32 @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr 
@_Z9tls_templI8CtorDtorE, ptr @__dso_handle)
+
 // CIR-BEFORE-LPP-LABEL: cir.func{{.*}}@_Z4usesv
 // CIR-LABEL: cir.func{{.*}}@_Z4usesv
-// LLVM-BOTH-LABEL: define dso_local void @_Z4usesv()
+// LLVM-LABEL: define dso_local void @_Z4usesv()
 void uses() {
   auto x = tls_templ<int>;
 // CIR-BEFORE-LPP: cir.get_global thread_local @_Z9tls_templIiE : 
!cir.ptr<!s32i>
 // CIR: cir.call @_ZTW9tls_templIiE() : () -> !cir.ptr<!s32i>
-// LLVM-BOTH: call ptr @_ZTW9tls_templIiE()
+// LLVM: call ptr @_ZTW9tls_templIiE()
   auto y = tls_templ<CtorDtor>;
 // CIR-BEFORE-LPP: cir.get_global thread_local @_Z9tls_templI8CtorDtorE : 
!cir.ptr<!rec_CtorDtor>
 // CIR: cir.call @_ZTW9tls_templI8CtorDtorE() : () -> !cir.ptr<!rec_CtorDtor>
-// LLVM-BOTH: call ptr @_ZTW9tls_templI8CtorDtorE()
+// LLVM: call ptr @_ZTW9tls_templI8CtorDtorE()
 }
-
-// OGCG-LABEL: define weak_odr hidden {{.*}}ptr @_ZTW9tls_templIiE() {{.*}} 
comdat {
-// OGCG:   call void @_ZTH9tls_templIiE()
-// OGCG:   call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@_Z9tls_templIiE)
-// OGCG: }
-
-// OGCG-LABEL: define weak_odr hidden {{.*}}ptr 
@_ZTW9tls_templI8CtorDtorE(){{.*}} comdat {
-// OGCG:   call void @_ZTH9tls_templI8CtorDtorE()
-// OGCG:   call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@_Z9tls_templI8CtorDtorE)
-// OGCG: }

>From e4dbb6902dda076d6b1833c235b035b5e90f4e9f Mon Sep 17 00:00:00 2001
From: erichkeane <[email protected]>
Date: Tue, 12 May 2026 11:14:01 -0700
Subject: [PATCH 2/3] clang-format

---
 clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index e041ad984c995..18eaa920682e8 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -138,7 +138,7 @@ struct LoweringPreparePass
   void buildCXXGlobalInitFunc();
   // Build a init function for all of the ordered global thread local storage
   // variables.
- void buildCXXGlobalTlsFunc();
+  void buildCXXGlobalTlsFunc();
 
   /// Materialize global ctor/dtor list
   void buildGlobalCtorDtorList();
@@ -1577,7 +1577,7 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
     // If this is a declaration and has no init function, we probably DO have 
to
     // create an alias that needs checking, so create it as extern-weak.
     initAlias = defineGlobalThreadLocalInitAlias(op, {});
-   }
+  }
 
   // We need a wrapper for TLS globals that MIGHT have a non-constant
   // initialization. The FE will have generated the DynTlsRefs for any with

>From 7e8c89c8aa4b5310e49c46c2e14fded6df4138bb Mon Sep 17 00:00:00 2001
From: erichkeane <[email protected]>
Date: Tue, 12 May 2026 11:39:49 -0700
Subject: [PATCH 3/3] Do the suggestions from xluanko

---
 .../Dialect/Transforms/LoweringPrepare.cpp    | 31 +++++++++----------
 1 file changed, 15 insertions(+), 16 deletions(-)

diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 18eaa920682e8..bd710b52c643a 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -1203,11 +1203,11 @@ LoweringPreparePass::getGuardReleaseFn(cir::PointerType 
guardPtrTy) {
 cir::FuncOp LoweringPreparePass::getTlsInitFn() {
   // void __tls_init(void);
   CIRBaseBuilderTy builder(getContext());
-  mlir::OpBuilder::InsertionGuard ipGuard{builder};
+  mlir::OpBuilder::InsertionGuard _{builder};
   builder.setInsertionPointToStart(mlirModule.getBody());
   mlir::Location loc = mlirModule.getLoc();
   cir::VoidType voidTy = cir::VoidType::get(&getContext());
-  auto fnType = cir::FuncType::get({}, voidTy);
+  auto fnType = builder.getVoidFnTy();
   return buildRuntimeFunction(builder, "__tls_init", loc, fnType,
                               cir::GlobalLinkageKind::InternalLinkage);
 }
@@ -1468,29 +1468,28 @@ void 
LoweringPreparePass::defineGlobalThreadLocalWrapper(cir::GlobalOp op,
   // If we are a situation where we have/need one, emit a call to the init
   // function.
   if (initAlias) {
+    mlir::Location aliasLoc = initAlias.getLoc();
     if (!isVarDefinition) {
       // If this isn't a definition, we have to check that the alias exists.
       mlir::Value funcLoad = cir::GetGlobalOp::create(
-          builder, initAlias.getLoc(),
-          cir::PointerType::get(initAlias.getFunctionType()),
+          builder, aliasLoc, 
cir::PointerType::get(initAlias.getFunctionType()),
           initAlias.getSymName());
       mlir::Value nullCheck =
-          builder.getNullValue(funcLoad.getType(), initAlias.getLoc());
+          builder.getNullValue(funcLoad.getType(), aliasLoc);
       mlir::Value cmp = cir::CmpOp::create(
-          builder, initAlias.getLoc(), cir::CmpOpKind::ne, funcLoad, 
nullCheck);
-      cir::IfOp::create(
-          builder, initAlias.getLoc(), cmp, /*withElseRegion=*/false,
-          [&](mlir::OpBuilder &, mlir::Location loc) {
-            builder.createCallOp(initAlias.getLoc(), initAlias, {});
-            cir::YieldOp::create(builder, initAlias.getLoc());
-          });
+          builder, aliasLoc, cir::CmpOpKind::ne, funcLoad, nullCheck);
+      cir::IfOp::create(builder, aliasLoc, cmp, /*withElseRegion=*/false,
+                        [&](mlir::OpBuilder &, mlir::Location loc) {
+                          builder.createCallOp(aliasLoc, initAlias, {});
+                          cir::YieldOp::create(builder, aliasLoc);
+                        });
     } else {
       // If this IS a definition, we know the alias exists, so we can just emit
       // a call to it.
-      builder.createCallOp(initAlias.getLoc(), initAlias, {});
+      builder.createCallOp(aliasLoc, initAlias, {});
     }
   }
-  auto get = builder.createGetGlobal(op, /*tls=*/true);
+  cir::GetGlobalOp get = builder.createGetGlobal(op, /*tls=*/true);
   cir::ReturnOp::create(builder, op.getLoc(), {get});
 }
 
@@ -1506,10 +1505,10 @@ 
LoweringPreparePass::defineGlobalThreadLocalInitAlias(cir::GlobalOp op,
   if (existingAliasIter != threadLocalInitAliases.end())
     return existingAliasIter->second;
 
-  auto funcType = cir::FuncType::get({}, builder.getVoidTy());
+  auto funcType = builder.getVoidFnTy();
   cir::FuncOp alias =
       cir::FuncOp::create(builder, op.getLoc(), aliasName, funcType);
-  alias.setLinkageAttr(op.getLinkageAttr());
+  alias.setLinkage(op.getLinkage());
 
   if (aliasee) {
     alias.setAliasee(aliasee.getSymName());

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to