https://github.com/erichkeane created 
https://github.com/llvm/llvm-project/pull/215380

Functionally, the only difference between these models is the tag that goes 
onto the variable in the LLVM-IR. This patch changes our "== GeneralDynamic" 
checks to just be whether they have a TLS Model defined, so all the previous 
logic still holds.

This patch DOES have to add the 'default' model to the module, as the top-level 
TLS guard needs to have this model, even if the individual variables are 
overridden by the tls_model attribute.

Like the previous inline-printing patch, this ensures we print the module 
result attribute as an enum value instead an integral. This requires some 
custom parse/printing here, but is pretty routine. We can't convert GlobalOp 
without vastly changing how it prints these, as there isn't really a 
'assemblyFormat' that works for this printed as an 'attribute' and a keyword.

We ALSO had to change the name of the enum to be TLSModel, because MLIR is 
inconsistent in tablegen where it gets the name from on an enum, so it would 
pick up the wrong spelling sometimes.

>From b97348e557abdb2a14cb8625237162cdb2b05571 Mon Sep 17 00:00:00 2001
From: erichkeane <[email protected]>
Date: Wed, 5 Aug 2026 14:43:45 -0700
Subject: [PATCH] [CIR] Implement the 'rest' of the TLS Model types

Functionally, the only difference between these models is the tag that
goes onto the variable in the LLVM-IR. This patch changes our "==
GeneralDynamic" checks to just be whether they have a TLS Model defined,
so all the previous logic still holds.

This patch DOES have to add the 'default' model to the module, as the
top-level TLS guard needs to have this model, even if the individual
variables are overridden by the tls_model attribute.

Like the previous inline-printing patch, this ensures we print the
module result attribute as an enum value instead an integral. This
requires some custom parse/printing here, but is pretty routine. We
can't convert GlobalOp without vastly changing how it prints these, as
there isn't really a 'assemblyFormat' that works for this printed as an
'attribute' and a keyword.

We ALSO had to change the name of the enum to be TLSModel, because MLIR
is inconsistent in tablegen where it gets the name from on an enum, so
it would pick up the wrong spelling sometimes.
---
 .../clang/CIR/Dialect/IR/CIRDialect.td        |  1 +
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 18 ++++-
 clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp       |  3 -
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        | 30 ++++---
 clang/lib/CIR/CodeGen/CIRGenModule.h          |  2 +-
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       | 26 +++++-
 .../Dialect/Transforms/LoweringPrepare.cpp    | 27 ++++---
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  4 +-
 clang/test/CIR/CodeGen/global-ctor-dtor.cpp   |  2 +-
 .../test/CIR/CodeGen/tls-model-func-scope.cpp | 79 +++++++++++++++++++
 clang/test/CIR/CodeGen/tls-model.cpp          | 58 +++++++-------
 clang/test/CIR/IR/global.cir                  |  8 ++
 clang/test/CIR/IR/invalid-tls.cir             |  9 ++-
 13 files changed, 196 insertions(+), 71 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/tls-model-func-scope.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td 
b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
index de90fed45f170..d9319a44d017e 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
@@ -86,6 +86,7 @@ def CIR_Dialect : Dialect {
     static llvm::StringRef getAMDGPUXnackAttrName() { return 
"cir.amdgpu_xnack"; }
     static llvm::StringRef getAMDGPUSramEccAttrName() { return 
"cir.amdgpu_sramecc"; }
     static llvm::StringRef getOpenCLKernelArgMetadataAttrName() { return 
"cir.cl.kernel_arg_metadata"; }
+    static llvm::StringRef getDefaultTlsModelAttrName() { return 
"cir.default.tls_model"; }
 
     void registerAttributes();
     void registerTypes();
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index d0f3c9ee6715f..53f9dccbba1a0 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3301,12 +3301,22 @@ def CIR_GlobalLinkageKind : CIR_I32EnumAttr<
 // properties of a global variable will be added over time as more of ClangIR
 // is upstreamed.
 
-def CIR_TLSModel : CIR_I32EnumAttr<"TLS_Model", "TLS model", [
+def CIR_TLSModel : CIR_I32EnumAttr<"TLSModel", "TLS model", [
   I32EnumAttrCase<"GeneralDynamic", 1, "tls_dyn">,
   I32EnumAttrCase<"LocalDynamic", 2, "tls_local_dyn">,
   I32EnumAttrCase<"InitialExec", 3, "tls_init_exec">,
   I32EnumAttrCase<"LocalExec", 4, "tls_local_exec">
-]>;
+  ]> {
+  let genSpecializedAttr = 0;
+}
+
+def CIR_TLSModelAttr: CIR_EnumAttr<CIR_TLSModel, "tls_model"> {
+  let summary = "TLS Model attribute";
+  let description = [{
+     The TLS mode for the global, which comes from either the
+    `tls_model` attribute, or `-ftls-model` flag.
+  }];
+}
 
 def CIR_GlobalOp : CIR_Op<"global", [
   DeclareOpInterfaceMethods<RegionBranchOpInterface, ["getSuccessorInputs"]>,
@@ -3347,7 +3357,7 @@ def CIR_GlobalOp : CIR_Op<"global", [
                        TypeAttr:$sym_type,
                        CIR_GlobalLinkageKind:$linkage,
                        OptionalAttr<MemorySpaceAttrInterface>:$addr_space,
-                       OptionalAttr<CIR_TLSModel>:$tls_model,
+                       OptionalAttr<CIR_TLSModelAttr>:$tls_model,
                        
OptionalAttr<CIR_ThreadLocalGlobalWrapperInitAttr>:$dyn_tls_refs,
                        OptionalAttr<AnyAttr>:$initial_value,
                        UnitProp:$comdat,
@@ -3371,7 +3381,7 @@ def CIR_GlobalOp : CIR_Op<"global", [
     (`constant` $constant^)?
     $linkage
     (`comdat` $comdat^)?
-    ($tls_model^)?
+    (` ` custom<TLSModel>($tls_model)^ )?
     (`dyn_tls_refs` `=` $dyn_tls_refs^)?
     (`dso_local` $dso_local^)?
     (`static_local_guard` `` $static_local_guard^)?
diff --git a/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp
index 03929e50f44f9..1e31877e51b74 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp
@@ -68,9 +68,6 @@ void CIRGenModule::setGlobalTlsReferences(const VarDecl &vd,
   if (!getLangOpts().CPlusPlus)
     return;
 
-  if (globalOp.getTlsModel() != cir::TLS_Model::GeneralDynamic)
-    return;
-
   llvm::SmallString<256> wrapperFuncName;
   llvm::SmallString<256> initFuncName;
   llvm::SmallString<256> guardName;
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index ae9cad0b7c30f..dfb47fb4cda62 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -144,6 +144,10 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,
                                              cgo.OptimizationLevel,
                                              cgo.OptimizeSize));
 
+  theModule->setAttr(
+      cir::CIRDialect::getDefaultTlsModelAttrName(),
+      cir::TLSModelAttr::get(&mlirContext, getDefaultCIRTLSModel()));
+
   if (langOpts.OpenMP) {
     mlir::omp::OffloadModuleOpts ompOpts(
         langOpts.OpenMPTargetDebug, langOpts.OpenMPTeamSubscription,
@@ -3070,24 +3074,24 @@ bool CIRGenModule::lookupRepresentativeDecl(StringRef 
mangledName,
   return true;
 }
 
-static cir::TLS_Model getCIRTLSModel(StringRef S) {
-  return llvm::StringSwitch<cir::TLS_Model>(S)
-      .Case("global-dynamic", cir::TLS_Model::GeneralDynamic)
-      .Case("local-dynamic", cir::TLS_Model::LocalDynamic)
-      .Case("initial-exec", cir::TLS_Model::InitialExec)
-      .Case("local-exec", cir::TLS_Model::LocalExec);
+static cir::TLSModel getCIRTLSModel(StringRef S) {
+  return llvm::StringSwitch<cir::TLSModel>(S)
+      .Case("global-dynamic", cir::TLSModel::GeneralDynamic)
+      .Case("local-dynamic", cir::TLSModel::LocalDynamic)
+      .Case("initial-exec", cir::TLSModel::InitialExec)
+      .Case("local-exec", cir::TLSModel::LocalExec);
 }
 
-cir::TLS_Model CIRGenModule::getDefaultCIRTLSModel() const {
+cir::TLSModel CIRGenModule::getDefaultCIRTLSModel() const {
   switch (getCodeGenOpts().getDefaultTLSModel()) {
   case CodeGenOptions::GeneralDynamicTLSModel:
-    return cir::TLS_Model::GeneralDynamic;
+    return cir::TLSModel::GeneralDynamic;
   case CodeGenOptions::LocalDynamicTLSModel:
-    return cir::TLS_Model::LocalDynamic;
+    return cir::TLSModel::LocalDynamic;
   case CodeGenOptions::InitialExecTLSModel:
-    return cir::TLS_Model::InitialExec;
+    return cir::TLSModel::InitialExec;
   case CodeGenOptions::LocalExecTLSModel:
-    return cir::TLS_Model::LocalExec;
+    return cir::TLSModel::LocalExec;
   }
   llvm_unreachable("Invalid TLS model!");
 }
@@ -3096,7 +3100,7 @@ void CIRGenModule::setTLSMode(mlir::Operation *op, const 
VarDecl &d,
                               bool isExtendingDecl) {
   assert(d.getTLSKind() && "setting TLS mode on non-TLS var!");
 
-  cir::TLS_Model tlm = getDefaultCIRTLSModel();
+  cir::TLSModel tlm = getDefaultCIRTLSModel();
 
   // Override the TLS model if it is explicitly specified.
   if (const auto *attr = d.getAttr<TLSModelAttr>())
@@ -3107,7 +3111,7 @@ void CIRGenModule::setTLSMode(mlir::Operation *op, const 
VarDecl &d,
 
   // For namespace-scope dyanmic TLS we need to set the wrapper, int, or guard
   // info.
-  if (d.isStaticLocal() || tlm != cir::TLS_Model::GeneralDynamic)
+  if (d.isStaticLocal())
     return;
 
   // If this function was called to set the TLS mode for a temporary whose
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h 
b/clang/lib/CIR/CodeGen/CIRGenModule.h
index ba7b20b91cc03..9d19a58a05b4d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -637,7 +637,7 @@ class CIRGenModule : public CIRGenTypeCache {
                   bool isExtendingDecl = false);
 
   /// Get TLS mode from CodeGenOptions.
-  cir::TLS_Model getDefaultCIRTLSModel() const;
+  cir::TLSModel getDefaultCIRTLSModel() const;
 
   /// Set function attributes for a function declaration.
   void setFunctionAttributes(GlobalDecl gd, cir::FuncOp f,
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index cd94219655e02..3a0b07c1daa1f 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -287,6 +287,30 @@ parseGlobalAddressSpaceValue(mlir::AsmParser &p,
 void printGlobalAddressSpaceValue(mlir::AsmPrinter &printer, cir::GlobalOp op,
                                   mlir::ptr::MemorySpaceAttrInterface attr);
 
+mlir::OptionalParseResult parseTLSModel(mlir::AsmParser &parser,
+                                        cir::TLSModelAttr &attr) {
+  static constexpr llvm::StringRef keywords[] = {
+      "tls_dyn", "tls_local_dyn", "tls_init_exec", "tls_local_exec"};
+  llvm::StringRef keyword;
+  if (parser.parseOptionalKeyword(&keyword, keywords).failed())
+    return success();
+
+  auto tlsModel = ::cir::symbolizeEnum<::cir::TLSModel>(keyword);
+  if (!tlsModel) {
+    return parser.emitError(parser.getCurrentLocation(), "expected one of [")
+           << llvm::join(llvm::ArrayRef(keywords), ", ")
+           << "] for TLSModel, got: " << keyword;
+  }
+
+  attr = ::cir::TLSModelAttr::get(parser.getContext(), *tlsModel);
+  return success();
+}
+
+void printTLSModel(mlir::AsmPrinter &printer, cir::GlobalOp op,
+                   const cir::TLSModelAttr &attr) {
+  printer << " " << stringifyTLSModel(attr.getValue());
+}
+
 
//===----------------------------------------------------------------------===//
 // AllocaOp
 
//===----------------------------------------------------------------------===//
@@ -2099,7 +2123,7 @@ mlir::LogicalResult cir::GlobalOp::verify() {
     if (getStaticLocalGuard().has_value())
       return emitOpError(
           "cannot have both static local and dynamic tls references");
-    if (!getTlsModel() || getTlsModel() != TLS_Model::GeneralDynamic)
+    if (!getTlsModel())
       return emitOpError("'dyn_tls_refs' only valid for dynamic tls");
   }
 
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 12d544fecbcc9..9628c64595e55 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -1460,9 +1460,9 @@ 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,
+static bool isThreadWrapperReplaceable(cir::TLSModel tls,
                                        clang::ASTContext &astCtx) {
-  return tls == cir::TLS_Model::GeneralDynamic &&
+  return tls == cir::TLSModel::GeneralDynamic &&
          astCtx.getTargetInfo().getTriple().isOSDarwin();
 }
 
@@ -1613,8 +1613,7 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
     dtorRegion.getBlocks().clear();
 
     assert(!cir::MissingFeatures::astVarDeclInterface());
-    if (op.getTlsModel() == TLS_Model::GeneralDynamic &&
-        !op.getStaticLocalGuard().has_value()) {
+    if (op.getTlsModel() && !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
@@ -1640,8 +1639,7 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
     } else {
       dynamicInitializers.push_back(f);
     }
-  } else if (op.getTlsModel() == TLS_Model::GeneralDynamic &&
-             op.getDynTlsRefs() && op.isDeclaration()) {
+  } else if (op.getTlsModel() && 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, {});
@@ -1650,7 +1648,7 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp 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
   // known dynamic init, or unknown (extern) init.
-  if (op.getTlsModel() == TLS_Model::GeneralDynamic && op.getDynTlsRefs())
+  if (op.getTlsModel() && op.getDynTlsRefs())
     defineGlobalThreadLocalWrapper(op, initAlias, !op.isDeclaration());
 
   assert(!cir::MissingFeatures::opGlobalAnnotations());
@@ -1666,8 +1664,7 @@ void LoweringPreparePass::lowerGetGlobalOp(GetGlobalOp 
op) {
   // get-global operations rewritten to be calls to a wrapper function.  If
   // we're not in a dynamic TLS (or one without the TLS markers), we can leave
   // this one as a get-global and return early.
-  if (globalOp.getTlsModel() != TLS_Model::GeneralDynamic ||
-      !globalOp.getDynTlsRefs())
+  if (!globalOp.getTlsModel() || !globalOp.getDynTlsRefs())
     return;
 
   // If this is a global TLS, we need to replace the call to 'get_global' with 
a
@@ -1792,9 +1789,15 @@ 
LoweringPreparePass::createGlobalThreadLocalGuard(CIRBaseBuilderTy &builder,
   g.setLinkageAttr(cir::GlobalLinkageKindAttr::get(
       builder.getContext(), cir::GlobalLinkageKind::InternalLinkage));
   g.setAlignment(clang::CharUnits::One().getAsAlign().value());
-  // At the moment, we only have implementation for this mode, as it is the
-  // default.  At one point we might need to load this mode from the module.
-  g.setTlsModel(TLS_Model::GeneralDynamic);
+
+  if (auto defTlsModel = mlirModule->getAttrOfType<TLSModelAttr>(
+          cir::CIRDialect::getDefaultTlsModelAttrName())) {
+    g.setTlsModel(defTlsModel.getValue());
+  } else {
+    // Default value, unless overridden in the IR/by the frontend.
+    g.setTlsModel(TLSModel::GeneralDynamic);
+  }
+
   g.setInitialValueAttr(cir::IntAttr::get(guardTy, 0));
   return g;
 }
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 717bf5e2e741e..be9e357fb7eee 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2777,10 +2777,10 @@ CIRToLLVMGlobalOpLowering::lowerGlobalAttributes(
 }
 
 static mlir::LLVM::ThreadLocalMode
-convertTlsModelAttrToLLVM(TLS_ModelAttr attr) {
+convertTlsModelAttrToLLVM(TLSModelAttr attr) {
   // assert that we can just static-cast these.
 #define CHECK_ENUM(CIR, LLVM_VAL)                                              
\
-  static_assert(static_cast<unsigned>(TLS_Model::CIR) ==                       
\
+  static_assert(static_cast<unsigned>(TLSModel::CIR) ==                        
\
                 static_cast<unsigned>(mlir::LLVM::ThreadLocalMode::LLVM_VAL))
   CHECK_ENUM(GeneralDynamic, GeneralDynamic);
   CHECK_ENUM(LocalDynamic, LocalDynamic);
diff --git a/clang/test/CIR/CodeGen/global-ctor-dtor.cpp 
b/clang/test/CIR/CodeGen/global-ctor-dtor.cpp
index 63f175281a02e..e58261ebf5446 100644
--- a/clang/test/CIR/CodeGen/global-ctor-dtor.cpp
+++ b/clang/test/CIR/CodeGen/global-ctor-dtor.cpp
@@ -36,7 +36,7 @@ void foo4(void) {
 
 // CIR-BEFORE-LPP: cir.func {{.*}} @_Z4foo4v() global_dtor(789)
 
-// CIR-AFTER: module @{{.*}} attributes {cir.global_ctors = 
[#cir.global_ctor<"_Z3foov", 65535>, #cir.global_ctor<"_Z4foo2v", 777>], 
cir.global_dtors = [#cir.global_dtor<"_Z4foo3v", 65535>, 
#cir.global_dtor<"_Z4foo4v", 789>]
+// CIR-AFTER: module @{{.*}} attributes {{{.*}}cir.global_ctors = 
[#cir.global_ctor<"_Z3foov", 65535>, #cir.global_ctor<"_Z4foo2v", 777>], 
cir.global_dtors = [#cir.global_dtor<"_Z4foo3v", 65535>, 
#cir.global_dtor<"_Z4foo4v", 789>]
 
 // LLVM: @llvm.global_ctors = appending global [2 x { i32, ptr, ptr }] [{ i32, 
ptr, ptr } { i32 65535, ptr @_Z3foov, ptr null }, { i32, ptr, ptr } { i32 777, 
ptr @_Z4foo2v, ptr null }]
 // LLVM: @llvm.global_dtors = appending global [2 x { i32, ptr, ptr }] [{ i32, 
ptr, ptr } { i32 65535, ptr @_Z4foo3v, ptr null }, { i32, ptr, ptr } { i32 789, 
ptr @_Z4foo4v, ptr null }]
diff --git a/clang/test/CIR/CodeGen/tls-model-func-scope.cpp 
b/clang/test/CIR/CodeGen/tls-model-func-scope.cpp
new file mode 100644
index 0000000000000..5567b6fdd9660
--- /dev/null
+++ b/clang/test/CIR/CodeGen/tls-model-func-scope.cpp
@@ -0,0 +1,79 @@
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -emit-cir -o - | FileCheck 
%s -check-prefix=CIR-GD
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -emit-llvm -fclangir -o - | 
FileCheck %s -check-prefix=LLVM-GD
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -emit-llvm -o - | FileCheck 
%s -check-prefix=LLVM-GD
+
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=global-dynamic 
-emit-cir -o - | FileCheck %s -check-prefix=CIR-GD
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=global-dynamic 
-emit-llvm -fclangir -o - | FileCheck %s -check-prefix=LLVM-GD
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=global-dynamic 
-emit-llvm -o - | FileCheck %s -check-prefix=LLVM-GD
+
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=local-dynamic 
-emit-cir -o - | FileCheck %s -check-prefix=CIR-LD
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=local-dynamic 
-emit-llvm -fclangir -o - | FileCheck %s -check-prefix=LLVM-LD
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=local-dynamic 
-emit-llvm -o - | FileCheck %s -check-prefix=LLVM-LD
+
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=initial-exec 
-emit-cir -o - | FileCheck %s -check-prefix=CIR-IE
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=initial-exec 
-emit-llvm -fclangir -o - | FileCheck %s -check-prefix=LLVM-IE
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=initial-exec 
-emit-llvm -o - | FileCheck %s -check-prefix=LLVM-IE
+
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=local-exec 
-emit-cir -o - | FileCheck %s -check-prefix=CIR-LE
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=local-exec 
-emit-llvm -fclangir -o - | FileCheck %s -check-prefix=LLVM-LE
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=local-exec 
-emit-llvm -o - | FileCheck %s -check-prefix=LLVM-LE
+
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -femulated-tls -emit-cir -o 
- 2>&1 | FileCheck %s -check-prefix=CIR-GD
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -femulated-tls -emit-llvm 
-fclangir -o - 2>&1 | FileCheck %s -check-prefix=LLVM-GD
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -femulated-tls -emit-llvm -o 
- 2>&1 | FileCheck %s -check-prefix=LLVM-GD
+
+int init();
+
+void func() {
+  thread_local int default_tls_mode = init();
+
+  __attribute__((tls_model("initial-exec")))
+  thread_local int override_tls_mode = init();
+}
+// CIR-GD: module {{.*}} attributes
+// CIR-GD-SAME: cir.default.tls_model = #cir.tls_model<tls_dyn>
+// CIR-GD: cir.global "private" internal tls_init_exec dso_local 
@_ZGVZ4funcvE17override_tls_mode = #cir.int<0> : !s8i
+// CIR-GD: cir.global "private" internal tls_dyn dso_local 
@_ZGVZ4funcvE16default_tls_mode = #cir.int<0> : !s8i
+// CIR-GD: cir.global "private" internal tls_init_exec dso_local 
static_local_guard<"_ZGVZ4funcvE17override_tls_mode"> 
@_ZZ4funcvE17override_tls_mode = #cir.int<0> : !s32i
+// CIR-GD: cir.global "private" internal tls_dyn dso_local 
static_local_guard<"_ZGVZ4funcvE16default_tls_mode"> 
@_ZZ4funcvE16default_tls_mode = #cir.int<0> : !s32i
+
+// LLVM-GD-DAG: @_ZGVZ4funcvE17override_tls_mode = internal 
thread_local(initialexec) global i8 0
+// LLVM-GD-DAG: @_ZGVZ4funcvE16default_tls_mode = internal thread_local global 
i8 0
+// LLVM-GD-DAG: @_ZZ4funcvE17override_tls_mode = internal 
thread_local(initialexec) global i32 0
+// LLVM-GD-DAG: @_ZZ4funcvE16default_tls_mode = internal thread_local global 
i32 0
+
+// CIR-LD: module {{.*}} attributes
+// CIR-LD-SAME: cir.default.tls_model = #cir.tls_model<tls_local_dyn>
+// CIR-LD: cir.global "private" internal tls_init_exec dso_local 
@_ZGVZ4funcvE17override_tls_mode = #cir.int<0> : !s8i
+// CIR-LD: cir.global "private" internal tls_local_dyn dso_local 
@_ZGVZ4funcvE16default_tls_mode = #cir.int<0> : !s8i
+// CIR-LD: cir.global "private" internal tls_init_exec dso_local 
static_local_guard<"_ZGVZ4funcvE17override_tls_mode"> 
@_ZZ4funcvE17override_tls_mode = #cir.int<0> : !s32i
+// CIR-LD: cir.global "private" internal tls_local_dyn dso_local 
static_local_guard<"_ZGVZ4funcvE16default_tls_mode"> 
@_ZZ4funcvE16default_tls_mode = #cir.int<0> : !s32i
+
+// LLVM-LD-DAG: @_ZGVZ4funcvE17override_tls_mode = internal 
thread_local(initialexec) global i8 0
+// LLVM-LD-DAG: @_ZGVZ4funcvE16default_tls_mode = internal 
thread_local(localdynamic) global i8 0
+// LLVM-LD-DAG: @_ZZ4funcvE17override_tls_mode = internal 
thread_local(initialexec) global i32 0
+// LLVM-LD-DAG: @_ZZ4funcvE16default_tls_mode = internal 
thread_local(localdynamic) global i32 0
+
+// CIR-IE: module {{.*}} attributes
+// CIR-IE-SAME: cir.default.tls_model = #cir.tls_model<tls_init_exec>
+// CIR-IE: cir.global "private" internal tls_init_exec dso_local 
@_ZGVZ4funcvE17override_tls_mode = #cir.int<0> : !s8i
+// CIR-IE: cir.global "private" internal tls_init_exec dso_local 
@_ZGVZ4funcvE16default_tls_mode = #cir.int<0> : !s8i
+// CIR-IE: cir.global "private" internal tls_init_exec dso_local 
static_local_guard<"_ZGVZ4funcvE17override_tls_mode"> 
@_ZZ4funcvE17override_tls_mode = #cir.int<0> : !s32i
+// CIR-IE: cir.global "private" internal tls_init_exec dso_local 
static_local_guard<"_ZGVZ4funcvE16default_tls_mode"> 
@_ZZ4funcvE16default_tls_mode = #cir.int<0> : !s32i
+
+// LLVM-IE-DAG: @_ZGVZ4funcvE17override_tls_mode = internal 
thread_local(initialexec) global i8 0
+// LLVM-IE-DAG: @_ZGVZ4funcvE16default_tls_mode = internal 
thread_local(initialexec) global i8 0
+// LLVM-IE-DAG: @_ZZ4funcvE17override_tls_mode = internal 
thread_local(initialexec) global i32 0
+// LLVM-IE-DAG: @_ZZ4funcvE16default_tls_mode = internal 
thread_local(initialexec) global i32 0
+
+// CIR-LE: module {{.*}} attributes
+// CIR-LE-SAME: cir.default.tls_model = #cir.tls_model<tls_local_exec>
+// CIR-LE: cir.global "private" internal tls_init_exec dso_local 
@_ZGVZ4funcvE17override_tls_mode = #cir.int<0> : !s8i
+// CIR-LE: cir.global "private" internal tls_local_exec dso_local 
@_ZGVZ4funcvE16default_tls_mode = #cir.int<0> : !s8i
+// CIR-LE: cir.global "private" internal tls_init_exec dso_local 
static_local_guard<"_ZGVZ4funcvE17override_tls_mode"> 
@_ZZ4funcvE17override_tls_mode = #cir.int<0> : !s32i
+// CIR-LE: cir.global "private" internal tls_local_exec dso_local 
static_local_guard<"_ZGVZ4funcvE16default_tls_mode"> 
@_ZZ4funcvE16default_tls_mode = #cir.int<0> : !s32i
+
+// LLVM-LE-DAG: @_ZGVZ4funcvE17override_tls_mode = internal 
thread_local(initialexec) global i8 0
+// LLVM-LE-DAG: @_ZGVZ4funcvE16default_tls_mode = internal 
thread_local(localexec) global i8 0
+// LLVM-LE-DAG: @_ZZ4funcvE17override_tls_mode = internal 
thread_local(initialexec) global i32 0
+// LLVM-LE-DAG: @_ZZ4funcvE16default_tls_mode = internal 
thread_local(localexec) global i32 0
diff --git a/clang/test/CIR/CodeGen/tls-model.cpp 
b/clang/test/CIR/CodeGen/tls-model.cpp
index 471891a16ca5b..e686faf585036 100644
--- a/clang/test/CIR/CodeGen/tls-model.cpp
+++ b/clang/test/CIR/CodeGen/tls-model.cpp
@@ -46,11 +46,13 @@ struct T thread_local t1;
 // Note that unlike normal C uninitialized global variables,
 // uninitialized TLS variables do NOT have COMMON linkage.
 
+// CIR-GD: module {{.*}} attributes 
+// CIR-GD-SAME: cir.default.tls_model = #cir.tls_model<tls_dyn>
 // CIR-GD-DAG: cir.global external @z1 = #cir.int<0> : !s32i {alignment = 4 : 
i64}
 // CIR-GD-DAG: cir.global external @z2 = #cir.int<0> : !s32i {alignment = 4 : 
i64}
 // CIR-GD-DAG: cir.global external tls_dyn dyn_tls_refs = <"_ZTW1x", "_ZTH1x"> 
@x = #cir.int<0> : !s32i {alignment = 4 : i64}
 // CIR-GD-DAG: cir.global "private" internal tls_dyn dso_local @_ZZ1fvE1y = 
#cir.int<0> : !s32i {alignment = 4 : i64}
-// CIR-GD-DAG: cir.global external tls_init_exec @z = #cir.int<0> : !s32i 
{alignment = 4 : i64}
+// CIR-GD-DAG: cir.global external tls_init_exec dyn_tls_refs = <"_ZTW1z", 
"_ZTH1z"> @z = #cir.int<0> : !s32i {alignment = 4 : i64}
 // CIR-GD-DAG: cir.global external tls_dyn dyn_tls_refs = <"_ZTW2s1", 
"_ZTH2s1"> @s1 = #cir.zero : !rec_S {alignment = 1 : i64, ast = 
#cir.var.decl.ast}
 // CIR-GD-DAG: cir.global external tls_dyn dyn_tls_refs = <"_ZTW2t1", 
"_ZTH2t1"> @t1 = #cir.zero : !rec_T {alignment = 1 : i64, ast = 
#cir.var.decl.ast}
 // CIR-GD-DAG: cir.global internal tls_dyn @__tls_guard = #cir.int<0> : !s8i 
{alignment = 1 : i64}
@@ -64,16 +66,16 @@ struct T thread_local t1;
 // LLVM-GD-DAG: @t1 ={{.*}} thread_local global %struct.T zeroinitializer
 // LLVM-GD-DAG: @__tls_guard = internal thread_local global i8 0
 
+// CIR-LD: module {{.*}} attributes 
+// CIR-LD-SAME: cir.default.tls_model = #cir.tls_model<tls_local_dyn>
 // CIR-LD-DAG: cir.global external @z1 = #cir.int<0> : !s32i {alignment = 4 : 
i64}
 // CIR-LD-DAG: cir.global external @z2 = #cir.int<0> : !s32i {alignment = 4 : 
i64}
-// CIR-LD-DAG: cir.global external tls_local_dyn @x = #cir.int<0> : !s32i 
{alignment = 4 : i64}
+// CIR-LD-DAG: cir.global external tls_local_dyn dyn_tls_refs = <"_ZTW1x", 
"_ZTH1x"> @x = #cir.int<0> : !s32i {alignment = 4 : i64}
 // CIR-LD-DAG: cir.global "private" internal tls_local_dyn dso_local 
@_ZZ1fvE1y = #cir.int<0> : !s32i {alignment = 4 : i64}
-// CIR-LD-DAG: cir.global external tls_init_exec @z = #cir.int<0> : !s32i 
{alignment = 4 : i64}
-// CIR-LD-DAG: cir.global external tls_local_dyn @s1 = #cir.zero : !rec_S 
{alignment = 1 : i64, ast = #cir.var.decl.ast}
-// CIR-LD-DAG: cir.global external tls_local_dyn @t1 = #cir.zero : !rec_T 
{alignment = 1 : i64, ast = #cir.var.decl.ast}
-// FIXME: our lowering for tls only emits the guard for tls_dyn, so this 
doesn't
-// appear.
-// cir.global internal tls_local_dyn @__tls_guard = #cir.int<0> : !s8i 
{alignment = 1 : i64}
+// CIR-LD-DAG: cir.global external tls_init_exec dyn_tls_refs = <"_ZTW1z", 
"_ZTH1z"> @z = #cir.int<0> : !s32i {alignment = 4 : i64}
+// CIR-LD-DAG: cir.global external tls_local_dyn dyn_tls_refs = <"_ZTW2s1", 
"_ZTH2s1"> @s1 = #cir.zero : !rec_S {alignment = 1 : i64, ast = 
#cir.var.decl.ast}
+// CIR-LD-DAG: cir.global external tls_local_dyn dyn_tls_refs = <"_ZTW2t1", 
"_ZTH2t1"> @t1 = #cir.zero : !rec_T {alignment = 1 : i64, ast = 
#cir.var.decl.ast}
+// CIR-LD-DAG: cir.global internal tls_local_dyn @__tls_guard = #cir.int<0> : 
!s8i {alignment = 1 : i64}
 
 // LLVM-LD-DAG: @z1 ={{.*}} global i32 0
 // LLVM-LD-DAG: @z2 ={{.*}} global i32 0
@@ -82,20 +84,18 @@ struct T thread_local t1;
 // LLVM-LD-DAG: @z ={{.*}} thread_local(initialexec) global i32 0
 // LLVM-LD-DAG: @s1 ={{.*}} thread_local(localdynamic) global %struct.S 
zeroinitializer
 // LLVM-LD-DAG: @t1 ={{.*}} thread_local(localdynamic) global %struct.T 
zeroinitializer
-// FIXME: our lowering for tls only emits the guard for tls_dyn, so this 
doesn't
-// appear.
-// @__tls_guard = internal thread_local(localdynamic) global i8 0
+// LLVM-LD-DAG: @__tls_guard = internal thread_local(localdynamic) global i8 0
 
+// CIR-IE: module {{.*}} attributes 
+// CIR-IE-SAME: cir.default.tls_model = #cir.tls_model<tls_init_exec>
 // CIR-IE-DAG: cir.global external @z1 = #cir.int<0> : !s32i {alignment = 4 : 
i64}
 // CIR-IE-DAG: cir.global external @z2 = #cir.int<0> : !s32i {alignment = 4 : 
i64}
-// CIR-IE-DAG: cir.global external tls_init_exec @x = #cir.int<0> : !s32i 
{alignment = 4 : i64}
+// CIR-IE-DAG: cir.global external tls_init_exec dyn_tls_refs = <"_ZTW1x", 
"_ZTH1x"> @x = #cir.int<0> : !s32i {alignment = 4 : i64}
 // CIR-IE-DAG: cir.global "private" internal tls_init_exec dso_local 
@_ZZ1fvE1y = #cir.int<0> : !s32i {alignment = 4 : i64}
-// CIR-IE-DAG: cir.global external tls_init_exec @z = #cir.int<0> : !s32i 
{alignment = 4 : i64}
-// CIR-IE-DAG: cir.global external tls_init_exec @s1 = #cir.zero : !rec_S 
{alignment = 1 : i64, ast = #cir.var.decl.ast}
-// CIR-IE-DAG: cir.global external tls_init_exec @t1 = #cir.zero : !rec_T 
{alignment = 1 : i64, ast = #cir.var.decl.ast}
-// FIXME: our lowering for tls only emits the guard for tls_dyn, so this 
doesn't
-// appear.
-// cir.global internal tls_init_exec @__tls_guard = #cir.int<0> : !s8i 
{alignment = 1 : i64}
+// CIR-IE-DAG: cir.global external tls_init_exec dyn_tls_refs = <"_ZTW1z", 
"_ZTH1z"> @z = #cir.int<0> : !s32i {alignment = 4 : i64}
+// CIR-IE-DAG: cir.global external tls_init_exec dyn_tls_refs = <"_ZTW2s1", 
"_ZTH2s1"> @s1 = #cir.zero : !rec_S {alignment = 1 : i64, ast = 
#cir.var.decl.ast}
+// CIR-IE-DAG: cir.global external tls_init_exec dyn_tls_refs = <"_ZTW2t1", 
"_ZTH2t1"> @t1 = #cir.zero : !rec_T {alignment = 1 : i64, ast = 
#cir.var.decl.ast}
+// CIR-IE-DAG: cir.global internal tls_init_exec @__tls_guard = #cir.int<0> : 
!s8i {alignment = 1 : i64}
 
 // LLVM-IE-DAG: @z1 ={{.*}} global i32 0
 // LLVM-IE-DAG: @z2 ={{.*}} global i32 0
@@ -104,20 +104,18 @@ struct T thread_local t1;
 // LLVM-IE-DAG: @z ={{.*}} thread_local(initialexec) global i32 0
 // LLVM-IE-DAG: @s1 ={{.*}} thread_local(initialexec) global %struct.S 
zeroinitializer
 // LLVM-IE-DAG: @t1 ={{.*}} thread_local(initialexec) global %struct.T 
zeroinitializer
-// FIXME: our lowering for tls only emits the guard for tls_dyn, so this 
doesn't
-// appear.
-// @__tls_guard = internal thread_local(initialexec) global i8 0
+// LLVM-IE-DAG: @__tls_guard = internal thread_local(initialexec) global i8 0
 
+// CIR-LE: module {{.*}} attributes 
+// CIR-LE-SAME: cir.default.tls_model = #cir.tls_model<tls_local_exec>
 // CIR-LE-DAG: cir.global external @z1 = #cir.int<0> : !s32i {alignment = 4 : 
i64}
 // CIR-LE-DAG: cir.global external @z2 = #cir.int<0> : !s32i {alignment = 4 : 
i64}
-// CIR-LE-DAG: cir.global external tls_local_exec @x = #cir.int<0> : !s32i 
{alignment = 4 : i64}
+// CIR-LE-DAG: cir.global external tls_local_exec dyn_tls_refs = <"_ZTW1x", 
"_ZTH1x"> @x = #cir.int<0> : !s32i {alignment = 4 : i64}
 // CIR-LE-DAG: cir.global "private" internal tls_local_exec dso_local 
@_ZZ1fvE1y = #cir.int<0> : !s32i {alignment = 4 : i64}
-// CIR-LE-DAG: cir.global external tls_init_exec @z = #cir.int<0> : !s32i 
{alignment = 4 : i64}
-// CIR-LE-DAG: cir.global external tls_local_exec @s1 = #cir.zero : !rec_S 
{alignment = 1 : i64, ast = #cir.var.decl.ast}
-// CIR-LE-DAG: cir.global external tls_local_exec @t1 = #cir.zero : !rec_T 
{alignment = 1 : i64, ast = #cir.var.decl.ast}
-// FIXME: our lowering for tls only emits the guard for tls_dyn, so this 
doesn't
-// appear.
-// cir.global internal tls_local_exec @__tls_guard = #cir.int<0> : !s8i 
{alignment = 1 : i64}
+// CIR-LE-DAG: cir.global external tls_init_exec dyn_tls_refs = <"_ZTW1z", 
"_ZTH1z"> @z = #cir.int<0> : !s32i {alignment = 4 : i64}
+// CIR-LE-DAG: cir.global external tls_local_exec dyn_tls_refs = <"_ZTW2s1", 
"_ZTH2s1"> @s1 = #cir.zero : !rec_S {alignment = 1 : i64, ast = 
#cir.var.decl.ast}
+// CIR-LE-DAG: cir.global external tls_local_exec dyn_tls_refs = <"_ZTW2t1", 
"_ZTH2t1"> @t1 = #cir.zero : !rec_T {alignment = 1 : i64, ast = 
#cir.var.decl.ast}
+// CIR-LE-DAG: cir.global internal tls_local_exec @__tls_guard = #cir.int<0> : 
!s8i {alignment = 1 : i64}
 
 // LLVM-LE-DAG: @z1 ={{.*}} global i32 0
 // LLVM-LE-DAG: @z2 ={{.*}} global i32 0
@@ -126,6 +124,4 @@ struct T thread_local t1;
 // LLVM-LE-DAG: @z ={{.*}} thread_local(initialexec) global i32 0
 // LLVM-LE-DAG: @s1 ={{.*}} thread_local(localexec) global %struct.S 
zeroinitializer
 // LLVM-LE-DAG: @t1 ={{.*}} thread_local(localexec) global %struct.T 
zeroinitializer
-// FIXME: our lowering for tls only emits the guard for tls_dyn, so this 
doesn't
-// appear.
-// @__tls_guard = internal thread_local(localexec) global i8 0
+// LLVM-LE-DAG: @__tls_guard = internal thread_local(localexec) global i8 0
diff --git a/clang/test/CIR/IR/global.cir b/clang/test/CIR/IR/global.cir
index fe3a0b95c65f3..3e78fa627b4aa 100644
--- a/clang/test/CIR/IR/global.cir
+++ b/clang/test/CIR/IR/global.cir
@@ -49,6 +49,10 @@ module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
   cir.global external @fp = #cir.ptr<null> : !cir.ptr<!cir.func<()>>
   cir.global external @fpii = #cir.ptr<null> : !cir.ptr<!cir.func<(!s32i) -> 
!s32i>>
   cir.global external @fpvar = #cir.ptr<null> : !cir.ptr<!cir.func<(!s32i, 
...)>>
+  cir.global external tls_dyn @tls1 = #cir.int<0> : !s8i
+  cir.global external tls_local_dyn @tls2 = #cir.int<0> : !s8i
+  cir.global external tls_init_exec @tls3 = #cir.int<0> : !s8i
+  cir.global external tls_local_exec @tls4 = #cir.int<0> : !s8i
 }
 
 // CHECK: cir.global external @c = #cir.int<0> : !s8i
@@ -87,3 +91,7 @@ module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
 // CHECK: cir.global external @fp = #cir.ptr<null> : !cir.ptr<!cir.func<()>>
 // CHECK: cir.global external @fpii = #cir.ptr<null> : 
!cir.ptr<!cir.func<(!s32i) -> !s32i>>
 // CHECK: cir.global external @fpvar = #cir.ptr<null> : 
!cir.ptr<!cir.func<(!s32i, ...)>>
+// CHECK: cir.global external tls_dyn @tls1 = #cir.int<0> : !s8i
+// CHECK: cir.global external tls_local_dyn @tls2 = #cir.int<0> : !s8i
+// CHECK: cir.global external tls_init_exec @tls3 = #cir.int<0> : !s8i
+// CHECK: cir.global external tls_local_exec @tls4 = #cir.int<0> : !s8i
diff --git a/clang/test/CIR/IR/invalid-tls.cir 
b/clang/test/CIR/IR/invalid-tls.cir
index e33b7070eb790..de10382b9ae1e 100644
--- a/clang/test/CIR/IR/invalid-tls.cir
+++ b/clang/test/CIR/IR/invalid-tls.cir
@@ -25,7 +25,10 @@ cir.global "private" internal tls_dyn dyn_tls_refs = 
<"asdf", "asdf", "asdf"> st
 !s32i = !cir.int<s, 32>
 
 module {
-  // expected-error@+1{{'dyn_tls_refs' only valid for dynamic tls}}
-cir.global "private" internal tls_local_dyn dyn_tls_refs = <"asdf", "asdf", 
"asdf"> @_ZZ1fvE1y : !s32i
+  // FIXME(cir) Parsing continues on when tls doesn't get recognized, and
+  // unfortunately Address space is the only thing that comes next. We should
+  // probably do a better job having address-space give up if we care enough 
here.
+  // expected-error@+2{{failed to parse Address Space Value for GlobalOp}}
+  // expected-error@+1{{custom op 'cir.global' unknown address space specifier 
'invalid_tls_dyn'}}
+cir.global "private" internal invalid_tls_dyn dyn_tls_refs = <"asdf", "asdf", 
"asdf"> @_ZZ1fvE1y : !s32i
 }
-

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

Reply via email to