feat(wasm): compile C++ with WebAssembly exception handling
wasm builds were compiled with -fno-exceptions. A throw did not compile, libc++'s own throws (vector::at, std::format, filesystem) aborted the program, and code that relies on exceptions could not be built for the browser at all. Every wasm unit and the std module now get -fwasm-exceptions, and the link adds -lunwind, which code built that way calls into and the driver does not add. The flag also selects the libc++ under the sysroot's lib/<triple>/eh, which wasi-sdk 33 and later ship next to the noeh one. Those libraries use the standardized encoding (try_table, exnref) while LLVM 23 still emits the legacy one by default, which wasmtime refuses to load and which would put both in one binary, so the flags ask for the standard form with -wasm-use-legacy-eh=false. That makes the setjmp lowering loadable under wasmtime too. The flags live in one constant, WasmBaselineFlags, which the compile command and the std module both take through WasmCompileFlags: a BMI is refused by a unit whose exception setting differs from its own. VariantId and StdPcmDir hash it, so the first build after the update starts fresh instead of importing PCMs built without exceptions, and two releases sharing the Flatpak's cache don't take turns rebuilding one std PCM. A sysroot without the exception-enabled libraries stops the build with a line saying so, rather than at the link with "unable to find library -lunwind". The result needs an engine with exnref: Chrome 137, Firefox 131, Safari 18.4, the bundled wasmtime. The runtime's exception-handling probe tested the legacy encoding; it now tests try_table, every variants.json entry carries it, and a browser without it is told which feature it lacks instead of failing with a CompileError. There is no switch back to -fno-exceptions: engines without exnref are old enough not to keep a second build for them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
eefd1df3eb
commit
63c4040000
4 changed files with 124 additions and 37 deletions
|
|
@ -804,6 +804,17 @@ std::string Crafter::BuildStdPcm(const Configuration& config, fs::path stdPcm) {
|
|||
std::string stdCppm;
|
||||
if (isWasm) {
|
||||
stdCppm = std::format("{}/share/libc++/v1/std.cppm", config.sysroot);
|
||||
// wasm builds link the sysroot's exception-enabled libc++, which
|
||||
// -fwasm-exceptions in WasmBaselineFlags selects, and the
|
||||
// libunwind Build adds. A sysroot without them (wasi-sdk before
|
||||
// 33, or one built without its exceptions option) otherwise stops
|
||||
// at the first link with "unable to find library -lunwind", which
|
||||
// says nothing about why it was asked for. The driver looks in eh/
|
||||
// first and then in the triple's own directory.
|
||||
const fs::path libDir = fs::path(config.sysroot) / "lib" / NormalizeTarget(config.target);
|
||||
if (!fs::exists(libDir / "eh" / "libunwind.a") && !fs::exists(libDir / "libunwind.a")) {
|
||||
return std::format("the WASI sysroot at {} has no C++ exception support (no {}): wasm builds use WebAssembly exception handling and need the exception-enabled libc++ and libunwind that wasi-sdk 33 and later ship. The Flatpak bundles such a sysroot; CRAFTER_BUILD_WASI_SYSROOT selects another", config.sysroot, (libDir / "eh" / "libunwind.a").string());
|
||||
}
|
||||
} else if (config.sysroot.empty()) {
|
||||
stdCppm = GetToolchain().stdCppm.string();
|
||||
} else {
|
||||
|
|
@ -818,19 +829,12 @@ std::string Crafter::BuildStdPcm(const Configuration& config, fs::path stdPcm) {
|
|||
std::string sysrootFlag = config.sysroot.empty()
|
||||
? std::string()
|
||||
: std::format(" --sysroot={}", config.sysroot);
|
||||
// wasm32 rejects -march. wasi-libc++ headers require these baselines
|
||||
// to compile: setjmp.h needs -mllvm -wasm-enable-sjlj (lowered to wasm
|
||||
// EH); signal.h requires the emulation define; and EH itself isn't
|
||||
// wired up so -fno-exceptions stays.
|
||||
// wasm32 rejects -march, and the std module has to be compiled the way
|
||||
// the units importing it are, a variant pass's flags (e.g.
|
||||
// -mrelaxed-simd) included, or the BMI is not feature-compatible.
|
||||
std::string archFlags = isWasm
|
||||
? std::string(" -fno-exceptions -msimd128 -fno-c++-static-destructors -mllvm -wasm-enable-sjlj -D_WASI_EMULATED_SIGNAL")
|
||||
? WasmCompileFlags(config)
|
||||
: std::format(" -march={} -mtune={}", config.march, config.mtune);
|
||||
// Match the variant codegen flags the consuming TUs are built with
|
||||
// (e.g. -mrelaxed-simd) so the std BMI is feature-compatible. wasm only;
|
||||
// wasmVariantFlags is empty on every other path.
|
||||
if (isWasm) {
|
||||
for (const std::string& f : config.wasmVariantFlags) archFlags += std::format(" {}", f);
|
||||
}
|
||||
// non-wasm cross builds: the driver ranks the host toolchain's own
|
||||
// libc++ headers (<install>/../include/c++/v1) above --sysroot, so
|
||||
// the PCM would mix host libc++ with the foreign libc — force the
|
||||
|
|
|
|||
Loading…
Reference in a new issue