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

>From 33dfdc8d6a5105799736367d27ddcc99b46fa84b Mon Sep 17 00:00:00 2001
From: erichkeane <[email protected]>
Date: Mon, 11 May 2026 08:27:35 -0700
Subject: [PATCH] [CIR] Global-TLS variable 'call' rewriting-

This is a followup to my previous patch to handle global/namespace
thread local variables.  This patch handles the
re-writing/lowering-prepare of the `get-global` for these variables.
Each call to one of these is required to go to a 'wrapper' function,
which optionally calls the initializer. This patch does not handle the
initializer call (so each wrapper call is a very simple 'return the
variable'), as that will be handled in a followup.

Also, variables without initialization don't use a wrapper in Classic
Codegen, however this patch does.  The followup patch that will call the
initializer will skip the call to the initializer, but leave the wrapper
in place.  This is a necessity due to how we handle global
ops/get-global ops: we won't know whether there is a required ctor/dtor
that needs an initializer at the time of wrapper-write-replacement.
---
 .../Dialect/Transforms/LoweringPrepare.cpp    | 154 +++++++++++++-
 .../test/CIR/CodeGen/global-tls-dyn-init.cpp  | 117 +++++++++++
 .../CIR/CodeGen/global-tls-simple-init.cpp    | 195 ++++++++++++++++++
 .../test/CIR/CodeGen/global-tls-templates.cpp |  55 +++++
 4 files changed, 518 insertions(+), 3 deletions(-)

diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index ec16c162dbad4..0b0a3ba6eef29 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -90,6 +90,7 @@ struct LoweringPreparePass
   void lowerComplexDivOp(cir::ComplexDivOp op);
   void lowerComplexMulOp(cir::ComplexMulOp op);
   void lowerUnaryOp(cir::UnaryOpInterface op);
+  void lowerGetGlobalOp(cir::GetGlobalOp op);
   void lowerGlobalOp(cir::GlobalOp op);
   void lowerThreeWayCmpOp(cir::CmpThreeWayOp op);
   void lowerArrayDtor(cir::ArrayDtor op);
@@ -119,6 +120,13 @@ struct LoweringPreparePass
   /// Build the function that initializes the specified global
   cir::FuncOp buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op);
 
+  /// When looking at the 'global' op, create the wrapper function.
+  void defineGlobalThreadLocalWrapper(cir::GlobalOp op, cir::FuncOp initAlias,
+                                      bool isVarDefinition);
+  /// 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,
@@ -255,6 +263,7 @@ struct LoweringPreparePass
   /// Tracks existing dynamic initializers.
   llvm::StringMap<uint32_t> dynamicInitializerNames;
   llvm::SmallVector<cir::FuncOp> dynamicInitializers;
+  llvm::StringMap<cir::FuncOp> threadLocalWrappers;
 
   /// Tracks guard variables for static locals (keyed by global symbol name).
   llvm::StringMap<cir::GlobalOp> staticLocalDeclGuardMap;
@@ -1360,6 +1369,88 @@ void 
LoweringPreparePass::lowerLocalInitOp(cir::LocalInitOp initOp) {
   // Remove the init local op, now that we've done everything we need with it.
   initOp.erase();
 }
+static bool isThreadWrapperReplaceable(cir::TLS_Model tls,
+                                       clang::ASTContext &astCtx) {
+  return tls == cir::TLS_Model::GeneralDynamic &&
+         astCtx.getTargetInfo().getTriple().isOSDarwin();
+}
+
+static cir::GlobalLinkageKind
+getThreadLocalWrapperLinkage(GlobalOp op, clang::ASTContext &astCtx) {
+  if (isLocalLinkage(op.getLinkage()))
+    return op.getLinkage();
+
+  if (isThreadWrapperReplaceable(*op.getTlsModel(), astCtx))
+    if (!isLinkOnceLinkage(op.getLinkage()) &&
+        !isWeakODRLinkage(op.getLinkage()))
+      return op.getLinkage();
+
+  // If this isn't a TU in which this variable is defined, the thread wrapper 
is
+  // discardable.
+  if (op.isDeclaration())
+    return cir::GlobalLinkageKind::LinkOnceODRLinkage;
+  return cir::GlobalLinkageKind::WeakODRLinkage;
+}
+
+cir::FuncOp
+LoweringPreparePass::getOrCreateThreadLocalWrapper(CIRBaseBuilderTy &builder,
+                                                   GlobalOp op) {
+  mlir::OpBuilder::InsertionGuard insertGuard(builder);
+  builder.setInsertionPointToStart(&mlirModule.getBodyRegion().front());
+
+  mlir::StringAttr wrapperName = op.getDynTlsRefs()->getWrapperName();
+
+  auto existingWrapperIter = threadLocalWrappers.find(wrapperName.getValue());
+  if (existingWrapperIter != threadLocalWrappers.end())
+    return existingWrapperIter->second;
+
+  // type is ptr-to-global-type(void);
+  auto funcType = cir::FuncType::get({}, 
builder.getPointerTo(op.getSymType()));
+  cir::FuncOp func =
+      cir::FuncOp::create(builder, op.getLoc(), wrapperName, funcType);
+
+  cir::GlobalLinkageKind linkageKind =
+      getThreadLocalWrapperLinkage(op, *astCtx);
+  func.setLinkageAttr(
+      cir::GlobalLinkageKindAttr::get(&getContext(), linkageKind));
+
+  // TODO(cir): This is supposed to refer to the comdat of the global symbol,
+  // but that isn't in CIR yet.
+  if (astCtx->getTargetInfo().getTriple().supportsCOMDAT() &&
+      func.isWeakForLinker())
+    func.setComdat(true);
+
+  mlir::SymbolTable::setSymbolVisibility(
+      func, mlir::SymbolTable::Visibility::Private);
+
+  if (!isLocalLinkage(linkageKind)) {
+    if (!isThreadWrapperReplaceable(*op.getTlsModel(), *astCtx) ||
+        isLinkOnceLinkage(linkageKind) || isWeakODRLinkage(linkageKind) ||
+        op.getGlobalVisibility() == cir::VisibilityKind::Hidden)
+      func.setGlobalVisibility(cir::VisibilityKind::Hidden);
+  }
+  if (isThreadWrapperReplaceable(*op.getTlsModel(), *astCtx))
+    op->emitError("Unhandled thread wrapper attributes for CC and Nounwind");
+
+  threadLocalWrappers.insert({wrapperName.getValue(), func});
+  return func;
+}
+
+void LoweringPreparePass::defineGlobalThreadLocalWrapper(cir::GlobalOp op,
+                                                         cir::FuncOp initAlias,
+                                                         bool isVarDefinition) 
{
+  CIRBaseBuilderTy builder(getContext());
+  cir::FuncOp wrapper = getOrCreateThreadLocalWrapper(builder, op);
+  mlir::Block *entryBB = wrapper.addEntryBlock();
+  builder.setInsertionPointToStart(entryBB);
+  // 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");
+  }
+  auto get = builder.createGetGlobal(op, /*tls=*/true);
+  cir::ReturnOp::create(builder, op.getLoc(), {get});
+}
 
 void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
   // Static locals are handled separately via guard variables.
@@ -1368,6 +1459,8 @@ 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()) {
     // Build a variable initialization function and move the initialzation code
@@ -1382,9 +1475,62 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
     dynamicInitializers.push_back(f);
   }
 
+  // We need a wrapper for TLS globals that MIGHT have a non-constant
+  // initialization. The FE will have generated the DynTlsRefs for any with
+  // known dynamic init, or unknown (extern) init.
+  if (op.getTlsModel() == TLS_Model::GeneralDynamic && op.getDynTlsRefs())
+    defineGlobalThreadLocalWrapper(op, initAlias, !op.isDeclaration());
+
   assert(!cir::MissingFeatures::opGlobalAnnotations());
 }
 
+void LoweringPreparePass::lowerGetGlobalOp(GetGlobalOp op) {
+  if (!op.getTls())
+    return;
+  auto globalOp = mlir::dyn_cast_or_null<cir::GlobalOp>(
+      symbolTables.lookupNearestSymbolFrom(op, op.getNameAttr()));
+
+  if (!globalOp || globalOp.getTlsModel() != TLS_Model::GeneralDynamic ||
+      globalOp.getStaticLocalGuard().has_value())
+    return;
+
+  // Global ops that have constant init don't actually end up with a wrapper, 
so
+  // don't replace anything.
+  if (!globalOp.getDynTlsRefs())
+    return;
+
+  // If this is a global TLS, we need to replace the call to 'get_global' with 
a
+  // call to the wrapper function.  Classic codegen figures out some cases 
where
+  // we can omit this, but for now we're going to always put it in, as it is
+  // effectively a no-op.
+
+  // The first 'GetGlobalOp' at the beginning of a ctor/dtor region on one of
+  // these is for the purpose of creating/destroying.  We want to skip 
replacing
+  // THAT one, but leave all other get-global-ops in place, else
+  // self-referential ops won't work right.
+  mlir::Operation *parentOp = op->getParentOp();
+  if (parentOp == globalOp) {
+    mlir::Region *ctorRegion = &globalOp.getCtorRegion();
+    mlir::Region *dtorRegion = &globalOp.getDtorRegion();
+
+    if (!ctorRegion->empty() && &*ctorRegion->op_begin() == op.getOperation())
+      return;
+    if (!dtorRegion->empty() && &*dtorRegion->op_begin() == op.getOperation())
+      return;
+  }
+
+  CIRBaseBuilderTy builder(getContext());
+  cir::FuncOp wrapperFunc = getOrCreateThreadLocalWrapper(builder, globalOp);
+
+  builder.setInsertionPoint(op);
+  cir::CallOp call = builder.createCallOp(
+      wrapperFunc.getLoc(),
+      mlir::FlatSymbolRefAttr::get(wrapperFunc.getSymNameAttr()),
+      wrapperFunc.getFunctionType().getReturnType(), {});
+  op->replaceAllUsesWith(call);
+  op.erase();
+}
+
 void LoweringPreparePass::lowerThreeWayCmpOp(CmpThreeWayOp op) {
   CIRBaseBuilderTy builder(getContext());
   builder.setInsertionPointAfter(op);
@@ -1877,6 +2023,8 @@ void LoweringPreparePass::runOnOp(mlir::Operation *op) {
     lowerComplexMulOp(complexMul);
   } else if (auto glob = mlir::dyn_cast<cir::GlobalOp>(op)) {
     lowerGlobalOp(glob);
+  } else if (auto getGlob = mlir::dyn_cast<cir::GetGlobalOp>(op)) {
+    lowerGetGlobalOp(getGlob);
   } else if (auto unaryOp = mlir::dyn_cast<cir::UnaryOpInterface>(op)) {
     lowerUnaryOp(unaryOp);
   } else if (auto callOp = dyn_cast<cir::CallOp>(op)) {
@@ -2284,9 +2432,9 @@ void LoweringPreparePass::runOnOperation() {
   op->walk([&](mlir::Operation *op) {
     if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp,
                   cir::ComplexMulOp, cir::ComplexDivOp, cir::DynamicCastOp,
-                  cir::FuncOp, cir::CallOp, cir::GlobalOp, cir::StoreOp,
-                  cir::CmpThreeWayOp, cir::IncOp, cir::DecOp, cir::MinusOp,
-                  cir::NotOp, cir::LocalInitOp>(op))
+                  cir::FuncOp, cir::CallOp, cir::GetGlobalOp, cir::GlobalOp,
+                  cir::StoreOp, cir::CmpThreeWayOp, cir::IncOp, cir::DecOp,
+                  cir::MinusOp, cir::NotOp, cir::LocalInitOp>(op))
       opsToTransform.push_back(op);
   });
 
diff --git a/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp 
b/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp
index ef3c1e306f62d..b974f61131353 100644
--- a/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp
+++ b/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp
@@ -1,4 +1,7 @@
 // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 
| FileCheck %s --check-prefix=CIR-BEFORE-LPP
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir %s -o - | FileCheck %s --check-prefix=CIR
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM-BOTH,LLVM
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s 
-o - | FileCheck %s --check-prefix=LLVM-BOTH,OGCG
 
 int get_i();
 struct CtorDtor {
@@ -7,11 +10,62 @@ struct CtorDtor {
     int i;
 };
 
+// Wrappers:
+// 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: %[[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-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: %[[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-LABEL: cir.func comdat weak_odr private hidden @_ZTW10tls_cd_dyn() -> 
!cir.ptr<!rec_CtorDtor> {
+// CIR-NOT: 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-LABEL: cir.func comdat weak_odr private hidden @_ZTW6tls_cd() -> 
!cir.ptr<!rec_CtorDtor> {
+// CIR-NOT: 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>
+
+// LLVM: define weak_odr hidden ptr @_ZTW19tls_cd_dyn_not_used() {
+// LLVM-NOT:   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:   %[[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:   %[[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:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_cd)
+// LLVM:   ret ptr %[[GET_GLOB]]
+// LLVM: }
+//
+
 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 {
 // CIR-BEFORE-LPP:   %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd : 
!cir.ptr<!rec_CtorDtor>
 // 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
+
+// OGCG: define internal void @[[TLS_CD_INIT:.*]]() {{.*}}{
+// OGCG:   call i32 @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr @tls_cd, 
ptr @__dso_handle)
+// OGCG:   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 {
@@ -22,6 +76,13 @@ thread_local CtorDtor tls_cd_dyn = get_i();
 // CIR-BEFORE-LPP:    %[[GET_GLOB:.*]] = cir.get_global thread_local 
@tls_cd_dyn : !cir.ptr<!rec_CtorDtor>
 // 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
+
+// OGCG: define internal void @[[TLS_CD_DYN_INIT:.*]]() {{.*}} {
+// OGCG:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
+// OGCG:   call void @_ZN8CtorDtorC1Ei(ptr {{.*}}@tls_cd_dyn, i32 
{{.*}}%[[CALL]])
+// OGCG:   call i32 @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr 
@tls_cd_dyn, ptr @__dso_handle)
+// OGCG:   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> {
@@ -29,6 +90,19 @@ thread_local CtorDtor &tls_cd_ref = tls_cd_dyn;
 // CIR-BEFORE-LPP:   %[[CALL:.*]] = cir.get_global thread_local @tls_cd_dyn : 
!cir.ptr<!rec_CtorDtor>
 // 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>
+
+// OGCG: define internal void @[[TLS_CD_REF_INIT:.*]]() {{.*}} {
+// OGCG:   %[[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
+
+// 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)
+// OGCG:   ret ptr %[[GET_GLOB]]
+// OGCG: }
 
 thread_local CtorDtor tls_cd_dyn_not_used = get_i();
 // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = 
<"_ZTW19tls_cd_dyn_not_used", "_ZTH19tls_cd_dyn_not_used"> @tls_cd_dyn_not_used 
= ctor : !rec_CtorDtor {
@@ -39,12 +113,55 @@ thread_local CtorDtor tls_cd_dyn_not_used = get_i();
 // CIR-BEFORE-LPP:   %[[GET_GLOB:.*]] = cir.get_global thread_local 
@tls_cd_dyn_not_used : !cir.ptr<!rec_CtorDtor>
 // 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
+
+// OGCG: define internal void @[[TLS_CD_DYN_NOT_USED_INIT:.*]]() {{.*}} {
+// OGCG:   %[[CALL:.*]] = call noundef i32 @_Z5get_iv()
+// OGCG:   call void @_ZN8CtorDtorC1Ei(ptr {{.*}}@tls_cd_dyn_not_used, i32 
{{.*}}%[[CALL]])
+// OGCG:   call i32 @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr 
@tls_cd_dyn_not_used, ptr @__dso_handle)
+// OGCG:   ret void
 
 void uses() {
   auto a = tls_cd;
 // CIR-BEFORE-LPP: cir.get_global thread_local @tls_cd : 
!cir.ptr<!rec_CtorDtor>
+// CIR: cir.call @_ZTW6tls_cd() : () -> !cir.ptr<!rec_CtorDtor>
+// LLVM-BOTH: call ptr @_ZTW6tls_cd()
   auto b = tls_cd_dyn;
 // CIR-BEFORE-LPP: cir.get_global thread_local @tls_cd_dyn : 
!cir.ptr<!rec_CtorDtor>
+// CIR: cir.call @_ZTW10tls_cd_dyn() : () -> !cir.ptr<!rec_CtorDtor>
+// LLVM-BOTH: call ptr @_ZTW10tls_cd_dyn()
   auto c = tls_cd_ref;
 // CIR-BEFORE-LPP: cir.get_global thread_local @tls_cd_ref : 
!cir.ptr<!cir.ptr<!rec_CtorDtor>>
+// CIR: cir.call @_ZTW10tls_cd_ref() : () -> !cir.ptr<!cir.ptr<!rec_CtorDtor>>
+// LLVM-BOTH: call ptr @_ZTW10tls_cd_ref()
 }
+
+// OGCG: define weak_odr hidden noundef ptr @_ZTW6tls_cd() {{.*}} comdat {
+// OGCG:   call void @_ZTH6tls_cd()
+// OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@tls_cd)
+// OGCG:   ret ptr %[[GET_GLOB]]
+// OGCG: }
+//
+// OGCG: define weak_odr hidden noundef ptr @_ZTW10tls_cd_ref() {{.*}} comdat {
+// OGCG:   call void @_ZTH10tls_cd_ref()
+// OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@tls_cd_ref)
+// OGCG:   %[[LOAD_GLOB:.*]] = load ptr, ptr %[[GET_GLOB]], align 8
+// OGCG:   ret ptr %[[LOAD_GLOB]]
+// OGCG: }
+//
+// OGCG: define internal void @__tls_init() {{.*}} {
+// OGCG:   %[[GET_GUARD:.*]] = load i8, ptr @__tls_guard, align 1
+// OGCG:   %[[IS_UNINIT:.*]] = icmp eq i8 %[[GET_GUARD]], 0
+// OGCG:   br i1 %[[IS_UNINIT]]
+// OGCG
+// OGCG:   store i8 1, ptr @__tls_guard, align 1
+// OGCG:   call void @[[TLS_CD_INIT]]()
+// OGCG:   call void @[[TLS_CD_DYN_INIT]]()
+// OGCG:   call void @[[TLS_CD_REF_INIT]]()
+// OGCG:   call void @[[TLS_CD_DYN_NOT_USED_INIT]]()
+//
+// OGCG: define weak_odr hidden noundef ptr @_ZTW19tls_cd_dyn_not_used() 
{{.*}} comdat {
+// OGCG:   call void @_ZTH19tls_cd_dyn_not_used()
+// 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 fef55b0298c33..b9030d6518222 100644
--- a/clang/test/CIR/CodeGen/global-tls-simple-init.cpp
+++ b/clang/test/CIR/CodeGen/global-tls-simple-init.cpp
@@ -1,4 +1,7 @@
 // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 
| FileCheck %s --check-prefix=CIR-BEFORE-LPP
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir %s -o - | FileCheck %s --check-prefix=CIR
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM-BOTH,LLVM
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s 
-o - | FileCheck %s --check-prefix=LLVM-BOTH,OGCG
 
 int get_i();
 struct CtorDtor {
@@ -7,8 +10,88 @@ struct CtorDtor {
     int i;
 };
 
+// Wrappers:
+// 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_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: %[[GET_GLOB:.*]] = cir.get_global thread_local @definitely_inited_dyn 
: !cir.ptr<!s32i>
+// CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!s32i>
+
+// 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:   cir.return %[[GET_GLOB]] : !cir.ptr<!s32i>
+
+// 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: %[[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: %[[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-LABEL: cir.func comdat weak_odr private hidden @_ZTW11tls_int_dyn() -> 
!cir.ptr<!s32i> {
+// CIR-NOT: 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-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]]
+
+// 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:   %[[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:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@definitely_inited_dyn)
+// LLVM:   ret ptr %[[GET_GLOB]]
+//
+// LLVM: define weak_odr hidden ptr @_ZTW17definitely_inited() {
+// LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@definitely_inited)
+// LLVM:   ret ptr %[[GET_GLOB]]
+// LLVM: }
+//
+// LLVM: define weak_odr hidden ptr @_ZTW17tls_int_self_init() {
+// LLVM-NOT:   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:   %[[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:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr 
@tls_int_dyn)
+// LLVM:   ret ptr %[[GET_GLOB]]
+
+// 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
 
 thread_local int tls_int_dyn = get_i();
 // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = 
<"_ZTW11tls_int_dyn", "_ZTH11tls_int_dyn"> @tls_int_dyn = ctor : !s32i {
@@ -16,6 +99,13 @@ thread_local int tls_int_dyn = get_i();
 // CIR-BEFORE-LPP:   %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i 
{llvm.noundef})
 // 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 
+
+// 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
 
 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> {
@@ -23,6 +113,18 @@ thread_local int &tls_int_ref = tls_int_dyn;
 // CIR-BEFORE-LPP:   %[[GET_OTHER:.*]] = cir.get_global thread_local 
@tls_int_dyn : !cir.ptr<!s32i>
 // 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>
+
+// 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
+
+// OGCG: define weak_odr hidden noundef ptr @_ZTW11tls_int_dyn() {{.*}} comdat 
{
+// OGCG:   call void @_ZTH11tls_int_dyn()
+// OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@tls_int_dyn)
+// OGCG:   ret ptr %[[GET_GLOB]]
 
 thread_local int tls_int_self_init = tls_int_self_init + get_i();
 // CIR-BEFORE-LPP:  cir.global external tls_dyn dyn_tls_refs = 
<"_ZTW17tls_int_self_init", "_ZTH17tls_int_self_init"> @tls_int_self_init = 
ctor : !s32i {
@@ -33,9 +135,31 @@ thread_local int tls_int_self_init = tls_int_self_init + 
get_i();
 // CIR-BEFORE-LPP:    %[[ADD:.*]] = cir.add nsw %[[LOAD_SELF]], %[[CALL]] : 
!s32i
 // 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
+
+// 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]]
+// 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
+
+// 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)
+// OGCG:   ret ptr %[[GET_GLOB]]
 
 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 {
@@ -43,6 +167,7 @@ extern thread_local int definitely_inited_dyn = get_i();
 // CIR-BEFORE-LPP:   %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i 
{llvm.noundef})
 // 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
 
 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
@@ -50,16 +175,86 @@ extern thread_local int maybe_inited;
 void uses() {
   auto a = tls_int;
 // CIR-BEFORE-LPP: cir.get_global thread_local @tls_int : !cir.ptr<!s32i>
+// CIR: cir.call @_ZTW7tls_int() : () -> !cir.ptr<!s32i>
+// Note: CIR is currently ALWAYS using the wrapper here even though it doesn't
+// need to, however this is a 'no-op' anyway, so we'd expect this to be
+// optimized away.
+// LLVM: call ptr @_ZTW7tls_int()
+// OGCG: call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@tls_int)
   auto b = tls_int_dyn;
 // CIR-BEFORE-LPP: cir.get_global thread_local @tls_int_dyn : !cir.ptr<!s32i>
+// CIR: cir.call @_ZTW11tls_int_dyn() : () -> !cir.ptr<!s32i>
+// LLVM-BOTH: call ptr @_ZTW11tls_int_dyn()
   auto c = tls_int_ref;
 // 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>
+// LLVM-BOTH: call ptr @_ZTW17tls_int_self_init()
   auto e = maybe_inited;
 // 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>
+// Note: CIR is currently ALWAYS using the wrapper here even though it doesn't
+// need to, however this is a 'no-op' anyway, so we'd expect this to be
+// optimized away.
+// LLVM: call ptr @_ZTW17definitely_inited()
+// OGCG: call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@definitely_inited)
   auto g = definitely_inited_dyn;
 // CIR-BEFORE-LPP: cir.get_global thread_local @definitely_inited_dyn : 
!cir.ptr<!s32i>
+// 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.
+// Also: these have 'comdat' but the above LLVM versions don't, because we
+// haven't yet lowered comdat on functions.
+// OGCG: define weak_odr hidden noundef ptr @_ZTW11tls_int_ref() {{.*}} comdat 
{
+// OGCG:   call void @_ZTH11tls_int_ref()
+// OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@tls_int_ref)
+// OGCG:   %[[GET_PTR:.*]] = load ptr, ptr %[[GET_GLOB]]
+// OGCG:   ret ptr %[[GET_PTR]]
+//
+// OGCG: define linkonce_odr hidden noundef ptr @_ZTW12maybe_inited() {{.*}} 
comdat {
+// OGCG:   %[[HAS_INIT_FUNC:.*]] = icmp ne ptr @_ZTH12maybe_inited, null
+// OGCG:   br i1 %[[HAS_INIT_FUNC]]
+// OGCG:   call void @_ZTH12maybe_inited()
+// OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@maybe_inited)
+// OGCG:   ret ptr %[[GET_GLOB]]
+//
+// OGCG: define weak_odr hidden noundef ptr @_ZTW21definitely_inited_dyn() 
{{.*}} comdat {
+// OGCG:   call void @_ZTH21definitely_inited_dyn()
+// OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@definitely_inited_dyn)
+// OGCG:   ret ptr %[[GET_GLOB]]
+//
+// The init function here happens in the middle for some reason?  
+// OGCG: define internal void @__tls_init()
+// OGCG:   %[[GET_GUARD:.*]] = load i8, ptr @__tls_guard, align 1
+// OGCG:   %[[IS_UNINIT:.*]] = icmp eq i8 %[[GET_GUARD]], 0
+// OGCG:   br i1 %[[IS_UNINIT]]
+//
+// OGCG:   store i8 1, ptr @__tls_guard, align 1
+// OGCG:   call void @[[TLS_INT_DYN_INIT]]()
+// OGCG:   call void @[[TLS_INT_REF_INIT]]()
+// OGCG:   call void @[[TLS_INT_SELF_REF_INIT]]()
+// OGCG:   call void @[[DEF_INITED_DYN]]()
+// OGCG:   br label 
+// OGCG:   ret void
+//
+// OGCG: define weak_odr hidden noundef ptr @_ZTW7tls_int() {{.*}} comdat {
+// OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@tls_int)
+// OGCG:   ret ptr %[[GET_GLOB]]
+// OGCG: }
+//
+// OGCG: define weak_odr hidden noundef ptr @_ZTW17definitely_inited() {{.*}} 
comdat {
+// OGCG:   %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@definitely_inited)
+// OGCG:   ret ptr %[[GET_GLOB]]
+// OGCG: }
diff --git a/clang/test/CIR/CodeGen/global-tls-templates.cpp 
b/clang/test/CIR/CodeGen/global-tls-templates.cpp
index bad1f1440dde5..36086f45e6543 100644
--- a/clang/test/CIR/CodeGen/global-tls-templates.cpp
+++ b/clang/test/CIR/CodeGen/global-tls-templates.cpp
@@ -1,4 +1,7 @@
 // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 
| FileCheck %s --check-prefix=CIR-BEFORE-LPP
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir %s -o - | FileCheck %s --check-prefix=CIR
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM,LLVM-BOTH
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s 
-o - | FileCheck %s --check-prefix=OGCG,LLVM-BOTH
 
 int get_i();
 struct CtorDtor {
@@ -25,10 +28,62 @@ thread_local T tls_templ = {get_i()};
 // CIR-BEFORE-LPP:    cir.call @_ZN8CtorDtorD1Ev(%[[GET_GLOB]]) : 
(!cir.ptr<!rec_CtorDtor>) -> ()
 // CIR-BEFORE-LPP:  }
 
+// 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:  %[[GET_GLOB:.*]] = cir.get_global thread_local 
@_Z9tls_templI8CtorDtorE : !cir.ptr<!rec_CtorDtor>
+// CIR:  cir.return %[[GET_GLOB]] : !cir.ptr<!rec_CtorDtor>
+// CIR:}
+
+// Wrapper: int
+// CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW9tls_templIiE() -> 
!cir.ptr<!s32i>
+// CIR-NOT:   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: }
+
+// 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
+// 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
+
+// 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
+
+// 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 {{.*}}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 {{.*}}ptr @llvm.threadlocal.address.p0(ptr 
{{.*}}@_Z9tls_templIiE)
+// LLVM: }
+
 // CIR-BEFORE-LPP-LABEL: cir.func{{.*}}@_Z4usesv
+// CIR-LABEL: cir.func{{.*}}@_Z4usesv
+// LLVM-BOTH-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()
   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()
 }
+
+// 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: }

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

Reply via email to