17
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

WebAssemblyのloopはまりどころ

Last updated at Posted at 2018-08-29

はじめて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)))

今回のコードはこちらで試せます。皆様ご安全に。

17
5
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
17
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?