Skip to the content.

What’s Wrong with WebAssembly? 2023.4.18

For those who don’t know WebAssembly is a no longer new addition to web which allows you to get near native performance within the browser. It’s a convenient compilation target and can Just-In-Time compile into whatever instruction set your machine uses.

After working at a company using webassembly for engineering simulations and spending the better parts of 2 years making a programming language that compiles to WebAssembly I’ve come to the realization that it’s far from perfect and seems to be getting worse.

I’m probably going to upload something related to this to my YouTube channel but I’ll post the rough script here for now as motivation and update it later to include video links.

*.store operands wrong order

This one is kinda technical. So webassembly is designed to have stack-machine-like semantics which means that you write the operands before the operators.

;; function that adds two i32s together and adds 1 to the result
func $sum_plus_1 (params $a i32 $b i32) (result i32) 
	local.get $a
	local.get $b
	i32.add
	i32.const 1
	i32.add
	return
end

And this is actually an awesome thing! Although I’m slightly biased given that I’ve made several languages with similar semantics.

Anyways lets use an example to start to understand what I think is the problem.

1 - 2 * 3 becomes 1 2 3 * - and this seems fairly intuitive to me and you can do the same thing in webassembly

i32.const 1
i32.const 2
i32.const 3
i32.mul
i32.sub

but what happens when we try to assign it to a variable?

we can assign it to a local which is comparable to using a register in assembly using local.set which is pretty easy to use and makes sense.

i32.const 3
local.set $a

However storing it to a location in memory (for example - an array or object) is a bit different.

...location in memory...
i32.const 3
i32.store

This works fine, why don’t I like this? While at first glance it might seem intuitive to keep the order of operands that we use in infix, I found out the hard way that this isn’t ideal.

a = 1 + 2 * 3 => 2 3 * 1 + a = or => a 2 3 * 1 + = ?

In a language I made in early 2019, I the same operand order for assignment and found that every time, I found it problematic for writing macros - I very often had to use a swap operator to get a value from the stack. Except webassembly does not have a swap operator.

# wasm ordering
$match_types (:
	dup $v2 swap = type $T swap = $v1 swap =
	v1 T cast v2 
) =
# vs.
(:
    dup $v2 = type $T = $v1 =
    v1 T cast v2
) $match_types =

Additionally because my most recent language uses a moving garbage collector, we can place an address onto the Webassembly stack but the operations required to compute the value that we want to store in it could invalidate that memory address and webassembly provides no efficient way to swap operands and no way to access or update pointers on the stack when we GC.

So to get around this I had to add extra locals to store the values in before writing them to memory… potentially incuring a performance hit because of the not ideal semantics.

These sematics might make sense in a language like C where the operations on the left side need to happen before those on the right, but I’d argue that it makes less sense in a postfix language and more importantly made my life more annoying.

int32_t* pn = malloc(8);
pn[0] = 1953789540;
*(pn + 1) = 1952804398;
printf("%s\n", pn);

Things I’ll probably mention in video