はじめてWebAssemblyを書く時は恐らくloopではまります。
以下の関数を見て下さい。1からnまでの和を計算する関数のつもりです。
;; i32型引数$nを取りi32の返り値を返す関数。さらにローカル変数として$iと$sumを使う。
;; ローカル変数は0初期化されるのでそのまま使う
(func $sum_bad (param $n i32) (result i32) (local $i i32) (local $sum i32)
(loop $loop
;; もし$iが$nを以上ならloopから抜ける
(br_if $loop (i32.le_s (get_local $n) (get_local $i)))
;; $sum = $sum + $i
(set_local $sum (i32.add (get_local $sum) (get_local $i)))
;; $i = $i + 1
(set_local $i (i32.add (get_local $i) (i32.const 1))))
;; ループから抜けたらreturn
(return (get_local $sum)))
これを動かすと
sum_bad: 0
と返ってきます。何故でしょう。
なんとloop
はただの後方ジャンプのためのラベルで、末尾まで到達してもloop
の位置まで戻りません。戻りたければ br
を呼ぶとloop
の位置に飛ぶことになっています。
loop
に対するbr
が一般の言語でいう continue
相当になっている訳ですね。なので上記実装だとloop内は一度しか実行されずに抜けてしまいます。
それを踏まえた実装がこちら。
(func $sum_good (param $n i32) (result i32) (local $i i32) (local $sum i32)
;; loopから脱出するためのラベルを作っておく
(block $exit
(loop $loop
;; ループから脱出するときは$exitの方を指定する
(br_if $exit (i32.le_s (get_local $n) (get_local $i)))
(set_local $sum (i32.add (get_local $sum) (get_local $i)))
(set_local $i (i32.add (get_local $i) (i32.const 1)))
;; **ちゃんと** ループするためにbrを呼ぶ
(br $loop)))
(return (get_local $sum)))
今回のコードはこちらで試せます。皆様ご安全に。