Files
wasm2go/testdata/eh_rethrow_outer.wat
ae7e9bd0e5 Support C++-shaped exception handling: multi-clause catch, delegate targets, rethrow depths (#39)
The legacy-EH support added for clang's SjLj lowering only covered the
single-tag patterns SjLj emits. Clang's -fwasm-exceptions C++ output
(e.g. llama.cpp) additionally needs:

- try with multiple catch clauses (catch $a / catch $b / catch_all),
  dispatched by tag in clause order
- catch_all-only trys and rethrow inside catch_all (the cleanup-during-
  unwind shape), including `rethrow l` with l > 0 naming an OUTER
  catch's exception
- `delegate l` forwarding to a specific outer try, skipping every try
  nested in between

Lowering now resolves delegate and rethrow label depths to their target
try regions (ctrl frames track handler state), records each region's
protected-body block set — the CFG alone cannot delimit it, because a
br that exits a try early makes its continuation reachable from inside
— and handles `delegate` closing a try inside a dead region.

The recover-trampoline emitter drops its single-non-catch_all-handler
restriction: every region gets an active flag and its own caught-
exception variable, the dispatch is an innermost-first if-chain with
per-clause tag tests, a delegate arm clears the flags of the regions it
skips, and any edge leaving a protected body clears its flag (a br out
of a try previously left the flag set, so a later unrelated throw could
misroute into the exited try's handler).

The structured emitter registers per-region exception variables so
outer-targeting rethrows resolve correctly, bails to the trampoline
when a delegate's target is not what a plain re-panic would reach or
when a body block escapes the region (its continuation would otherwise
be duplicated inside the protective closure), and closes a try no path
falls out of with trap+spin so Go's termination analysis accepts the
function.

SSA verification computes dominators over the CFG augmented with
virtual entry-to-handler edges, so handler landing pads (which have no
CFG predecessors) may use values defined before their try.

gcasm routes functions that call their own EH closures to the pure
fallback: the outer function of a structured try has no panic/recover
runtime call of its own — the defer/recover lives inside the closure it
calls.

Verified this session: `go test -race -timeout 20m ./...` exit 0 (all
packages ok). New fixtures cover multi-clause dispatch on both emit
paths, catch_all+rethrow, a delegate skipping an intermediate handler,
an outer-targeting rethrow, and the try-exit flag drop. A full
llama.cpp module (wasm32-wasip1, -fwasm-exceptions, SIMD disabled)
transpiles and generates correct text end-to-end. The
googlesql/wasmify five-step e2e chain was NOT run this session.

Co-authored-by: Claude Fable 5 <[email protected]>
2026-08-08 10:59:07 +09:00

22 lines
551 B
WebAssembly Text Format

;; `rethrow l` with a non-zero depth re-raises an OUTER catch's exception.
;; The inner catch_all holds $a(99) but rethrow 1 names the outer catch $a,
;; whose exception is $a(42); the top-level try must observe 42, not 99.
(module
(tag $a (param i32))
(func (export "run") (result i32)
try (result i32)
try (result i32)
i32.const 42
throw $a
catch $a
drop
try (result i32)
i32.const 99
throw $a
catch_all
rethrow 1
end
end
catch $a
end))