Alex Crichton 1 month ago
parent 5faf808053
commit 526321ead1

@ -11,7 +11,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
project(wasi-sdk)
include(ExternalProject)
set(WASI_SDK_TARGETS "wasm32-wasi;wasm32-wasip1;wasm32-wasip2;wasm32-wasip1-threads;wasm32-wasi-threads"
set(WASI_SDK_TARGETS "wasm32-wasi;wasm32-wasip1;wasm32-wasip2;wasm32-wasip3;wasm32-wasip1-threads;wasm32-wasi-threads"
CACHE STRING "List of WASI targets to build")
option(WASI_SDK_BUILD_TOOLCHAIN "Build a toolchain instead of the sysroot" OFF)

@ -186,6 +186,8 @@ function(define_wasi_libc_sub target target_suffix lto)
if(${target} MATCHES threads)
set(libcompiler_rt_a ${wasi_resource_dir}/lib/wasm32-unknown-wasip1-threads/libclang_rt.builtins.a)
elseif(${target} MATCHES wasip3)
set(libcompiler_rt_a ${wasi_resource_dir}/lib/wasm32-unknown-wasip3/libclang_rt.builtins.a)
else()
set(libcompiler_rt_a ${wasi_resource_dir}/lib/wasm32-unknown-wasip1/libclang_rt.builtins.a)
endif()
@ -375,7 +377,7 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
"git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-186054.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-186054.patch -R --check"
COMMAND
${CMAKE_COMMAND} -E chdir .. bash -c
"git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185770.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185770.patch -R --check"
"git apply ${CMAKE_SOURCE_DIR}/src/llvm-undo-part-of-194317.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-undo-part-of-194317.patch -R --check"
)
add_dependencies(libcxx-${target} libcxx-${target}${target_suffix}-build)
endfunction()

@ -250,7 +250,7 @@ ExternalProject_Add(llvm-build
USES_TERMINAL_INSTALL ON
PATCH_COMMAND
${CMAKE_COMMAND} -E chdir .. bash -c
"git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185775.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185775.patch -R --check"
"git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-200855.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-200855.patch -R --check"
)
add_custom_target(build ALL DEPENDS llvm-build)
@ -265,7 +265,7 @@ install(DIRECTORY ${wasi_tmp_install}/bin ${wasi_tmp_install}/lib ${wasi_tmp_ins
# Build logic for `wasm-component-ld` installed from Rust code.
set(wasm_component_ld_root ${CMAKE_CURRENT_BINARY_DIR}/wasm-component-ld)
set(wasm_component_ld ${wasm_component_ld_root}/bin/wasm-component-ld${CMAKE_EXECUTABLE_SUFFIX})
set(wasm_component_ld_version 0.5.22)
set(wasm_component_ld_version 0.5.24)
if(RUST_TARGET)
set(rust_target_flag --target=${RUST_TARGET})
endif()

@ -1,105 +0,0 @@
From d702761d9135ebbb83590d4dd1323be433701ebd Mon Sep 17 00:00:00 2001
From: Alex Crichton <alex@alexcrichton.com>
Date: Tue, 10 Mar 2026 15:49:55 -0700
Subject: [PATCH] [WebAssembly] Move __cpp_exception to libunwind
The `__cpp_exception` symbol is now defined in libunwind instead of
compiler-rt. This is moved for a few reasons, but the primary reason is
that compiler-rt is linked duplicate-ly into all shared objects meaning
that it's not suitable for define-once symbols such as
`__cpp_exception`. By moving the definition to the user of the symbol,
libunwind itself, that guarantees that the symbol should be defined
exactly once and only when appropriate. A secondary reason for this
movement is that it avoids the need to compile compiler-rt twice: once
with exception and once without, and instead the same build can be used
for both exceptions-and-not.
---
compiler-rt/lib/builtins/CMakeLists.txt | 1 -
.../lib/builtins/wasm/__cpp_exception.S | 26 -------------------
libunwind/src/Unwind-wasm.c | 15 +++++++++++
.../compiler-rt/lib/builtins/sources.gni | 1 -
4 files changed, 15 insertions(+), 28 deletions(-)
delete mode 100644 compiler-rt/lib/builtins/wasm/__cpp_exception.S
diff --git a/compiler-rt/lib/builtins/CMakeLists.txt b/compiler-rt/lib/builtins/CMakeLists.txt
index 6c27f6d4d529e..f0570a9092f40 100644
--- a/compiler-rt/lib/builtins/CMakeLists.txt
+++ b/compiler-rt/lib/builtins/CMakeLists.txt
@@ -891,7 +891,6 @@ set(s390x_SOURCES
set(wasm_SOURCES
wasm/__c_longjmp.S
- wasm/__cpp_exception.S
${GENERIC_TF_SOURCES}
${GENERIC_SOURCES}
)
diff --git a/compiler-rt/lib/builtins/wasm/__cpp_exception.S b/compiler-rt/lib/builtins/wasm/__cpp_exception.S
deleted file mode 100644
index 0496e1dbf6158..0000000000000
--- a/compiler-rt/lib/builtins/wasm/__cpp_exception.S
+++ /dev/null
@@ -1,26 +0,0 @@
-//===-- __cpp_exception.S - Implement __cpp_exception ---------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements __cpp_exception which LLVM uses to implement exception
-// handling when Wasm EH is enabled.
-//
-//===----------------------------------------------------------------------===//
-
-#ifdef __wasm_exception_handling__
-
-#ifdef __wasm64__
-#define PTR i64
-#else
-#define PTR i32
-#endif
-
-.globl __cpp_exception
-.tagtype __cpp_exception PTR
-__cpp_exception:
-
-#endif // !__wasm_exception_handling__
diff --git a/libunwind/src/Unwind-wasm.c b/libunwind/src/Unwind-wasm.c
index 2f4498c3f3989..c0ca9b775d244 100644
--- a/libunwind/src/Unwind-wasm.c
+++ b/libunwind/src/Unwind-wasm.c
@@ -69,6 +69,21 @@ _Unwind_RaiseException(_Unwind_Exception *exception_object) {
__builtin_wasm_throw(0, exception_object);
}
+// Define the `__cpp_exception` symbol which `__builtin_wasm_throw` above will
+// reference. This is defined here in `libunwind` as the single canonical
+// definition for this API and it's required for users to ensure that there's
+// only one copy of `libunwind` within a wasm module to ensure this is only
+// defined once and exactly once.
+__asm__(".globl __cpp_exception\n"
+#if defined(__wasm32__)
+ ".tagtype __cpp_exception i32\n"
+#elif defined(__wasm64__)
+ ".tagtype __cpp_exception i64\n"
+#else
+#error "Unsupported Wasm architecture"
+#endif
+ "__cpp_exception:\n");
+
/// Called by __cxa_end_catch.
_LIBUNWIND_EXPORT void
_Unwind_DeleteException(_Unwind_Exception *exception_object) {
diff --git a/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni b/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni
index 2ac71aa8e8367..c9eeede16e3eb 100644
--- a/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni
+++ b/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni
@@ -539,7 +539,6 @@ if (current_cpu == "ve") {
if (current_cpu == "wasm") {
builtins_sources += [
"wasm/__c_longjmp.S",
- "wasm/__cpp_exception.S",
]
}

@ -1,152 +0,0 @@
From 0e36e8f304cd5f3997916f5d85201bb17e340337 Mon Sep 17 00:00:00 2001
From: Alex Crichton <alex@alexcrichton.com>
Date: Tue, 10 Mar 2026 16:14:36 -0700
Subject: [PATCH] [WebAssembly] Clang support for exception-based lookup paths
This commit is an attempt to make progress on WebAssembly/wasi-sdk#565
where with wasi-sdk I'd like to ship a single toolchain which is
capable of building binaries both with C++ exceptions and without. This
means that there can't be a single set of precompiled libraries that are
used because one set of libraries is wrong for the other mode. The
support added here is to use `-fwasm-exceptions` to automatically select
a lookup path in the sysroot. The intention is then that wasi-sdk will
ship both a "eh" set of C++ libraries as well as a "noeh" set of C++
libraries too. Clang will automatically select the correct one based on
compilation flags which means that the final distribution will be able
to build both binaries with exceptions and without.
---
clang/lib/Driver/ToolChains/WebAssembly.cpp | 51 ++++++++++++++-------
clang/test/Driver/wasm-toolchain.cpp | 35 ++++++++++++++
2 files changed, 70 insertions(+), 16 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index b5fa5760a46a0..e532ef0743cc2 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -34,6 +34,15 @@ std::string WebAssembly::getMultiarchTriple(const Driver &D,
TargetTriple.getOSAndEnvironmentName()).str();
}
+/// Returns a directory name in which separate objects compile with/without
+/// exceptions may lie. This is used both for `#include` paths as well as lib
+/// paths.
+static std::string GetCXXExceptionsDir(const ArgList &DriverArgs) {
+ if (DriverArgs.getLastArg(options::OPT_fwasm_exceptions))
+ return "eh";
+ return "noeh";
+}
+
std::string wasm::Linker::getLinkerPath(const ArgList &Args) const {
const ToolChain &ToolChain = getToolChain();
if (const Arg* A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
@@ -230,12 +239,16 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
}
}
-/// Given a base library directory, append path components to form the
-/// LTO directory.
-static std::string AppendLTOLibDir(const std::string &Dir) {
+/// Append `Dir` to `Paths`, but also include the LTO directories before that if
+/// LTO is eanbled.
+static void AppendLibDirAndLTODir(ToolChain::path_list &Paths, const Driver &D,
+ const std::string &Dir) {
+ if (D.isUsingLTO()) {
// The version allows the path to be keyed to the specific version of
// LLVM in used, as the bitcode format is not stable.
- return Dir + "/llvm-lto/" LLVM_VERSION_STRING;
+ Paths.push_back(Dir + "/llvm-lto/" LLVM_VERSION_STRING);
+ }
+ Paths.push_back(Dir);
}
WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple,
@@ -256,14 +269,15 @@ WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple,
} else {
const std::string MultiarchTriple =
getMultiarchTriple(getDriver(), Triple, SysRoot);
- if (D.isUsingLTO()) {
- // For LTO, enable use of lto-enabled sysroot libraries too, if available.
- // Note that the directory is keyed to the LLVM revision, as LLVM's
- // bitcode format is not stable.
- auto Dir = AppendLTOLibDir(SysRoot + "/lib/" + MultiarchTriple);
- getFilePaths().push_back(Dir);
- }
- getFilePaths().push_back(SysRoot + "/lib/" + MultiarchTriple);
+ std::string TripleLibDir = SysRoot + "/lib/" + MultiarchTriple;
+ // Allow sysroots to segregate objects based on whether exceptions are
+ // enabled or not. This is intended to assist with distribution of pre-built
+ // sysroots that contain libraries that are capable of producing binaries
+ // entirely without exception-handling instructions but also with if
+ // exceptions are enabled, for example.
+ AppendLibDirAndLTODir(getFilePaths(), D,
+ TripleLibDir + "/" + GetCXXExceptionsDir(Args));
+ AppendLibDirAndLTODir(getFilePaths(), D, TripleLibDir);
}
if (getTriple().getOS() == llvm::Triple::WASI) {
@@ -580,13 +594,18 @@ void WebAssembly::addLibCxxIncludePaths(
if (Version.empty())
return;
- // First add the per-target include path if the OS is known.
+ // First add the per-target-per-exception-handling include path if the
+ // OS is known, then second add the per-target include path.
if (IsKnownOs) {
- std::string TargetDir = LibPath + "/" + MultiarchTriple + "/c++/" + Version;
- addSystemInclude(DriverArgs, CC1Args, TargetDir);
+ std::string TargetDir = LibPath + "/" + MultiarchTriple;
+ std::string Suffix = "/c++/" + Version;
+ addSystemInclude(DriverArgs, CC1Args,
+ TargetDir + "/" + GetCXXExceptionsDir(DriverArgs) +
+ Suffix);
+ addSystemInclude(DriverArgs, CC1Args, TargetDir + Suffix);
}
- // Second add the generic one.
+ // Third add the generic one.
addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version);
}
diff --git a/clang/test/Driver/wasm-toolchain.cpp b/clang/test/Driver/wasm-toolchain.cpp
index d7ff76cedfd10..30a2f9397e3f4 100644
--- a/clang/test/Driver/wasm-toolchain.cpp
+++ b/clang/test/Driver/wasm-toolchain.cpp
@@ -111,3 +111,38 @@
// COMPILE_WALI_STDCXX: "-internal-isystem" "[[RESOURCE_DIR]]{{(/|\\\\)}}include"
// COMPILE_WALI_STDCXX: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-linux-muslwali"
// COMPILE_WALI_STDCXX: "-internal-isystem" "[[SYSROOT:[^"]+]]/include"
+
+// With a known OS "eh" and "noeh" directories are added to enable segregating
+// object built with/without exception-handling
+
+// RUN: %clangxx -### --target=wasm32-wasi --stdlib=libc++ %s 2>&1 \
+// RUN: --sysroot=%S/Inputs/basic_linux_libcxx_tree/usr \
+// RUN: | FileCheck -check-prefix=EH_OFF %s
+// EH_OFF: "-cc1"
+// EH_OFF: "-isysroot" "[[SYSROOT:[^"]+]]"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/noeh/c++/v1"
+// EH_OFF-NOT: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/eh/c++/v1"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/c++/v1"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/c++/v1"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include"
+
+// RUN: %clangxx -### --target=wasm32-wasi -fwasm-exceptions --stdlib=libc++ %s 2>&1 \
+// RUN: --sysroot=%S/Inputs/basic_linux_libcxx_tree/usr \
+// RUN: | FileCheck -check-prefix=EH_ON %s
+// EH_ON: "-cc1"
+// EH_ON: "-isysroot" "[[SYSROOT:[^"]+]]"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/eh/c++/v1"
+// EH_ON-NOT: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/noeh/c++/v1"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/c++/v1"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/c++/v1"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include"
+//
+// RUN: %clangxx -### --target=wasm32-wasi --sysroot=/foo --stdlib=libc++ %s 2>&1 \
+// RUN: | FileCheck -check-prefix=EH_OFF_LINK %s
+// EH_OFF_LINK: wasm-ld{{.*}}" "-L/foo/lib/wasm32-wasi/noeh" "-L/foo/lib/wasm32-wasi"
+//
+// RUN: %clangxx -### --target=wasm32-wasi -fwasm-exceptions --sysroot=/foo --stdlib=libc++ %s 2>&1 \
+// RUN: | FileCheck -check-prefix=EH_ON_LINK %s
+// EH_ON_LINK: wasm-ld{{.*}}" "-L/foo/lib/wasm32-wasi/eh" "-L/foo/lib/wasm32-wasi"

@ -1,8 +1,8 @@
From f71fdfcbd6fcc7b521c74b5856ebeacdd6cf55d9 Mon Sep 17 00:00:00 2001
From 46301f073b8cc85ad0d2f1dcdda923209bff173b Mon Sep 17 00:00:00 2001
From: Catherine <whitequark@whitequark.org>
Date: Thu, 12 Mar 2026 08:19:49 +0000
Subject: [PATCH] [libc++abi] Revert gating of `__cxa_thread_atexit` on
Linux||Fuchsia
Wasm/WASI
This was done in the commit 3c100d5d548d with the description
"Enable -Wmissing-prototypes" which seems incongruent to me.
@ -23,26 +23,28 @@ wasm-ld: error: /tmp/repro-dd1ad7.o: undefined symbol: __cxa_thread_atexit
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
```
---
libcxxabi/src/cxa_thread_atexit.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
libcxxabi/src/cxa_thread_atexit.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/libcxxabi/src/cxa_thread_atexit.cpp b/libcxxabi/src/cxa_thread_atexit.cpp
index 402a52c741012..1bdcb4ef192b4 100644
index 402a52c741012..099be242c6dc6 100644
--- a/libcxxabi/src/cxa_thread_atexit.cpp
+++ b/libcxxabi/src/cxa_thread_atexit.cpp
@@ -106,7 +106,6 @@ namespace {
@@ -106,7 +106,7 @@ namespace {
#endif // HAVE___CXA_THREAD_ATEXIT_IMPL
-#if defined(__linux__) || defined(__Fuchsia__)
+#if defined(__linux__) || defined(__Fuchsia__) || defined(__wasm__)
extern "C" {
_LIBCXXABI_FUNC_VIS int __cxa_thread_atexit(Dtor dtor, void* obj, void* dso_symbol) throw() {
@@ -141,6 +140,5 @@ extern "C" {
@@ -141,6 +141,7 @@ extern "C" {
}
#endif // HAVE___CXA_THREAD_ATEXIT_IMPL
}
-} // extern "C"
+
} // extern "C"
-#endif // defined(__linux__) || defined(__Fuchsia__)
+ } // extern "C"
+#endif // defined(__linux__) || defined(__Fuchsia__) || defined(__wasm__)
} // namespace __cxxabiv1

@ -0,0 +1,638 @@
diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp
index 46f9bd10f01ecb..a483e3d6f9b105 100644
--- a/clang/lib/Basic/Targets/WebAssembly.cpp
+++ b/clang/lib/Basic/Targets/WebAssembly.cpp
@@ -424,7 +424,7 @@ void WebAssemblyTargetInfo::adjust(DiagnosticsEngine &Diags, LangOptions &Opts,
// Turn off POSIXThreads and ThreadModel so that we don't predefine _REENTRANT
// or __STDCPP_THREADS__ if we will eventually end up stripping atomics
// because they are unsupported.
- if (!HasAtomics || !HasBulkMemory) {
+ if ((!HasCooperativeThreading && !HasAtomics) || !HasBulkMemory) {
Opts.POSIXThreads = false;
Opts.setThreadModel(LangOptions::ThreadModelKind::Single);
Opts.ThreadsafeStatics = false;
diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index 60851974981633..ef523d602ac0ef 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -63,6 +63,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
bool HasBulkMemory = false;
bool HasBulkMemoryOpt = false;
bool HasCallIndirectOverlong = false;
+ bool HasCooperativeThreading = false;
bool HasCompactImports = false;
bool HasExceptionHandling = false;
bool HasExtendedConst = false;
@@ -111,8 +112,10 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
PtrDiffType = SignedLong;
IntPtrType = SignedLong;
}
- if (T.getOS() == llvm::Triple::WASIp3)
+ if (T.getOS() == llvm::Triple::WASIp3) {
HasLibcallThreadContext = true;
+ HasCooperativeThreading = true;
+ }
}
StringRef getABI() const override;
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index 5bc43b37d06bfe..b64c02375a61ed 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -85,14 +85,23 @@ static bool WantsPthread(const llvm::Triple &Triple, const ArgList &Args) {
if (Triple.isOSWASI() && Triple.getEnvironmentName() == "threads")
WantsPthread = true;
+ // WASIp3 also implies pthreads support
+ if (Triple.getOS() == llvm::Triple::WASIp3)
+ WantsPthread = true;
+
return WantsPthread;
}
-static bool WantsLibcallThreadContext(const llvm::Triple &Triple,
- const ArgList &Args) {
+static bool WantsCooperativeMultithreading(const llvm::Triple &Triple,
+ const ArgList &Args) {
return Triple.getOS() == llvm::Triple::WASIp3;
}
+static bool WantsSharedMemory(const llvm::Triple &Triple, const ArgList &Args) {
+ return WantsPthread(Triple, Args) &&
+ !WantsCooperativeMultithreading(Triple, Args);
+}
+
void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
@@ -174,10 +183,10 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
- if (WantsLibcallThreadContext(ToolChain.getTriple(), Args))
- CmdArgs.push_back("--libcall-thread-context");
+ if (WantsCooperativeMultithreading(ToolChain.getTriple(), Args))
+ CmdArgs.push_back("--cooperative-threading");
- if (WantsPthread(ToolChain.getTriple(), Args))
+ if (WantsSharedMemory(ToolChain.getTriple(), Args))
CmdArgs.push_back("--shared-memory");
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
@@ -332,9 +341,12 @@ void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs,
options::OPT_fno_use_init_array, true))
CC1Args.push_back("-fno-use-init-array");
- // '-pthread' implies atomics, bulk-memory, mutable-globals, and sign-ext
+ // '-pthread' implies bulk-memory, mutable-globals, and sign-ext.
+ // It also implies atomics, so long as we're not targeting a cooperative
+ // threading environment.
if (WantsPthread(getTriple(), DriverArgs)) {
- if (DriverArgs.hasFlag(options::OPT_mno_atomics, options::OPT_matomics,
+ if (!WantsCooperativeMultithreading(getTriple(), DriverArgs) &&
+ DriverArgs.hasFlag(options::OPT_mno_atomics, options::OPT_matomics,
false))
getDriver().Diag(diag::err_drv_argument_not_allowed_with)
<< "-pthread"
@@ -354,8 +366,10 @@ void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs,
getDriver().Diag(diag::err_drv_argument_not_allowed_with)
<< "-pthread"
<< "-mno-sign-ext";
- CC1Args.push_back("-target-feature");
- CC1Args.push_back("+atomics");
+ if (!WantsCooperativeMultithreading(getTriple(), DriverArgs)) {
+ CC1Args.push_back("-target-feature");
+ CC1Args.push_back("+atomics");
+ }
CC1Args.push_back("-target-feature");
CC1Args.push_back("+bulk-memory");
CC1Args.push_back("-target-feature");
diff --git a/clang/test/Driver/wasm-toolchain.c b/clang/test/Driver/wasm-toolchain.c
index 29a94aeec77a94..c02a102fab0817 100644
--- a/clang/test/Driver/wasm-toolchain.c
+++ b/clang/test/Driver/wasm-toolchain.c
@@ -303,3 +303,9 @@
// RUN: | FileCheck -check-prefix=LINK_WALI_BASIC %s
// LINK_WALI_BASIC: "-cc1" {{.*}} "-o" "[[temp:[^"]*]]"
// LINK_WALI_BASIC: wasm-ld{{.*}}" "-L/foo/lib/wasm32-linux-muslwali" "crt1.o" "[[temp]]" "-lc" "{{.*[/\\]}}libclang_rt.builtins.a" "-o" "a.out"
+
+// Test that `wasm32-wasip3` passes `--cooperative-threading` to the linker.
+
+// RUN: %clang -### --target=wasm32-wasip3 -fuse-ld=lld %s --sysroot /foo 2>&1 \
+// RUN: | FileCheck -check-prefix=LINK_WASIP3_COOP %s
+// LINK_WASIP3_COOP: wasm-ld{{.*}}" {{.*}} "--cooperative-threading"
diff --git a/lld/test/wasm/cooperative-threading.s b/lld/test/wasm/cooperative-threading.s
new file mode 100644
index 00000000000000..39df73858b6dac
--- /dev/null
+++ b/lld/test/wasm/cooperative-threading.s
@@ -0,0 +1,85 @@
+# Test that --cooperative-threading uses the libcall ABI naming for
+# thread-context globals (__init_stack_pointer, __init_tls_base, etc.) and
+# works without --shared-memory and atomics.
+
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
+# RUN: wasm-ld --cooperative-threading -no-gc-sections -o %t.wasm %t.o
+# RUN: obj2yaml %t.wasm | FileCheck %s
+# RUN: llvm-objdump -d --no-print-imm-hex --no-show-raw-insn %t.wasm | FileCheck %s --check-prefix=DIS
+
+# Test that --cooperative-threading and --shared-memory are mutually exclusive.
+# RUN: not wasm-ld --cooperative-threading --shared-memory %t.o -o %t2.wasm 2>&1 | FileCheck %s --check-prefix=INCOMPAT
+# INCOMPAT: --cooperative-threading is incompatible with --shared-memory
+
+.globl __wasm_get_tls_base
+__wasm_get_tls_base:
+ .functype __wasm_get_tls_base () -> (i32)
+ i32.const 0
+ end_function
+
+.globl _start
+_start:
+ .functype _start () -> (i32)
+ call __wasm_get_tls_base
+ i32.const tls1@TLSREL
+ i32.add
+ i32.load 0
+ call __wasm_get_tls_base
+ i32.const tls2@TLSREL
+ i32.add
+ i32.load 0
+ i32.add
+ end_function
+
+.section .tdata.tls1,"",@
+.globl tls1
+tls1:
+ .int32 1
+ .size tls1, 4
+
+.section .tdata.tls2,"",@
+.globl tls2
+tls2:
+ .int32 2
+ .size tls2, 4
+
+.section .custom_section.target_features,"",@
+ .int8 2
+ .int8 43
+ .int8 11
+ .ascii "bulk-memory"
+ .int8 43
+ .int8 7
+ .ascii "atomics"
+
+# Memory must NOT be marked as shared.
+# CHECK: - Type: MEMORY
+# CHECK-NEXT: Memories:
+# CHECK-NEXT: - Minimum: 0x2
+# CHECK-NOT: Shared
+
+# Globals should use the libcall ABI naming, not the global ABI.
+# CHECK: GlobalNames:
+# CHECK-NEXT: - Index: 0
+# CHECK-NEXT: Name: __init_stack_pointer
+# CHECK-NEXT: - Index: 1
+# CHECK-NEXT: Name: __init_tls_base
+# CHECK-NEXT: - Index: 2
+# CHECK-NEXT: Name: __tls_size
+# CHECK-NEXT: - Index: 3
+# CHECK-NEXT: Name: __tls_align
+
+# DIS-LABEL: <__wasm_init_memory>:
+
+# DIS-LABEL: <_start>:
+# DIS-EMPTY:
+# DIS-NEXT: call {{[0-9]+}}
+# DIS-NEXT: i32.const 0
+# DIS-NEXT: i32.add
+# DIS-NEXT: i32.load 0
+# DIS-NEXT: call {{[0-9]+}}
+# DIS-NEXT: i32.const 4
+# DIS-NEXT: i32.add
+# DIS-NEXT: i32.load 0
+# DIS-NEXT: i32.add
+# DIS-NEXT: end
diff --git a/lld/test/wasm/stack-pointer-abi.s b/lld/test/wasm/stack-pointer-abi.s
index 869f9727109910..fbae0475bcba22 100644
--- a/lld/test/wasm/stack-pointer-abi.s
+++ b/lld/test/wasm/stack-pointer-abi.s
@@ -1,5 +1,5 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
-# RUN: wasm-ld --libcall-thread-context --no-gc-sections -o %t.libcall.wasm %t.o
+# RUN: wasm-ld --cooperative-threading --no-gc-sections -o %t.libcall.wasm %t.o
# RUN: obj2yaml %t.libcall.wasm | FileCheck %s --check-prefix=LIBCALL
# RUN: wasm-ld --no-gc-sections -o %t.global.wasm %t.o
# RUN: obj2yaml %t.global.wasm | FileCheck %s --check-prefix=GLOBAL
diff --git a/lld/test/wasm/thread-context-abi-mismatch.s b/lld/test/wasm/thread-context-abi-mismatch.s
index 069534cbe5762e..3debc1de662a1d 100644
--- a/lld/test/wasm/thread-context-abi-mismatch.s
+++ b/lld/test/wasm/thread-context-abi-mismatch.s
@@ -3,10 +3,9 @@
# as an indication that the global thread context ABI is being used.
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
-# RUN: not wasm-ld --libcall-thread-context %t.o -o %t.wasm 2>&1 | FileCheck %s
-
-# CHECK: object file uses globals for thread context, but --libcall-thread-context was specified
+# RUN: not wasm-ld --cooperative-threading %t.o -o %t.wasm 2>&1 | FileCheck %s
+# CHECK: object file uses globals for thread context, but --cooperative-threading was specified
.globl _start
_start:
.functype _start () -> ()
diff --git a/lld/test/wasm/tls-libcall.s b/lld/test/wasm/tls-libcall.s
index df8b8f8be0207f..d8fb1c5e8a9cae 100644
--- a/lld/test/wasm/tls-libcall.s
+++ b/lld/test/wasm/tls-libcall.s
@@ -1,5 +1,5 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
-# RUN: wasm-ld --libcall-thread-context --shared-memory -no-gc-sections -o %t.wasm %t.o
+# RUN: wasm-ld --cooperative-threading -no-gc-sections -o %t.wasm %t.o
# RUN: obj2yaml %t.wasm | FileCheck %s
# RUN: llvm-objdump -d --no-print-imm-hex --no-show-raw-insn %t.wasm | FileCheck %s --check-prefix=DIS
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index fb1c3f9f2e739d..f2f1b895f5b69e 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -65,6 +65,7 @@ struct Config {
bool growableTable;
bool gcSections;
llvm::StringSet<> keepSections;
+ bool cooperativeThreading;
bool libcallThreadContext;
std::optional<std::pair<llvm::StringRef, llvm::StringRef>> memoryImport;
std::optional<llvm::StringRef> memoryExport;
@@ -134,6 +135,8 @@ struct Config {
std::optional<std::vector<std::string>> features;
std::optional<std::vector<std::string>> extraFeatures;
llvm::SmallVector<uint8_t, 0> buildIdVector;
+
+ bool isMultithreaded() const { return sharedMemory || cooperativeThreading; }
};
// The Ctx object hold all other (non-configuration) global state.
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index fe1e2eec95037e..ae203cd7e904d8 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -561,7 +561,8 @@ static void readConfigs(opt::InputArgList &args) {
ctx.arg.soName = args.getLastArgValue(OPT_soname);
ctx.arg.importTable = args.hasArg(OPT_import_table);
ctx.arg.importUndefined = args.hasArg(OPT_import_undefined);
- ctx.arg.libcallThreadContext = args.hasArg(OPT_libcall_thread_context);
+ ctx.arg.cooperativeThreading = args.hasArg(OPT_cooperative_threading);
+ ;
ctx.arg.ltoo = args::getInteger(args, OPT_lto_O, 2);
if (ctx.arg.ltoo > 3)
error("invalid optimization level for LTO: " + Twine(ctx.arg.ltoo));
@@ -755,6 +756,11 @@ static void setConfigs() {
if (!ctx.arg.memoryExport.has_value() && !ctx.arg.memoryImport.has_value()) {
ctx.arg.memoryExport = memoryName;
}
+ if (ctx.arg.cooperativeThreading) {
+ if (ctx.arg.sharedMemory)
+ error("--cooperative-threading is incompatible with --shared-memory");
+ ctx.arg.libcallThreadContext = true;
+ }
}
// Some command line options or some combinations of them are not allowed.
@@ -964,7 +970,7 @@ static void createSyntheticSymbols() {
createGlobalVariable(stack_pointer_name, !ctx.arg.libcallThreadContext);
}
- if (ctx.arg.sharedMemory) {
+ if (ctx.arg.isMultithreaded()) {
// TLS symbols are all hidden/dso-local
auto tls_base_name =
ctx.arg.libcallThreadContext ? "__init_tls_base" : "__tls_base";
@@ -986,9 +992,11 @@ static void createSyntheticSymbols() {
static WasmSignature setTLSBaseSignature{{}, {ValType::I32}};
ctx.sym.setTLSBase =
createUndefinedFunction("__wasm_set_tls_base", &setTLSBaseSignature);
+ ctx.sym.setTLSBase->markLive();
static WasmSignature getTLSBaseSignature{{ValType::I32}, {}};
ctx.sym.getTLSBase =
createUndefinedFunction("__wasm_get_tls_base", &getTLSBaseSignature);
+ ctx.sym.getTLSBase->markLive();
}
}
}
@@ -1019,16 +1027,12 @@ static void createOptionalSymbols() {
if (ctx.sym.firstPageEnd)
ctx.sym.firstPageEnd->setVA(ctx.arg.pageSize);
- // For non-shared memory programs we still need to define __tls_base since we
- // allow object files built with TLS to be linked into single threaded
- // programs, and such object files can contain references to this symbol.
- //
- // However, in this case __tls_base is immutable and points directly to the
- // start of the `.tdata` static segment.
- //
- // __tls_size and __tls_align are not needed in this case since they are only
- // needed for __wasm_init_tls (which we do not create in this case).
- if (!ctx.arg.sharedMemory)
+ // TLS object files may be linked into single-threaded programs, so
+ // __tls_base must always be defined. In this case it is immutable and points
+ // directly to the start of the `.tdata` segment. __tls_size and __tls_align
+ // are omitted since they are only used by __wasm_init_tls, which is not
+ // created in this case.
+ if (!ctx.sym.tlsBase)
ctx.sym.tlsBase = createOptionalGlobal("__tls_base", false);
}
diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td
index 144eee33061e1d..bd46794e067b3a 100644
--- a/lld/wasm/Options.td
+++ b/lld/wasm/Options.td
@@ -238,8 +238,8 @@ def page_size: JJ<"page-size=">,
def initial_memory: JJ<"initial-memory=">,
HelpText<"Initial size of the linear memory">;
-def libcall_thread_context: FF<"libcall-thread-context">,
- HelpText<"Use library calls for thread context access instead of globals.">;
+def cooperative_threading: FF<"cooperative-threading">,
+ HelpText<"Enable cooperative multithreading.">;
def max_memory: JJ<"max-memory=">,
HelpText<"Maximum size of the linear memory">;
diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index a1840abe88b3a7..cb597fdeffcf34 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -125,7 +125,7 @@ void scanRelocations(InputChunk *chunk) {
// In single-threaded builds TLS is lowered away and TLS data can be
// merged with normal data and allowing TLS relocation in non-TLS
// segments.
- if (ctx.arg.sharedMemory) {
+ if (ctx.arg.isMultithreaded()) {
if (!sym->isTLS()) {
error(toString(file) + ": relocation " +
relocTypeToString(reloc.Type) +
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index d1a01c7ec3f9d9..050f61c7f5c564 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -57,7 +57,7 @@ void writeGetTLSBase(const Ctx &ctx, raw_ostream &os) {
writeU8(os, WASM_OPCODE_CALL, "call");
writeUleb128(os, ctx.sym.getTLSBase->getFunctionIndex(), "function index");
} else {
- writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_SET");
+ writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "__tls_base");
}
}
@@ -532,7 +532,7 @@ void GlobalSection::writeBody() {
mutable_ = true;
// With multi-threading any TLS globals must be mutable since they get
// set during `__wasm_apply_global_tls_relocs`
- if (ctx.arg.sharedMemory && sym->isTLS())
+ if (ctx.arg.isMultithreaded() && sym->isTLS())
mutable_ = true;
}
WasmGlobalType type{itype, mutable_};
@@ -569,10 +569,11 @@ void GlobalSection::writeBody() {
} else {
WasmInitExpr initExpr;
if (auto *d = dyn_cast<DefinedData>(sym))
- // In the sharedMemory case TLS globals are set during
- // `__wasm_apply_global_tls_relocs`, but in the non-shared case
+ // In the multithreaded case, TLS globals are set during
+ // `__wasm_apply_global_tls_relocs`, but in the single-threaded case
// we know the absolute value at link time.
- initExpr = intConst(d->getVA(/*absolute=*/!ctx.arg.sharedMemory), is64);
+ initExpr =
+ intConst(d->getVA(/*absolute=*/!ctx.arg.isMultithreaded()), is64);
else if (auto *f = dyn_cast<FunctionSymbol>(sym))
initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64);
else {
@@ -680,7 +681,7 @@ bool DataCountSection::isNeeded() const {
// instructions are not yet supported in input files. However, in the case
// of shared memory, lld itself will generate these instructions as part of
// `__wasm_init_memory`. See Writer::createInitMemoryFunction.
- return numSegments && ctx.arg.sharedMemory;
+ return numSegments && ctx.arg.isMultithreaded();
}
void LinkingSection::writeBody() {
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index b617ea2912b913..6de3918a3f74cf 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -423,7 +423,7 @@ void Writer::layoutMemory() {
// Even in the absense of any actual TLS data, this symbol can still be
// referenced (for example by __builtin_thread_pointer, which should not
// return NULL).
- if (!ctx.arg.sharedMemory && ctx.sym.tlsBase) {
+ if (!ctx.arg.isMultithreaded() && ctx.sym.tlsBase) {
setGlobalPtr(ctx.sym.tlsBase, fixedTLSBase);
}
@@ -650,7 +650,7 @@ void Writer::populateTargetFeatures() {
sym->importModule && sym->importModule == "env";
}))
error(fileName + ": object file uses globals for thread context, "
- "but --libcall-thread-context was specified");
+ "but --cooperative-threading was specified");
}
if (inferFeatures)
@@ -673,10 +673,12 @@ void Writer::populateTargetFeatures() {
}
if (tlsUsed) {
- for (auto feature : {"atomics", "bulk-memory"})
- if (!allowed.contains(feature))
- error(StringRef("'") + feature +
- "' feature must be used in order to use thread-local storage");
+ if (!allowed.contains("bulk-memory"))
+ error("'bulk-memory' feature must be used in order to use thread-local "
+ "storage");
+ if (!allowed.contains("atomics") && !ctx.arg.cooperativeThreading)
+ error("'atomics' feature must be used in order to use thread-local "
+ "storage");
}
// Validate that used features are allowed in output
@@ -1054,7 +1056,17 @@ static StringRef getOutputDataSegmentName(const InputChunk &seg) {
OutputSegment *Writer::createOutputSegment(StringRef name) {
LLVM_DEBUG(dbgs() << "new segment: " << name << "\n");
OutputSegment *s = make<OutputSegment>(name);
- if (ctx.arg.sharedMemory)
+ // In the shared memory case, all data segments must be passive since they
+ // will be initialized once by the main thread and then shared with other
+ // threads. In the cooperative threading case, TLS segments must be passive
+ // so they can be re-initialized per-thread via memory.init, and .bss
+ // segments are passive to avoid serializing their zero bytes into the binary;
+ // they are still present as passive segment entries and zero-filled via
+ // memory.fill in __wasm_init_memory.
+ bool needsPassiveInit =
+ ctx.arg.sharedMemory || (ctx.arg.cooperativeThreading &&
+ (s->isTLS() || s->name.starts_with(".bss")));
+ if (needsPassiveInit)
s->initFlags = WASM_DATA_SEGMENT_IS_PASSIVE;
if (!ctx.arg.relocatable && name.starts_with(".bss"))
s->isBss = true;
@@ -1113,7 +1125,7 @@ void Writer::combineOutputSegments() {
// This restriction does not apply when the extended const extension is
// available: https://github.com/WebAssembly/extended-const
assert(!ctx.arg.extendedConst);
- assert(ctx.isPic && !ctx.arg.sharedMemory);
+ assert(ctx.isPic && !ctx.arg.isMultithreaded());
if (segments.size() <= 1)
return;
OutputSegment *combined = make<OutputSegment>(".data");
@@ -1195,7 +1207,7 @@ void Writer::createSyntheticInitFunctions() {
}
}
- if (ctx.arg.sharedMemory) {
+ if (ctx.arg.isMultithreaded()) {
if (out.globalSec->needsTLSRelocations()) {
ctx.sym.applyGlobalTLSRelocs = symtab->addSyntheticFunction(
"__wasm_apply_global_tls_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
@@ -1375,7 +1387,7 @@ void Writer::createInitMemoryFunction() {
// When we initialize the TLS segment we also set the TLS base.
// This allows the runtime to use this static copy of the TLS data
// for the first/main thread.
- if (ctx.arg.sharedMemory && s->isTLS()) {
+ if (ctx.arg.isMultithreaded() && s->isTLS()) {
if (ctx.isPic) {
// Cache the result of the addionion in local 0
writeU8(os, WASM_OPCODE_LOCAL_TEE, "local.tee");
@@ -1446,7 +1458,7 @@ void Writer::createInitMemoryFunction() {
if (needsPassiveInitialization(s) && !s->isBss) {
// The TLS region should not be dropped since its is needed
// during the initialization of each thread (__wasm_init_tls).
- if (ctx.arg.sharedMemory && s->isTLS())
+ if (ctx.arg.isMultithreaded() && s->isTLS())
continue;
// data.drop instruction
writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
@@ -1499,7 +1511,7 @@ void Writer::createApplyDataRelocationsFunction() {
writeUleb128(os, 0, "num locals");
bool generated = false;
for (const OutputSegment *seg : segments)
- if (!ctx.arg.sharedMemory || !seg->isTLS())
+ if (!ctx.arg.isMultithreaded() || !seg->isTLS())
for (const InputChunk *inSeg : seg->inputSegments)
generated |= inSeg->generateRelocationCode(os);
@@ -1655,7 +1667,6 @@ void Writer::createInitTLSFunction() {
if (tlsSeg) {
writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
writeUleb128(os, 0, "local index");
-
writeSetTLSBase(ctx, os);
// FIXME(wvo): this local needs to be I64 in wasm64, or we need an extend
@@ -1788,9 +1799,9 @@ void Writer::run() {
// `__memory_base` import. Unless we support the extended const expression we
// can't do addition inside the constant expression, so we much combine the
// segments into a single one that can live at `__memory_base`.
- if (ctx.isPic && !ctx.arg.extendedConst && !ctx.arg.sharedMemory) {
- // In shared memory mode all data segments are passive and initialized
- // via __wasm_init_memory.
+ if (ctx.isPic && !ctx.arg.extendedConst && !ctx.arg.isMultithreaded()) {
+ // In multithreaded modes (shared or cooperative), data segments may be
+ // passive and must not be combined into a single active segment.
log("-- combineOutputSegments");
combineOutputSegments();
}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
index 6326b7d76db821..9dea29fb0205d5 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
@@ -40,9 +40,12 @@ WebAssemblySubtarget::initializeSubtargetDependencies(StringRef CPU,
ParseSubtargetFeatures(CPU, /*TuneCPU*/ CPU, FS);
- // WASIP3 implies using the libcall thread context.
- if (TargetTriple.getOS() == Triple::WASIp3)
+ // WASIP3 uses cooperative multithreading, which implies using libcall
+ // thread context.
+ if (TargetTriple.getOS() == Triple::WASIp3) {
+ HasCooperativeMultithreading = true;
HasLibcallThreadContext = true;
+ }
FeatureBitset Bits = getFeatureBits();
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
index 5c6f4cb5b36ffe..f637ce59ebfceb 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
@@ -52,6 +52,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
bool HasExtendedConst = false;
bool HasFP16 = false;
bool HasGC = false;
+ bool HasCooperativeMultithreading = false;
bool HasLibcallThreadContext = false;
bool HasMultiMemory = false;
bool HasMultivalue = false;
@@ -117,6 +118,9 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
bool hasExtendedConst() const { return HasExtendedConst; }
bool hasFP16() const { return HasFP16; }
bool hasGC() const { return HasGC; }
+ bool hasCooperativeMultithreading() const {
+ return HasCooperativeMultithreading;
+ }
bool hasLibcallThreadContext() const { return HasLibcallThreadContext; }
bool hasMultiMemory() const { return HasMultiMemory; }
bool hasMultivalue() const { return HasMultivalue; }
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 886ea0a8ab5743..ae1e31af65f5a3 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -278,10 +278,17 @@ class CoalesceFeaturesAndStripAtomics final : public ModulePass {
bool StrippedAtomics = false;
bool StrippedTLS = false;
+ // In cooperative threading mode, thread locals are meaningful even without
+ // atomics.
+ bool CooperativeThreading =
+ WasmTM->getSubtargetImpl()->hasCooperativeMultithreading();
+
if (!Features[WebAssembly::FeatureAtomics]) {
StrippedAtomics = stripAtomics(M);
- StrippedTLS = stripThreadLocals(M);
- } else if (!Features[WebAssembly::FeatureBulkMemory]) {
+ if (!CooperativeThreading)
+ StrippedTLS = stripThreadLocals(M);
+ }
+ if (!Features[WebAssembly::FeatureBulkMemory]) {
StrippedTLS |= stripThreadLocals(M);
}
diff --git a/llvm/test/CodeGen/WebAssembly/cooperative-strip-tls.ll b/llvm/test/CodeGen/WebAssembly/cooperative-strip-tls.ll
new file mode 100644
index 00000000000000..0cefa1b6b1f21e
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/cooperative-strip-tls.ll
@@ -0,0 +1,25 @@
+; Test that in cooperative threading mode (wasm32-wasip3), thread-local variables
+; are NOT stripped even when atomics are absent. In non-cooperative mode
+; (wasm32-unknown-unknown) TLS is treated as normal data when atomics are absent.
+
+; RUN: llc < %s -mtriple=wasm32-wasip3 -mcpu=mvp -mattr=-atomics,+bulk-memory \
+; RUN: | FileCheck %s --check-prefixes=COOP
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -mcpu=mvp -mattr=-atomics,+bulk-memory \
+; RUN: | FileCheck %s --check-prefixes=PLAIN
+
+target triple = "wasm32-unknown-unknown"
+
+@foo = internal thread_local global i32 0
+@bar = internal thread_local global i32 1
+
+; Cooperative threading: TLS is preserved — the section stays .tbss.
+; COOP: .tbss.foo
+; COOP: .tdata.bar
+; COOP-NOT: .bss.foo
+; COOP-NOT: .data.bar
+
+; Non-cooperative: TLS stripped
+; PLAIN: .bss.foo
+; PLAIN: .data.bar
+; PLAIN-NOT: .tbss.foo
+; PLAIN-NOT: .tdata.bar

@ -1 +1 @@
Subproject commit 4434dabb69916856b824f68a64b029c67175e532
Subproject commit 08f2c2808b8115a29cf57d59d3673c1430a7dbf4

@ -0,0 +1,15 @@
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index e1e60e18fd7c..d1fb4cdbcd43 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -132,7 +132,9 @@
// (by providing global non-reserved names) and the new API. As we move individual platforms
// towards the new way of defining the locale base API, this should disappear since each platform
// will define those directly.
-# include <__locale_dir/locale_base_api/ibm.h>
+# if defined(__MVS__)
+# include <__locale_dir/locale_base_api/ibm.h>
+# endif
# include <__locale_dir/locale_base_api/bsd_locale_fallbacks.h>

@ -124,7 +124,7 @@ function(add_testcase test)
list(APPEND runner -Wexceptions)
endif()
if(target MATCHES "wasip3")
list(APPEND runner -Wcomponent-model-async -Sp3)
list(APPEND runner -Wcomponent-model-async,component-model-threading -Sp3)
endif()
endif()

Loading…
Cancel
Save