In this post I’ll explore how to use an existing C library in a JavaScript with WebAssembly.
This uses information from previous posts for
dataframes,
and
wasi.
You can find the source code
here.
The Toolchain
In this example I used the ready made clang toolchain from
wasi-sdk. I downloaded
wasi-sdk-11.0-linux.tar.gz
and unpacked it in /opt/wasi-sdk
as follows.
1 | cd /tmp |
I also installed wabt 1.0.16, and
wasmer, and unpacked them in /opt. I then
put the bin
directories from all on my path.
Cross Compiling
I decided to use the
GNU Scientific Library
as it is written in plain old C, and I can use it to further the
dataframes
project I have discuessed in previous posts.
Compiling it proved to be remarkably straightforward. After downloading and
unpacking the tarball I did the following.
1 | # Ensure wasi-sdk is on the path |
Amazingly that just worked. The result of the install was the folder/opt/gsl-2.6
with three sub-folders: include
, lib
, and share
.
Testing the Static Library
I created an example.c
with the following contents taken from the
gsl documentation.
1 |
|
I compiled this in the following manner (using the wasi-sdk clang).
1 | clang example.c -o example -O2 --target=wasm32-wasi -I/opt/gsl-2.6/include -L/opt/gsl-2.6/lib -lgsl -lm -lc |
No errors! But is it really a wasm file? We can test with the file
utility.
1 | $ file ./example |
This is all very cool, but what’s in the static library? Let’s take a look.
1 | # Make sure we're using the llvm archive function. |
It seems that the “object” files are themselves wasm, which get linked in to the
finaly module, which is itself wasm. Interesting!
We can run the “executable” file with wasmer
. Wasmer is a runtime engine for WebAssembly
modules.
1 | $ /opt/wasmer/bin/wasmer example1 |
It works :) Let’s see how to use the library in our JavaScript code.
Create the JavaScript
This is definately TL;DR
Making a concise example of this would have been much shorted, but I’m exploring
the data science possibilities of WebAssembly, so what I’ve done is wire the
functions in to my growing dataframe/series code.
First I need to create a helper to call aggregate functions. I do this in
the file wasi-memorymanager.js
.
1 | class WasmMemoryManager { |
To use the aggregate function I add it to the setup-wasi.js
.
1 | ... |
You can see here I’m calling the raw gsl function gsl_stats_mean
(I needed the
arrow function to provide a stride
parameter of 1
). What I want to show is
that there is little or no requirement for a binding library. We’re using generic
marshalling code.
The Series.js
needs a small patch to handle functions that return a single
value, rather than a new series. I added a check for the return value of the
function.
1 | class Series { |
Dude Where’s My Function?
Ideally we would like to call the function without any extra effort, but a
function from a static library will not be included in the final module unless
there is a reference to it. This means we need a C file to create a reference so
it gets exported. I did this as follows.
1 |
|
This is all we need to ensure the function is included in the emitted wasm module. A
specific binding function is unnecessary.
The example program
Now we just need to see if it works.
1 | const height = new Series('height', [1.82, 1.72, 1.64, 1.88], 'double') |
Success!
Thoughts
The thing that surprised me the most was how simple it was to create the static
library (although it’s possible I just got lucky with the library I chose).
My example was too verbose, But I’m hoping you’re following the journey with me
and have read my previous posts.
The gsl already provides a BLAS library, so we could write a JavaScript matrix
class and do linear algebra. My hope is that the anticipated FORTRAN compiler
(flang) will be just as easy to create a library from.