A December update from the ocaml-wasm organisation

December is upon us and so it is time for an update since our last ocaml-wasm organisation September update.

Introduction

Earlier this year we formed the ocaml-wasm organisation to join efforts between two WebAssembly (Wasm) targeting compiler projects for OCaml:

  • wasocaml – a prototype backend for OCaml’s native code compiler based on the Flambda-intermediate representation
  • wasm_of_ocaml – a fork of js_of_ocaml that translates OCaml bytecode to Wasm instead of JavaScript

Exciting news: The WebAssembly Garbage Collection (WasmGC) extension which both wasocaml and wasm_of_ocaml depend on is now enabled by default in the latest:

wasm_of_ocaml

wasm_of_ocaml currently requires the following Wasm extensions:

As you may have inferred, with the latest release Chrome 119 can now run the output of wasm_of_ocaml out of the box!

For Firefox 120, one needs to enable the Wasm tail call extension first, but this will be remedied in the forthcoming 121 release.

OCaml 5.x code using effect handlers can be compiled in two different ways: one can either enable the CPS transformation from js_of_ocaml or emit code requiring the JavaScript-Promise Integration extension.

Previously, wasm_of_ocaml also required the experimental stringref proposal to convert strings between OCaml and JavaScript. The stringref proposal may however be superseeded by the newer JS-string-builtins proposal, which is being implemented in both Chrome and Firefox. With the JavaScript string situation up in the air, for now wasm_of_ocaml instead uses the TextEncoder/TextDecoder API on the JavaScript side to convert JavaScript to or from UTF-8 by writing strings to or reading them from a buffer in Wasm linear memory as an intermediate state.

Since September @vouillon has continued extending and improving wasm_of_ocaml:

In addition, @vouillon also contributed with a couple of broader Wasm community improvements:

  • Making binaryen more effective at cancelling out a boxing followed by an unboxing: WebAssembly/binaryen#5952 and
  • Improved the performance of Wasm exceptions in V8, the JavaScript / Wasm engine used by Chrome. After a number of optimisations, like using a cache to find which Wasm code correponds to a code pointer, they are about a order of magnitude faster. As a consequence the Boyer benchmark runs about 5 time faster.

Finally, he prepared and gave two presentations about wasm_of_ocaml:

26 Likes

What are the plans for integration with WASI (The WebAssembly System Interface)?

1 Like

WASI is certainly on our radar! :+1:

For a start wasm_of_ocaml has gone for building an almost drop-in replacement for js_of_ocaml. This lets us reuse and rely on a number of things in JavaScript (math function, string/float coersions, JS weak pointers, …) to get off the ground. For a non-web-hosted Wasm engine these will naturally have to be changed.

This biggest blocker is that none of the stand-alone “no-JS” engines support the WasmGC extension that both wasocaml and wasm_of_ocaml rely on, AFAIK.
There’s a (slightly out-of-date) extension overview available at Roadmap - WebAssembly.
If someone is aware of WasmGC progress in other such engines, please share! :slightly_smiling_face:

The AFAIK most advanced GC implementations in non-browser runtimes (apart from V8) are tracked in these issues

Wasmtime:

WasmEdge:

2 Likes

That’s great news! I had the impression that one of the limitations of js_of_ocaml is the support of libraries written in C. If I understand correctly, js_of_ocaml required a stubbed version written in JavaScript to work. With a compilation from Ocaml to WebAssembly, and other existing compilers from C to WebAssembly, would this mean we could run in the browser OCaml programs relying on some external C libraries?

With a compilation from Ocaml to WebAssembly, and other existing compilers from C to WebAssembly, would this mean we could run in the browser OCaml programs relying on some external C libraries?

Thanks to recent changes in clang, I can tell it will be possible with Wasocaml without requiring any change (or almost no change :slight_smile: ). We will provide alternative headers of the native FFI where current macros will be replaced by hand-written Wasm. The only limitation to expect is that Field won’t be usable as an l-value anymore. We’ll provide a Set_field one or equivalent.

1 Like

Out of curiosity, is providing a Set_field (distinguishing direct assignment to a field from Store_field/caml_modify) because wasocaml benefits from this distinction, or just because it allows the C code to be more easily shared between wasocaml and the native C runtime?

Would the aim be to get the addition of that macro into the main OCaml FFI asap? It sounds very related to the proposed Load_field change for 5.x (see Make Field macro a volatile cast by kayceesrk · Pull Request #11255 · ocaml/ocaml · GitHub and the full detail of 5.x’s Field issues in OCaml multicore memory model and C (runtime, FFI, VM) · Issue #10992 · ocaml/ocaml · GitHub)

2 Likes

I have not tried it but it should indeed be possible to link the code generated by Wasm_of_ocaml with a C library compiled to Wasm. You still need to write stubs to convert between OCaml values and C values.

As @zapashcanon is saying, with LLVM 17, it should be possible to reuse existing stubs written in C with minimal changes by compiling them with an alternative set of macros. We probably need some small changes in Wasm_of_ocaml as well for this to work, since LLVM only supports the reference type externref but we are using type (ref eq) for OCaml values, so some conversions have to be performed when calling the C stub functions.

@dra27 Field accesses have to be implemented by function calls, And there is no way to define a macro so that Field(x,n)=e is rewritten into set_field(x,n,e).

1 Like

But there is already a macro Store_field. Is it not possible to redefine it? I don’t understand where the need for a new macro Set_field comes from.

That’s not quite what I meant - I was wondering whether the intention was to upstream something like #define Set_field(x, n, e) Field(x, n) = e so that it could be overridden to be a function call?

If the block has not been initialized yet, it is not safe to use Store_field. When the block has been allocated with caml_alloc_small, one currently needs to perform direct assignments Field(v, n) = v. If the block has been allocated with caml_alloc_shr, one currently needs to use caml_initialize. One needs an alternative to this function as well.

@dra27 Yes, it would make a lot of change to upstream these macros for compatibility.