WebAssembly is a new binary format for executing code on the web, allowing much faster start times (smaller download, much faster parsing in browsers) for Emscripten projects. Emscripten supports compiling to WebAssembly with a compiler flag, so it is easy for projects to target both WebAssembly and asm.js.
For more background, see
WebAssembly is emitted by default, without the need for any special flags.
If you don’t want WebAssembly, you can disable it with something like
emcc [..args..] -s WASM=0
Emscripten’s WebAssembly support depends on Binaryen, which will be automatically fetched and built for you (you may see logging about that, the first time you compile to WebAssembly).
BINARYEN*, etc. options only matter when compiling to your final executable. In other words, the same .o files are used for both asm.js and WebAssembly. Only when linking them and compiling to asm.js or WebAssembly do you need to specify WebAssembly if you want that. That means that it is easy to build your project to both asm.js and WebAssembly.
Emscripten can currently (April 2019) use 2 backends to generate WebAssembly: fastcomp (the asm.js backend, together with asm2wasm) and the upstream LLVM wasm backend.
Fastcomp is currently the default, but we hope to switch the default soon to the upstream backend.
To use fastcomp, just use the emsdk normally to get
latest. For the upstream backend, you can use
latest-upstream for now on Linux, until we finish setting up builders, or you can set LLVM in the
.emscripten file to point to a build you make of very recent LLVM (preferably from git/svn master).
float unrepresentable in integer range,
integer result unrepresentable,
integer overflow, or
Out of bounds Trunc operation.
In fastcomp/asm2wasm, emscripten will emit code that is optimized for size and speed, which means it emits code that may trap on the things mentioned before. That mode is called
allow. The other modes are
clamp, which will avoid traps by clamping values to a reasonable range, and
In general, using
clamp is safest, as whether such a trap occurs depends on how the LLVM optimizer optimizes code. In other words, there is no guarantee that this will not be an issue, and updating LLVM can make a problem appear or vanish (the wasm spec process has recognized this problem and intends to standardize new operations that avoid it). Also, there is not a big downside to using
clamp: it is only slightly larger and slower than the default
allow, in most cases. To do so, build with
However, if the default (to allow traps) works in your codebase, then it may be worth keeping it that way, for the (small) benefits. Note that
js is often useful for debugging, though).
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.
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
.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).
.wasmfile is not standalone - it’s not easy to manually run it without that
.jscode, as it depends on getting the proper imports that integrate with JS. For example, it receives imports for syscalls so that it can do things like print to the console. There is work in progress towards ways to create standalone
.wasmfiles, see the WebAssembly Standalone page.
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.
asm.js support is considered very stable now, and you can change between it and wasm with
-s WASM=1, so if you see something odd in a wasm build, comparing to a parallel asm.js build can help. In general, any difference between the two could be a compiler bug or browser bug, but there are a few legitimate causes of different behavior between the two, that you may want to rule out:
-s SAFE_HEAP=1, that will catch all such invalid accesses.
"js"when comparing builds. The
"js"trap mode is also useful in a single build, as otherwise operations like division or float-to-int may trap, and the optimizer may happen to change whether a trap occurs or not, which can be confusing (for example, enabling
SAFE_HEAPmay prevent some optimizations, and a trap may start to occur). Instead, in the
-s "BINARYEN_METHOD='asmjs,native-wasm'"etc.) but due to its complexity and low value it was removed.
-s PRECISE_F32=1, in which case it should be identical to wasm.
interpret-binarymethod, as discussed above).
If you find that an asm.js build has the same behavior as a wasm one, then it is currently easier to debug the asm.js build: you can edit the source easily (add debug printouts, etc.), there is debug info and source maps support, etc.
When you do need to debug a WebAssembly build, the following tips might help you.
WebAssembly doesn’t have source maps support yet, but building with
-g will emit both a text and a binary wasm, and it will include function names in both, and also include source file and line number information in the text, for example, building hello world might have this in the
;; tests/hello_world.c:4 (drop (call $_printf (i32.const 1144) (get_local $$vararg_buffer) ) ) ;; tests/hello_world.c:5 (return (i32.const 0) )
This indicates that the
printf call comes from line 4, and the return from line 5, of
.wasmfiles 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.
Module['wasmBinary']and it will be used from there, and then (with async compilation off) compilation should be synchronous.
To serve wasm in the most efficient way over the network, make sure your web server has the proper MIME time 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