SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|10|11|

2023-10-15(Sun) WebAssembly $00

  で、実践プログラミングの前の最後のテストとして、WebAssemblyの「制御構造」をテストしてみることにする。いわゆる、条件分岐(if)、繰り返し(loop)というヤツだ。

;; https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/Control_flow
(module
    (func $log (import "console" "log") (param i32))
 
    (func (export "ifTest") (param $val i32)
        local.get   $val
        if  (result i32)
            i32.const   456                     ;; not 0: True
        else
            i32.const   123                     ;; 0: False
        end
        call        $log
    )
 
    (func (export "loopTest") (param $times i32) (result i32)
        (local $i i32)
        (local $sum i32)
 
        i32.const   0                           ;; 初期値
        local.tee   $i
        local.set   $sum
        loop        $loop1
            local.get   $i                      ;; 処理1
            call        $log
 
            local.get   $sum                    ;; 処理2 $sum += $i
            local.get   $i
            i32.add
            local.set   $sum
 
            local.get   $i                      ;; $i += 1
            i32.const   1
            i32.add
            local.tee   $i
            local.get   $times
            i32.lt_s                            ;; $i < $times
            br_if       $loop1
        end
 
        local.get   $sum
    )
 
    (func (export "whileTest") (param $times i32)
        (local $i i32)
 
        local.get   $times                      ;; 初期値
        local.set   $i
        block       $loop1
            loop        $loop2
                local.get   $i                  ;; $i == 0
                i32.const   0
                i32.eq
                br_if       $loop1              ;; break
 
                local.get   $i                  ;; 処理1
                call $log
 
                local.get   $i                  ;; $i -= 1
                i32.const   1
                i32.sub
                local.set   $i
                br          $loop2              ;; continue
            end
        end
    )
)
<!-- https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/Control_flow -->
<HTML>
    <HEAD>
        <TITLE>WebAssembly Control Flow Test</TITLE>
    </HEAD>
    <BODY>
        <SCRIPT>
            async function main() {
                const importObjects = {
                    console:    { log: (arg) => console.log(arg) },
                };
                const obj = await WebAssembly.instantiateStreaming(fetch('controlflow.wasm'), importObjects);
 
                obj.instance.exports.ifTest(0);
                obj.instance.exports.ifTest(1);
                obj.instance.exports.ifTest(2);
 
                let sum = obj.instance.exports.loopTest(11);
                console.log('total:', sum);
 
                obj.instance.exports.whileTest(5);
            }
            main();
        </SCRIPT>
    </BODY>
</HTML>
123
456
456
0
1
2
3
4
5
6
7
8
9
10
total: 55
5
4
3
2
1

  まずは条件分岐(if)。WebAssemblyのifはスタックの先頭の値がゼロか非ゼロかで処理を分岐するものだ。JavaScriptから0を渡すとelseの123が返り、非ゼロを渡すと456が返っている。ちょっとクセがあるのは、アセンブラにはスタックのズレを検出する機能があり、if構造を抜けた際にスタックが高くなって(ズレて)いると、それが検出されてエラーになってしまうところだ。そういう場合「スタックに返り値を積んだんのですよ」と明示する必要がある。ifの後の「(result i32)」がそれだ。思い返せば、Z80の頃にプログラムを暴走させてしまう一番の原因はPUSHとPOPのズレだったよなぁ。

  次は繰り返し(loop)。カウンタは0から加算され、指定した回数だけ実行される。中身に処理がないのもアレなんでカウンタの累計を計算して返すようにしてみた。11回のループで0〜10が加算され結果の55が返っている。

  最後はwhileっぽい繰り返し。条件は最初に評価され、一度も実行されないことがありうるループ構造で、最も使用頻度が高いループ構造じゃないかな。WebAssemblyの場合、2重ループのようにしないと実現できないようだ。カウンタは指定した値から減算され、指定した回数だけ実行される。しかし、インデントをかますとアセンブラらしく見えないな。ないほうがいいのかな。

  というわけで、先月末くらいからダラダラと進めていたら半月もかかってしまったが、別に急ぐ必要もないのだし、やめてしまうより、やらないより、ダラダラと進めることには十分な価値があるのだ、ということにしておこう。さて、引き続き、最初に思いついた「WebAssemblyのひとつだけの使い道」のプログラミングに入るかな。イヒヒ。