Going Straight to clang for WebAssembly

Doug Schaefer
May 24 · 3 min read

A few years ago at EclipseCon I gave a demo of a C++ app using libSDL2 and showed how you build it with CDT and launch it for multiple platforms, my desktop, a BeagleBone running QNX, and finally in a web browser using Emscripten. I used CMake for the build system and that worked fine for the first two, but Emscripten really fought the idea of something else driving the build. I finally figured it out but it left the impression that there had to be a simpler way to build WebAssembly apps.

Recently with version 8 of clang, they have made the wasm target a first class citizen available with the standard distribution. I thought I’d take a look and found at least one example on github that showed how. Here’s a quick summary on how to get started. Be warned, though, one of the arguments is nostdlib which means this is a very barebones example. But that’s another area where I think Emscripten has gone a little to far with. More on that later.

To start this example is a pretty basic Fibonacci calculator, pretty standard for WebAssembly. Here’s the C++ file.

#include “wasm.h”WASM_IMPORT void log(int i);WASM_EXPORT int fib(int i) {
    int res = i <= 1 ? i : fib(i — 1) + fib(i — 2);
    log(res);
    return res;
}

I wanted to show C++ calling back into JavaScript so there’s a very contrived log method we import. The fib function itself is pretty basic. I’ve created a couple of macros in the wasm.h file to manage marking functions as import or export.

#define WASM_EXPORT __attribute__((visibility(“default”))) \
    extern “C”
#define WASM_IMPORT extern “C”

Since I’m writing C++ I want to make sure the compiler doesn’t mangle the names so I declare them as extern “C”. As you can see, the export also turns on the visibility of the symbol which is hidden by default in the Makefile.

I’m running this with node.js which has had WebAssembly support since at least version 8 that I have on my Linux box. The idea is to do some of the more computationally expensive tasks in my node server using wasm. Here’s my js file.

const fs = require(‘fs’)async function run() {
  const buf = fs.readFileSync(‘./fib.wasm’)
  return await WebAssembly.instantiate(buf, {
    ‘env’: {
      ‘log’: function(i) { console.log(`log: ${i}`) }
    }
  })
}run().then(res => {
  const { fib } = res.instance.exports
  console.log(fib(10))
})

It simply loads up the wasm file and instantiates it passing in the log function. When that’s complete, I extract my fib function from the exports and run it. You should see the output of the log function (more times that I was expecting at least), then the result, 55.

As with most things C++, the magic is actually in the Makefile.

CXX = $(HOME)/wasm/clang-8/bin/clang
CXXFLAGS = \
    -Wall \
    --target=wasm32 \
    -Os \
    -flto \
    -nostdlib \
    -fvisibility=hidden \
    -std=c++14 \
    -ffunction-sections \
    -fdata-sectionsLD = $(HOME)/wasm/clang-8/bin/wasm-ld
LDFLAGS = \
    --no-entry \
    --strip-all \
    --export-dynamic \
    --initial-memory=131072 \
    -error-limit=0 \
    --lto-O3 \
    -O3 \
    --gc-sectionsfib.wasm: fib.o
    $(LD) $(LDFLAGS) -o $@ $<

There’s lots of magic flags here and I have to thank the author of the example I linked above for getting me started. I’ll have to play with them to see what’s actually necessary. The key here is that it isn’t Emscripten but straight clang 8 that I downloaded from llvm.org. There’s no standard library, so don’t go and try and do a printf. You’re a bit on your own for now.

But that’s somewhat a conclusion I reached. Emscripten allows C++ developer to easily port their apps to run on the web. It doesn’t make the C++ developer think like a Web developer. What would be interesting to me is what it would look like if you weren’t handed those fancy libraries you get with Emscripten and really just wanted to build a web app, like a game, using the standard JavaScript APIs you get with node or the browser. I think you’d end up writing programs like an Arduino developer where you don’t have printf either…

Doug Schaefer

Written by

I am the Eclipse CDT project lead and a Software Architect at QNX, a BlackBerry company, working on the Momentics IDE. My opinions are my own.