Screenshot from https://tsoding.github.io/jai-wasm/
Jai does not officially support WebAssembly compilation target. BUT! It allows you to dump LLVM Bitcode via the Llvm_Options.output_bitcode
flag. This Proof-of-Concept demonstrates how to exploit this feature to compile Jai program to WebAssembly.
The compilation works only on Linux right now, sorry. You also need to have Clang installed on your machine.
$ jai -version
Version: beta 0.1.051, built on 9 January 2022.
$ jai first.jai
$ python3 -m http.server 6969
$ iexplore.exe http://localhost:6969/
For detailed information on how everything works I recommend to read the ./first.jai. The main idea is to dump the LLVM Bitcode of the compiled program and translate it with Clang to WebAssembly bytecode.
Also check out ./js/load.js which loads up the final WebAssembly module and sets up the environment for it.
The library that comes with Jai uses quite a few #asm
blocks. The problem is that they are not translatable to WebAssembly. We wrote a simple meta program in ./first.jai that simply removes the blocks during the compilation and advises not to call the functions that had the #asm
blocks. (If you have a better solution, please let me know).
If you ever tried to compile Jai to WebAssembly you are probably familiar with that error. It happens when you try to compile the program specifically to wasm32. Jai Compiler only supports 64 bit platform and does all of its data segments computations around 64 bit pointers (at least this is how I think it works).
So to avoid that error you need to compiler with --target=wasm64
flag of Clang which generates a binary with memory64 extension. But since this extension is experimental (yes, addressing memory with 64 bits is experimental in 2022...) such binary may not work everywhere. To make the binary more portable we convert it to wasm32 using wasm64232 utility.
TODO
Each Jai function (except the ones that are marked with #no_context
) accepts an implicit pointer to Context. If you want to enable the Jai code to use the Context you need to figure out that pointer in JavaScript and pass it accordingly.
The way we figure it out is by calling the entry point of the Jai program from JavaScript. Literally the main
function. Within that function we call set_context(*context)
which jumps back to JavaScript and in JavaScript we save that pointer in a global variable somewhere.
From now on every time we need to pass the Context to any of the Jai functions we just pass the saved pointer.
For more details see main
procedure in ./main.jai and set_context
function in ./js/load.js.
This code was written on a livestream:
If you have any questions, suggestions or something is not documented well feel free to file an Issue or a PR.