Announcing the ocaml-wasm organisation

We are happy to announce the creation of the ocaml-wasm Github organisation which will work towards WebAssembly (Wasm) support for OCaml. The goal of the organisation is to enable users to build high-performance and secure applications for the Wasm platform using the OCaml programming language and the platform tools.

Wasm is a binary instruction format, designed as a portable compilation target for programming languages, enabling deployment on the Web for client and server applications. It provides a sandboxed execution environment, which also makes it attractive for both blockchains and Cloud deployment. For example, Fastly’s Compute@Edge platform is based on WebAssembly.

Compilers

The ocaml-wasm organisation brings together the ongoing efforts at compiling OCaml to Wasm.

wasocaml

OCamlPro has been developing wasocaml, an experimental compiler backend for OCaml that targets Wasm starting from the flambda intermediate representation of the OCaml compiler. OCamlPro engineers have also been contributing to the Wasm GC working group to ensure that the GC and other extensions in Wasm are amenable to targeting OCaml.

wasm_of_ocaml

Alongside this effort, Tarides has been working on implementing wasm_of_ocaml, a toolchain to compile OCaml to WebAssembly based on the battle-tested js_of_ocaml compiler. Both js_of_ocaml and wasm_of_ocaml use OCaml bytecode as the input and emit JavaScript and Wasm respectively.

It is notable that these efforts started off jointly a few years ago with an exploration aimed at adding a Wasm backend to OCaml. At that time, it was clear that Wasm did not have enough features to support OCaml efficiently. With the implementation of several key Wasm extensions in browser engines, the time is right for this effort.

There exist other approaches to running OCaml programs using Wasm runtimes, where the speed of generated code is less of a concern. For example, a relatively straightforward approach is to compile the OCaml bytecode interpreter to Wasm and interpret the bytecode programs. ocamlrun-wasm and wasicaml are ports of the OCaml bytecode interpreter to Wasm. Wasicaml furthermore has a compiler mode somewhat similar to wasm_of_ocaml (but simpler) that parses a bytecode executable and translates it into Wasm.

Evolving Wasm

Wasm is an evolving standard. Wasm 1.0 focuses on low-level support for compiling low-level languages such as C and C++. There are several extensions under development which aim to provide support for targeting high-level languages including garbage collection, tail calls, stack switching and threads. In particular, the Wasm development process is completely open and anyone can participate in the evolution of Wasm.

The OCamlPro engineers contributed to the Wasm GC extension with the help of their experimental flambda to Wasm compiler. Both OCamlPro and Tarides engineers are actively involved with the Wasm development process on multiple fronts. Having more than one approach to compile OCaml to Wasm allows us to build comprehensive evidence and make informed arguments to influence the design of Wasm extensions. In addition, a major implementation effort is the runtime system support for Wasm-compiled OCaml code, which will be shared between the different OCaml to Wasm compilers.

OCaml-Wasm monthly update

We will post a summary of the work done in the ocaml-wasm org on OCaml discuss.

Happy hacking!

80 Likes

My understanding is that Wasm (WebAssembly) is now seen both as a way to improve the performance of Webapps, but also as a new portable format to deploy applications in the Cloud!

Note that, if you are interested in the wasocaml approach (directly targeting WASM from the OCaml compiler) by OCamlPro, we are looking for sponsors!

Currently, wasocaml is still a very early prototype (and we will add soon some doc on how to play with it). It allows the OCaml native compiler to compile an OCaml module to both native code and Wasm at the same time, and to finally generate a full Wasm binary. The long term goal would be to upstream it as an official backend of the standard compiler.

We still have much work to do:

  • Finish and polish the work on the compiler (adapt to latest Wasm versions, switch to OCaml 5, Flambda2, etc.)
  • Implement a full Wasm version of the runtime
  • Provide two FFIs: one compatible with js_of_ocaml, one compatible with C/emscripten
  • Integration into dune for easy build of projects

Also interesting to mention, as OCamlPro started investigating and working on Wasm two years ago, participating actively in the GC-related Working Group, Leo Andres and Pierre Chambart have implemented a complete and efficient Wasm interpreter in OCaml, called owi (GitHub - OCamlPro/owi: OCaml WebAssembly Interpreter), with as goal to be able to easily prototype new evolution proposals for Wasm. Leo is currently doing a PhD on Garbage Collection in Wasm under the supervision of Pierre and Jean-Christophe Filliâtre.

Anyway, do not hesitate to contact us to support these two projects !

14 Likes

I added a few notes on how to try Wasocaml. Don’t expect everything to work now as this was indeed a quick prototype (more on this below).

The goal of Wasocaml was to convince the Wasm-GC working group of the usefulness of i31ref because we knew it would be needed by OCaml. Without this work they may had been removed from the proposal. We presented the work in January to the working group. In March we also presented this work at the Dagstuhl seminar on the foundations of WebAssemby which was the opportunity to discuss with some active members of the Wasm community such as Andreas Rossberg, Ben Titzer and Conrad Watt. In April, the Guile compiler to Wasm-GC was also presented and it was also using i31ref. Having two languages using i31ref convinced the working group. This is what allows Wasm_of_ocaml to use i31ref today. :slight_smile:

Wasocaml is also the first compiler for a real-world functional languages to Wasm-GC. We developed many strategies for the memory representation of values. For now we only presented two of them (more are coming!) and they should work for other languages/compilers. Wasm_of_ocaml and Guile are using similar techniques to what we presented.

A little bit more about the differences between Wasocaml and Wasm_of_ocaml… First, Wasm_of_ocaml being a tool external to the compiler, it is of course easier to deploy quickly. Wasocaml starts from Flambda and will thus benefit all the optimizations that are missing from the bytecode. Binaryen will be able to recover some performances but it has its limits. Moreover, Wasocaml is going to play nicely with separate compilation. Also, Wasm_of_ocaml is limited in its value representation choice (it must represent blocks as eqref arrays) whereas Wasocaml can use more sophisticated representations.

Also note that none of the two are actually usable in a production today. They both depends on many Wasm extenstions (typed function references, GC, tail-call, exception handling, string reference…) that are not yet available in browsers (you can use them in chrome if you turn some experimental flags on).

Alongside all the future works that @lefessan described, I’m also formalizing the compilation of flambda to Wasm-GC with a proper semantics.

On a related note, we indeed implemented Owi, a Wasm interpreter written in OCaml. It was a way to experiment with language extensions. Currently, our main work on the interpreter is to add the possibility to do symbolic execution of Wasm code (aka whitebox fuzzing). This allows to automatically find input values that lead to a crash in the program. The nice thing is that it allows to find bugs in code written in another language and compiled to Wasm (e.g. C).

18 Likes

Can’t find a reference on what i31ref is, but does this mean that the OCaml int type is going to be 31-bits in wasocaml ?

It’s quite limiting in practice (e.g. you can only index 1GB worth of bytes). That’s the only thing I was happy to see go when OCaml dropped native support for 32-bit systems.

1 Like

In Wasm1 there was only int32, int64, float32 and float64.

Wasm-GC adds some reference types:

They’re all managed by the GC. There’s a subtyping hierarchy between these types, as illustrated on the picture.

In Wasocaml, we represents everything as an eq which is the type of values that can be tested for physical equality. Then, small scalars (int, bool, char, constant constructors of sum types etc.) are represented using i31. Blocks can be represented either by an array of eq (with the tag stored at the beginning or at the end) or by a struct (which is more complicated and has many possible representations), we have strategies for both.

At runtime, there’s a (very cheap) cast from eqref to either i31 or struct/array when we need to access the value and do something specific with it.

The various Wasm engines are expected to represent struct and array with a pointer, but i31 will be an unboxed 31 bits integer (probably through a one bit tag). This was done to avoid having to box each small scalar.

So yes, int is going to be 31 bits. But even if it was i32, the problem would be the same: Wasm GC arrays can only be indexed by an i32.

Note that a similar limitation is already present in Wasm1. First, a module’s memory can be at most 4GB and each module can have only one memory. There’s a proposal to have multi-memory. But then again, you can only use an i32 as a memory index. But as always with Wasm, there’s a proposal for that ! It’s the memory64 proposal, to allow using i64 as an index.

There are some discussions about allowing to e.g. have the memory readable through a GC array. Combining this with the memory64 proposal would allow to index much more bytes. But then we would still need to change other things either in the OCaml side or on the Wasm-GC side. I’ve not been thinking about this that much so it’s quite prospective.

3 Likes

Hi there

I appreciate the work on this, and find myself a bit left in the dark, as to which implementation is at which stage of development, and suitable for which task.

Specifically, I am interested to run a language on https://lunatic.solutions

This is an Erlang like runtime, that turns every wasm code into an actor based platform.
So you dont have to write your code asynchronously, and it still behaves that way.
And interacts with all other wasm languages on the platform.

There is a simple wrapper to write, but I am willing to do that. I am just baffled about the current state of compiler, and how fast we could see one production ready, for that platform.

As an example: The README of wasm_of_ocaml suggests it runs only in web focused platforms, specifically the beta/canary versions of V8 (Node): Why would that be?

There are lots of platforms like wasmtime, wasmer and wasm3, who can probably all provide support for OCaml at this point.

The READMEs of all the wasm related compiler seem to be silent on their current state.
How can we improve that?

wasm_of_ocaml builds on top of GC and stringref proposals. These extensions are still in development and on its way to make it into Wasm proper. Many runtimes may not implement these experimental extensions. If a runtime supports both of these, then wasm_of_ocaml compiled code can run on them.

5 Likes

I believe wasm_of_ocaml also requires the Typed Function References proposal. And I believe the Reference-Typed Strings proposal is needed only for the JS FFI ?

For wasocaml, it doesn’t require the Reference-Typed Strings proposal. But it requires the Tail Call one (and optionally the Exception Handling, as we recently added a way to not use Wasm exceptions as they’re too slow on V8) because it’s not doing brutal CPS transformation as jsoo is doing.

You can get an overview of which proposal are implemented on which engine/tool on the Wasm website (I’m not sure this is completely up to date tbh).

(spoiler: V8 is the only one likely to be able to run anything related to WasmGC for now)

1 Like

At the moment, wasm_of_ocaml is primarily designed as an alternative to js_of_ocaml. But I agree that we should eventually consider other platforms. It is at a rather advanced stage of development, with a large part of the OCaml standard library supported.

wasm_of_ocaml builds on top of the GC proposal (itself based on the Typed Function References proposal), which is indeed only supported by V8 at the moment. Doing without taking advantage of this proposal would require a completely different implementation strategy (and would make it significantly more difficult to interoperate with JavaScript). Note that there are some plans to implement this proposal in Wasmtime (which is used by Lunatic).

wasm_of_ocaml makes a liberal use of other Wasm proposals, to make the implementation simpler, and to see how well the proposals fit OCaml’s needs. It also relies quite a lot on JavaScript for now, in particular for math functions and to implement weak pointers.

Reference-Typed Strings (stringref) is the most experimental proposal we are relying on. It allows to convert between JavaScript strings and Wasm byte arrays. We are indeed using it to interoperate with JavaScript, but also for float parsing and formatting (using JavaScript helper functions). We will reconsider using it if it does not get widely implemented.

wasm_of_ocaml is optionally relying on the JS Promise Integration proposal to implement effects. Alternatively, effects can be implemented by performing a partial CPS transformation of the code.

It is also taking advantage of the Tail Call and Exception Handling proposals.

4 Likes

wasm_of_ocaml is optionally relying on the JS Promise Integration proposal to implement effects. Alternatively, effects can be implemented by performing a partial CPS transformation of the code.

On this note, we have been working on WasmFX, an effect handler extension for Wasm that closely mirrors the primitives used by OCaml effect handlers. WasmFX has been prototyped in Wasmtime. When the GC extension appears in Wasmtime, I’m very keen to try to compile OCaml effect handlers to Wasmtime + WasmFX. Note that WasmFX is still in the early stage of the Wasm proposal process compared to the other proposals such as GC, tail calls, exception handling, etc.

The paper on WasmFX, [2308.08347] Continuing WebAssembly with Effect Handlers, will appear at OOPSLA 2023.

3 Likes

WasmEdge should also add GC support soon https://github.com/WasmEdge/WasmEdge/issues/1122#issuecomment-1636690425

WasmEdge is so far the only WASM runtime apart from V8 (browser, Node, Deno) that supports tail calls and the runtime I use for stuff that doesn’t need GC.

1 Like

No, this site is completely wrong (GC in Deno and Node for example) and missing some runtimes (like WasmEdge and Wasm3). You have to check each runtime for yourself.

1 Like