Learning WASM #1

dotnetからwasmを実行するのが面白そう。これがモノになったら多言語間をまたいだプラグインシステムが作るんじゃないかな。
まだ、File IOとかNetwork Stackとかは使えないらしいけど、専用関数用意すればよさそう。FastlyとかCloudflareとかはエッジ用のエンジンでwasm実行できるようにしてるしね。

まずは、wasmになれたい。

https://github.com/bytecodealliance/wasmtime-dotnet/blob/main/examples/memory/memory.wat

(module
  (type $t0 (func (param i32 i32)))
  (import "" "log" (func $.log (type $t0)))
  (memory (export "mem") 1 2)
  (data (i32.const 0) "Hello World")
  (func $run
    i32.const 0
    i32.const 11
    call $.log
  )
  (export "run" (func $run))
)

これはテキスト形式、本当はバイナリだけど、読みやすいテキスト形式で学習していく。

見た目は、S式だね。func $run ~ callのところを見た感じ、値、値、callと続いてるからスタックマシンぽいね。

https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format

MDNのドキュメントを読みながら解読してく。

(module)

モジュールの塊を表す式(module)が最小のモジュールらしい。中身は空。

モジュールに入ってないコードは不正なんだろうか? module式で纏まってなかったらそれは文って感じだろうし違反しそう。

(func (param i32) (param i32) (result f64) ... )

モジュールに関数を生やすには、 func式を使う。 任意の(引数)(戻り値)が続き、そのあと式の中身が続くぽい。

名前はどこだ? 一番最初のサンプルみるに、引数の前っぽいけど。名前の先頭に$がつくのは作法なのかな。

(module
    (func $myadd (param $l i32) (param $r i32) (result i32)
        local.get $l
        local.get $r
        i32.add)
)

こんな感じかな。引数は local.get でスタックに載せられるみたい。 i32.add はcallしなくていいから組み込み命令なのかな。

スタックマシンだから結果はスタックに乗る感じかな。

戻り値はどうやって指定するんだろ? スタックの一番上が自動的に返されるのかな。

便利なページ見つけた。
https://webassembly.github.io/spec/core/exec/index.html

これで構文がすぐわかるね。

作った関数は、 exportでモジュールのそとに公開できる。

(module
    (func $myadd (param $l i32) (param $r i32) (result i32)
        local.get $l
        local.get $r
        i32.add)
    (export "add" (func $myadd))
)

逆に外の関数を使えるようにするには import が必要。

import          ::= {module name, name name, desc importdesc}
importdesc  ::= func typeidxtable 
                        |       tabletypemem
                        |       memtypeglobal
                        |       globaltype

importにはモジュール名、対象名、説明が必要で説明の種類を見る限り、関数だけじゃななさそう。memoryとかglobalにも使う。tableはちょっとまだわからん。

プリミティブ以外の値はどうすればいいのか、memoryを使って、バッファーを確保して使うみたい。

js側からしたらArrayBufferをwasmに渡して、処理が終わったらそのArrayBufferからデータを取り出す感じ。

function consoleLogString(offset, length) {
  var bytes = new Uint8Array(memory.buffer, offset, length);
  var string = new TextDecoder('utf8').decode(bytes);
  console.log(string);
}

なるほどね。

(memory 1 2) みたいにかけるけど、これは、1が最小ページサイズ、2が最大ページサイズらしい。1ページ64KB。

この共有メモリにprotocol bufferとかmessage packのバイナリ置けば、言語間でオブジェクトをやり取りできそう。もちろんJSONでもいいわけだけど。


You'll only receive email when they publish something new.

More from iwate