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
|
|
@ -830,7 +830,8 @@ class Wasi {
|
|||
// variant in variants.json lists the probe names that must all pass for that
|
||||
// variant to be served; an unknown name is treated as unsupported (we fall
|
||||
// through to a less demanding variant, ultimately the baseline). Keep the keys
|
||||
// in sync with the `probes` a WasmVariant declares in Crafter.Build.
|
||||
// in sync with the `probes` a WasmVariant declares in Crafter.Build, and with
|
||||
// WasmBaselineProbes, which every entry carries.
|
||||
const WASM_PROBES = {
|
||||
// relaxed SIMD: i8x16.relaxed_swizzle (Chrome 114+, Firefox 120+; Safari
|
||||
// still flag-gated as of mid-2026).
|
||||
|
|
@ -842,8 +843,11 @@ const WASM_PROBES = {
|
|||
"tail-call": () => WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,2,1,0,10,6,1,4,0,18,0,11])),
|
||||
// bulk memory ops (memory.init).
|
||||
"bulk-memory": () => WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,2,1,0,5,3,1,0,1,10,14,1,12,0,65,0,65,0,65,0,252,10,0,0,11])),
|
||||
// legacy exception handling (try/catch).
|
||||
"exception-handling": () => WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,2,1,0,10,8,1,6,0,6,64,25,11,11])),
|
||||
// exception handling in its standardized form (try_table, exnref; Chrome
|
||||
// 137, Firefox 131, Safari 18.4), which every crafter-build wasm uses for
|
||||
// C++ exceptions. Probing the legacy try/catch encoding instead says
|
||||
// nothing about this one: V8 accepts it with exnref switched off.
|
||||
"exception-handling": () => WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,2,1,0,10,8,1,6,0,31,64,0,11,11])),
|
||||
// threads/atomics — also requires SharedArrayBuffer (cross-origin isolated).
|
||||
"threads": () => {
|
||||
try {
|
||||
|
|
@ -866,6 +870,9 @@ function variantSupported(probes) {
|
|||
|
||||
// Pick the wasm URL: prefer the variants.json manifest (first entry whose
|
||||
// probes all pass), else fall back to the single baked window.CRAFTER_WASM_URL.
|
||||
// When the manifest has no entry this engine can run, not even the baseline,
|
||||
// say which features it lacks: loading the baseline anyway would only fail
|
||||
// with a CompileError about an unknown opcode.
|
||||
async function pickWasmUrl() {
|
||||
let variants = null;
|
||||
try {
|
||||
|
|
@ -881,6 +888,9 @@ async function pickWasmUrl() {
|
|||
return v.url;
|
||||
}
|
||||
}
|
||||
const baseline = variants[variants.length - 1];
|
||||
const missing = (baseline.probes || []).filter((name) => !variantSupported([name]));
|
||||
throw new Error(`[wasi] ${baseline.url} needs WebAssembly ${missing.join(", ")}, which this browser does not support; a current Chrome, Firefox or Safari does`);
|
||||
}
|
||||
return window.CRAFTER_WASM_URL;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue