wasm S式 (WebAssemblyテキスト形式)入門

通常は拡張子 .wat で表される、wasmのS式について
バイナリ形式をwasm,人間が読めるS式のものをwatと呼ぶ。

#S式
```:watの例
(module (memory 1) (func))
```
watの基本的なコードの単位はモジュールで、S式はモジュールのツリー構造を表現する。
一つのモジュールは一つの`(...)`にあたる。括弧の中の1つ目のラベル`module`はこのモジュールの種類を、それ以下のラベル`(memory 1)`と`(func)`がモジュールの子ノードにあたる。
この例ではモジュールが入れ子構造になっているが、子ノードは存在しなくてもよい。そのため、最小構成は以下のようになる。

```:watモジュールの最小構成
(module)
```

ちなみに上の最小構成をバイナリに変換すると、(参考：[WebAssembly テキストフォーマットから wasm に変換する](https://developer.mozilla.org/ja/docs/WebAssembly/Text_format_to_wasm))8バイトで表現されるモジュールヘッダのみになる。

```
0000000: 0061 736d      ; WASM_BINARY_MAGIC
0000004: 0100 0000      ; WASM_BINARY_VERSION
```

#watモジュール
モジュールの括弧`(...)`の先頭は、モジュールの種類を表す。
以下の種類がある。

||説明|
|:--|:--|
|type|関数の型を宣言する|
|import|外部から関数やデータを読み込み、wasm内で使えるように定義する|
|func|関数を定義する|
|global|グローバル変数を宣言する|
|export|外部に関数やデータをエクスポート|
|elem|テーブルの値を定義する|
|data|メモリの初期値を定義する|

# funcモジュール
wasm内での関数定義は、以下のような形式になる。

```
( func <signature> <locals> <body> )
```

## シグネチャ

signatureは、単体パラメータを(param i32)、戻り値を(return i32)のように書く。
したがって、2つの32ビット整数を引数にとり、64ビット浮動小数点数を返すバイナリ関数は次のように記述する:

```
(func (param i32) (param i32) (result f64) ... )
;;はコメントを表す。以下のS式でも有効
(func (param i32 i32) (result f64) ... )
```

また、現在のwasmでは、以下の4つの型が有効である。

- `i32` 32bit整数
- `i64` 64bit整数
- `f32` 32bit浮動小数点数
- `f64` 64bit浮動小数点数

## ローカル変数
シグネチャの後に、`(local i32)`のように型付けされたローカル変数の並びが続く。

## スタックマシン
関数が呼ばれると、空のスタックからスタートして、命令(local.getなど)にスタックにデータが積まれていく。

```
(func (param $p i32)
  local.get $p
  local.get $p
  i32.add)
```
`local.get $p`で`$p`が指し示すものをスタックに詰め込む。それを2回行なった後にi32.addを行なっているので、スタックには`$p + $p`が詰め込まれている。


### 本体で有効なオペコードの例
- `local.get 0` ローカル変数(0番目)からスタックへ
- `local.set 0` スタックからローカル変数(0番目)へ
- `i32.const 19` 19という定数(型はi32)を定義してスタックへ
- `i32.add` スタックの最上の2つを足して消し、結果をスタックに書き込む

全て見るには、以下を参照。
https://webassembly.org/docs/semantics/

## 関数を呼ぶ
外部(Javascriptなど)から関数を呼び出すには、exportを用いる。

```
(module
  (func $add (param $lhs i32) (param $rhs i32) (result i32)
    get_local $lhs
    get_local $rhs
    i32.add)
  (export "add" (func $add))
)
```

一方、定義した関数をwasmモジュール内で呼び出すには、callを用いる。

```
(module
  (func $getAnswer (result i32)
    i32.const 42)
  (func (export "getAnswerPlus1") (result i32)
    call $getAnswer
    i32.const 1
    i32.add))
```

# memoryの使い方
文字列や複雑なデータ型を用いるのに、wasmではメモリを用いる。i32.loadやi32.storeを用いて、線形メモリの読み書きをする。javascriptと文字列などをやり取りするのは、メモリを通して行う。

```javascript
//メモリを作る
var memory = new WebAssembly.Memory({initial:1});

//メモリのオフセットと長さから文字列を書き出す関数を作る
function consoleLogString(offset, length) {
  var bytes = new Uint8Array(memory.buffer, offset, length);
  var string = new TextDecoder('utf8').decode(bytes);
  console.log(string);
}

//wasmに渡す関数とメモリのデータを作る。
var importObject = { console: { log: consoleLogString }, js: { mem: memory } };

WebAssembly.instantiateStreaming(fetch('logger2.wasm'), importObject)
  .then(obj => {
    obj.instance.exports.writeHi();
  });
```

wasm内では、以下のようにしてメモリを利用する。

```
(module
  (import "console" "log" (func $log (param i32 i32)))
  (import "js" "mem" (memory 1))
  (data (i32.const 0) "Hi")
  (func (export "writeHi")
    i32.const 0  ;; pass offset 0 to log
    i32.const 2  ;; pass length 2 to log
    call $log))
```
`(import "js" "mem" (memory 1))`でメモリを読み込む。`(memory 1)`で、1ページ(64kB)分のメモリを読み込んでいる。
`(data (i32.const 0) "Hi")`では、i32.constで指定されたオフセットであるメモリの0番目以降に文字列"Hi"を書き込んでいる。オフセット0と長さ2から"Hi"を出力できる"writeHi"をexportする。

# tableの使い方
テーブルは、以下のように定義する。

```
(module
  (table 2 anyfunc)
  (elem (i32.const 0) $f1 $f2)
  (func $f1 (result i32) ;; 42を返す関数
    i32.const 42)
  (func $f2 (result i32) ;; 13を返す関数
    i32.const 13)
  ...
)
```
`i32.const 0`はオフセットをあらわし、この場合はテーブルのindexが0から始める。0が関数`$f1`,1が関数`$f2`に対応する。

テーブルで定義した関数を使うには、以下のようにする。

```
(type $return_i32 (func (result i32))) ;; if this was f32, type checking would fail
(func (export "callByIndex") (param $i i32) (result i32)
  local.get $i
  call_indirect (type $return_i32))
```

これにより、定義したテーブルの`$i`番目の関数(`$i`はスタックに置かれた値)をcall_indirectで呼び出すことができる。関数の型は`(type $return_i32)`に一致しているかどうか実行時にチェックされる。一致すると、WebAssembly.RuntimeError 例外がスローされる。
以上2つを合わせると、以下のようにしてテーブルで定義した関数を呼び出すことが出来る。

```
(module
  (table 2 anyfunc)
  (func $f1 (result i32)
    i32.const 42)
  (func $f2 (result i32)
    i32.const 13)
  (elem (i32.const 0) $f1 $f2)
  (type $return_i32 (func (result i32)))
  (func (export "callByIndex") (param $i i32) (result i32)
    get_local $i
    call_indirect (type $return_i32))
)
```
tableはミュータブルなので、Javascriptからは動的に操作できる(←やばい)

#参考
[WebAssembly テキストフォーマットを理解する / MDN](https://developer.mozilla.org/ja/docs/WebAssembly/Understanding_the_text_format)
[公式ドキュメント](http://webassembly.github.io/spec/core/index.html)