EDIT November 2017: recently the target wasm32-unknown-unknown was added to rustc which uses the LLVM WASM backend and works without Emscripten. This is now the recommended way of generating WASM code from Rust (as it is much easier). Thus, this gist document is pretty much useless now. A great resource on getting started with WASM and Rust is hellorust.com: Setup and Minimal Example.
- Download the latest llvm version, preferably directly from their svn repo or this git mirror. Compile LLVM with enabled wasm-backend (this will take around one hour):
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly ..
make -j8
-
Download and compile Binaryen. Just clone the git repo, cd into it and follow the build instructions.
-
Install a recent version of
rustc(stable channel is fine, though!). -
Make sure all of the above software is installed and correctly in your
$PATH. We need several executables usually hidden in thebin/folder.
- Write your awesome Rust library (
add.rs):
#[no_mangle]
pub fn add_twenty_seven(n: i32) -> i32 {
n + 27
}We add the #[no_mangle] attribute to preserve the exact function name instead of mangling it (this is useful for usage from other Rust code, therefore it's on by default).
- Compile the library into llvm bitcode (resulting in
add.bc):
rustc --crate-type=lib --emit=llvm-bc add.rs
- Convert llvm-bc to wasm assembly in a linear assembly format (resulting in
add.s):
llc -march=wasm32 add.bc
- Convert the
.sassembly into an s-expression assembly format (resulting inadd.wast):
s2wasm -o add.wast add.s
This should result in something like this:
(module
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "add_twenty_seven" (func $add_twenty_seven))
(func $add_twenty_seven (param $0 i32) (result i32)
(i32.add
(get_local $0)
(i32.const 27)
)
)
)
- Finally, assembly the
wastfile into the binaryadd.wasmfile:
wasm-as add.wast
You can embed it into a website like so:
index.html:
<html>
<head>
<script src="main.js"></script>
</head>
<body>Hi</body>
</html>main.js:
function loadWasm() {
fetch('add.wasm').then(response =>
response.arrayBuffer()
).then(buffer =>
WebAssembly.instantiate(buffer)
).then(({module, instance}) => {
console.log(instance.exports.add_twenty_seven);
console.log(instance.exports.add_twenty_seven(4));
});
}
loadWasm()Next, start a local webserver. This is necessary, file server does not work!
python3 -m http.server 8000
Visit localhost:8000 in your browser, open the JS console and you should see:
function 0() { [native code] }
31
print.rs:
extern {
fn print(n: i32);
}
#[no_mangle]
pub fn print_twenty_seven_more(n: i32) {
unsafe {
print(n + 27);
}
}main.js:
function loadWasm() {
var importObj = {env: {
print: console.log,
}};
fetch('print.wasm').then(response =>
response.arrayBuffer()
).then(buffer =>
WebAssembly.instantiate(buffer, importObj)
).then(({module, instance}) => {
console.log(instance.exports.print_twenty_seven_more(2));
});
}
loadWasm()Compile the Rust file exactly as explained above. Also use the index.html from above. The JS console should show 29.
I follow these instructions and get the following
rust.wastcontent:And I have to modify main.js accordingly
What's with the
_ZN4core9panicking5panic17hc8058a23bb7ed97dEimport?My rustc and llvm version:
root@1c77e03e1f13:/work# rustc --version rustc 1.21.0 (3b72af97e 2017-10-09) root@1c77e03e1f13:/work# llc --version LLVM (http://llvm.org/): LLVM version 6.0.0svn Optimized build. Default target: x86_64-unknown-linux-gnu Host CPU: cannonlake Registered Targets: aarch64 - AArch64 (little endian) aarch64_be - AArch64 (big endian) amdgcn - AMD GCN GPUs arm - ARM arm64 - ARM64 (little endian) armeb - ARM (big endian) bpf - BPF (host endian) bpfeb - BPF (big endian) bpfel - BPF (little endian) hexagon - Hexagon lanai - Lanai mips - Mips mips64 - Mips64 [experimental] mips64el - Mips64el [experimental] mipsel - Mipsel msp430 - MSP430 [experimental] nvptx - NVIDIA PTX 32-bit nvptx64 - NVIDIA PTX 64-bit ppc32 - PowerPC 32 ppc64 - PowerPC 64 ppc64le - PowerPC 64 LE r600 - AMD GPUs HD2XXX-HD6XXX sparc - Sparc sparcel - Sparc LE sparcv9 - Sparc V9 systemz - SystemZ thumb - Thumb thumbeb - Thumb (big endian) wasm32 - WebAssembly 32-bit wasm64 - WebAssembly 64-bit x86 - 32-bit X86: Pentium-Pro and above x86-64 - 64-bit X86: EM64T and AMD64 xcore - XCore