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
.s
assembly 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
wast
file into the binaryadd.wasm
file:
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
.
@OpenGG Sorry, apparently I don't receive notifications for gist documents. Unfortunately, I don't really know why you have this
panic
reference in your output. But please see the edit at the very top of this gist: compiling to wasm is much easier now.