diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 3323c6ceb2bcf..88844ad9b4c55 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -719,6 +719,10 @@ Bug Fixes in This Version points (i.e., uses function descriptor objects instead). - Fixes a ``clang-17`` regression where ``LLVM_UNREACHABLE_OPTIMIZE=OFF`` cannot be used with ``Release`` mode builds. (`#68237 `_). +- No longer use C++ ``thread_local`` semantics in C23 when using + ``thread_local`` instead of ``_Thread_local``. + Fixes (`#70068 `_) and + (`#69167 `_) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 43b2a32cce71c..46315cc4e4115 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3997,9 +3997,16 @@ void Parser::ParseDeclarationSpecifiers( break; case tok::kw_thread_local: if (getLangOpts().C2x) - Diag(Tok, diag::warn_c2x_compat_keyword) << Tok.getName(); - isInvalid = DS.SetStorageClassSpecThread(DeclSpec::TSCS_thread_local, Loc, - PrevSpec, DiagID); + Diag(Tok, diag::warn_c23_compat_keyword) << Tok.getName(); + // We map thread_local to _Thread_local in C23 mode so it retains the C + // semantics rather than getting the C++ semantics. + // FIXME: diagnostics will show _Thread_local when the user wrote + // thread_local in source in C23 mode; we need some general way to + // identify which way the user spelled the keyword in source. + isInvalid = DS.SetStorageClassSpecThread( + getLangOpts().C2x ? DeclSpec::TSCS__Thread_local + : DeclSpec::TSCS_thread_local, + Loc, PrevSpec, DiagID); isStorageClass = true; break; case tok::kw__Thread_local: diff --git a/clang/test/CodeGen/thread_local.c b/clang/test/CodeGen/thread_local.c new file mode 100644 index 0000000000000..b97f31c2fff2f --- /dev/null +++ b/clang/test/CodeGen/thread_local.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple i686-pc-linux-gnu -std=c23 -emit-llvm -o - %s | FileCheck %s + +// Ensure that thread_local and _Thread_local emit the same codegen. See +// https://github.com/llvm/llvm-project/issues/70068 for details. + +void func(void) { + static thread_local int i = 12; + static _Thread_local int j = 13; + + extern thread_local int k; + extern thread_local int l; + + (void)k; + (void)l; +} + +// CHECK: @func.i = internal thread_local global i32 12, align 4 +// CHECK-NEXT: @func.j = internal thread_local global i32 13, align 4 +// CHECK-NEXT: @k = external thread_local global i32, align 4 +// CHECK-NEXT: @l = external thread_local global i32, align 4 + +// CHECK: define dso_local void @func() +// CHECK-NEXT: entry: +// CHECK-NEXT: %[[K:.+]] = call align 4 ptr @llvm.threadlocal.address.p0(ptr align 4 @k) +// CHECK-NEXT: load i32, ptr %[[K]], align 4 +// CHECK-NEXT: %[[L:.+]] = call align 4 ptr @llvm.threadlocal.address.p0(ptr align 4 @l) +// CHECK-NEXT: load i32, ptr %[[L]], align 4 diff --git a/clang/test/Sema/thread_local.c b/clang/test/Sema/thread_local.c new file mode 100644 index 0000000000000..a0de0aa4e39a6 --- /dev/null +++ b/clang/test/Sema/thread_local.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c23 %s -verify + +// Ensure that thread_local and _Thread_local are synonyms in C23 and both +// restrict local variables to be explicitly static or extern. +void func(void) { + // FIXME: it would be nice if the diagnostic said 'thread_local' in this case. + thread_local int i = 12; // expected-error {{'_Thread_local' variables must have global storage}} + _Thread_local int j = 13; // expected-error {{'_Thread_local' variables must have global storage}} + + static thread_local int k = 14; + static _Thread_local int l = 15; + + extern thread_local int m; + extern thread_local int n; +} + +// This would previously fail because the tls models were different. +extern thread_local unsigned a; +_Thread_local unsigned a = 0;