Building to WebAssembly — Emscripten 4.0.9-git (dev) documentation (original) (raw)

WebAssembly is a binary format for executing code on the web, allowing fast start times (smaller download and much faster parsing in browsers when compared to JS or asm.js). Emscripten compiles to WebAssembly by default, but you can also compile to JS for older browsers.

For some historical background, see these slides and this blogpost.

Setup

WebAssembly is emitted by default, without the need for any special flags.

Note

If you don’t want WebAssembly, you can disable it with something like

Note

Deciding to compile to Wasm or JS can be done at the linking stage: it doesn’t affect the object files.

Backends

Emscripten emits WebAssembly using the upstream LLVM Wasm backend, since version 1.39.0 (October 2019). Previously emscripten also supported the old fastcomp backend which was removed in 2.0.0 (August 2020).

There are some differences you may notice between the two backends, if you upgrade from fastcomp to upstream:

Trapping

WebAssembly can trap - throw an exception - on things like division by zero, rounding a very large float to an int, and so forth. In asm.js such things were silently ignored, as in JavaScript they do not throw, so this is a difference between JavaScript and WebAssembly that you may notice, with the browser reporting an error like float unrepresentable in integer range, integer result unrepresentable, integer overflow, or Out of bounds Trunc operation.

The LLVM Wasm backend avoids traps by adding more code around each possible trap (basically clamping the value if it would trap). This can increase code size and decrease speed, if you don’t need that extra code. The proper solution for this is to use newer Wasm instructions that do not trap, by calling emcc or clang with -mnontrapping-fptoint. That code may not run in older VMs, though.

Compiler output

When using emcc to build to WebAssembly, you will see a .wasm file containing that code, as well as the usual .js file that is the main target of compilation. Those two are built to work together: run the .js (or .html, if that’s what you asked for) file, and it will load and set up the WebAssembly code for you, properly setting up imports and exports for it, etc. Basically, you don’t need to care about whether the compiled code is asm.js or WebAssembly, it’s just a compiler flag, and otherwise everything should just work (except the WebAssembly should be faster).

You may also see additional files generated, like a .data file if you are preloading files into the virtual filesystem. All that is exactly the same as when building to asm.js. One difference you may notice is the lack of a .mem file, which for asm.js contains the static memory initialization data, which in WebAssembly we can pack more efficiently into the WebAssembly binary itself.

WebAssembly feature extensions

Since its original launch, WebAssembly has been expanded with various feature extensions, which have been implmented in browsers. A list of features (including already-shipped and in-progress) and details about browser versions that support them can be found onwebassembly.org.

Several of these features can be used by Emscripten (or are by default) and can be enabled or disabled individually (using either Clang or emscripten flags) or by selecting which version of browsers Emscripten should target.

Examples:

For the features that are enabled by default (or will be when sufficient browser support exists), it’s also possible to control them by specifying which browser versions you want to target. You can use the-sMIN_FIREFOX_VERSION setting(and also MIN_CHROME_VERSION, MIN_SAFARI_VERSION andMIN_NODE_VERSION). Setting a value lower than the default version will disable features not supported by the specified version. Some features (e.g. Exception handling and threads) are not enabled by default because they have tradeoffs (e.g. binary size costs or restrictions on how the resulting wasm can be used such as COEP headers). These are not controlled by the browser version flags and must be enabled explicitly. See the settings page for details of the default browser versions Emscripten targets.

.wasm files and compilation

WebAssembly code is prepared somewhat differently than asm.js. asm.js can be bundled inside the main JS file, while as mentioned earlier WebAssembly is a binary file on the side, so you will have more than one file to distribute.

Another noticeable effect is that WebAssembly is compiled asynchronously by default, which means you must wait for compilation to complete before calling compiled code (by waiting for main(), or the onRuntimeInitialized callback, etc., which you also need to do when you have anything else that makes startup async, like a .mem file for asm.js, or preloaded file data, etc.). You can turn off async compilation by setting WASM_ASYNC_COMPILATION=0, but that may not work in Chrome due to current limitations there.

Web server setup

To serve Wasm in the most efficient way over the network, make sure your web server has the proper MIME type for .wasm files, which is application/wasm. That will allow streaming compilation, where the browser can start to compile code as it downloads.

In Apache, you can do this with

AddType application/wasm .wasm

Also make sure that gzip is enabled:

AddOutputFilterByType DEFLATE application/wasm

If you serve large .wasm files, the webserver will consume CPU compressing them on the fly at each request. Instead you can pre-compress them to .wasm.gz and use content negotiation:

Options Multiviews RemoveType .gz AddEncoding x-gzip .gz AddType application/wasm .wasm