diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 14d51b5b2..83c2c8b22 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -170,10 +170,16 @@ jobs: - uses: ./.github/actions/checkout - uses: ./.github/actions/install-deps - run: cargo install wasm-component-ld@0.5.21 - - run: sudo apt-get update -y && sudo apt-get install -y clang-20 lld-20 + - name: Install LLVM 22 + run: | + v=22 + rel=$(lsb_release -cs) + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/keyrings/llvm.asc + echo "deb [signed-by=/etc/apt/keyrings/llvm.asc] http://apt.llvm.org/$rel/ llvm-toolchain-$rel-$v main" | sudo tee /etc/apt/sources.list.d/llvm-$v.list + sudo apt-get update -y && sudo apt-get install -y clang-$v lld-$v - run: | cmake -G Ninja -B build -S . \ - -DCMAKE_C_COMPILER=/usr/lib/llvm-20/bin/clang \ + -DCMAKE_C_COMPILER=/usr/lib/llvm-22/bin/clang \ -DCMAKE_SYSTEM_NAME=WASI \ -DWASI_SDK_INCLUDE_TESTS=ON \ -DWASI_SDK_CPU_CFLAGS="" \ diff --git a/CppExceptions.md b/CppExceptions.md index 7ea1e2d8c..1ca79bf3c 100644 --- a/CppExceptions.md +++ b/CppExceptions.md @@ -1,25 +1,17 @@ # Support for C++ Exceptions -The released artifacts for wasi-sdk at this time do not support C++ exceptions. -LLVM and Clang, however, have support for C++ exceptions in WebAssembly and this -is intended to serve as documentation of the current state of affairs of using -C++ exceptions. It should be noted though that the current status of C++ -exceptions support is not intended to be the final state of support, and this is -all continuing to be iterated on over time. +> **Note**: this documentation does not cover wasi-sdk-31, the latest version +> of wasi-sdk at this time. -## Building wasi-sdk with exceptions +From wasi-sdk-33 and onwards the artifacts produced by this repository support +compiling C++ code both with and without exceptions. The sysroot for wasm +targets contains two copies of the C++ standard library and headers -- one with +exceptions enabled and one with exceptions disabled. These are automatically +selected based on compilation flags. This means that wasi-sdk-produced binaries +can avoid using wasm exceptions entirely by disabling C++ exceptions, or C++ +exceptions can be enabled in which case wasm exceptions will be used. -When building the sysroot with wasi-sdk you can pass `-DWASI_SDK_EXCEPTIONS=ON` -to enable support for C++ exceptions. For example: - -```shell script -$ cmake -G Ninja -B build/sysroot -S . \ - -DCMAKE_TOOLCHAIN_FILE=$path/to/wasi-sdk-p1.cmake \ - -DWASI_SDK_EXCEPTIONS=ON -``` - -The C++ standard library will be compiled with support for exceptions for the -desired targets and the resulting sysroot supports using exceptions. +Currently the default is for C++ exceptions to be disabled. ## Compiling code with C++ exceptions @@ -36,25 +28,43 @@ This can be specified for example with: ```shell script $ export CFLAGS="-fwasm-exceptions -mllvm -wasm-use-legacy-eh=false" -$ export LDFLAGS="-lunwind" +$ export LDFLAGS="-fwasm-exceptions -lunwind" ``` -## Limitations +Note that `-fwasm-exceptions` must be present when linking to select the +correct C++ standard library to link. + +## Building wasi-sdk with exceptions -Currently C++ exceptions support in wasi-sdk does not support shared libraries. -Fixing this will require resolving some miscellaneous build issues in this -repository itself. +When building the sysroot with wasi-sdk you can pass `-DWASI_SDK_EXCEPTIONS=ON` +to enable support for C++ exceptions. For example: -## Future Plans +```shell script +$ cmake -G Ninja -B build/sysroot -S . \ + -DCMAKE_TOOLCHAIN_FILE=$path/to/wasi-sdk-p1.cmake \ + -DWASI_SDK_EXCEPTIONS=ON +``` + +The C++ standard library will be compiled with support for exceptions for the +desired targets and the resulting sysroot supports using exceptions. Note that +enabling C++ exceptions requires LLVM 22 or later. + +C++ exceptions are disabled by default for local builds. With a future release +of LLVM 23 the dual-sysroot nature will be on-by-default. + +## Limitations -There are a few tracking issues with historical discussion about C++ exceptions -support in wasi-sdk such as [#334](https://github.com/WebAssembly/wasi-sdk/issues/334) -and [#565](https://github.com/WebAssembly/wasi-sdk/issues/565). The major -remaining items are: +There are a few known limitations/bugs/todos around exceptions support in +wasi-sdk at this time: -* Figure out support for shared libraries. -* Determine how to ship a sysroot that supports both with-and-without - exceptions. -* Figure out how to avoid the need for extra compiler flags when using - exceptions. -* Figure out if a new wasm target is warranted. +* Currently C++ exceptions support in wasi-sdk does not support shared + libraries. Fixing this will require resolving some miscellaneous build + issues in this repository itself as well as [resolving some upstream + issues](https://github.com/llvm/llvm-project/issues/188077). +* Currently `-fwasm-exceptions` is a required flag to enable C++ exceptions. + It's unclear whether `-fexceptions` should also be supported as a substitute. +* Currently LLVM defaults to using the legacy exception-handling proposal and + this will likely change in the future. Precompiled libraries for wasi-sdk are + all built with the standard exception-handling proposal. +* Currently `-lunwind` is required when linking, but this may become automatic + in the future. diff --git a/README.md b/README.md index 7d28f6957..7893c9639 100644 --- a/README.md +++ b/README.md @@ -209,8 +209,8 @@ disabled in a configure step before building with WASI SDK. ## Notable Limitations -* C++ exceptions are disabled by default. For more information see - [CppExceptions.md]. +* C++ exceptions are disabled by default and require extra configuration to get + working, see [CppExceptions.md]. * C `setjmp`/`longjmp` require some extra configuration to get working, see [SetjmpLongjmp.md]. * Most targets do not support spawning a thread. Experimental support for diff --git a/ci/build.sh b/ci/build.sh index 7525aa4d4..7c02fff8f 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -37,6 +37,7 @@ cmake -G Ninja -B $build_dir/sysroot -S . \ -DCMAKE_C_COMPILER_WORKS=ON \ -DCMAKE_CXX_COMPILER_WORKS=ON \ -DWASI_SDK_INCLUDE_TESTS=ON \ + -DWASI_SDK_EXCEPTIONS=DUAL \ "-DCMAKE_INSTALL_PREFIX=$build_dir/install" ninja -C $build_dir/sysroot install dist -v diff --git a/cmake/wasi-sdk-sysroot.cmake b/cmake/wasi-sdk-sysroot.cmake index 38ed42636..491180ee4 100644 --- a/cmake/wasi-sdk-sysroot.cmake +++ b/cmake/wasi-sdk-sysroot.cmake @@ -20,13 +20,28 @@ message(STATUS "Found executable for `ar`: ${CMAKE_AR}") find_program(MAKE make REQUIRED) +set(EXCEPTIONS_DEFAULT "OFF") +if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 23.0.0) + set(EXCEPTIONS_DEFAULT "DUAL") +endif() + option(WASI_SDK_DEBUG_PREFIX_MAP "Pass `-fdebug-prefix-map` for built artifacts" ON) option(WASI_SDK_INCLUDE_TESTS "Whether or not to build tests by default" OFF) option(WASI_SDK_INSTALL_TO_CLANG_RESOURCE_DIR "Whether or not to modify the compiler's resource directory" OFF) option(WASI_SDK_LTO "Whether or not to build LTO assets" ON) -option(WASI_SDK_EXCEPTIONS "Whether or not C++ exceptions are enabled" OFF) +set(WASI_SDK_EXCEPTIONS "${EXCEPTIONS_DEFAULT}" CACHE STRING "Whether or not C++ exceptions are enabled") set(WASI_SDK_CPU_CFLAGS "-mcpu=lime1" CACHE STRING "CFLAGS to specify wasm features to enable") +if ((WASI_SDK_EXCEPTIONS STREQUAL "DUAL") OR (WASI_SDK_EXCEPTIONS STREQUAL "ON")) + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 22.0.0) + message(FATAL_ERROR "enabling C++ exceptions requires Clang 22 or later") + endif() +elseif(WASI_SDK_EXCEPTIONS STREQUAL "OFF") + # No extra validation needed +else() + message(FATAL_ERROR "unknown WASI_SDK_EXCEPTIONS value ${WASI_SDK_EXCEPTIONS}, expected one of: OFF, ON, DUAL") +endif() + set(wasi_tmp_install ${CMAKE_CURRENT_BINARY_DIR}/install) set(wasi_sysroot ${wasi_tmp_install}/share/wasi-sysroot) set(wasi_resource_dir ${wasi_tmp_install}/wasi-resource-dir) @@ -225,7 +240,7 @@ execute_process( OUTPUT_VARIABLE llvm_version OUTPUT_STRIP_TRAILING_WHITESPACE) -function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_suffix) +function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_suffix exceptions) if(${target} MATCHES threads) set(pic OFF) set(target_flags -pthread) @@ -251,7 +266,9 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ --sysroot ${wasi_sysroot} -resource-dir ${wasi_resource_dir}) - if (WASI_SDK_EXCEPTIONS) + set(exnsuffix "") + + if (exceptions) # TODO: lots of builds fail with shared libraries and `-fPIC`. Looks like # things are maybe changing in llvm/llvm-project#159143 but otherwise I'm at # least not really sure what the state of shared libraries and exceptions @@ -260,6 +277,13 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ set(pic OFF) set(runtimes "libunwind;${runtimes}") list(APPEND extra_flags -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false) + if (WASI_SDK_EXCEPTIONS STREQUAL "DUAL") + set(exnsuffix "/eh") + endif() + else() + if (WASI_SDK_EXCEPTIONS STREQUAL "DUAL") + set(exnsuffix "/noeh") + endif() endif() # The `wasm32-wasi` target is deprecated in clang, so ignore the deprecation @@ -279,7 +303,7 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ ${default_cmake_args} # Ensure headers are installed in a target-specific path instead of a # target-generic path. - -DCMAKE_INSTALL_INCLUDEDIR=${wasi_sysroot}/include/${target} + -DCMAKE_INSTALL_INCLUDEDIR=${wasi_sysroot}/include/${target}${exnsuffix} -DCMAKE_STAGING_PREFIX=${wasi_sysroot} -DCMAKE_POSITION_INDEPENDENT_CODE=${pic} -DLIBCXX_ENABLE_THREADS:BOOL=ON @@ -288,20 +312,20 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ -DLIBCXX_HAS_WIN32_THREAD_API:BOOL=OFF -DLLVM_COMPILER_CHECKED=ON -DLIBCXX_ENABLE_SHARED:BOOL=${pic} - -DLIBCXX_ENABLE_EXCEPTIONS:BOOL=${WASI_SDK_EXCEPTIONS} + -DLIBCXX_ENABLE_EXCEPTIONS:BOOL=${exceptions} -DLIBCXX_ENABLE_FILESYSTEM:BOOL=ON -DLIBCXX_ENABLE_ABI_LINKER_SCRIPT:BOOL=OFF -DLIBCXX_CXX_ABI=libcxxabi -DLIBCXX_HAS_MUSL_LIBC:BOOL=OFF -DLIBCXX_ABI_VERSION=2 - -DLIBCXXABI_ENABLE_EXCEPTIONS:BOOL=${WASI_SDK_EXCEPTIONS} + -DLIBCXXABI_ENABLE_EXCEPTIONS:BOOL=${exceptions} -DLIBCXXABI_ENABLE_SHARED:BOOL=${pic} -DLIBCXXABI_SILENT_TERMINATE:BOOL=ON -DLIBCXXABI_ENABLE_THREADS:BOOL=ON -DLIBCXXABI_HAS_PTHREAD_API:BOOL=ON -DLIBCXXABI_HAS_EXTERNAL_THREAD_API:BOOL=OFF -DLIBCXXABI_HAS_WIN32_THREAD_API:BOOL=OFF - -DLIBCXXABI_USE_LLVM_UNWINDER:BOOL=${WASI_SDK_EXCEPTIONS} + -DLIBCXXABI_USE_LLVM_UNWINDER:BOOL=${exceptions} -DLIBUNWIND_ENABLE_SHARED:BOOL=${pic} -DLIBUNWIND_ENABLE_THREADS:BOOL=ON -DLIBUNWIND_USE_COMPILER_RT:BOOL=ON @@ -310,9 +334,9 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ -DCMAKE_C_FLAGS=${extra_cflags} -DCMAKE_ASM_FLAGS=${extra_cflags} -DCMAKE_CXX_FLAGS=${extra_cxxflags} - -DLIBCXX_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix} - -DLIBCXXABI_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix} - -DLIBUNWIND_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix} + -DLIBCXX_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix} + -DLIBCXXABI_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix} + -DLIBUNWIND_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix} -DLIBCXX_INCLUDE_TESTS=OFF -DLIBCXX_INCLUDE_BENCHMARKS=OFF @@ -327,21 +351,45 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ USES_TERMINAL_CONFIGURE ON USES_TERMINAL_BUILD ON USES_TERMINAL_INSTALL ON + USES_TERMINAL_PATCH ON PATCH_COMMAND ${CMAKE_COMMAND} -E chdir .. bash -c "git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-168449.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-168449.patch -R --check" COMMAND ${CMAKE_COMMAND} -E chdir .. bash -c "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" ) + add_dependencies(libcxx-${target} libcxx-${target}${target_suffix}-build) endfunction() -function(define_libcxx target) - define_libcxx_sub(${target} "" "" "") - if(WASI_SDK_LTO) +function(define_libcxx_and_lto target target_suffix exceptions) + define_libcxx_sub(${target} "${target_suffix}" "" "" ${exceptions}) + if (WASI_SDK_LTO) # Note: clang knows this /llvm-lto/${llvm_version} convention. # https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/clang/lib/Driver/ToolChains/WebAssembly.cpp#L204-L210 - define_libcxx_sub(${target} "-lto" "-flto=full" "/llvm-lto/${llvm_version}") + define_libcxx_sub(${target} ${target_suffix}-lto "-flto=full" "/llvm-lto/${llvm_version}" ${exceptions}) + endif() +endfunction() + +function(define_libcxx target) + add_custom_target(libcxx-${target}) + + # For dual-mode exceptions-and-not there are two versions of libcxx which are + # compiled and placed into the sysroot. They're named slightly differently to + # have unique CMake rules. + # + # Otherwise there's only one build of libcxx and it's either got exceptions or + # it doesn't depending on configuration. + if (WASI_SDK_EXCEPTIONS STREQUAL "DUAL") + define_libcxx_and_lto(${target} "" OFF) + define_libcxx_and_lto(${target} "-exn" ON) + elseif(WASI_SDK_EXCEPTIONS STREQUAL "ON") + define_libcxx_and_lto(${target} "" ON) + else() + define_libcxx_and_lto(${target} "" OFF) endif() # As of this writing, `clang++` will ignore the target-specific include dirs @@ -349,8 +397,7 @@ function(define_libcxx target) add_custom_target(libcxx-${target}-extra-dir COMMAND ${CMAKE_COMMAND} -E make_directory ${wasi_sysroot}/include/c++/v1 COMMENT "creating libcxx-specific header file folder") - add_custom_target(libcxx-${target} - DEPENDS libcxx-${target}-build $<$:libcxx-${target}-lto-build> libcxx-${target}-extra-dir) + add_dependencies(libcxx-${target} libcxx-${target}-extra-dir) endfunction() foreach(target IN LISTS WASI_SDK_TARGETS) diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake index 5468d930a..664746a70 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -247,6 +247,9 @@ ExternalProject_Add(llvm-build USES_TERMINAL_CONFIGURE ON USES_TERMINAL_BUILD ON 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" ) add_custom_target(build ALL DEPENDS llvm-build) diff --git a/src/llvm-pr-168449.patch b/src/llvm-pr-168449.patch index a0da1d8d4..f5b873844 100644 --- a/src/llvm-pr-168449.patch +++ b/src/llvm-pr-168449.patch @@ -1,22 +1,17 @@ -diff --git a/libunwind/src/assembly.h b/libunwind/src/assembly.h -index f8e83e138eff..c5097d25b0c6 100644 ---- a/libunwind/src/assembly.h -+++ b/libunwind/src/assembly.h -@@ -249,6 +249,9 @@ aliasname: \ - #define WEAK_ALIAS(name, aliasname) - #define NO_EXEC_STACK_DIRECTIVE - -+#elif defined(__wasm__) -+#define NO_EXEC_STACK_DIRECTIVE -+ - // clang-format on - #else - +From 852c8a2ebc0fdb1e781591e3e6e08d3a539bcfc3 Mon Sep 17 00:00:00 2001 +From: Yerzhan Zhamashev +Date: Wed, 21 Jan 2026 16:50:41 +0200 +Subject: [PATCH] libunwind: exclude __declspec from wasm build + +--- + libunwind/src/config.h | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + diff --git a/libunwind/src/config.h b/libunwind/src/config.h -index deb5a4d4d73d..23c9f012cbcf 100644 +index f017403fa2234..6014a37e27212 100644 --- a/libunwind/src/config.h +++ b/libunwind/src/config.h -@@ -66,7 +66,8 @@ +@@ -75,7 +75,8 @@ #define _LIBUNWIND_EXPORT #define _LIBUNWIND_HIDDEN #else diff --git a/src/llvm-pr-185770.patch b/src/llvm-pr-185770.patch new file mode 100644 index 000000000..2d47923ef --- /dev/null +++ b/src/llvm-pr-185770.patch @@ -0,0 +1,105 @@ +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 new file mode 100644 index 000000000..f1fdb6a57 --- /dev/null +++ b/src/llvm-pr-185775.patch @@ -0,0 +1,152 @@ +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/tests/CMakeLists.txt b/tests/CMakeLists.txt index c573aea65..9bb5fc2eb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -67,9 +67,9 @@ function(add_testcase runwasi test) # Apply language-specific options and dependencies. if(test MATCHES "cc$") - if(WASI_SDK_EXCEPTIONS) + if(NOT (WASI_SDK_EXCEPTIONS STREQUAL "OFF")) target_compile_options(${target_name} PRIVATE -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false) - target_link_options(${target_name} PRIVATE -lunwind) + target_link_options(${target_name} PRIVATE -fwasm-exceptions -lunwind) else() target_compile_options(${target_name} PRIVATE -fno-exceptions) endif()