Skip to content

WebAssembly Standalone

Alon Zakai edited this page Oct 23, 2019 · 40 revisions

When compiling to WebAssembly, emcc by default emits both a JavaScript file and a WebAssembly file. The JavaScript loads the WebAssembly which contains the compiled code. This is necessary in many cases as WebAssembly currently depends on a JavaScript runtime for features like longjmp, C++ exceptions, checking the date or time, printing to the console, using an API like WebGL, etc., and Emscripten will emit a JavaScript runtime to support those things.

However, if you don't need those things - if you have mostly pure computational code - or if you want to write your own JavaScript runtime, you can tell Emscripten to emit a "standalone" wasm file, one which doesn't depend on an Emscripten JS runtime. That lets you write your own runtime if you want, and you may be able to run the wasm in a a non-JS environment such as Wasmer, WAVM, or wasmtime.

Emscripten has a STANDALONE_WASM option that is the main way we support this (note that it requires the LLVM wasm backend). The flag is turned on automatically if you tell emcc to only emit a wasm file,

$ emcc source.c -o output.wasm

Otherwise, if you build with the flag,

$ emcc source.c -s STANDALONE_WASM

then it will emit a .js and .wasm file. The wasm is standalone as before, so you don't need the JS, but it may be convenient for running it on the Web.

Many APIs that emscripten uses depend on JS or Web APIs, though, like C++ exceptions as mentioned earlier, things like WebGL or WebAudio, etc. Calls to those APIs will still be emitted in standalone mode because we have no better alternative; this also gives you the option to add necessary APIs to a custom wasm embedding.

Aside from APIs that require JS or Web APIs, though, we try to use WASI APIs as much as possible so that the wasm can run in wasi-supporting runtimes.

JS/wasm ABI

emcc can optionally emit useful metadata in the wasm file, that a runtime can use. See the EMIT_EMSCRIPTEN_METADATA option, added in #7815. The metadata includes versioning as well as things like the memory and table sizes the wasm needs, that the runtime needs to provide.

Let the optimizer remove the runtime

As of 1.37.29, emcc's optimizer is powerful enough to remove all runtime elements that are not used (it does this using meta-dce, dead code elimination that crosses the JavaScript/WebAssembly boundary). This can be helpful as then the necessary runtime is smaller and easier to replace, making the output even more standalone. To try this, simply build with something like

emcc source.c -Os

-Os is needed to make the optimizer work at full power. You can also use -Oz or -O3 (but not -O2 or lower).

Notes:

  • If you use C++ objects, the compiler must in many cases emit code to catch exceptions so that RAII destructors are called, and exceptions require a lot of runtime support. Build with -fno-exceptions to disable exceptions entirely.
  • More details on how this works.

Create a dynamic library

Dynamic libraries have a formal definition, and are designed to be loadable in a standard way. They also do not link in system libraries like libc. For those reasons they may be useful in some cases, but dynamic libraries also have downsides, such as having relocations for memory and function pointers, which add overhead that may be unnecessary if you are only using one module (and not linking several together), so overall they are not recommended - use STANDALONE_WASM as described earlier.

If you do want to build a dynamic library, use

emcc source.c -s SIDE_MODULE=1 -o target.wasm

That will emit the output dynamic library as target.wasm.

To use a side module, see loadWebAssemblyModule in src/runtime.js for some example loading code.

  • The conventions for a wasm dynamic library are here.
  • Note that there is no special handling of a C stack. A module can have one internally if it wants one (it needs to ask for the memory for it, then handle it however it wants).
  • Full example.

Just view your own compiled code

To just see your own compiled code you can build to a wasm object file,

emcc input.cpp -o output.wasm -c

You can inspect that wasm file using wabt's wasm2wat or binaryen's wasm-dis.