From 526321ead161ca4402c8afc8eaa5916bd6870d98 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 11 Jun 2026 13:10:15 -0700 Subject: [PATCH] wip --- CMakeLists.txt | 2 +- cmake/wasi-sdk-sysroot.cmake | 4 +- cmake/wasi-sdk-toolchain.cmake | 4 +- src/llvm-pr-185770.patch | 105 ----- src/llvm-pr-185775.patch | 152 ------- src/llvm-pr-186054.patch | 20 +- src/llvm-pr-200855.patch | 638 +++++++++++++++++++++++++++++ src/llvm-project | 2 +- src/llvm-undo-part-of-194317.patch | 15 + tests/CMakeLists.txt | 2 +- 10 files changed, 672 insertions(+), 272 deletions(-) delete mode 100644 src/llvm-pr-185770.patch delete mode 100644 src/llvm-pr-185775.patch create mode 100644 src/llvm-pr-200855.patch create mode 100644 src/llvm-undo-part-of-194317.patch diff --git a/CMakeLists.txt b/CMakeLists.txt index a0d3fca..f3f78cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/cmake/wasi-sdk-sysroot.cmake b/cmake/wasi-sdk-sysroot.cmake index 613aaa7..2706ea1 100644 --- a/cmake/wasi-sdk-sysroot.cmake +++ b/cmake/wasi-sdk-sysroot.cmake @@ -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() diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake index 1d023c8..27acc39 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -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() diff --git a/src/llvm-pr-185770.patch b/src/llvm-pr-185770.patch deleted file mode 100644 index 2d47923..0000000 --- a/src/llvm-pr-185770.patch +++ /dev/null @@ -1,105 +0,0 @@ -From d702761d9135ebbb83590d4dd1323be433701ebd Mon Sep 17 00:00:00 2001 -From: Alex Crichton -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", - ] - } - diff --git a/src/llvm-pr-185775.patch b/src/llvm-pr-185775.patch deleted file mode 100644 index f1fdb6a..0000000 --- a/src/llvm-pr-185775.patch +++ /dev/null @@ -1,152 +0,0 @@ -From 0e36e8f304cd5f3997916f5d85201bb17e340337 Mon Sep 17 00:00:00 2001 -From: Alex Crichton -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" diff --git a/src/llvm-pr-186054.patch b/src/llvm-pr-186054.patch index c7756d7..3347c0a 100644 --- a/src/llvm-pr-186054.patch +++ b/src/llvm-pr-186054.patch @@ -1,8 +1,8 @@ -From f71fdfcbd6fcc7b521c74b5856ebeacdd6cf55d9 Mon Sep 17 00:00:00 2001 +From 46301f073b8cc85ad0d2f1dcdda923209bff173b Mon Sep 17 00:00:00 2001 From: Catherine 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 diff --git a/src/llvm-pr-200855.patch b/src/llvm-pr-200855.patch new file mode 100644 index 0000000..c35c0e5 --- /dev/null +++ b/src/llvm-pr-200855.patch @@ -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> memoryImport; + std::optional memoryExport; +@@ -134,6 +135,8 @@ struct Config { + std::optional> features; + std::optional> extraFeatures; + llvm::SmallVector 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(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(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(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(".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 diff --git a/src/llvm-project b/src/llvm-project index 4434dab..08f2c28 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 4434dabb69916856b824f68a64b029c67175e532 +Subproject commit 08f2c2808b8115a29cf57d59d3673c1430a7dbf4 diff --git a/src/llvm-undo-part-of-194317.patch b/src/llvm-undo-part-of-194317.patch new file mode 100644 index 0000000..a5c74c0 --- /dev/null +++ b/src/llvm-undo-part-of-194317.patch @@ -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> + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3b5972d..b911343 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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()