Crystal Wasm

Another weekend, another wabt-hole.

I got crystal to compile to WebAsembly and then run in Brave, and it only took two days to get setup and get it to print out “Hello, World!” in the console.

I have tried to use WebAssembly a couple of years ago, but dropped it once I failed to get a simple hello world program to function. This is the same reason I don’t have Haskell under my programming utility belt, no hello world. It is rather helpful that the environment, tools and tutorials have come a long way in the past couple of years.

I got started with this YouTube video on creating a simple hello world program with hand-written HTML+JavaScript+WAT. I was able to clear that very low hurdle in a few minutes, then proceeded onto the much more difficult compiling crystal to WASM.

I threw together a one-line crystal hello world:

puts "Hello, World!"

then tried ```crystal build –target=wasm32 main.cr`` and got this lovely error that should not be possible to get:

[teknomunk@artemis cr-webasm]$ crystal build --target=wasm32 main.cr
Unhandled exception: Crystal::Compiler#program cannot be nil (NilAssertionError)
  from ???
  from ???
  from ???
  from __crystal_main
  from main
  from __libc_start_main
  from _start
  from ???

Huh… Digging around on the web got me zero useful results, which is always fun (not) when you are fighting a compiler. After a while, I stumbled upon this site that has this useful snip-it:

clang \
  --target=wasm32 \ # Target WebAssembly
  -emit-llvm \ # Emit LLVM IR (instead of host machine code)
  -c \ # Only compile, no linking just yet
  -S \ # Emit human-readable assembly rather than binary
  add.c

Checking with the crystal man page revealed that it had a --emit llvm-ir option. Excellent. Now to get rid of the standard library. An attempt to write an operating system in crystal had the flags I needed: --prelude=empty --static --cross-compile. Then I just had to llvm-ir to wasm with llc -march=wasm32 -typetype-ojb main.ll, and link it with a entry point code written in C/C++ (wasm-ld --no-entry --allow-undefined --export main -o main.wasm $OBJ_DIR/*.o) and a bunch of attitude adjustment got the desired “Hello, World!” in the browser console.

Next was to start adding the standard library back in so that it could be useful for actual work. In particular, I have my eye on using it to create a git repository browser for my repos on IPFS.

For the moment, I’m stuck on trying to get fix the error Can only invoke 'to_s' once on String::Builder from the line 5 of this slightly-expanded program:

p = StaticArray(UInt8,10).new(0)
p[0] = 0x45

puts "Hello, world!"
puts p.to_s

Comments