15. Using the GHC WebAssembly backend¶
15.1. What does the WebAssembly “backend” mean¶
In order to compile Haskell to wasm, you need a custom GHC build that
targets wasm. There isn’t a GHC option which allows you to use a
stock GHC installed via ghcup
or stack
to generate wasm. That’s
because GHC is still a single-target compiler, so each GHC build is only
capable of compiling code that runs on a single architecture and operating
system.
So, the term GHC wasm backend isn’t the same sense as the
unregisterised/LLVM/NCG backends. It merely describes GHC’s support as a
cross compiler that targets wasm, and more specifically,
wasm32-wasi
.
The generated wasm module makes use of a few post-MVP extensions that are supported by default in latest releases of Chrome/Firefox/Safari/wasmtime. The wasm module uses WASI as the system call layer, so it’s supported by any wasm engine that implements WASI (including browsers, which can provide the WASI layer via JavaScript).
15.2. Setting up the GHC wasm backend¶
The wasm backend is still a tech preview and not included in the official bindists yet. If you are using x86_64-linux, you can follow the “getting started” subsections in ghc-wasm-meta to quickly set up the GHC wasm backend using the nightly artifacts.
It’s also possible to build the GHC wasm backend manually, if your host
system is one of {x86_64,aarch64}-{linux,darwin}. Refer to the
ghc-wasm-meta
readme for detailed instructions.
15.3. Using the GHC wasm backend to compile & link code¶
Once the GHC wasm backend is set up, you can use it to compile and link
code. The compiler executables follow the cross compiler linking
convention, so you need to call wasm32-wasi-ghc
,
wasm32-wasi-ghc-pkg
and wasm32-wasi-hsc2hs
instead of ghc
,
ghc-pkg
and hsc2hs
.
You can also use the --with-compiler=
, --with-hc-pkg=
and
--with-hsc2hs
flags of cabal
to build cabal projects. The
wasm32-wasi-cabal
wrapper script set up by the ghc-wasm-meta
installer does this automatically for you, but using flags manually also
works with stock cabal
installations. When cabal
builds an
executable component, that executable will be built as a wasm module,
and you can use cabal list-bin exe:foo
to find the wasm module’s
location in the build directory.
15.4. Running the GHC wasm backend’s output¶
Once you have a wasm module, you can run it with a dedicated wasm engine
like wasmtime
, or inside the browsers.
To run it with wasmtime
, you can simply do:
$ wasmtime run foo.wasm
Just like native executables, you can pass command line arguments, and
also RTS options, as long as it’s built with -rtsopts
:
$ wasmtime run foo.wasm --bar +RTS --nonmoving-gc -RTS
You can also mount some host directory into it:
$ wasmtime run --mapdir /::$PWD foo.wasm
As long as the filesystem capability is provided, in addition to filesystem I/O in Haskell code, you can use the RTS eventlog and profiling functionality, then inspect the report files:
$ wasmtime run --mapdir /::$PWD foo.wasm +RTS -hc -l -RTS
To run the wasm module in the browsers, refer to the ghc-wasm-meta
documentation for more details.