Skip to content

test/wasm: compare EH paths in Node and Chrome - #2654

Merged
cpunion merged 8 commits into
xgo-dev:mainfrom
cpunion:codex/wasm-eh-matrix
Sep 27, 2026
Merged

cpunion merged 8 commits into
xgo-dev:mainfrom
cpunion:codex/wasm-eh-matrix

Conversation

@cpunion

@cpunion cpunion commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

LLGo needs an EH encoding decision that preserves C++ and Go behavior through the Emscripten/Asyncify browser build. This PR compares legacy EH, direct standard exnref, and Binaryen --translate-to-exnref with the same C++ throw/catch and setjmp/longjmp fixture at O0 and O2. Every variant is validated, DWARF-verified, and executed in Node and Chrome. It also checks Go panic/recover and a Go→C++ wrapper that catches C++ exceptions internally, returns a C ABI status, and translates that status to a Go panic.

The supported boundary remains the current Emscripten/LLGo JS EH encoding with C++ catch inside the wrapper; it is distinct from the three Wasm EH encodings compared here. The standalone comparison does not authorize a C++ exception to unwind through Go or switch the full LLGo link to exnref. The result and revisit criteria are recorded in dev/wasm-eh-comparison.md.

This branch is stacked on #2652 so the new wasm-runtime CI step selects the published llgo-v132.3 Binaryen release through EM_BINARYEN_ROOT and WASMOPT. Once #2652 lands, its commits will disappear from this PR's diff.

Local validation before the pin update used Emscripten 6.0.8, Binaryen llgo-v132.2, Node 26.8.1, Chrome 153.0.8010.53, wasm-tools 1.258.0, and LLVM 22.1.8: all six C++ variants and both Go/C++ wrappers passed Node and Chrome at O0/O2; Go panic/recover passed; actionlint, Python compilation, and git diff --check passed.

The current head carries the published llgo-v132.3 pin. The comparison requires Emscripten 6.0.8's JS glue and Node >=24.15.0; its preflight rejects Node 22.16.0 with a clear diagnostic before building. The DWARF verifier is likewise scoped to this Emscripten version and LLGo-patched Binaryen. The complete Node 26.8.1 and Chrome EH comparison passed again with that release at O0/O2, including Go panic/recover and a Go/C++ wrapper that propagates the actual C ABI status into the Go panic; full PR CI is re-running.

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: WASM exception-handling comparison dev tool

Nicely scoped, self-contained dev tool. The Python is clean and idiomatic, the temp-dir handling and subprocess centralization are good, and dev/wasm-eh-comparison.md is commendably honest — it explicitly labels the numbers a smoke comparison and enumerates what is not yet tested (Go/C++/JS boundary, goroutine unwinding, browser compat).

No security concerns: all subprocess.run calls use argument lists with shell=False, temp handling is safe, and tool resolution is developer-controlled. No production performance risk — the one available speedup (parallelizing the 4 independent em++ compiles) is optional for a manual dev tool.

Findings below are all minor/low; the doc/behavior mismatch on env vars is the most worthwhile to address.

Comment thread dev/wasm-eh-comparison.md Outdated
Comment thread dev/compare_wasm_eh.py Outdated
Comment thread dev/compare_wasm_eh.py
Comment thread dev/testdata/wasm-eh/exception.cpp
@codecov

codecov Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

LLGo baseline benchmarks

77ca3f785cef | workflow run | long-term charts

Program measurements

Platform Workload File size vs base Text size vs base Build vs base Run vs base
Linux cprintf 7160 B 0 B / +0.0% 387 B 0 B / +0.0% 595.444 ms +51.16 ms / +9.4% (worse) 1.361 ms +16.94 us / +1.3% (worse)
Linux cprintf-lto 6912 B 0 B / +0.0% 368 B 0 B / +0.0% 616.715 ms +75.03 ms / +13.9% (worse) 1.382 ms +17.14 us / +1.3% (worse)
Linux fmtprintf 1637960 B -8 B / -0.0004884% (better) 484730 B 0 B / +0.0% 4.903 s +414.9 ms / +9.2% (worse) 3.554 ms +222.7 us / +6.7% (worse)
Linux fmtprintf-lto 1478416 B 0 B / +0.0% 422446 B 0 B / +0.0% 12.210 s -113.3 ms / -0.9% (better) 3.205 ms -351.7 us / -9.9% (better)
Linux println 61344 B 0 B / +0.0% 14171 B 0 B / +0.0% 575.783 ms +29.39 ms / +5.4% (worse) 1.805 ms +32.59 us / +1.8% (worse)
Linux println-lto 54152 B 0 B / +0.0% 12073 B 0 B / +0.0% 806.564 ms +12.64 ms / +1.6% (worse) 1.851 ms +86.72 us / +4.9% (worse)
macOS cprintf 68064 B 0 B / +0.0% 4429 B 0 B / +0.0% 626.791 ms -34.82 ms / -5.3% (better) 3.431 ms +479 us / +16.2% (worse)
macOS cprintf-lto 84288 B 0 B / +0.0% 193 B 0 B / +0.0% 802.061 ms -53.06 ms / -6.2% (better) 4.875 ms +1.427 ms / +41.4% (worse)
macOS fmtprintf 1483856 B 0 B / +0.0% 860140 B 0 B / +0.0% 2.944 s -1.465 s / -33.2% (better) 4.188 ms -3.623 ms / -46.4% (better)
macOS fmtprintf-lto 1175808 B 0 B / +0.0% 832752 B 0 B / +0.0% 6.888 s -2.918 s / -29.8% (better) 14.007 ms +7.909 ms / +129.7% (worse)
macOS println 114512 B 0 B / +0.0% 34330 B 0 B / +0.0% 787.795 ms +95.91 ms / +13.9% (worse) 6.493 ms +852.5 us / +15.1% (worse)
macOS println-lto 118720 B 0 B / +0.0% 32308 B 0 B / +0.0% 1.058 s +208.6 ms / +24.5% (worse) 4.298 ms +432.9 us / +11.2% (worse)
Windows MinGW cprintf 19456 B 0 B / +0.0% 4550 B 0 B / +0.0% 850.488 ms -28.34 ms / -3.2% (better) 2.764 ms -5.5 us / -0.2% (better)
Windows MinGW cprintf-lto 17920 B 0 B / +0.0% 4486 B 0 B / +0.0% 900.206 ms -6.074 ms / -0.7% (better) 2.795 ms +60.3 us / +2.2% (worse)
Windows MinGW fmtprintf 1910784 B 0 B / +0.0% 586502 B 0 B / +0.0% 3.198 s -154.5 ms / -4.6% (better) 6.120 ms -193.9 us / -3.1% (better)
Windows MinGW fmtprintf-lto 1934848 B 0 B / +0.0% 535206 B 0 B / +0.0% 7.766 s -259.7 ms / -3.2% (better) 6.207 ms -193.5 us / -3.0% (better)
Windows MinGW println 70656 B 0 B / +0.0% 23062 B 0 B / +0.0% 870.640 ms -10.33 ms / -1.2% (better) 5.058 ms -3.4 us / -0.1% (better)
Windows MinGW println-lto 65024 B 0 B / +0.0% 20358 B 0 B / +0.0% 1.020 s -24.22 ms / -2.3% (better) 6.626 ms +1.56 ms / +30.8% (worse)
Windows MinGW 386 cprintf 43008 B 0 B / +0.0% 5326 B 0 B / +0.0% 1.123 s -10.84 ms / -1.0% (better) 5.096 ms +87.6 us / +1.7% (worse)
Windows MinGW 386 cprintf-lto 20992 B 0 B / +0.0% 5094 B 0 B / +0.0% 1.129 s -176.7 ms / -13.5% (better) 5.069 ms -845.5 us / -14.3% (better)
Windows MinGW 386 fmtprintf 1876480 B 0 B / +0.0% 462574 B 0 B / +0.0% 4.191 s +79.56 ms / +1.9% (worse) 10.304 ms +322.1 us / +3.2% (worse)
Windows MinGW 386 fmtprintf-lto 2152448 B 0 B / +0.0% 440194 B 0 B / +0.0% 9.668 s +45.03 ms / +0.5% (worse) 10.355 ms -966.2 us / -8.5% (better)
Windows MinGW 386 println 91136 B 0 B / +0.0% 19814 B 0 B / +0.0% 1.128 s -211.3 ms / -15.8% (better) 8.742 ms -64.1 us / -0.7% (better)
Windows MinGW 386 println-lto 69120 B 0 B / +0.0% 17726 B 0 B / +0.0% 1.331 s -82.05 ms / -5.8% (better) 8.363 ms -427.1 us / -4.9% (better)
Windows MinGW ARM64 cprintf 18944 B 0 B / +0.0% 4408 B 0 B / +0.0% 1.303 s -10.59 ms / -0.8% (better) 6.187 ms +104.9 us / +1.7% (worse)
Windows MinGW ARM64 cprintf-lto 17920 B 0 B / +0.0% 4340 B 0 B / +0.0% 1.340 s +14.64 ms / +1.1% (worse) 6.056 ms -328.7 us / -5.1% (better)
Windows MinGW ARM64 fmtprintf 1798656 B 0 B / +0.0% 499196 B 0 B / +0.0% 4.063 s +446.5 us / +0.01099% (worse) 12.146 ms -105.4 us / -0.9% (better)
Windows MinGW ARM64 fmtprintf-lto 1857536 B 0 B / +0.0% 465268 B 0 B / +0.0% 9.307 s +53.58 ms / +0.6% (worse) 12.372 ms +124.4 us / +1.0% (worse)
Windows MinGW ARM64 println 67072 B 0 B / +0.0% 21816 B 0 B / +0.0% 1.317 s -20.23 ms / -1.5% (better) 10.691 ms -155.4 us / -1.4% (better)
Windows MinGW ARM64 println-lto 63488 B 0 B / +0.0% 19436 B 0 B / +0.0% 1.513 s -14.52 ms / -1.0% (better) 11.209 ms +292.8 us / +2.7% (worse)
Windows MSVC cprintf 120320 B 0 B / +0.0% 65782 B 0 B / +0.0% 899.622 ms +4.712 ms / +0.5% (worse) 3.289 ms -117.3 us / -3.4% (better)
Windows MSVC cprintf-lto 119808 B 0 B / +0.0% 65718 B 0 B / +0.0% 1.088 s +183.7 ms / +20.3% (worse) 4.529 ms +1.179 ms / +35.2% (worse)
Windows MSVC fmtprintf 1625088 B 0 B / +0.0% 682038 B 0 B / +0.0% 3.663 s +40.92 ms / +1.1% (worse) 8.776 ms -2.501 ms / -22.2% (better)
Windows MSVC fmtprintf-lto 1616384 B 0 B / +0.0% 635206 B 0 B / +0.0% 8.719 s +47.57 ms / +0.5% (worse) 9.448 ms +54.3 us / +0.6% (worse)
Windows MSVC println 192000 B 0 B / +0.0% 118486 B 0 B / +0.0% 918.076 ms +31.28 ms / +3.5% (worse) 6.871 ms -167.3 us / -2.4% (better)
Windows MSVC println-lto 189952 B 0 B / +0.0% 116326 B 0 B / +0.0% 1.069 s -24.93 ms / -2.3% (better) 7.179 ms +25.5 us / +0.4% (worse)
Windows MSVC 386 cprintf 9728 B 0 B / +0.0% 3931 B 0 B / +0.0% 725.713 ms -89.66 ms / -11.0% (better) 4.283 ms -297.6 us / -6.5% (better)
Windows MSVC 386 cprintf-lto 9216 B 0 B / +0.0% 3853 B 0 B / +0.0% 765.748 ms -52.98 ms / -6.5% (better) 4.406 ms -334.8 us / -7.1% (better)
Windows MSVC 386 fmtprintf 1190400 B 0 B / +0.0% 445957 B 0 B / +0.0% 3.075 s -78.19 ms / -2.5% (better) 9.162 ms -711 us / -7.2% (better)
Windows MSVC 386 fmtprintf-lto 1224192 B 0 B / +0.0% 416649 B 0 B / +0.0% 7.207 s -428 ms / -5.6% (better) 9.646 ms -353.5 us / -3.5% (better)
Windows MSVC 386 println 34304 B 0 B / +0.0% 18641 B 0 B / +0.0% 794.585 ms -17.33 ms / -2.1% (better) 7.738 ms -228.2 us / -2.9% (better)
Windows MSVC 386 println-lto 32256 B 0 B / +0.0% 16839 B 0 B / +0.0% 888.536 ms -56.82 ms / -6.0% (better) 7.275 ms -147.4 us / -2.0% (better)
Windows MSVC ARM64 cprintf 11776 B 0 B / +0.0% 4192 B 0 B / +0.0% 1.053 s -373.1 us / -0.03543% (better) 6.569 ms -103.2 us / -1.5% (better)
Windows MSVC ARM64 cprintf-lto 10752 B 0 B / +0.0% 4084 B 0 B / +0.0% 1.060 s -23.3 ms / -2.2% (better) 6.653 ms +49.4 us / +0.7% (worse)
Windows MSVC ARM64 fmtprintf 1370624 B 0 B / +0.0% 499068 B 0 B / +0.0% 3.728 s +28.88 ms / +0.8% (worse) 14.209 ms +893.3 us / +6.7% (worse)
Windows MSVC ARM64 fmtprintf-lto 1387008 B 0 B / +0.0% 465900 B 0 B / +0.0% 8.455 s +91.51 ms / +1.1% (worse) 13.690 ms +334.6 us / +2.5% (worse)
Windows MSVC ARM64 println 41984 B 0 B / +0.0% 21824 B 0 B / +0.0% 1.060 s +359.5 us / +0.03394% (worse) 11.507 ms -581.7 us / -4.8% (better)
Windows MSVC ARM64 println-lto 40448 B 0 B / +0.0% 19556 B 0 B / +0.0% 1.207 s -5.374 ms / -0.4% (better) 11.825 ms -207.9 us / -1.7% (better)
Core language and compiler benchmarks
Platform Benchmark ns/op vs base
Linux BenchmarkLookupPCRandom 14.840 ns/op +0.09 ns/op / +0.6% (worse)
Linux BenchmarkMergeCompilerFlags 210.400 ns/op +1.6 ns/op / +0.8% (worse)
Linux BenchmarkMergeLinkerFlags 147.200 ns/op +1.3 ns/op / +0.9% (worse)
Linux BenchmarkChannelBuffered 55.990 ns/op +0.68 ns/op / +1.2% (worse)
Linux BenchmarkChannelHandoff 12934 ns/op +31 ns/op / +0.2% (worse)
Linux BenchmarkDefer 51.880 ns/op +1.2 ns/op / +2.4% (worse)
Linux BenchmarkDirectCall 1.537 ns/op +0.008 ns/op / +0.5% (worse)
Linux BenchmarkGlobalRead 1.562 ns/op -0.004 ns/op / -0.3% (better)
Linux BenchmarkGlobalWrite 7.777 ns/op +0.005 ns/op / +0.1% (worse)
Linux BenchmarkGoroutine 23118 ns/op -1713 ns/op / -6.9% (better)
Linux BenchmarkInterfaceCall 5.887 ns/op -0.083 ns/op / -1.4% (better)
Linux BenchmarkRuntimeGetG 2.445 ns/op -0.058 ns/op / -2.3% (better)
macOS BenchmarkLookupPCRandom 12.290 ns/op -3.1 ns/op / -20.1% (better)
macOS BenchmarkMergeCompilerFlags 116.400 ns/op -0.5 ns/op / -0.4% (better)
macOS BenchmarkMergeLinkerFlags 73.400 ns/op -4.16 ns/op / -5.4% (better)
macOS BenchmarkChannelBuffered 25.370 ns/op -3.88 ns/op / -13.3% (better)
macOS BenchmarkChannelHandoff 9522 ns/op +3084 ns/op / +47.9% (worse)
macOS BenchmarkDefer 33.020 ns/op -3.25 ns/op / -9.0% (better)
macOS BenchmarkDirectCall 1.203 ns/op +0.106 ns/op / +9.7% (worse)
macOS BenchmarkGlobalRead 1.096 ns/op -0.207 ns/op / -15.9% (better)
macOS BenchmarkGlobalWrite 1.070 ns/op -0.244 ns/op / -18.6% (better)
macOS BenchmarkGoroutine 50282 ns/op +2520 ns/op / +5.3% (worse)
macOS BenchmarkInterfaceCall 3.964 ns/op -0.698 ns/op / -15.0% (better)
macOS BenchmarkRuntimeGetG 2.223 ns/op -0.003 ns/op / -0.1% (better)
Windows MinGW BenchmarkLookupPCRandom 9.652 ns/op +0.063 ns/op / +0.7% (worse)
Windows MinGW BenchmarkMergeCompilerFlags 377.500 ns/op -34.7 ns/op / -8.4% (better)
Windows MinGW BenchmarkMergeLinkerFlags 338.500 ns/op +2.4 ns/op / +0.7% (worse)
Windows MinGW BenchmarkChannelBuffered 23.930 ns/op -0.08 ns/op / -0.3% (better)
Windows MinGW BenchmarkChannelHandoff 1132 ns/op -35 ns/op / -3.0% (better)
Windows MinGW BenchmarkDefer 41.620 ns/op +0.37 ns/op / +0.9% (worse)
Windows MinGW BenchmarkDirectCall 1.357 ns/op 0 ns/op / +0.0%
Windows MinGW BenchmarkGlobalRead 1.375 ns/op +0.017 ns/op / +1.3% (worse)
Windows MinGW BenchmarkGlobalWrite 2.180 ns/op +0.013 ns/op / +0.6% (worse)
Windows MinGW BenchmarkGoroutine 57496 ns/op +668 ns/op / +1.2% (worse)
Windows MinGW BenchmarkInterfaceCall 6.954 ns/op +0.135 ns/op / +2.0% (worse)
Windows MinGW BenchmarkRuntimeGetG 1.412 ns/op +0.004 ns/op / +0.3% (worse)
Windows MinGW 386 BenchmarkLookupPCRandom 26.490 ns/op -0.04 ns/op / -0.2% (better)
Windows MinGW 386 BenchmarkMergeCompilerFlags 770.900 ns/op +8.6 ns/op / +1.1% (worse)
Windows MinGW 386 BenchmarkMergeLinkerFlags 655.800 ns/op +2.7 ns/op / +0.4% (worse)
Windows MinGW 386 BenchmarkChannelBuffered 39.190 ns/op +0.34 ns/op / +0.9% (worse)
Windows MinGW 386 BenchmarkChannelHandoff 900.900 ns/op -24.4 ns/op / -2.6% (better)
Windows MinGW 386 BenchmarkDefer 43.860 ns/op +1.25 ns/op / +2.9% (worse)
Windows MinGW 386 BenchmarkDirectCall 1.548 ns/op 0 ns/op / +0.0%
Windows MinGW 386 BenchmarkGlobalRead 1.558 ns/op +0.007 ns/op / +0.5% (worse)
Windows MinGW 386 BenchmarkGlobalWrite 7.780 ns/op +0.008 ns/op / +0.1% (worse)
Windows MinGW 386 BenchmarkGoroutine 86117 ns/op -2398 ns/op / -2.7% (better)
Windows MinGW 386 BenchmarkInterfaceCall 8.359 ns/op -0.016 ns/op / -0.2% (better)
Windows MinGW 386 BenchmarkRuntimeGetG 2.166 ns/op -0.006 ns/op / -0.3% (better)
Windows MinGW ARM64 BenchmarkLookupPCRandom 12.050 ns/op -0.03 ns/op / -0.2% (better)
Windows MinGW ARM64 BenchmarkMergeCompilerFlags 584.200 ns/op +3.8 ns/op / +0.7% (worse)
Windows MinGW ARM64 BenchmarkMergeLinkerFlags 538 ns/op +3.2 ns/op / +0.6% (worse)
Windows MinGW ARM64 BenchmarkChannelBuffered 37.760 ns/op -0.01 ns/op / -0.02648% (better)
Windows MinGW ARM64 BenchmarkChannelHandoff 2867 ns/op +386 ns/op / +15.6% (worse)
Windows MinGW ARM64 BenchmarkDefer 56.570 ns/op +1.47 ns/op / +2.7% (worse)
Windows MinGW ARM64 BenchmarkDirectCall 0.590 ns/op -0.0073 ns/op / -1.2% (better)
Windows MinGW ARM64 BenchmarkGlobalRead 0.663 ns/op -0.0002 ns/op / -0.03014% (better)
Windows MinGW ARM64 BenchmarkGlobalWrite 0.663 ns/op -0.0263 ns/op / -3.8% (better)
Windows MinGW ARM64 BenchmarkGoroutine 59801 ns/op +881 ns/op / +1.5% (worse)
Windows MinGW ARM64 BenchmarkInterfaceCall 4.159 ns/op +0.003 ns/op / +0.1% (worse)
Windows MinGW ARM64 BenchmarkRuntimeGetG 1.808 ns/op +0.01 ns/op / +0.6% (worse)
Windows MSVC BenchmarkLookupPCRandom 12.800 ns/op -0.27 ns/op / -2.1% (better)
Windows MSVC BenchmarkMergeCompilerFlags 611.800 ns/op -3 ns/op / -0.5% (better)
Windows MSVC BenchmarkMergeLinkerFlags 537.500 ns/op -8.5 ns/op / -1.6% (better)
Windows MSVC BenchmarkChannelBuffered 27.890 ns/op -0.3 ns/op / -1.1% (better)
Windows MSVC BenchmarkChannelHandoff 1095 ns/op -26 ns/op / -2.3% (better)
Windows MSVC BenchmarkDefer 53.670 ns/op -0.95 ns/op / -1.7% (better)
Windows MSVC BenchmarkDirectCall 1.546 ns/op 0 ns/op / +0.0%
Windows MSVC BenchmarkGlobalRead 1.548 ns/op +0.001 ns/op / +0.1% (worse)
Windows MSVC BenchmarkGlobalWrite 2.469 ns/op +0.001 ns/op / +0.04052% (worse)
Windows MSVC BenchmarkGoroutine 79730 ns/op -911 ns/op / -1.1% (better)
Windows MSVC BenchmarkInterfaceCall 8.684 ns/op +0.011 ns/op / +0.1% (worse)
Windows MSVC BenchmarkRuntimeGetG 2.167 ns/op 0 ns/op / +0.0%
Windows MSVC 386 BenchmarkLookupPCRandom 21.850 ns/op +0.22 ns/op / +1.0% (worse)
Windows MSVC 386 BenchmarkMergeCompilerFlags 680.200 ns/op +103.3 ns/op / +17.9% (worse)
Windows MSVC 386 BenchmarkMergeLinkerFlags 582 ns/op +53 ns/op / +10.0% (worse)
Windows MSVC 386 BenchmarkChannelBuffered 34.260 ns/op 0 ns/op / +0.0%
Windows MSVC 386 BenchmarkChannelHandoff 649.700 ns/op +8.4 ns/op / +1.3% (worse)
Windows MSVC 386 BenchmarkDefer 38.680 ns/op -3.27 ns/op / -7.8% (better)
Windows MSVC 386 BenchmarkDirectCall 1.357 ns/op 0 ns/op / +0.0%
Windows MSVC 386 BenchmarkGlobalRead 1.365 ns/op +0.007 ns/op / +0.5% (worse)
Windows MSVC 386 BenchmarkGlobalWrite 6.981 ns/op -0.003 ns/op / -0.04296% (better)
Windows MSVC 386 BenchmarkGoroutine 60635 ns/op -475 ns/op / -0.8% (better)
Windows MSVC 386 BenchmarkInterfaceCall 7.341 ns/op -0.013 ns/op / -0.2% (better)
Windows MSVC 386 BenchmarkRuntimeGetG 1.630 ns/op 0 ns/op / +0.0%
Windows MSVC ARM64 BenchmarkLookupPCRandom 12.070 ns/op -0.01 ns/op / -0.1% (better)
Windows MSVC ARM64 BenchmarkMergeCompilerFlags 569.700 ns/op +6.4 ns/op / +1.1% (worse)
Windows MSVC ARM64 BenchmarkMergeLinkerFlags 526.500 ns/op -13.7 ns/op / -2.5% (better)
Windows MSVC ARM64 BenchmarkChannelBuffered 38.910 ns/op -0.72 ns/op / -1.8% (better)
Windows MSVC ARM64 BenchmarkChannelHandoff 1892 ns/op +23 ns/op / +1.2% (worse)
Windows MSVC ARM64 BenchmarkDefer 62.160 ns/op +0.02 ns/op / +0.03219% (worse)
Windows MSVC ARM64 BenchmarkDirectCall 0.589 ns/op -0.0001 ns/op / -0.01697% (better)
Windows MSVC ARM64 BenchmarkGlobalRead 0.663 ns/op -0.0002 ns/op / -0.03015% (better)
Windows MSVC ARM64 BenchmarkGlobalWrite 3.755 ns/op +0.002 ns/op / +0.1% (worse)
Windows MSVC ARM64 BenchmarkGoroutine 54196 ns/op +1311 ns/op / +2.5% (worse)
Windows MSVC ARM64 BenchmarkInterfaceCall 4.141 ns/op +0.004 ns/op / +0.1% (worse)
Windows MSVC ARM64 BenchmarkRuntimeGetG 1.790 ns/op -0.019 ns/op / -1.1% (better)
Timer runtime benchmarks
Platform Operation and runtime ns/op vs base
Linux AfterFuncZeroDelivery/Go 941 ns/op -50.9 ns/op / -5.1% (better)
Linux AfterFuncZeroDelivery/LLGo 41540 ns/op -197 ns/op / -0.5% (better)
Linux CreateStop/Go 310.900 ns/op -16.7 ns/op / -5.1% (better)
Linux CreateStop/LLGo 2017 ns/op +218 ns/op / +12.1% (worse)
Linux RearmStopped/Go 114.700 ns/op -9.3 ns/op / -7.5% (better)
Linux RearmStopped/LLGo 1143 ns/op -136 ns/op / -10.6% (better)
Linux ResetActive/Go 67.570 ns/op -6.85 ns/op / -9.2% (better)
Linux ResetActive/LLGo 822.200 ns/op +46.3 ns/op / +6.0% (worse)
Linux ResetHeap1024/Go 67.230 ns/op +0.13 ns/op / +0.2% (worse)
Linux ResetHeap1024/LLGo 185.600 ns/op -56.3 ns/op / -23.3% (better)
macOS AfterFuncZeroDelivery/Go 503.600 ns/op -12.4 ns/op / -2.4% (better)
macOS AfterFuncZeroDelivery/LLGo 71428 ns/op -16333 ns/op / -18.6% (better)
macOS CreateStop/Go 141.800 ns/op -48.1 ns/op / -25.3% (better)
macOS CreateStop/LLGo 408.800 ns/op -254.4 ns/op / -38.4% (better)
macOS RearmStopped/Go 69.800 ns/op +9.39 ns/op / +15.5% (worse)
macOS RearmStopped/LLGo 344 ns/op -3.5 ns/op / -1.0% (better)
macOS ResetActive/Go 64.200 ns/op +19.82 ns/op / +44.7% (worse)
macOS ResetActive/LLGo 168.200 ns/op -3.8 ns/op / -2.2% (better)
macOS ResetHeap1024/Go 47.090 ns/op +1.23 ns/op / +2.7% (worse)
macOS ResetHeap1024/LLGo 89.450 ns/op +1.35 ns/op / +1.5% (worse)
Windows MinGW AfterFuncZeroDelivery/Go 380.700 ns/op +1.7 ns/op / +0.4% (worse)
Windows MinGW AfterFuncZeroDelivery/LLGo 103034 ns/op +792 ns/op / +0.8% (worse)
Windows MinGW CreateStop/Go 90.500 ns/op -1.06 ns/op / -1.2% (better)
Windows MinGW CreateStop/LLGo 343.100 ns/op +9.4 ns/op / +2.8% (worse)
Windows MinGW RearmStopped/Go 24.470 ns/op +0.04 ns/op / +0.2% (worse)
Windows MinGW RearmStopped/LLGo 223.300 ns/op +0.2 ns/op / +0.1% (worse)
Windows MinGW ResetActive/Go 14.780 ns/op -0.02 ns/op / -0.1% (better)
Windows MinGW ResetActive/LLGo 118.800 ns/op -2.8 ns/op / -2.3% (better)
Windows MinGW ResetHeap1024/Go 14.870 ns/op -0.2 ns/op / -1.3% (better)
Windows MinGW ResetHeap1024/LLGo 106.600 ns/op -2.5 ns/op / -2.3% (better)
Windows MinGW 386 AfterFuncZeroDelivery/Go 950.200 ns/op +1.1 ns/op / +0.1% (worse)
Windows MinGW 386 AfterFuncZeroDelivery/LLGo 189301 ns/op -2281 ns/op / -1.2% (better)
Windows MinGW 386 CreateStop/Go 194.600 ns/op +4.3 ns/op / +2.3% (worse)
Windows MinGW 386 CreateStop/LLGo 507.400 ns/op -25 ns/op / -4.7% (better)
Windows MinGW 386 RearmStopped/Go 63.520 ns/op -0.05 ns/op / -0.1% (better)
Windows MinGW 386 RearmStopped/LLGo 337 ns/op -16.4 ns/op / -4.6% (better)
Windows MinGW 386 ResetActive/Go 38.980 ns/op -0.02 ns/op / -0.1% (better)
Windows MinGW 386 ResetActive/LLGo 990.400 ns/op +10 ns/op / +1.0% (worse)
Windows MinGW 386 ResetHeap1024/Go 39.300 ns/op -0.04 ns/op / -0.1% (better)
Windows MinGW 386 ResetHeap1024/LLGo 186.400 ns/op +1.3 ns/op / +0.7% (worse)
Windows MinGW ARM64 AfterFuncZeroDelivery/Go 675.200 ns/op +4.1 ns/op / +0.6% (worse)
Windows MinGW ARM64 AfterFuncZeroDelivery/LLGo 139630 ns/op +6294 ns/op / +4.7% (worse)
Windows MinGW ARM64 CreateStop/Go 197.100 ns/op -10.6 ns/op / -5.1% (better)
Windows MinGW ARM64 CreateStop/LLGo 371.200 ns/op +12.8 ns/op / +3.6% (worse)
Windows MinGW ARM64 RearmStopped/Go 70.640 ns/op +0.08 ns/op / +0.1% (worse)
Windows MinGW ARM64 RearmStopped/LLGo 254 ns/op +2.7 ns/op / +1.1% (worse)
Windows MinGW ARM64 ResetActive/Go 31.040 ns/op +0.01 ns/op / +0.03223% (worse)
Windows MinGW ARM64 ResetActive/LLGo 126 ns/op +4.8 ns/op / +4.0% (worse)
Windows MinGW ARM64 ResetHeap1024/Go 31.110 ns/op +0.06 ns/op / +0.2% (worse)
Windows MinGW ARM64 ResetHeap1024/LLGo 127.100 ns/op +0.7 ns/op / +0.6% (worse)
Windows MSVC AfterFuncZeroDelivery/Go 539.100 ns/op -47.3 ns/op / -8.1% (better)
Windows MSVC AfterFuncZeroDelivery/LLGo 151361 ns/op -9663 ns/op / -6.0% (better)
Windows MSVC CreateStop/Go 115.500 ns/op -3.3 ns/op / -2.8% (better)
Windows MSVC CreateStop/LLGo 444.900 ns/op -111 ns/op / -20.0% (better)
Windows MSVC RearmStopped/Go 31.650 ns/op -3.59 ns/op / -10.2% (better)
Windows MSVC RearmStopped/LLGo 264.900 ns/op -19.1 ns/op / -6.7% (better)
Windows MSVC ResetActive/Go 20.030 ns/op -0.93 ns/op / -4.4% (better)
Windows MSVC ResetActive/LLGo 153.300 ns/op -9.7 ns/op / -6.0% (better)
Windows MSVC ResetHeap1024/Go 20.280 ns/op -0.75 ns/op / -3.6% (better)
Windows MSVC ResetHeap1024/LLGo 124.700 ns/op -1.9 ns/op / -1.5% (better)
Windows MSVC 386 AfterFuncZeroDelivery/Go 771.300 ns/op -9.2 ns/op / -1.2% (better)
Windows MSVC 386 AfterFuncZeroDelivery/LLGo 132111 ns/op +2682 ns/op / +2.1% (worse)
Windows MSVC 386 CreateStop/Go 203.700 ns/op +32.2 ns/op / +18.8% (worse)
Windows MSVC 386 CreateStop/LLGo 354.900 ns/op -2.5 ns/op / -0.7% (better)
Windows MSVC 386 RearmStopped/Go 57.490 ns/op +0.76 ns/op / +1.3% (worse)
Windows MSVC 386 RearmStopped/LLGo 261 ns/op -1.3 ns/op / -0.5% (better)
Windows MSVC 386 ResetActive/Go 36.190 ns/op +3.63 ns/op / +11.1% (worse)
Windows MSVC 386 ResetActive/LLGo 850.600 ns/op -48.9 ns/op / -5.4% (better)
Windows MSVC 386 ResetHeap1024/Go 32.940 ns/op +0.06 ns/op / +0.2% (worse)
Windows MSVC 386 ResetHeap1024/LLGo 145.600 ns/op +2.6 ns/op / +1.8% (worse)
Windows MSVC ARM64 AfterFuncZeroDelivery/Go 670.200 ns/op +0.5 ns/op / +0.1% (worse)
Windows MSVC ARM64 AfterFuncZeroDelivery/LLGo 132527 ns/op +2439 ns/op / +1.9% (worse)
Windows MSVC ARM64 CreateStop/Go 209 ns/op +10.1 ns/op / +5.1% (worse)
Windows MSVC ARM64 CreateStop/LLGo 399.700 ns/op -12.8 ns/op / -3.1% (better)
Windows MSVC ARM64 RearmStopped/Go 70.580 ns/op -0.07 ns/op / -0.1% (better)
Windows MSVC ARM64 RearmStopped/LLGo 275.400 ns/op +5.6 ns/op / +2.1% (worse)
Windows MSVC ARM64 ResetActive/Go 31.060 ns/op +0.07 ns/op / +0.2% (worse)
Windows MSVC ARM64 ResetActive/LLGo 140.800 ns/op -4.6 ns/op / -3.2% (better)
Windows MSVC ARM64 ResetHeap1024/Go 31.140 ns/op +0.03 ns/op / +0.1% (worse)
Windows MSVC ARM64 ResetHeap1024/LLGo 137 ns/op -0.6 ns/op / -0.4% (better)

Compared with a34388d0a9ab measured in the same runner job.

@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

LLGo WebAssembly build benchmarks

77ca3f785cef | workflow run | long-term charts

WebAssembly output sizes
Example, profile and compiler Wasm module vs base Generated JS glue vs base
cprintf/j32-emscripten/LLGo 142925 B 0 B / +0.0% 73538 B 0 B / +0.0%
cprintf/j32-goos-js/LLGo 141404 B 0 B / +0.0% 71853 B 0 B / +0.0%
cprintf/j64-emscripten-memory64/LLGo 131242 B 0 B / +0.0% 77211 B 0 B / +0.0%
cprintf/w32-goos-wasip1/LLGo 138444 B 0 B / +0.0% 0 B 0 B / 0.0%
cprintf/w32-wasi/LLGo 138170 B 0 B / +0.0% 0 B 0 B / 0.0%
fmtprintf/j32-emscripten/LLGo 3184145 B 0 B / +0.0% 117962 B 0 B / +0.0%
fmtprintf/j32-goos-js/Go 2526852 B 0 B / +0.0% 0 B 0 B / 0.0%
fmtprintf/j32-goos-js/LLGo 3176231 B 0 B / +0.0% 101617 B 0 B / +0.0%
fmtprintf/j64-emscripten-memory64/LLGo 2921714 B 0 B / +0.0% 125341 B 0 B / +0.0%
fmtprintf/w32-goos-wasip1/Go 2500019 B 0 B / +0.0% 0 B 0 B / 0.0%
fmtprintf/w32-goos-wasip1/LLGo 2829062 B 0 B / +0.0% 0 B 0 B / 0.0%
fmtprintf/w32-wasi/LLGo 2695473 B 0 B / +0.0% 0 B 0 B / 0.0%
j32-emscripten/LLGo 142171 B 0 B / +0.0% 73538 B 0 B / +0.0%
j32-goos-js/Go 1895533 B 0 B / +0.0% 0 B 0 B / 0.0%
j32-goos-js/LLGo 140880 B 0 B / +0.0% 71853 B 0 B / +0.0%
j64-emscripten-memory64/LLGo 130580 B 0 B / +0.0% 77211 B 0 B / +0.0%
reflectcall/j32-emscripten/LLGo 1527329 B 0 B / +0.0% 91998 B 0 B / +0.0%
reflectcall/j32-goos-js/Go 2191221 B 0 B / +0.0% 0 B 0 B / 0.0%
reflectcall/j32-goos-js/LLGo 1530093 B 0 B / +0.0% 90313 B 0 B / +0.0%
reflectcall/j64-emscripten-memory64/LLGo 1413394 B 0 B / +0.0% 97731 B 0 B / +0.0%
reflectcall/w32-goos-wasip1/Go 2205707 B 0 B / +0.0% 0 B 0 B / 0.0%
reflectcall/w32-goos-wasip1/LLGo 1534667 B 0 B / +0.0% 0 B 0 B / 0.0%
reflectcall/w32-wasi/LLGo 1460122 B 0 B / +0.0% 0 B 0 B / 0.0%
w32-goos-wasip1/Go 1909947 B 0 B / +0.0% 0 B 0 B / 0.0%
w32-goos-wasip1/LLGo 137725 B 0 B / +0.0% 0 B 0 B / 0.0%
w32-wasi/LLGo 137518 B 0 B / +0.0% 0 B 0 B / 0.0%
LLGo WebAssembly build measurements
Example and profile Build vs base
j32-emscripten 6.114 s -274.3 ms / -4.3% (better)
j32-goos-js 6.238 s +201 ms / +3.3% (worse)
j64-emscripten-memory64 5.422 s +133.1 ms / +2.5% (worse)
reflectcall/w32-wasi 29.301 s -815.1 ms / -2.7% (better)
w32-goos-wasip1 5.179 s -57.37 ms / -1.1% (better)
w32-wasi 4.991 s +73.13 ms / +1.5% (worse)

Compared with a34388d0a9ab measured in the same runner job.

@cpunion cpunion changed the title test/wasm: compare EH translation paths test/wasm: compare EH paths in Node and Chrome Sep 23, 2026
@cpunion
cpunion force-pushed the codex/wasm-eh-matrix branch from dd6d709 to 9097e17 Compare September 26, 2026 14:38

@visualfc visualfc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

Can merge. This is a comparison experiment plus a decision record; it does not change LLGo's default EH encoding. The conclusion is appropriately conservative: keep the current Emscripten/LLGo browser path, catch C++ exceptions inside the wrapper, translate a C ABI status into a Go panic, and do not switch the full link to exnref from an isolated C++ comparison.

The earlier fennoai notes (tool env docs, tool() error text, glue replace comment, sjlj counter) are addressed in 9097e17.

Suggestions

1. Node 22 cannot run the direct / translated variants

run_cpp invokes Node without enabling exnref. CI uses Node 24, where V8 enables it by default, so the job is green. Emscripten's bundled Node 22 defaults to --no-experimental-wasm-exnref. On Node v22.16.0, O2 direct fails with:

CompileError: WebAssembly.instantiate(): invalid value type 'exn',
enable with --experimental-wasm-exnref

The same module prints cpp catch and sjlj ok after adding --experimental-wasm-exnref. Legacy already runs on Node 22 without that flag.

Either pass --experimental-wasm-exnref (still accepted on Node 24+, where it is already the default) or document Node 24+ and fail clearly on older versions. Developers commonly use emsdk's Node 22; without this, a local run looks like exnref is broken.

2. The decision table does not include the encoding actually in use

The table compares three Wasm EH encodings: legacy (-fwasm-exceptions -sWASM_LEGACY_EXCEPTIONS=1), direct exnref, and Binaryen translation. LLGo's browser default link does not pass -fwasm-exceptions. The Go/C++ boundary uses JS EH (-sDEFAULT_TO_CXX -sDISABLE_EXCEPTION_CATCHING=0).

Keeping the current encoding is the right call, but readers can take the Legacy column as the status quo. A row or note on the table would help: the current browser contract is JS EH plus catch-inside-wrapper; the three Wasm EH columns are later full-link candidates.

3. DWARF --verify is tightly coupled to the pinned toolchain

The script treats llvm-dwarfdump --verify failure as fatal. On Emscripten 6.0.2 + upstream Binaryen 132, legacy -O0 reports No errors. while direct -O2 reports parent-range and overlap errors (exit 1). CI is Emscripten 6.0.8 + llgo-v132.3, so it passes. The size snapshot in the doc is also from llgo-v132.2.

Worth stating in the doc that the DWARF check is meaningful only with emsdk 6.0.8 and EM_BINARYEN_ROOT / WASMOPT pointing at the LLGo Binaryen. Otherwise a local python3 dev/compare_wasm_eh.py fails on DWARF and looks like an EH regression.

Nits

  • Doc still cites Binaryen llgo-v132.2; CI is pinned to llgo-v132.3. The sizes are a snapshot; labeling the toolchain version is enough.
  • The Go/C++ "translation" is a predicate, not a dataflow: after Catch()==7 the code panics a fixed string, so the payload is independent of the status value 7. Enough for catch-inside-wrapper → C ABI → Go panic/recover; it does not show the status being carried into the panic.
  • The stronger Go panic fixture (internal/build/testdata/wasm-runtime, panic/recover inside loop defers) runs only on Node. --browser does not execute it; Chrome only sees the trivial panic/recover in the wrapper.
  • The translated path reuses legacy JS glue and only rewrites the wasm filename. Fine for same-module throw/catch; it is not a full Emscripten exnref link. One sentence in the doc would make that explicit.
  • CI step timeout-minutes: 10 covers eight serial Chrome runs (six C++ + two wrappers), each up to 90s on the Python side. The current job is ~15m, so this is fine unless Chrome hangs.
  • The try_table substring check is whole-module (including libc++abi), not proof that main still takes the EH path. O2 IR still has invoke __cxa_throw, so this is OK today.

@cpunion
cpunion merged commit 5858cbc into xgo-dev:main Sep 27, 2026
88 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants