Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,10 @@ class CIRGenCXXABI {
// directly or require access through a thread wrapper function.
virtual bool usesThreadWrapperFunction(const VarDecl *VD) const = 0;

virtual LValue EmitThreadLocalVarDeclLValue(CIRGenFunction &CGF,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

camelBack here and everyelse it makes sense

const VarDecl *VD,
QualType LValType) = 0;

/// Emit the code to initialize hidden members required to handle virtual
/// inheritance, if needed by the ABI.
virtual void
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ CIRGenModule::getOrCreateStaticVarDecl(const VarDecl &D,
GV.setComdat(true);

if (D.getTLSKind())
llvm_unreachable("TLS mode is NYI");
setTLSMode(GV, D);

setGVProperties(GV, &D);

Expand Down
11 changes: 8 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -937,7 +937,7 @@ static LValue emitGlobalVarDeclLValue(CIRGenFunction &CGF, const Expr *E,
// If it's thread_local, emit a call to its wrapper function instead.
if (VD->getTLSKind() == VarDecl::TLS_Dynamic &&
CGF.CGM.getCXXABI().usesThreadWrapperFunction(VD))
assert(0 && "not implemented");
return CGF.CGM.getCXXABI().EmitThreadLocalVarDeclLValue(CGF, VD, T);

// Check if the variable is marked as declare target with link clause in
// device codegen.
Expand Down Expand Up @@ -1095,8 +1095,13 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *E) {
}

// Handle threadlocal function locals.
if (VD->getTLSKind() != VarDecl::TLS_None)
llvm_unreachable("thread-local storage is NYI");
// ClangIR marks the GlobalOp with thread_local, which gets lowered to
// @llvm.threadlocal.address intrinsics during LLVM lowering. This approach
// provides correct TLS semantics without requiring wrapper functions in
// CIR.
if (VD->getTLSKind() != VarDecl::TLS_None) {
// Nothing additional needed - the GlobalOp is already thread_local.
}

// Check for OpenMP threadprivate variables.
if (getLangOpts().OpenMP && !getLangOpts().OpenMPSimd &&
Expand Down
28 changes: 28 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,9 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
return !isEmittedWithConstantInitializer(VD) || mayNeedDestruction(VD);
}

LValue EmitThreadLocalVarDeclLValue(CIRGenFunction &CGF, const VarDecl *VD,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

QualType LValType) override;

bool doStructorsInitializeVPtrs(const CXXRecordDecl *VTableClass) override {
return true;
}
Expand Down Expand Up @@ -2925,3 +2928,28 @@ Address CIRGenARMCXXABI::initializeArrayCookie(CIRGenFunction &cgf,
return Address(dataPtr, cgf.getBuilder().getUIntNTy(8),
newPtr.getAlignment());
}

LValue CIRGenItaniumCXXABI::EmitThreadLocalVarDeclLValue(CIRGenFunction &CGF,
const VarDecl *VD,
QualType LValType) {
// ClangIR's approach to thread-local variables differs from traditional
// CodeGen. Traditional CodeGen creates wrapper functions (e.g., _ZTW*) that
// handle dynamic initialization. ClangIR instead marks the GlobalOp with
// tls_dyn and relies on LLVM lowering to insert @llvm.threadlocal.address
// intrinsics, which achieves the same semantics without intermediate wrapper
// functions in CIR.

mlir::Value V = CGF.CGM.getAddrOfGlobalVar(VD);

auto RealVarTy = CGF.convertTypeForMem(VD->getType());
CharUnits Alignment = CGF.getContext().getDeclAlign(VD);
Address Addr(V, RealVarTy, Alignment);

LValue LV;
if (VD->getType()->isReferenceType())
LV = CGF.emitLoadOfReferenceLValue(Addr, CGF.getLoc(VD->getLocation()),
VD->getType(), AlignmentSource::Decl);
else
LV = CGF.makeAddrLValue(Addr, LValType, AlignmentSource::Decl);
return LV;
}
4 changes: 2 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1197,7 +1197,7 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, mlir::Type ty,

if (d->getTLSKind()) {
if (d->getTLSKind() == VarDecl::TLS_Dynamic)
llvm_unreachable("NYI");
CXXThreadLocals.push_back(d);
setTLSMode(gv, *d);
}

Expand Down Expand Up @@ -1622,7 +1622,7 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *d,

if (d->getTLSKind() && !gv.getTlsModelAttr()) {
if (d->getTLSKind() == VarDecl::TLS_Dynamic)
llvm_unreachable("NYI");
CXXThreadLocals.push_back(d);
setTLSMode(gv, *d);
}

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ class CIRGenModule : public CIRGenTypeCache {
/// for the same decl.
llvm::DenseSet<clang::GlobalDecl> DiagnosedConflictingDefinitions;

/// thread_local variables defined or used in this TU.
std::vector<const clang::VarDecl *> CXXThreadLocals;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be lowercase too!


/// -------
/// Annotations
/// -------
Expand Down
65 changes: 65 additions & 0 deletions clang/test/CIR/CodeGen/thread-local.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.og.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.og.ll %s

// Test basic thread_local variable with constant initialization
thread_local int tls_const = 42;
// CIR: cir.global{{.*}}tls_dyn{{.*}}@tls_const = #cir.int<42> : !s32i
// LLVM: @tls_const = thread_local global i32 42
// OGCG: @tls_const = thread_local global i32 42

// Test __thread (GNU-style) thread_local
__thread int tls_gnu_style = 10;
// CIR: cir.global{{.*}}tls_dyn{{.*}}@tls_gnu_style = #cir.int<10> : !s32i
// LLVM: @tls_gnu_style = thread_local global i32 10
// OGCG: @tls_gnu_style = thread_local global i32 10

// Test thread_local function-local static (constant init)
int get_tls_static() {
thread_local int tls_func_static = 100;
return ++tls_func_static;
}
// CIR-LABEL: cir.func{{.*}}@_Z14get_tls_staticv
// CIR: cir.get_global{{.*}}@_ZZ14get_tls_staticvE15tls_func_static
// LLVM-LABEL: @_Z14get_tls_staticv
// LLVM: load{{.*}}@_ZZ14get_tls_staticvE15tls_func_static
// OGCG-LABEL: @_Z14get_tls_staticv
// OGCG: @llvm.threadlocal.address.p0(ptr{{.*}}@_ZZ14get_tls_staticvE15tls_func_static)

// Test reading from thread_local variable
int read_tls() {
return tls_const;
}
// CIR-LABEL: cir.func{{.*}}@_Z8read_tlsv
// CIR: cir.get_global thread_local @tls_const
// LLVM-LABEL: @_Z8read_tlsv
// LLVM: @llvm.threadlocal.address.p0(ptr @tls_const)
// OGCG-LABEL: @_Z8read_tlsv
// OGCG: @llvm.threadlocal.address.p0(ptr{{.*}}@tls_const)

// Test writing to thread_local variable
void write_tls(int val) {
tls_const = val;
}
// CIR-LABEL: cir.func{{.*}}@_Z9write_tlsi
// CIR: cir.get_global thread_local @tls_const
// CIR: cir.store
// LLVM-LABEL: @_Z9write_tlsi
// LLVM: @llvm.threadlocal.address.p0(ptr @tls_const)
// OGCG-LABEL: @_Z9write_tlsi
// OGCG: @llvm.threadlocal.address.p0(ptr{{.*}}@tls_const)

// Test extern thread_local
extern thread_local int tls_extern;
int use_extern_tls() {
return tls_extern;
}
// CIR-LABEL: cir.func{{.*}}@_Z14use_extern_tlsv
// CIR: cir.get_global thread_local @tls_extern
// LLVM-LABEL: @_Z14use_extern_tlsv
// LLVM: @llvm.threadlocal.address.p0(ptr @tls_extern)
// OGCG-LABEL: @_Z14use_extern_tlsv
// OGCG: call ptr @_ZTW10tls_extern()