LoginSignup
2
0

More than 1 year has passed since last update.

WebAssembly をためしてみる

Posted at

WebAssemblyとは

スタックベースのインストラクションセットで、Webブラウザ内でネイティブバイナリに近い速度で動作することを目的としている。Mozillaが主導していたようだが、基本的にすべての近代的なブラウザで動作するようだ。RustやC言語のバックエンドとして使うことができる。

watとwasm

WebAssemblyにはテキスト表現とバイナリ表現があり、それぞれwatとwasmを拡張子として用いる。多くのコンパイラはバイナリ表現を直接吐くようだ。テキスト表現からバイナリ表現にコンパイル?するツールがあるのだが、シンボル名が最近変わったらしく、Web上にあるコードがコンパイルできなくて混乱した。

wabt

コード変換やネイティブコードへの変換を実現するツールチェーンがこれ。

cmakeで普通にビルドできるが、m1 macのバイナリフォーマットがサポートされておらず、ネイティブコンパイラはビルドできなかった。が、watからwasmに変換するコードはm1でもビルドできたので気にしない。

コード例

フィボナッチ数を計算するコードを書いてみた。
WebAssemblyはスタックマシンなのだけど、純粋なスタックマシンではなく、例えば関数呼び出しの引数は、localと呼ばれる領域に用意されるし、スタック以外にローカルな変数領域を持つことができるらしい。そのあたりのことは下のページに書かれている。

とはいえば基本的な演算の考え方はごく普通のスタックマシンで、スタックトップに対して演算が行われ、結果がスタックトップに置かれる。関数呼び出しを行う際も、スタック上に引数をプッシュしてから呼び出す。にもかかわらず、呼び出された側ではローカルに入って見えるというのはちょっと不思議?
このコードのように、引数を何度も使う場合には、何度も複製しなくて済むので楽ではある。

;; if n < 2 return 1
;; else return fib(n-1) + fib(n-2)

(module
  (func $fib (param f64) (result f64)
    local.get 0    ; 引数nをスタックに積む
    f64.const 2    ; 定数2をスタックに積む
    f64.lt         ; 比較する n < 2
    if (result f64)
      f64.const 1  ; 返り値をスタックに積む
    else
      local.get 0  ; 引数nをスタックに積む
      f64.const 1  ; 定数1をスタックに積む 
      f64.sub      ; 引く. n - 1
      call $fib.   ; n-1を引数にしてfibを再帰呼び出し。結果はスタックトップに置かれる。
      local.get 0  ; 引数nをスタックに積む
      f64.const 2  ; 定数2をスタックに積む 
      f64.sub      ; 引く. n - 2
      call $fib.   ; n-2を引数にしてfibを再帰呼び出し。結果はスタックトップに置かれる。
      f64.add      ; fib(n-1) と fib(n-2)の結果がスタック上にあるのでそれらを加算。
    end)
  (export "fib" (func $fib)))

コンパイル

wabt のwat2wasmを使ってコンパイルする。

% ../wabt/build/wat2wasm fib.wat
% od -x fib.wasm               
0000000      6100    6d73    0001    0000    0601    6001    7c01    7c01
0000020      0203    0001    0707    0301    6966    0062    0a00    013a
0000040      0038    0020    0044    0000    0000    0000    6340    7c04
0000060      0044    0000    0000    f000    053f    0020    0044    0000
0000100      0000    f000    a13f    0010    0020    0044    0000    0000
0000120      0000    a140    0010    0ba0    000b                        
0000131

実行

Webブラウザで実行するには、JavaScriptから呼び出してやる。ごく普通の関数として利用できる。

<script type="text/javascript">
WebAssembly.instantiateStreaming(fetch('fib.wasm'))
   .then(obj => {
       or (let i = 1; i < 20; i++) {
          console.log('fib '+ i +' = '+ obj.instance.exports.fib(i));
       }
   });
</script>

実行すると下のように出力がブラウザのコーソルに出る。

(index):5 fib 1 = 1
(index):5 fib 2 = 2
:
(index):5 fib 17 = 2584
(index):5 fib 18 = 4181
(index):5 fib 19 = 6765

所感

スタックベースのVMをブラウザに埋め込む、というのはJavaと何が違うのか、と考えてみたり。まあ、色々違うわけですが。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0