> C without the standard library (called “libc”) is pretty rough
If you don't want to pull in musl or a traditional libc, there's a more Wasm-y solution known as the Web Assembly System Interface (WASI) [0] that delegates the libc functionality to the runtime.
A WASI Wasm module can be compiled using clang, as in the article. The only difference is to use the WASI sysroot [1].
> optimization
LTO and -O3 are great! I've also found the Twiggy [2] tool useful for more "manual" optimization.
One of the things I like about Rust as an embedded developer is that they make a distinction between core and std portions of the standard library. Core is everything that only depends on memset, memcpy, and memcmp,.which means you know you get it for free on new ports.
Author here :) I am very excited about WASI, but as I mentioned in a comment below, wanted to keep it to the fundamentals so you can appreciate what WASI does for you.
And definitely agree with the shout-out to Twiggy (which I mention in the previous post in the series)!
If size is really important, you probably want to try -Os (optimize for size) and -Oz (try harder to optimize for size, including at the expense of CPU) as well.
> LTO
If your project gets big enough that -flto results in unacceptable link times, try -flto=thin.
I just created a code snippet in C [1] which prints hello world by using the (bare-metal) WASI interface. No headers included. The compiled wasm file works with wasmer, lucet, wasmtime and an web implementation of WASI [2]
I've been thinking of writing a blogpost comparing the advantages and disadvantages of emscripten, wasi, and plain llvm (which is what is discussed here), and also how those interact with web vs server. This space has definitely gotten more interesting recently!
One of the pain points to using Wasm in the real world, is the lack of decent debugging.
eg no ability to run code in a debugger, set breakpoints, etc.
That being said, it's an area being worked on.
Wasm generated by LLVM can already have debugging info stored in it using Wasm "custom sections" (they're a thing) in DWARF format. eg .debug_info, .debug_str, (etc)
So, debuggers are at least possible.
Unfortunately, the way Wasm does variables doesn't map to the way DWARF currently does them. So they can't be encoded correctly. A Major problem. :(
Yury Delendik is working through a spec for fixing that (officially):
Nothing specific, just pointing out that debug symbols are not necessary to find value from a debugger. A disassembly and registers (or stack, in this case?) view, along with a place to run debugger commands, is very useful in and of itself.
>AssemblyScript compiles strictly typed TypeScript (basically JavaScript with types) to WebAssembly using Binaryen. It generates lean and mean WebAssembly modules while being just an npm install away.
Since you can compile AssemblyScript into JavaScript with the TypeScript compiler as well as into WebAssembly, you can compare the speed of JavaScript -vs- WebAssembly on the same source code. Aaron did some interesting benchmarks using wasmboy, in the great tradition of using GameBoy emulators to benchmark JavaScript engines:
Hi Aaron! I got to the assemblyscript slack signin page here -- https://assemblyscript.slack.com/ -- but there's no obvious way to get an invitation. Where should I click or send a request to? Or if you could please send one to don@donhopkins.com, I'd appreciate that. Thank you!
This is a great article that takes you pretty far with very little. I think it's much easier to tinker and experiment when the boilerplate and tooling is reduced to a minimum -- you get a much deeper understanding in what's actually going on behind the scenes.
Yes, much easier to tinker with a more barebones system. The downside of powerful toolchains is often their complexity. So there's a difference between one being better for shipping code and one better for learning.
Nice comprehensive writeup. The next step, beyond the basic allocator provided, would be to use wasi-sdk (https://github.com/cranestation/wasi-sdk) which provides a full musl-based libc, targeting the WASI interfaces. With this, you can invoke a C program with arguments, environment variables, and filesystem access.
The inNative WebAssembly Runtime ran into this problem as well, and includes wasm_malloc.c, which can be linked against your application to provide a simple malloc() implementation without having to write one yourself or depend on WASI.
Author here :D WASI is great and has me all kinds of excited, but I wanted to cover the fundamentals so people can appreciate what WASI really gives you.
Off-topic: the visual aesthetic of this blog really has its own unique charm. It's truly something else and nonetheless seems to do the trick pretty well.
Though I'm certain the security concerns were integral to the design of WASM, the ability to pass a pointer from JavaScript downstream into WASM terrifies the hell out of me.
I know that theoretically every WASM module is supposed to have a fully isolated memory block, but I can't help but wonder about the day where a bug allows WASM to deliver malware payloads to read other web browser tabs. Let's hope that WASM doesn't become everyday in advert networks.
If you don't want to pull in musl or a traditional libc, there's a more Wasm-y solution known as the Web Assembly System Interface (WASI) [0] that delegates the libc functionality to the runtime.
A WASI Wasm module can be compiled using clang, as in the article. The only difference is to use the WASI sysroot [1].
> optimization
LTO and -O3 are great! I've also found the Twiggy [2] tool useful for more "manual" optimization.
[0] https://wasi.dev [1] https://github.com/CraneStation/wasi-sdk/releases [2] https://rustwasm.github.io/twiggy/