mirror of https://github.com/WebAssembly/wasi-sdk
Build a sysroot that supports C++ exceptions by default (#606)
This commit collects together some LLVM PRs, some changes in the build configuration here, and some thoughts from #565 and related issues. Specifically the changes here are: * The patch for llvm/llvm-project#168449 is updated to its upstream (unlanded) form. * Patches for the (landed) llvm/llvm-project#185770 and llvm/llvm-project#185775 are added. * The `WASI_SDK_EXCEPTIONS` configuration is now either `ON`, `OFF`, or `DUAL`. The default depends on the version of Clang in use, where 23.0.0+ (which isn't released officially yet) will be `DUAL` and otherwise it's `OFF`. CI for our custom-built patched toolchain defaults to `DUAL`. * In `DUAL` mode libcxx is built twice into two different directories, once with exceptions and once without. This is supported by LLVM patches and means that Clang will select the right set of libraries based on compiler flags. The end result here is that the produced toolchain from this repository, by default, supports C++ exceptions. Additionally if exceptions-related flags are not passed then the final binary will not use C++ exceptions nor require the wasm exception-handling proposal. There's still follow-up work from #565, such as: * Subjectively it feels wordy to pass `-fwasm-exceptions` vs `-fexceptions`. * Personally I think `-mllvm -wasm-use-legacy-eh=false` should become the default upstream. * Subjectively I don't think that `-lunwind` should be necessary and it should be injected automatically with `-fwasm-exceptions` (or `-fexceptions`). * Shared libraries for exceptions remain disabled due to build errors I do not personally know how to resolve. I'll file follow-up issues for these once this has landed since they're more minor compared to the main body of "anything works". Closes #334 Closes #565 --------- Co-authored-by: Joel Dice <joel.dice@akamai.com>pull/624/head
parent
42b7263534
commit
003cf14969
@ -0,0 +1,105 @@
|
||||
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",
|
||||
]
|
||||
}
|
||||
|
||||
@ -0,0 +1,152 @@
|
||||
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"
|
||||
Loading…
Reference in new issue