WASMのテキスト表現(つまりアセンブリ言語)WATについて、MDNのイントロダクション的なドキュメントを読んでいる。WASMはスタックマシンで、WATはS式だ。だからなのかわからないが、見た目としてスタックマシンらしい後置記法も、Lispぽい前置記法もできる。

(module
  (func $add (param $lhs i32) (param $rhs i32) (result i32)
    local.get $lhs
    local.get $rhs
    i32.add)
  (export "add" (func $add))
)

上記はMDNにも出ている例で、単純にふたつの数を足す関数を持つモジュールだ。このモジュールは、こうもかける。

 (module
  (func $add (param $lhs i32) (param $rhs i32) (result i32)
    (i32.add (local.get $lhs) (local.get $rhs)))
  (export "add" (func $add))
)

前者で関数$addのbodyは、

local.get $lhs
local.get $rhs
i32.add

と書かれている。これは、$lhsとラベルが付けられた引数をスタックにつみ、$rhsを同じくスタックにつみ、i32.addという組み込みの(という表現が適切なのかわかっていないが)関数を呼び出す、という意味だ。スタックに積まれたふたつの引数は消費され、加算された結果が残る。

後者では、関数のbodyはこう書かれている。

(i32.add (local.get $lhs) (local.get $rhs))

ぱっと見で意味は読み取れるが、スタックマシンの痕跡は消えている。そして、これは前者と完全に等価のようだ(wasmに変換した結果の差もないし、wasm2watの結果も差がない)。つまり、「引数をスタックに積んでから関数を呼ぶ」という処理を、見た目はLispぽい後置記法で書ける、ということだ。

これはWATの仕様なんだろうけど、手で書くことはおそらくほぼ想定していないであろうWATでこの「便利」さ必要だったのかなあ、と疑問に思う。まだWASMのことをほとんど何も知らないので、見当違いな疑問なのかもしれない。

7/6 追記

この件はWASMの仕様書に記載があった。S式表現の方を「Folded Instruction」という。

Instructions can be written as S-expressions by grouping them into folded form.

そして、これは純粋に構文糖衣とのこと。「手で書くことはおそらくほぼ想定していない」という私の推測が間違っていたようだ。

Folded instructions are solely syntactic sugar, no additional syntactic or type-based checking is implied.

WASMのSpecのIssueにはこんな例もあった。

All of these:

(i32.add (i32.const 1) (i32.const 2) (i32.const 3))
(i32.const 1) (i32.add (i32.const 2) (i32.const 3))
(i32.const 1) (i32.const 2) (i32.add (i32.const 3))
(i32.const 1) (i32.const 2) (i32.const 3) (i32.add)

Are sugar for:

i32.const 1
i32.const 2
i32.const 3
i32.add

Even though i32.add only has only two operands, all of these cases are valid in the s-expression format.