.. _wasm:

Using the GHC WebAssembly backend
=================================

.. _wasm-clarify:

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 <https://wasmtime.dev>`__. The wasm
module uses `WASI <https://wasi.dev>`__ 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).

.. _wasm-setup:

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 <https://gitlab.haskell.org/ghc/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.

.. _wasm-compile:

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.

.. _wasm-run:

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:

.. code:: sh

   $ 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``:

.. code:: sh

   $ wasmtime run foo.wasm --bar +RTS --nonmoving-gc -RTS

You can also mount some host directory into it:

.. code:: sh

   $ 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:

.. code:: sh

   $ wasmtime run --mapdir /::$PWD foo.wasm +RTS -hc -l -RTS

.. _wasm-jsffi:

JavaScript FFI in the wasm backend
----------------------------------

The GHC wasm backend supports the JavaScript FFI feature. For Haskell
projects that are meant to be run in JavaScript environments like
browsers or nodejs, the JavaScript FFI enables:

-  Calling JavaScript from Haskell via foreign imports and vice versa
   via foreign exports.
-  Representing JavaScript values as first-class garbage collected
   Haskell values on the Haskell heap.
-  Blocking for asynchronous JavaScript computation in a single Haskell
   thread without blocking the entire runtime.
-  Not paying for JavaScript when not using it, the same toolchain still
   generates self-contained ``wasm32-wasi`` modules by default.

The JavaScript FFI as implemented in the GHC wasm backend is pioneered
by the asterius project and heavily inspired by GHCJS, the predecessor
of the GHC JavaScript backend. Despite some similarities, it still
differs from the GHC JavaScript backend’s implementation significantly.
The rest of this guide is a canonical reference for the GHC wasm
backend’s JavaScript FFI, which we’ll now abbreviate as JSFFI.

.. _wasm-jsffi-types:

Marshalable types and ``JSVal``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

JSFFI supports all boxed marshalable foreign types in C FFI:

-  ``Bool``
-  ``Char``
-  ``Int`` / ``Word``
-  ``Int8`` / ``Int16`` / ``Int32`` / ``Int64``
-  ``Word8`` / ``Word16`` / ``Word32`` / ``Word64``
-  ``Ptr`` / ``FunPtr`` / ``StablePtr``
-  ``Float`` / ``Double``

The above types and their ``newtype``\ s can be used as argument/result
types in JSFFI. Some caveats to keep in mind:

-  ``Bool`` is marshaled to ``0`` / ``1`` instead of ``false`` /
   ``true`` in JavaScript. This is affected by implementation details of
   JSFFI, which is layered on top of C FFI and shares some
   characteristics of C FFI. It should be fine in most cases, since
   implicit conversion to ``boolean`` happens when it’s used as a
   boolean. It’s also fine to pass a JavaScript ``boolean`` into
   Haskell, since it’ll be implicitly converted to a number first.
-  Likewise, ``Char`` is marshaled to 32-bit integer that represents its
   Unicode code point. Do not pass a single character JavaScript
   ``string`` as ``Char``, since implicit conversion to number results
   in ``NaN``! If you absolutely need to use ``Char`` as a JSFFI
   argument/result type, you’re in charge of handling ``Char``\ s as
   code points. Most likely you only need to marshal between Haskell
   ``String`` or ``Text`` and JavaScript ``string``\ s, for which there
   already exist conversion functions.
-  64-bit integer types are marshaled to JavaScript ``bigint``\ s. In
   JavaScript, mixing ``bigint`` and regular numbers in arithmetic
   results in type errors, so keep this in mind. As for ``Int`` /
   ``Word``, they are 32-bit since the GHC wasm backend is based on
   ``wasm32`` .
-  JSFFI doesn’t support unboxed foreign types like ``Int#``,
   ``ByteArray#``, etc, even when ``UnliftedFFITypes`` is enabled.

In addition to the above types, JSFFI supports the ``JSVal`` type and
its ``newtype``\ s as argument/result types. ``JSVal`` is defined in
``GHC.Wasm.Prim`` in ``ghc-experimental``, which represents an opaque
reference to a JavaScript value.

``JSVal``\ s are first-class Haskell values on the Haskell heap. You can
get them via foreign import results or foreign export arguments, store
them in Haskell data structures and pass them between
Haskell/JavaScript. They are garbage-collected by the GHC RTS:

-  There can be multiple ``JSVal``\ s that point to the same JavaScript
   value. As long as there’s at least one ``JSVal`` still alive on the
   Haskell heap, that JavaScript value will still be alive on the
   JavaScript heap.
-  If there’s no longer any live ``JSVal`` that points to the JavaScript
   value, then after Haskell garbage collection, the runtime no longer
   retain any reference to it, allowing the JavaScript runtime to
   eventually garbage collect it as well.

In addition to garbage collection, ``GHC.Wasm.Prim`` also exports
``freeJSVal :: JSVal -> IO ()``, allowing the user to drop the
JavaScript reference from the runtime eagerly. You’re encouraged to make
use of ``freeJSVal`` when you’re sure about a ``JSVal``\ ’s lifetime,
especially for the temporary ``JSVal``\ s. This will help reducing the
memory footprint at runtime.

Note that ``freeJSVal`` is not idempotent and it’s only safe to call it
exactly once or not at all. Once it’s called, any subsequent usage of
that ``JSVal`` results in a runtime panic.

.. _wasm-jsffi-import:

Foreign imports
~~~~~~~~~~~~~~~

One can embed a JavaScript code snippet in a foreign import declaration
and call that piece of JavaScript code by calling the foreign import
function:

.. code:: haskell

   import GHC.Wasm.Prim

   foreign import javascript unsafe "console.log($1)"
     js_print :: JSString -> IO ()

   foreign import javascript unsafe "typeof $1 === 'object'"
     js_is_obj :: JSVal -> Bool

   foreign import javascript unsafe "let acc = 1; for (let i = 1; i <= $1; ++i) acc *= i; return acc;"
     js_fac :: Word -> Word

A JSFFI import code snippet can be either a single JavaScript expression
or a series of JavaScript statements as function body, in which case you
can use ``return`` to return the import result value. The import code
snippet has access to:

-  The import argument values, bound to arguments ``$1``, ``$2``, etc.
-  The ``__export`` binding, which contain all wasm module exports. For
   instance, you could use ``__exports.memory`` to access the
   ``WebAssembly.Memory`` object and use it to copy blobs between the
   Haskell/JavaScript side. The ``memory`` export exists by default.
-  The full Web API that exists in the JavaScript global scope.

There are two kinds of JSFFI imports: synchronous/asynchronous imports.
``unsafe`` indicates synchronous imports, which has the following
caveats:

-  The calling thread as well as the entire runtime blocks on waiting
   for the import result.
-  If the JavaScript code throws, the runtime crashes with the same
   error. A JavaScript exception cannot be handled as a Haskell
   exception here, so you need to use a JavaScript ``catch`` explicitly
   shall the need arise.
-  Like ``unsafe`` C imports, re-entrance is not supported, the imported
   foreign code must not call into Haskell again. Doing so would result
   in a runtime panic.

When a JSFFI import is marked as ``safe`` / ``interruptible`` or lacks
safety annotation, then it’s treated as an asynchronous import. The
asynchronous JSFFI imports combine the Haskell concurrency model and the
JavaScript event loop, allowing Haskell code to work with async
JavaScript computation without blocking the entire runtime.

.. code:: haskell

   import Control.Exception

   foreign import javascript safe "new Promise(res => setTimeout(res, $1))"
     js_sleep :: Int -> IO ()

   sleep :: Int -> IO ()
   sleep t = evaluate =<< js_sleep t

   foreign import javascript safe "const r = await fetch($1); return r.text();"
     js_fetch :: JSString -> IO JSString

Asynchronous import code is wrapped in async JavaScript functions,
therefore ``await`` is also supported. Async JavaScript functions always
return ``Promise``\ s, and you can also explicitly create and return a
``Promise`` that resolves to the final result of the async computation.

When an asynchronous JSFFI import is called, the Haskell function
returns immediately once the async JavaScript function returns a
``Promise``. The value returned by the Haskell function is a thunk. When
the thunk is evaluated later, the evaluating thread is suspended by the
runtime, and resumed when the ``Promise`` actually resolves or rejects.

Compared to synchronous JSFFI imports, asynchronous JSFFI imports have
the following notable pros/cons:

-  Waiting for the result only blocks a single Haskell thread, other
   threads can still make progress and garbage collection may still
   happen.
-  If the ``Promise`` rejects, Haskell code can catch JavaScript errors
   as ``JSException``\ s.
-  Re-entrance is supported. The JavaScript code may call into Haskell
   again and vice versa.
-  Of course, it has higher overhead than synchronous JSFFI imports.

Using thunks to encapsulate ``Promise`` result allows cheaper
concurrency without even needing to fork Haskell threads just for
waiting for a bunch of async calls to return. Just like lazy I/O, the
convenience comes with caveat, you need to take some care to force the
result thunk before closing the underlying resource. And even if the
result type is ``()``, it’s still a thunk that needs to be explicitly
forced to ensure the ``Promise`` has actually resolved, so you likely
need to write a worker/wrapper function pair for cases like ``sleep``.

There’s also a special kind of JSFFI import that allow converting a
callable ``JSVal`` to a Haskell function:

.. code:: haskell

   type Logger = JSString -> IO ()

   type JSFunction = JSVal

   foreign import javascript unsafe "s => console.log(s)"
     js_logger :: JSFunction

   foreign import javascript unsafe "dynamic"
     js_logger_to_hs :: JSFunction -> Logger

Much like ``foreign import ccall "dynamic"`` which wraps a C function
pointer as a Haskell function, ``foreign import javascript "dynamic"``
wraps a ``JSVal`` that represent a JavaScript function as a Haskell
function. The returned Haskell function retains the reference to that
``JSVal``, and the ``unsafe`` / ``safe`` annotation indicates whether
that JavaScript function is synchronous or asynchronous.

Of course, without ``foreign import javascript "dynamic"``, one could
still easily implement similar functionality:

.. code:: haskell

   foreign import javascript unsafe "$1($2)"
     js_logger_to_hs :: JSFunction -> JSString -> IO ()

And that’s how it’s implemented under the hood. It’s handled as a JSFFI
import with an auto-generated code snippet that calls the first
argument, passing the rest of arguments.

.. _wasm-jsffi-export:

Foreign exports
~~~~~~~~~~~~~~~

One can use ``foreign export javascript`` to export a top-level Haskell
binding as a wasm module export which can be called in JavaScript:

.. code:: haskell

   foreign export javascript "my_fib"
     fib :: Word -> Word

Give ``fib :: Word -> Word``, the above declaration exports ``fib`` as
``my_fib``. It is a wasm module export function without any JavaScript
wrapper, and as long as the wasm instance is properly initialized, you
can call ``await instance.exports.my_fib(10)`` to invoke the exported
Haskell function and get the result.

Unlike JSFFI imports which have synchronous/asynchronous flavors, JSFFI
exports are always asynchronous. Calling them always return a
``Promise`` in JavaScript that needs to be ``await``\ ed for the real
result. If the Haskell function throws, the ``Promise`` is rejected with
a ``WebAssembly.RuntimeError``, and the ``message`` field contains a
JavaScript string of the Haskell exception.

Above is the static flavor of JSFFI exports. It’s also possible to
export a dynamically created Haskell function closure as a JavaScript
function and obtain its ``JSVal``:

.. code:: haskell

   type BinOp a = a -> a -> a

   foreign import javascript "wrapper"
     js_func_from_hs :: BinOp Int -> IO JSVal

This is also much like ``foreign import ccall "wrapper"``, which wraps a
Haskell function closure as a C function pointer. Note that ``unsafe`` /
``safe`` annotation is ignored here, since the ``JSVal`` that represent
the exported function is always returned synchronously, but it is always
an asynchronous JavaScript function, just like static JSFFI exports.

The ``JSVal`` callbacks created by dynamic JSFFI exports can be passed
to the rest of JavaScript world to be invoked later. But wait, didn’t we
say earlier that ``JSVal``\ s are garbage collected? Isn’t a
use-after-free trap waiting ahead of the road, when the ``JSVal`` is
collected in Haskell but the JavaScript callback is invoked later?

So, normal ``JSVal``\ s created by JSFFI import results or JSFFI export
arguments only manage a single kind of resource: the JavaScript value it
refers to. But ``JSVal``\ s created by dynamic JSFFI exports manage two
kinds of resources: the JavaScript callback it refers to, as well as a
stable pointer that retains the Haskell function closure. If this
``JSVal`` is garbage collected, the Haskell runtime no longer retains
the JavaScript callback, but the JavaScript side may still hold that
callback and intends to call it later, so the Haskell function closure
is still retained by default.

Still, the runtime can gradually drop these retainers by using
``FinalizerRegistry`` to invoke the finalizers to free the underlying
stable pointers once the JavaScript callbacks are recycled.

One last corner case is cyclic reference between the two heaps: if a
JavaScript callback is retained only by ``JSVal`` and that ``JSVal`` is
retained only by a Haskell function closure that gets exported, this
creates a cyclic reference that can’t be automatically recycled. This is
a fundamental limit of the GHC wasm backend today since the Haskell heap
lives in the linear memory, distinct from the host JavaScript heap, and
coordination between two heaps is always a non-trivial challenge.
However, one can still use ``freeJSVal`` to break the cycle. When
``freeJSVal`` is applied to a ``JSVal`` that represents a JavaScript
callback created by a dynamic JSFFI export, both kinds of resources are
freed at once: the JavaScript callback, as well as the Haskell function
closure.

.. _wasm-jsffi-flag:

Detect whether JSFFI is being used
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you’re writing a Haskell library, you may want to detect whether the
final linked module involves JSFFI logic, or is it still a
self-contained ``wasm32-wasi`` module. One obvious reason is ``wasi``
implementations in JavaScript environments are often incomplete and lack
certain features (e.g. the ``poll_oneoff`` syscall), so it makes sense
to dispatch code.

``GHC.Wasm.Prim`` exports ``isJSFFIUsed :: Bool`` that can be used for
this purpose. As long as there’s a single JSFFI import/export or
anything involving ``JSVal`` linked into the final wasm module, it
will be ``True``. It is ``False`` if and only if the user code has
absolutely no transitive dependency on anything related to JSFFI, in
which case the linked wasm module will be a self-contained
``wasm32-wasi`` module. If something compiles fine with the GHC wasm
backend before JSFFI feature is merged, but ``isJSFFIUsed`` is still
``True``, then it’s definitely a bug.

.. _wasm-jsffi-async-exception:

Interaction with async exception
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When a thread is blocked waiting for an async JSFFI import call to
return, it can be interrupted by a Haskell async exception, with some
caveats:

-  The async exception would not magically cancel the ``Promise``. In
   general, JavaScript ``Promise``\ s aren’t cancelable anyway. There do
   exist third-party ``Promise`` libraries to provide a “cancel”
   interface, which only guarantees all ``.then()`` continuations
   registered on that ``Promise`` will no longer be invoked. For
   simplicity of implementation, we aren’t using those for the time
   being.
-  Normally, ``throwTo`` would block until the async exception has been
   delivered. In the case of JSFFI, ``throwTo`` would always return
   successfully immediately, while the target thread is still left in a
   suspended state. The target thread will only be waken up when the
   ``Promise`` actually resolves or rejects, though the ``Promise``
   result will be discarded at that point.

The current way async exceptions are handled in JSFFI is subject to
change though. Ideally, once the exception is delivered, the target
thread can be waken up immediately and continue execution, and the
pending ``Promise`` will drop reference to that thread and no longer
invoke any continuations.

.. _wasm-jsffi-cffi:

Interaction with C FFI
~~~~~~~~~~~~~~~~~~~~~~

User code can take advantage of JSFFI and C FFI together, and make use
of third party C/C++ code as long as they work on ``wasm32-wasi``.
However, there is an important limitation to keep in mind when it comes
to the interaction between JSFFI and C FFI:

A Haskell thread cannot force an async JSFFI import thunk when it
represents a Haskell function exported via C FFI. Doing so would throw
``WouldBlockException``.

For example, suppose we’re using the Haskell bindings of a certain C
library, and some of the C functions expect callers to pass a C function
pointer as the callback argument. Yes, we can use
``foreign import ccall "wrapper"`` to wrap a Haskell function closure
and pass it to that C function. The wrapped Haskell function can even
call sync JSFFI imports, but it cannot call an async JSFFI import and
block on the result.

The other direction is fine. Regardless of whether a C import is
``unsafe`` or ``safe``, it can be called in a Haskell thread that
represents a Haskell function exported to JavaScript. Do keep in mind
that we’re using the single-threaded runtime at the moment, so other
than supporting re-entrancy, ``safe`` C calls don’t offer extra
advantage than ``unsafe``.

As mentioned before, JSFFI is layered on top of C FFI under the hood,
and they share the same C symbol namespace. In most cases, the JSFFI
related symbols are auto-generated so they can’t collide, but for each
``foreign export javascript "my_func"``, there will be a ``my_func``
externally visible C symbol, so you need to take a bit of care not to
duplicate symbol with the C side.

.. _wasm-jsffi-jsapi:

The JavaScript API
~~~~~~~~~~~~~~~~~~

When linking a wasm module that makes use of JSFFI, correct link-time
arguments must be passed to GHC and this needs to be adjusted on a
per-project basis:

.. code:: haskell

   ghc -no-hs-main -optl-mexec-model=reactor -optl-Wl,--export=my_func

Why?

Consider the case ``ghc hello.hs`` where ``hello.hs`` is just a good old
``main = putStrLn "hello world"``. By default, ghc exports ``main`` as a
C function, compiles and links a little C stub file that contain the
actual ``main`` that initializes the runtime and call the Haskell
``main``, and ``clang`` would link everything as a ``wasm32-wasi``
command module.

Conceptually, a ``wasm32-wasi`` command module is like an executable,
with a single entry point and meant to be invoked only once and then
exits. But certain link-time arguments can tell ``clang`` to target a
``wasm32-wasi`` reactor module instead. A reactor module is a bit like a
shared library: it has internal functions as well as user-defined entry
points, and once it’s initialized, the entry points can be called as
many times as the user wants.

Given the nature of JSFFI, if a project uses JSFFI, then it surely is
meant to target a ``wasm32-wasi`` reactor module. And there’s no
sensible default entry point, not even ``main``, you need to explicitly
pass the export names you need via link-time arguments, otherwise those
exports will be absent from the resulting wasm module due to linker dead
code elimination.

Now, suppose a wasm module has already been linked and it makes use of
JSFFI. This wasm module will contain ``ghc_wasm_jsffi`` custom
sections, and the section payloads include information like JSFFI
import code snippets and function arities. The next step is calling a
small post-linker script, which will parse the wasm module and emit a
JavaScript module. The `post-linker
<https://gitlab.haskell.org/ghc/ghc/-/blob/master/utils/jsffi/post-link.mjs>`_
is written in nodejs, though the resulting JavaScript module has
nothing nodejs specific and work in browsers as well.

.. code:: sh

   $ $(wasm32-wasi-ghc --print-libdir)/post-link.mjs -i hello.wasm -o hello.js

The generated JavaScript module contains a default export which is a
function. The function takes an ``__exports`` argument and generates the
``ghc_wasm_jsffi`` wasm imports. There is knot-tying going on here: only
after a wasm module is instantiated, you can access its exports, but the
``ghc_wasm_jsffi`` imports required for instantiation need access to the
exports! JavaScript is not a lazy language, but we can achieve
knot-tying less elegantly by using mutation:

.. code:: javascript

   let __exports = {};

   const { instance } = await WebAssembly.instantiateStreaming(
     fetch(wasm_url),
     {
       ghc_wasm_jsffi: (await import(js_url)).default(__exports),
       wasi_snapshot_preview1: ...
     }
   );

   Object.assign(__exports, wasm_instance.exports);

This way, the ``ghc_wasm_jsffi`` imports will have access to all exports
of the wasm instance.

After the wasm instance is created, initialization needs to be done:

.. code:: javascript

   wasi.initialize(wasm_instance);

The ``wasm32-wasi`` reactor module ABI defines the ``_initialize``
export function, which is auto generated at link time and it must be
called exactly once before any other wasm exports can be called. The
correct way to call it depends on the wasi implementation provider in
JavaScript.

Finally, in JavaScript, you can use ``await __exports.my_func()`` to
call your exported ``my_func`` function and get its result, pass
arguments, do error handling, etc etc.
