There is a phase 1 proposal for directly supporting JIT in WebAssembly: https://github.com/webassembly/jit-interface
Probably won't be usable for a while though.
#Write-up: https://github.com/nasomers/flycast-wasm/blob/main/TECHNICAL_WRITEUP.md
From r/emulation:
Six months ago I got upstream Flycast compiling to WebAssembly and running as a libretro core. It booted games at about 2 FPS on the interpreter. Fun proof of concept, completely unplayable.
Today, heavy 3D titles like Jet Grind Radio and Shenmue hold their target framerate at native resolution with clean audio, in a browser tab, on ordinary hardware.
Here's how that gap closed.
Every Dreamcast emulator that runs at full speed depends on a dynamic recompiler. A dynarec generates native code at runtime and jumps into it. WebAssembly makes that structurally impossible: code and data live in separate address spaces, so there's no writable-executable memory to generate into, and nothing you write into linear memory can ever be executed.
Upstream had already declined WASM support, and the consensus was that this was a dead end. Browser Dreamcast would stay a slideshow.
Fuck that.
The JIT doesn't patch memory. It generates entire WebAssembly modules at runtime.
SH4 code is decoded into Flycast's SHIL IR, lowered to wasm bytecode in the browser, compiled and instantiated through the wasm API, and dispatched via call_indirect from a C dispatch loop. The host boundary gets crossed once per module, at compile time. Steady-state execution is wasm calling wasm, with no JavaScript anywhere on the dispatch path.
Months of walls, and slightly less hair. Every one of these is documented with numbers in the writeup:
The whole JIT was validated with differential test harnesses against the unmodified reference interpreter. Hundreds of thousands of block-level state comparisons, zero divergence.
Building a correct dynarec solo is mostly a testing problem, not a codegen problem. Once the harness existed, every bug became a bisect instead of a mystery. The writeup covers the methodology in detail, and it generalizes to any target where you have a working interpreter to diff against.
Windows CE titles. They need full SH4 MMU support in the JIT. That's next on my list, at a slower pace than the sprint that built the rest. The phased plan and root cause analysis are already published, so if you want to help, or beat me to it, the hard analysis is done.
I also haven't tested every title. If something breaks, open an issue.
Repo: https://github.com/nasomers/flycast-wasm
Release: https://github.com/nasomers/flycast-wasm/releases/tag/v1.0
Credit where it's due: Flycast is flyinghead's emulator, and this stands on that codebase and its reference interpreter. The WASM port and the JIT are my work.
Happy to answer questions.
There is a phase 1 proposal for directly supporting JIT in WebAssembly: https://github.com/webassembly/jit-interface
Probably won't be usable for a while though.