We try to list wasm-as' extensions to the spec, i.e. code modification made by wasm-as silently that do not appear specification. To our knowledge, this behaviours are not documented. The list is non-exhaustive. Feel free to contact us if you find anything missing.
Wasm-as tend to replace dead code by unreachable and
add unreachable after blocks that don't exit. While
semantically preserving, this transformation can transform invalid code in
valid code. For instance, the following function is not well-typed according
to the spec. But wasm-as inserts an unreachable at the end,
making it well-typed.
(func (result i32)
(if (i32.const 0)
(then (return (i32.const 0)))
(else (return (i32.const 0)))))
In the following piece of code, the br is not well-typed but
wasm-as accepts it:
(func
(result i32)
(block $l (result i32)
(i32.const 0)
(if
(then (return (i32.const 0)))
(else (return (i32.const 0))))
(br $l)))
According to the specification, functions appearing in
a ref.func have to appear outside the function bodies (in a
global or in an elem section). Wasm-as puts all the undeclared function
reference in an elem declare func section.
According to the specification, when using array.get
or struct.get on a packed field (i8
or i16), a sign extension has to be specified. It means that
either .get_u or .get_s has to be used. Wasm-as
allows .get instructions on packed types, defaulting to
unsigned.
Binaryen's README mentions that block input values are represented in the IR by
pop subexpressions for catch blocks and not
supported for the others. It is not mentionned that code using block input
values will be replaced by the use local variables. For instance, the
following code:
(i32.const 0) (block (param i32) (drop))
is assembled to:
(i32.const 0) (local.set 0) (local.get 0) (drop)