https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/214337
>From 2d25e88f777bee30247fc8be32e22bf66a6323d8 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Wed, 5 Aug 2026 06:06:36 -0700 Subject: [PATCH 1/4] [CIR][MLIR-LLVM] Add 'thread local mode/model' attribute support The main purpose of this patch is to implement the 'tls_model' attribute for ClangIR. However, this required updating the LLVM-IR dialect to ALSO support this, not just as a bool. This patch threads it into both sides. CIR tries to refer to it as "TLS_Model" to match the C/C++ attribute closely, the LLVM Dialect refers to it as ThreadLocalMode to reflect better what LLVM does. Left as still 'not done' (in CIR only!) is the lowering of the 'other' kinds of thread-local settings, which are intended to do various levels of locking/initialization. Those are left for a future implementation effort. The C++ test itself is taken directly from classic codegen. --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 8 +- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 12 +- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 37 +++-- clang/test/CIR/CodeGen/tls-model.cpp | 131 ++++++++++++++++++ mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td | 20 +++ mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 8 +- mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 64 ++++++--- mlir/lib/Target/LLVMIR/ModuleImport.cpp | 15 +- mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | 8 +- mlir/test/Dialect/LLVMIR/alias.mlir | 56 ++++++++ mlir/test/Dialect/LLVMIR/global.mlir | 12 ++ mlir/test/Dialect/LLVMIR/invalid.mlir | 27 ++++ mlir/test/Dialect/LLVMIR/roundtrip.mlir | 8 ++ 13 files changed, 356 insertions(+), 50 deletions(-) create mode 100644 clang/test/CIR/CodeGen/tls-model.cpp diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 49ecec207cd45..75b17e48312d6 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -3302,10 +3302,10 @@ def CIR_GlobalLinkageKind : CIR_I32EnumAttr< // is upstreamed. def CIR_TLSModel : CIR_I32EnumAttr<"TLS_Model", "TLS model", [ - I32EnumAttrCase<"GeneralDynamic", 0, "tls_dyn">, - I32EnumAttrCase<"LocalDynamic", 1, "tls_local_dyn">, - I32EnumAttrCase<"InitialExec", 2, "tls_init_exec">, - I32EnumAttrCase<"LocalExec", 3, "tls_local_exec"> + I32EnumAttrCase<"GeneralDynamic", 1, "tls_dyn">, + I32EnumAttrCase<"LocalDynamic", 2, "tls_local_dyn">, + I32EnumAttrCase<"InitialExec", 3, "tls_init_exec">, + I32EnumAttrCase<"LocalExec", 4, "tls_local_exec"> ]>; def CIR_GlobalOp : CIR_Op<"global", [ diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index a7143408c9bee..4a520bb7be476 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -3053,6 +3053,14 @@ 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); +} + cir::TLS_Model CIRGenModule::getDefaultCIRTLSModel() const { switch (getCodeGenOpts().getDefaultTLSModel()) { case CodeGenOptions::GeneralDynamicTLSModel: @@ -3074,8 +3082,8 @@ void CIRGenModule::setTLSMode(mlir::Operation *op, const VarDecl &d, cir::TLS_Model tlm = getDefaultCIRTLSModel(); // Override the TLS model if it is explicitly specified. - if (d.getAttr<TLSModelAttr>()) - errorNYI(d.getSourceRange(), "TLS model attribute"); + if (const auto *attr = d.getAttr<TLSModelAttr>()) + tlm = getCIRTLSModel(attr->getModel()); auto global = cast<cir::GlobalOp>(op); global.setTlsModel(tlm); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 4e923f2bcee28..08c2405547dae 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -2597,7 +2597,7 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewriteAlias( mlir::Location loc = op.getLoc(); auto aliasOp = rewriter.replaceOpWithNewOp<mlir::LLVM::AliasOp>( op, ty, convertLinkage(op.getLinkage()), op.getName(), op.getDsoLocal(), - /*threadLocal=*/false, attributes); + mlir::LLVM::ThreadLocalMode::NotThreadLocal, attributes); // Create the alias body mlir::OpBuilder builder(op.getContext()); @@ -2769,6 +2769,24 @@ CIRToLLVMGlobalOpLowering::lowerGlobalAttributes( return attributes; } +static mlir::LLVM::ThreadLocalMode +convertTlsModelAttrToLLVM(TLS_ModelAttr attr) { + // assert that we can just static-cast these. +#define CHECK_ENUM(CIR, LLVM_VAL) \ + static_assert(static_cast<unsigned>(TLS_Model::CIR) == \ + static_cast<unsigned>(mlir::LLVM::ThreadLocalMode::LLVM_VAL)) + CHECK_ENUM(GeneralDynamic, GeneralDynamic); + CHECK_ENUM(LocalDynamic, LocalDynamic); + CHECK_ENUM(InitialExec, InitialExec); + CHECK_ENUM(LocalExec, LocalExec); +#undef CHECK_ENUM + + if (!attr) + return mlir::LLVM::ThreadLocalMode::NotThreadLocal; + + return static_cast<mlir::LLVM::ThreadLocalMode>(attr.getValue()); +} + /// Replace CIR global with a region initialized LLVM global and update /// insertion point to the end of the initializer block. void CIRToLLVMGlobalOpLowering::setupRegionInitializedLLVMGlobalOp( @@ -2792,7 +2810,8 @@ void CIRToLLVMGlobalOpLowering::setupRegionInitializedLLVMGlobalOp( op.getAddrSpaceAttr())) addrSpace = targetAS.getValue(); const bool isDsoLocal = op.getDsoLocal(); - const bool isThreadLocal = (bool)op.getTlsModelAttr(); + mlir::LLVM::ThreadLocalMode threadLocalMode = + convertTlsModelAttrToLLVM(op.getTlsModelAttr()); const uint64_t alignment = op.getAlignment().value_or(0); const mlir::LLVM::Linkage linkage = convertLinkage(op.getLinkage()); const StringRef symbol = op.getSymName(); @@ -2804,7 +2823,7 @@ void CIRToLLVMGlobalOpLowering::setupRegionInitializedLLVMGlobalOp( mlir::LLVM::GlobalOp newGlobalOp = rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>( op, llvmType, isConst, linkage, symbol, nullptr, alignment, addrSpace, - isDsoLocal, isThreadLocal, comdatAttr, attributes); + isDsoLocal, threadLocalMode, comdatAttr, attributes); newGlobalOp.getRegion().emplaceBlock(); rewriter.setInsertionPointToEnd(newGlobalOp.getInitializerBlock()); } @@ -2871,7 +2890,8 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( op.getAddrSpaceAttr())) addrSpace = targetAS.getValue(); const bool isDsoLocal = op.getDsoLocal(); - const bool isThreadLocal = (bool)op.getTlsModelAttr(); + mlir::LLVM::ThreadLocalMode threadLocalMode = + convertTlsModelAttrToLLVM(op.getTlsModelAttr()); const uint64_t alignment = op.getAlignment().value_or(0); const mlir::LLVM::Linkage linkage = convertLinkage(op.getLinkage()); const StringRef symbol = op.getSymName(); @@ -2882,7 +2902,7 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( if (std::optional<llvm::StringRef> aliasee = op.getAliasee()) { mlir::Location loc = op.getLoc(); auto aliasOp = rewriter.replaceOpWithNewOp<mlir::LLVM::AliasOp>( - op, llvmType, linkage, symbol, isDsoLocal, isThreadLocal, attributes); + op, llvmType, linkage, symbol, isDsoLocal, threadLocalMode, attributes); mlir::OpBuilder builder(op.getContext()); mlir::Block *block = builder.createBlock(&aliasOp.getInitializerRegion()); @@ -2922,7 +2942,7 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter); rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>( op, llvmType, isConst, linkage, symbol, bulkInit.value(), - alignment, addrSpace, isDsoLocal, isThreadLocal, comdatAttr, + alignment, addrSpace, isDsoLocal, threadLocalMode, comdatAttr, attributes); return mlir::success(); } @@ -2941,7 +2961,7 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter); rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>( op, llvmType, isConst, linkage, symbol, bulkInit.value(), alignment, - addrSpace, isDsoLocal, isThreadLocal, comdatAttr, attributes); + addrSpace, isDsoLocal, threadLocalMode, comdatAttr, attributes); return mlir::success(); } return matchAndRewriteRegionInitializedGlobal(op, init.value(), rewriter); @@ -2965,7 +2985,8 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter); rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>( op, llvmType, isConst, linkage, symbol, init.value_or(mlir::Attribute()), - alignment, addrSpace, isDsoLocal, isThreadLocal, comdatAttr, attributes); + alignment, addrSpace, isDsoLocal, threadLocalMode, comdatAttr, + attributes); return mlir::success(); } diff --git a/clang/test/CIR/CodeGen/tls-model.cpp b/clang/test/CIR/CodeGen/tls-model.cpp new file mode 100644 index 0000000000000..471891a16ca5b --- /dev/null +++ b/clang/test/CIR/CodeGen/tls-model.cpp @@ -0,0 +1,131 @@ + +// 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 z1 = 0; +int z2; +int __thread x; +int f() { + static int __thread y; + return y++; +} +int __thread __attribute__((tls_model("initial-exec"))) z; + +struct S { + S(); + ~S(); +}; +struct T { + ~T(); +}; + +struct S thread_local s1; +struct T thread_local t1; + +// Note that unlike normal C uninitialized global variables, +// uninitialized TLS variables do NOT have COMMON linkage. + +// 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_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} + +// LLVM-GD-DAG: @z1 ={{.*}} global i32 0 +// LLVM-GD-DAG: @z2 ={{.*}} global i32 0 +// LLVM-GD-DAG: @x ={{.*}} thread_local global i32 0 +// LLVM-GD-DAG: @_ZZ1fvE1y = internal thread_local global i32 0 +// LLVM-GD-DAG: @z ={{.*}} thread_local(initialexec) global i32 0 +// LLVM-GD-DAG: @s1 ={{.*}} thread_local global %struct.S zeroinitializer +// LLVM-GD-DAG: @t1 ={{.*}} thread_local global %struct.T zeroinitializer +// LLVM-GD-DAG: @__tls_guard = internal thread_local global i8 0 + +// 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 "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} + +// LLVM-LD-DAG: @z1 ={{.*}} global i32 0 +// LLVM-LD-DAG: @z2 ={{.*}} global i32 0 +// LLVM-LD-DAG: @x ={{.*}} thread_local(localdynamic) global i32 0 +// LLVM-LD-DAG: @_ZZ1fvE1y = internal thread_local(localdynamic) global i32 0 +// 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 + +// 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 "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} + +// LLVM-IE-DAG: @z1 ={{.*}} global i32 0 +// LLVM-IE-DAG: @z2 ={{.*}} global i32 0 +// LLVM-IE-DAG: @x ={{.*}} thread_local(initialexec) global i32 0 +// LLVM-IE-DAG: @_ZZ1fvE1y = internal thread_local(initialexec) global i32 0 +// 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 + +// 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 "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} + +// LLVM-LE-DAG: @z1 ={{.*}} global i32 0 +// LLVM-LE-DAG: @z2 ={{.*}} global i32 0 +// LLVM-LE-DAG: @x ={{.*}} thread_local(localexec) global i32 0 +// LLVM-LE-DAG: @_ZZ1fvE1y = internal thread_local(localexec) global i32 0 +// 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 diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td index 5a8f195a91a10..4304ec207acf6 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td @@ -969,4 +969,24 @@ def GEPNoWrapFlagsProp : EnumProp<GEPNoWrapFlags> { let defaultValue = interfaceType # "::none"; } +//===----------------------------------------------------------------------===// +// TLSModel +//===----------------------------------------------------------------------===// + +def LLVM_TLSMode + : LLVM_EnumAttr< + "ThreadLocalMode", "::llvm::GlobalValue::ThreadLocalMode", "TLS Mode", + [ + LLVM_EnumAttrCase<"NotThreadLocal", "", "NotThreadLocal", 0>, + LLVM_EnumAttrCase<"GeneralDynamic", "generaldynamic", + "GeneralDynamicTLSModel", 1>, + LLVM_EnumAttrCase<"LocalDynamic", "localdynamic", + "LocalDynamicTLSModel", 2>, + LLVM_EnumAttrCase<"InitialExec", "initialexec", + "InitialExecTLSModel", 3>, + LLVM_EnumAttrCase<"LocalExec", "localexec", "LocalExecTLSModel", 4>, + ]> { + let cppNamespace = "::mlir::LLVM"; +} + #endif // LLVMIR_ENUMS diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td index 84f71054994f4..fc674a7afb55c 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -1386,7 +1386,7 @@ def LLVM_GlobalOp : LLVM_Op<"mlir.global", StrAttr:$sym_name, Linkage:$linkage, UnitAttr:$dso_local, - UnitAttr:$thread_local_, + DefaultValuedAttr<LLVM_TLSMode, "mlir::LLVM::ThreadLocalMode::NotThreadLocal">:$tls_mode, UnitAttr:$externally_initialized, OptionalAttr<AnyAttr>:$value, OptionalAttr<I64Attr>:$alignment, @@ -1518,7 +1518,7 @@ def LLVM_GlobalOp : LLVM_Op<"mlir.global", CArg<"uint64_t", "0">:$alignment, CArg<"unsigned", "0">:$addrSpace, CArg<"bool", "false">:$dsoLocal, - CArg<"bool", "false">:$thread_local_, + CArg<"ThreadLocalMode", "ThreadLocalMode::NotThreadLocal">:$tls_mode, CArg<"SymbolRefAttr", "{}">:$comdat, CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs, CArg<"ArrayRef<Attribute>", "{}">:$dbgExprs)> @@ -1631,7 +1631,7 @@ def LLVM_AliasOp : LLVM_Op<"mlir.alias", StrAttr:$sym_name, Linkage:$linkage, UnitAttr:$dso_local, - UnitAttr:$thread_local_, + DefaultValuedAttr<LLVM_TLSMode, "mlir::LLVM::ThreadLocalMode::NotThreadLocal">:$tls_mode, OptionalAttr<UnnamedAddr>:$unnamed_addr, DefaultValuedAttr<Visibility, "mlir::LLVM::Visibility::Default">:$visibility_ ); @@ -1676,7 +1676,7 @@ def LLVM_AliasOp : LLVM_Op<"mlir.alias", OpBuilder<(ins "Type":$type, "Linkage":$linkage, "StringRef":$name, CArg<"bool", "false">:$dsoLocal, - CArg<"bool", "false">:$thread_local_, + CArg<"ThreadLocalMode", "ThreadLocalMode::NotThreadLocal">:$tls_mode, CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)> ]; diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp index 6382dfcbc8ad2..3bda6f5295747 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -115,6 +115,7 @@ REGISTER_ENUM_TYPE(Linkage); REGISTER_ENUM_TYPE(UnnamedAddr); REGISTER_ENUM_TYPE(CConv); REGISTER_ENUM_TYPE(TailCallKind); +REGISTER_ENUM_TYPE(ThreadLocalMode); REGISTER_ENUM_TYPE(Visibility); } // namespace @@ -2373,12 +2374,15 @@ LogicalResult ComdatOp::verifyRegions() { void GlobalOp::build(OpBuilder &builder, OperationState &result, Type type, bool isConstant, Linkage linkage, StringRef name, Attribute value, uint64_t alignment, unsigned addrSpace, - bool dsoLocal, bool threadLocal, SymbolRefAttr comdat, - ArrayRef<NamedAttribute> attrs, + bool dsoLocal, ThreadLocalMode threadModel, + SymbolRefAttr comdat, ArrayRef<NamedAttribute> attrs, ArrayRef<Attribute> dbgExprs) { result.addAttribute(getSymNameAttrName(result.name), builder.getStringAttr(name)); result.addAttribute(getGlobalTypeAttrName(result.name), TypeAttr::get(type)); + result.addAttribute( + getTlsModeAttrName(result.name), + ThreadLocalModeAttr::get(builder.getContext(), threadModel)); if (isConstant) result.addAttribute(getConstantAttrName(result.name), builder.getUnitAttr()); @@ -2387,9 +2391,6 @@ void GlobalOp::build(OpBuilder &builder, OperationState &result, Type type, if (dsoLocal) result.addAttribute(getDsoLocalAttrName(result.name), builder.getUnitAttr()); - if (threadLocal) - result.addAttribute(getThreadLocal_AttrName(result.name), - builder.getUnitAttr()); if (comdat) result.addAttribute(getComdatAttrName(result.name), comdat); @@ -2420,8 +2421,15 @@ static void printCommonGlobalAndAlias(OpAsmPrinter &p, OpType op) { StringRef visibility = stringifyVisibility(op.getVisibility_()); if (!visibility.empty()) p << visibility << ' '; - if (op.getThreadLocal_()) - p << "thread_local "; + + if (ThreadLocalMode mode = op.getTlsMode(); + mode != ThreadLocalMode::NotThreadLocal) { + p << "thread_local"; + if (mode != ThreadLocalMode::GeneralDynamic) + p << '(' << mode << ')'; + p << ' '; + } + if (auto unnamedAddr = op.getUnnamedAddr()) { StringRef str = stringifyUnnamedAddr(*unnamedAddr); if (!str.empty()) @@ -2448,7 +2456,7 @@ void GlobalOp::print(OpAsmPrinter &p) { {SymbolTable::getSymbolAttrName(), getGlobalTypeAttrName(), getConstantAttrName(), getValueAttrName(), getLinkageAttrName(), - getUnnamedAddrAttrName(), getThreadLocal_AttrName(), + getUnnamedAddrAttrName(), getTlsModeAttrName(), getVisibility_AttrName(), getComdatAttrName()}); // Print the trailing type unless it's a string global. @@ -2512,9 +2520,25 @@ static ParseResult parseCommonGlobalAndAlias(OpAsmParser &parser, parseOptionalLLVMKeyword<LLVM::Visibility, int64_t>( parser, LLVM::Visibility::Default))); - if (succeeded(parser.parseOptionalKeyword("thread_local"))) - result.addAttribute(OpType::getThreadLocal_AttrName(result.name), - parser.getBuilder().getUnitAttr()); + if (succeeded(parser.parseOptionalKeyword("thread_local"))) { + ThreadLocalMode threadModel = ThreadLocalMode::GeneralDynamic; + + if (succeeded(parser.parseOptionalLParen())) { + SMLoc kwLoc; + if (parser.getCurrentLocation(&kwLoc)) + return failure(); + threadModel = parseOptionalLLVMKeyword<ThreadLocalMode>( + parser, ThreadLocalMode::NotThreadLocal); + if (threadModel == ThreadLocalMode::NotThreadLocal) { + parser.emitError(kwLoc, "invalid value for thread_local"); + return failure(); + } + if (parser.parseRParen()) + return failure(); + } + result.addAttribute(OpType::getTlsModeAttrName(result.name), + ThreadLocalModeAttr::get(ctx, threadModel)); + } // Parse optional UnnamedAddr, default to None. result.addAttribute(OpType::getUnnamedAddrAttrName(result.name), @@ -2527,7 +2551,8 @@ static ParseResult parseCommonGlobalAndAlias(OpAsmParser &parser, // operation ::= `llvm.mlir.global` linkage? visibility? // (`unnamed_addr` | `local_unnamed_addr`)? -// `thread_local`? `constant`? `@` identifier +// (`thread_local` (`(` tls-mode `)`)? )? +// `constant`? `@` identifier // `(` attribute? `)` (`comdat(` symbol-ref-id `)`)? // attribute-list? (`:` type)? region? // @@ -2765,16 +2790,17 @@ LogicalResult GlobalDtorsOp::verify() { void AliasOp::build(OpBuilder &builder, OperationState &result, Type type, Linkage linkage, StringRef name, bool dsoLocal, - bool threadLocal, ArrayRef<NamedAttribute> attrs) { + ThreadLocalMode threadModel, + ArrayRef<NamedAttribute> attrs) { result.addAttribute(getSymNameAttrName(result.name), builder.getStringAttr(name)); result.addAttribute(getAliasTypeAttrName(result.name), TypeAttr::get(type)); + result.addAttribute( + getTlsModeAttrName(result.name), + ThreadLocalModeAttr::get(builder.getContext(), threadModel)); if (dsoLocal) result.addAttribute(getDsoLocalAttrName(result.name), builder.getUnitAttr()); - if (threadLocal) - result.addAttribute(getThreadLocal_AttrName(result.name), - builder.getUnitAttr()); result.addAttribute(getLinkageAttrName(result.name), LinkageAttr::get(builder.getContext(), linkage)); @@ -2790,7 +2816,7 @@ void AliasOp::print(OpAsmPrinter &p) { p.printOptionalAttrDict((*this)->getAttrs(), {SymbolTable::getSymbolAttrName(), getAliasTypeAttrName(), getLinkageAttrName(), - getUnnamedAddrAttrName(), getThreadLocal_AttrName(), + getUnnamedAddrAttrName(), getTlsModeAttrName(), getVisibility_AttrName()}); // Print the trailing type. @@ -2801,8 +2827,8 @@ void AliasOp::print(OpAsmPrinter &p) { // operation ::= `llvm.mlir.alias` linkage? visibility? // (`unnamed_addr` | `local_unnamed_addr`)? -// `thread_local`? `@` identifier -// `(` attribute? `)` +// (`thread_local` (`(` tls-mode `)`)? )? +// `@` identifier `(` attribute? `)` // attribute-list? `:` type region // ParseResult AliasOp::parse(OpAsmParser &parser, OperationState &result) { diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp index 725d7cf6c8413..48e1b7d26ac67 100644 --- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp @@ -1477,12 +1477,12 @@ LogicalResult ModuleImport::convertAlias(llvm::GlobalAlias *alias) { OpBuilder::InsertionGuard guard = setGlobalInsertionPoint(); Type type = convertType(alias->getValueType()); - AliasOp aliasOp = AliasOp::create(builder, mlirModule.getLoc(), type, - convertLinkageFromLLVM(alias->getLinkage()), - alias->getName(), - /*dsoLocal=*/alias->isDSOLocal(), - /*thread_local=*/alias->isThreadLocal(), - /*attrs=*/ArrayRef<NamedAttribute>()); + AliasOp aliasOp = AliasOp::create( + builder, mlirModule.getLoc(), type, + convertLinkageFromLLVM(alias->getLinkage()), alias->getName(), + /*dsoLocal=*/alias->isDSOLocal(), + convertThreadLocalModeFromLLVM(alias->getThreadLocalMode()), + /*attrs=*/ArrayRef<NamedAttribute>()); globalInsertionOp = aliasOp; clearRegionState(); @@ -1622,7 +1622,8 @@ LogicalResult ModuleImport::convertGlobal(llvm::GlobalVariable *globalVar) { convertLinkageFromLLVM(globalVar->getLinkage()), StringRef(globalName), valueAttr, alignment, /*addrSpace=*/globalVar->getAddressSpace(), /*dsoLocal=*/globalVar->isDSOLocal(), - /*thread_local=*/globalVar->isThreadLocal(), /*comdat=*/SymbolRefAttr(), + convertThreadLocalModeFromLLVM(globalVar->getThreadLocalMode()), + /*comdat=*/SymbolRefAttr(), /*attrs=*/ArrayRef<NamedAttribute>(), /*dbgExprs=*/globalExpressionAttrs); globalInsertionOp = globalOp; diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp index 47e63e2aa279a..536327e64bb9f 100644 --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -1241,9 +1241,7 @@ LogicalResult ModuleTranslation::convertGlobalsAndAliases() { auto *var = new llvm::GlobalVariable( *llvmModule, type, op.getConstant(), linkage, cst, op.getSymName(), - /*InsertBefore=*/nullptr, - op.getThreadLocal_() ? llvm::GlobalValue::GeneralDynamicTLSModel - : llvm::GlobalValue::NotThreadLocal, + /*InsertBefore=*/nullptr, convertThreadLocalModeToLLVM(op.getTlsMode()), op.getAddrSpace(), op.getExternallyInitialized()); if (std::optional<mlir::SymbolRefAttr> comdat = op.getComdat()) { @@ -1368,9 +1366,7 @@ LogicalResult ModuleTranslation::convertGlobalsAndAliases() { type, op.getAddrSpace(), linkage, op.getSymName(), /*placeholder*/ cst, &llvmMod); - var->setThreadLocalMode(op.getThreadLocal_() - ? llvm::GlobalAlias::GeneralDynamicTLSModel - : llvm::GlobalAlias::NotThreadLocal); + var->setThreadLocalMode(convertThreadLocalModeToLLVM(op.getTlsMode())); // Note there is no need to setup the comdat because GlobalAlias calls into // the aliasee comdat information automatically. diff --git a/mlir/test/Dialect/LLVMIR/alias.mlir b/mlir/test/Dialect/LLVMIR/alias.mlir index 5f578a382a668..32c795113a035 100644 --- a/mlir/test/Dialect/LLVMIR/alias.mlir +++ b/mlir/test/Dialect/LLVMIR/alias.mlir @@ -142,6 +142,62 @@ llvm.mlir.alias private thread_local unnamed_addr @a30 {dso_local} : i32 { // ----- +llvm.mlir.global private @g30(0 : i32) {dso_local} : i32 + +llvm.mlir.alias private thread_local(generaldynamic) unnamed_addr @a30 {dso_local} : i32 { + %0 = llvm.mlir.addressof @g30 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// CHECK: llvm.mlir.alias private thread_local unnamed_addr @a30 {dso_local} : i32 { +// CHECK: %0 = llvm.mlir.addressof @g30 : !llvm.ptr +// CHECK: llvm.return %0 : !llvm.ptr +// CHECK: } + +// ----- + +llvm.mlir.global private @g30(0 : i32) {dso_local} : i32 + +llvm.mlir.alias private thread_local(localdynamic) unnamed_addr @a30 {dso_local} : i32 { + %0 = llvm.mlir.addressof @g30 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// CHECK: llvm.mlir.alias private thread_local(localdynamic) unnamed_addr @a30 {dso_local} : i32 { +// CHECK: %0 = llvm.mlir.addressof @g30 : !llvm.ptr +// CHECK: llvm.return %0 : !llvm.ptr +// CHECK: } + +// ----- + +llvm.mlir.global private @g30(0 : i32) {dso_local} : i32 + +llvm.mlir.alias private thread_local(initialexec) unnamed_addr @a30 {dso_local} : i32 { + %0 = llvm.mlir.addressof @g30 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// CHECK: llvm.mlir.alias private thread_local(initialexec) unnamed_addr @a30 {dso_local} : i32 { +// CHECK: %0 = llvm.mlir.addressof @g30 : !llvm.ptr +// CHECK: llvm.return %0 : !llvm.ptr +// CHECK: } + +// ----- + +llvm.mlir.global private @g30(0 : i32) {dso_local} : i32 + +llvm.mlir.alias private thread_local(localexec) unnamed_addr @a30 {dso_local} : i32 { + %0 = llvm.mlir.addressof @g30 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// CHECK: llvm.mlir.alias private thread_local(localexec) unnamed_addr @a30 {dso_local} : i32 { +// CHECK: %0 = llvm.mlir.addressof @g30 : !llvm.ptr +// CHECK: llvm.return %0 : !llvm.ptr +// CHECK: } + +// ----- + // Test that llvm.call and llvm.invoke can use an alias as the callee. llvm.func @aliased_func() { diff --git a/mlir/test/Dialect/LLVMIR/global.mlir b/mlir/test/Dialect/LLVMIR/global.mlir index 193ab7987a2b6..2e6459386389e 100644 --- a/mlir/test/Dialect/LLVMIR/global.mlir +++ b/mlir/test/Dialect/LLVMIR/global.mlir @@ -98,6 +98,18 @@ llvm.mlir.global internal constant @sectionvar("teststring") {section = ".mysec // CHECK: llvm.mlir.global internal thread_local constant @thread_local(42 : i32) llvm.mlir.global internal thread_local constant @thread_local(42 : i32) : i32 +// CHECK: llvm.mlir.global internal thread_local constant @thread_local_gd(42 : i32) +llvm.mlir.global internal thread_local(generaldynamic) constant @thread_local_gd(42 : i32) : i32 + +// CHECK: llvm.mlir.global internal thread_local(localdynamic) constant @thread_local_ld(42 : i32) +llvm.mlir.global internal thread_local(localdynamic) constant @thread_local_ld(42 : i32) : i32 + +// CHECK: llvm.mlir.global internal thread_local(initialexec) constant @thread_local_ie(42 : i32) +llvm.mlir.global internal thread_local(initialexec) constant @thread_local_ie(42 : i32) : i32 + +// CHECK: llvm.mlir.global internal thread_local(localexec) constant @thread_local_le(42 : i32) +llvm.mlir.global internal thread_local(localexec) constant @thread_local_le(42 : i32) : i32 + // Visibility types. // CHECK: llvm.mlir.global internal hidden constant @hidden(42 : i32) llvm.mlir.global internal hidden constant @hidden(42 : i32) : i32 diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir index bde7807dadb87..6265f67e594d0 100644 --- a/mlir/test/Dialect/LLVMIR/invalid.mlir +++ b/mlir/test/Dialect/LLVMIR/invalid.mlir @@ -2171,3 +2171,30 @@ func.func @nvvm_read_sreg_clock64_wrong_type() { %0 = nvvm.read.ptx.sreg.clock64 : i32 return } + +// ----- + +// expected-error@+1{{custom op 'llvm.mlir.alias' invalid value for thread_local}} +llvm.mlir.alias private thread_local(invalid) unnamed_addr @a30 {dso_local} : i32 { + %0 = llvm.mlir.addressof @g30 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// ----- + +// expected-error@+1{{expected ')'}} +llvm.mlir.alias private thread_local(localexec, generaldynamic) unnamed_addr @a30 {dso_local} : i32 { + %0 = llvm.mlir.addressof @g30 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// ----- + +// expected-error@+1{{custom op 'llvm.mlir.global' invalid value for thread_local}} +llvm.mlir.global internal thread_local(invalid) constant @thread_local(42 : i32) : i32 + +// ----- + +// expected-error@+1{{expected ')'}} +llvm.mlir.global internal thread_local(generaldynamic, localexec) constant @thread_local(42 : i32) : i32 + diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir index d1766052964ba..0f268cf6a5fe7 100644 --- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir +++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir @@ -1200,6 +1200,14 @@ llvm.func @llvm.aarch64.neon.st3.v8i8.p0(vector<8xi8>, vector<8xi8>, vector<8xi8 llvm.mlir.global internal thread_local unnamed_addr @myglobal(-1 : i32) {addr_space = 0 : i32, alignment = 4 : i64, dso_local} : i32 // CHECK: llvm.mlir.global internal thread_local unnamed_addr @myglobal(-1 : i32) {addr_space = 0 : i32, alignment = 4 : i64, dso_local} : i32 +llvm.mlir.global internal thread_local(generaldynamic) unnamed_addr @myglobal_gd(-1 : i32) {addr_space = 0 : i32, alignment = 4 : i64, dso_local} : i32 +// CHECK: llvm.mlir.global internal thread_local unnamed_addr @myglobal_gd(-1 : i32) {addr_space = 0 : i32, alignment = 4 : i64, dso_local} : i32 +llvm.mlir.global internal thread_local(localdynamic) unnamed_addr @myglobal_ld(-1 : i32) {addr_space = 0 : i32, alignment = 4 : i64, dso_local} : i32 +// CHECK: llvm.mlir.global internal thread_local(localdynamic) unnamed_addr @myglobal_ld(-1 : i32) {addr_space = 0 : i32, alignment = 4 : i64, dso_local} : i32 +llvm.mlir.global internal thread_local(initialexec) unnamed_addr @myglobal_ie(-1 : i32) {addr_space = 0 : i32, alignment = 4 : i64, dso_local} : i32 +// CHECK: llvm.mlir.global internal thread_local(initialexec) unnamed_addr @myglobal_ie(-1 : i32) {addr_space = 0 : i32, alignment = 4 : i64, dso_local} : i32 +llvm.mlir.global internal thread_local(localexec) unnamed_addr @myglobal_le(-1 : i32) {addr_space = 0 : i32, alignment = 4 : i64, dso_local} : i32 +// CHECK: llvm.mlir.global internal thread_local(localexec) unnamed_addr @myglobal_le(-1 : i32) {addr_space = 0 : i32, alignment = 4 : i64, dso_local} : i32 // CHECK-LABEL: llvm.func @escapedtypename llvm.func @escapedtypename() { >From 125e5d87c7f8365fc195689e3cda19d84f172577 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Wed, 5 Aug 2026 14:27:55 -0700 Subject: [PATCH 2/4] Update Flang reference to GlobalOp::Create --- flang/lib/Optimizer/CodeGen/CodeGen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp index 55c6afee45996..af0e1e88a234c 100644 --- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -3663,8 +3663,8 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> { llvm::ArrayRef<mlir::NamedAttribute> attrs; auto g = mlir::LLVM::GlobalOp::create( rewriter, loc, tyAttr, isConst, linkage, global.getSymName(), initAttr, - 0, getGlobalAddressSpace(rewriter), false, false, comdat, attrs, - dbgExprs); + 0, getGlobalAddressSpace(rewriter), false, + ThreadLocalMode::NotThreadLocal, comdat, attrs, dbgExprs); if (global.getAlignment() && *global.getAlignment() > 0) g.setAlignment(*global.getAlignment()); >From f614179064db2198ce9c50a93d5b49bdbdadb9bc Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Wed, 5 Aug 2026 14:57:15 -0700 Subject: [PATCH 3/4] Another try at fixing flang --- flang/lib/Optimizer/CodeGen/CodeGen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp index af0e1e88a234c..4b7891bd46df1 100644 --- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -3664,7 +3664,7 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> { auto g = mlir::LLVM::GlobalOp::create( rewriter, loc, tyAttr, isConst, linkage, global.getSymName(), initAttr, 0, getGlobalAddressSpace(rewriter), false, - ThreadLocalMode::NotThreadLocal, comdat, attrs, dbgExprs); + mlir::LLVM::ThreadLocalMode::NotThreadLocal, comdat, attrs, dbgExprs); if (global.getAlignment() && *global.getAlignment() > 0) g.setAlignment(*global.getAlignment()); >From fb54de986df705365aa742181fb74282be8d9267 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Thu, 6 Aug 2026 06:57:16 -0700 Subject: [PATCH 4/4] add import/export tests --- .../test/Target/LLVMIR/Import/global-variables.ll | 15 +++++++++++++++ mlir/test/Target/LLVMIR/llvmir.mlir | 12 ++++++++++++ 2 files changed, 27 insertions(+) diff --git a/mlir/test/Target/LLVMIR/Import/global-variables.ll b/mlir/test/Target/LLVMIR/Import/global-variables.ll index 2be36c4366304..bf2f48f26fdbc 100644 --- a/mlir/test/Target/LLVMIR/Import/global-variables.ll +++ b/mlir/test/Target/LLVMIR/Import/global-variables.ll @@ -92,6 +92,21 @@ ; CHECK-SAME: {addr_space = 0 : i32} : !llvm.struct<"my_struct", (struct<"sub_struct", ()>, i64)> @thread_local_var = external thread_local global %my_struct +; Note: Classic LLVM-IR doesn't parse 'generaldynamic' in 'thread_local' as the dialect does, +; so there is no _gd test. + +; CHECK: llvm.mlir.global external thread_local(localdynamic) @thread_local_var_ld +; CHECK-SAME: {addr_space = 0 : i32} : !llvm.struct<"my_struct", (struct<"sub_struct", ()>, i64)> +@thread_local_var_ld = external thread_local(localdynamic) global %my_struct + +; CHECK: llvm.mlir.global external thread_local(initialexec) @thread_local_var_ie +; CHECK-SAME: {addr_space = 0 : i32} : !llvm.struct<"my_struct", (struct<"sub_struct", ()>, i64)> +@thread_local_var_ie = external thread_local(initialexec) global %my_struct + +; CHECK: llvm.mlir.global external thread_local(localexec) @thread_local_var_le +; CHECK-SAME: {addr_space = 0 : i32} : !llvm.struct<"my_struct", (struct<"sub_struct", ()>, i64)> +@thread_local_var_le = external thread_local(localexec) global %my_struct + ; // ----- ; addr_space attribute. diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir index 5ffbb3eef7bb0..f27c0a46780f2 100644 --- a/mlir/test/Target/LLVMIR/llvmir.mlir +++ b/mlir/test/Target/LLVMIR/llvmir.mlir @@ -215,6 +215,18 @@ llvm.mlir.global @has_dso_local(42 : i64) {dso_local} : i64 llvm.mlir.global thread_local @has_thr_local(42 : i64) : i64 // CHECK: @has_thr_local = thread_local global i64 42 +llvm.mlir.global thread_local(generaldynamic) @has_thr_local_gd(42 : i64) : i64 +// CHECK: @has_thr_local_gd = thread_local global i64 42 + +llvm.mlir.global thread_local(localdynamic) @has_thr_local_ld(42 : i64) : i64 +// CHECK: @has_thr_local_ld = thread_local(localdynamic) global i64 42 + +llvm.mlir.global thread_local(initialexec) @has_thr_local_ie(42 : i64) : i64 +// CHECK: @has_thr_local_ie = thread_local(initialexec) global i64 42 + +llvm.mlir.global thread_local(localexec) @has_thr_local_le(42 : i64) : i64 +// CHECK: @has_thr_local_le = thread_local(localexec) global i64 42 + // // Section attribute. // _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
