Learning Wasm

 from Red Blob Games
17 Mar 2025

I wanted to try writing wasm manually (wat format).

 1  Lox test code#

I decided to try manually translating my Lox test code into wasm

print "# Testing scope";
var a = 5;
print "outer a";
print a;
{
  var a = 10;
  print "inner a";
  print a;
}
print "outer a";
print a;

print "# Testing expressions";
print true;
print 2 + 1;
print !((3 + 5 / 10) == 3.5);

print "# Testing if statements";
if (false) {
  print "N true";
} else {
  print "Y false";
}
if (true) {
  print "Y true";
}

print "# Testing logical operators";
print "hi" or 2; // "hi"
print "hi" and 2; // 2
print nil or "yeh"; // "yeh"
print nil or nil; // nil

print "# Testing while loops";
var i = 3;
while (i > 0) {
  print i;
  i = i - 1;
}

print "# Testing for loops, Fibonacci sequence";
var a = 0;
var temp;
for (var b = 1; a < 10000; b = temp + b) {
  print a;
  temp = a;
  a = b;
}

Some things I don’t understand:

other:

Source: learning-wasm.js

 2  JS vs wasm#

I’m going to convert this js code to wasm:

function main(seed, N) {
    const m = 0x7fff;
    const a = 1103515245;
    const c = 12345;

    for (let i = 0; i < N; i++) {
        seed = (a * seed + c) & m;
    }
    return seed;
}
  (func $loop (param $seed i32) (param $N i32) (result i32)
    (local $i i32)
    (local.set $i (i32.const 0))
    (block $for_2_end
      (loop $for_2_start
        (local.get $i)    ;; test !(i < N)
        (local.get $N)
        (i32.ge_s)
        (br_if $for_2_end)

        (local.get $seed)
        (i32.const 1103515245)
        (i32.mul)
        (i32.const 12345)
        (i32.add)
        (i32.const 0x7fff)
        (i32.and)
        (local.set $seed) ;; seed = (a * seed + c) & m

        (local.get $i)
        (i32.const 1)
        (i32.add)
        (local.set $i)    ;; i++
        (br $for_2_start)
      )
    )
    (local.get $seed)
  )

I also tried AssemblyScript[9]:

export function main(seed: i32, N: i32): i32 {
    const m: i32 = 0x7fff;
    const a: i32 = 1103515245;
    const c: i32 = 12345;

    for (let i = 0; i < N; i++) {
        seed = (a * seed + c) & m;
    }
    return seed;
}

which generated this wasm:

 (func $assembly/index/main (param $seed i32) (param $N i32) (result i32)
  (local $i i32)
  i32.const 0
  local.set $i
  loop $for-loop|0
   local.get $i
   local.get $N
   i32.lt_s
   if
    i32.const 1103515245
    local.get $seed
    i32.mul
    i32.const 12345
    i32.add
    i32.const 32767
    i32.and
    local.set $seed
    local.get $i
    i32.const 1
    i32.add
    local.set $i
    br $for-loop|0
   end
  end
  local.get $seed
  return
 )

and that ran in the same speed as my hand-written wasm, 725ms for js and 145ms for wasm in Firefox; 660ms for js and 145ms for wasm in Chrome. My test code doens’t work in safari yet.

I also tried wasm2c and ran the c code (with optimizer on) on my machine, and it took 116ms, so wasm wasn’t much slower than native code.

Email me , or comment here: