0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

wasmで、アセンブラ その4

0
Posted at

概要

wasmが、スタックマシンらしいので、やってみた。
plunkerでwasmのwatのsandboxやってみた。
ボタンでサンプルが変わる。

写真

image.png

サンプルコード


const templates = {
    calc: `(module
  ;; 足し算
  (func $add (param $a i32) (param $b i32) (result i32)
    local.get $a
    local.get $b
    i32.add)
  (export "add" (func $add))

  ;; 引き算
  (func $sub (param $a i32) (param $b i32) (result i32)
    local.get $a
    local.get $b
    i32.sub)
  (export "sub" (func $sub))

  ;; 3乗の計算
  (func $cube (param $n i32) (result i32)
    local.get $n
    local.get $n
    local.get $n
    i32.mul
    i32.mul)
  (export "cube" (func $cube))
)`,
    hello: `(module
  (memory (export "memory") 1)
  (data (i32.const 0) "Hello, World! ")
  (func $hello (export "hello") (result i32 i32)
    (i32.const 0) 
    (i32.const 13) 
  )
)`,
    fizzbuzz: `(module
  (memory (export "mem") 1)
  (func $get_ptr (result i32) i32.const 0)
  (export "get_ptr" (func $get_ptr))

  (func $fizzbuzz (result i32)
    (local $i i32) (local $ptr i32)
    (local.set $i (i32.const 1)) (local.set $ptr (i32.const 0))
    (block $break
      (loop $top
        (br_if $break (i32.eq (local.get $i) (i32.const 101)))
        (if (i32.gt_u (local.get $i) (i32.const 1))
          (then
            (i32.store8 (local.get $ptr) (i32.const 44))
            (local.set $ptr (i32.add (local.get $ptr) (i32.const 1)))
            (i32.store8 (local.get $ptr) (i32.const 32))
            (local.set $ptr (i32.add (local.get $ptr) (i32.const 1)))
          )
        )
        (if (i32.eqz (i32.rem_u (local.get $i) (i32.const 15)))
          (then
            (i32.store8 (local.get $ptr) (i32.const 70)) (i32.store8 (i32.add (local.get $ptr) (i32.const 1)) (i32.const 105))
            (i32.store8 (i32.add (local.get $ptr) (i32.const 2)) (i32.const 122)) (i32.store8 (i32.add (local.get $ptr) (i32.const 3)) (i32.const 122))
            (i32.store8 (i32.add (local.get $ptr) (i32.const 4)) (i32.const 66)) (i32.store8 (i32.add (local.get $ptr) (i32.const 5)) (i32.const 117))
            (i32.store8 (i32.add (local.get $ptr) (i32.const 6)) (i32.const 122)) (i32.store8 (i32.add (local.get $ptr) (i32.const 7)) (i32.const 122))
            (local.set $ptr (i32.add (local.get $ptr) (i32.const 8)))
          )
          (else
            (if (i32.eqz (i32.rem_u (local.get $i) (i32.const 3)))
              (then
                (i32.store8 (local.get $ptr) (i32.const 70)) (i32.store8 (i32.add (local.get $ptr) (i32.const 1)) (i32.const 105))
                (i32.store8 (i32.add (local.get $ptr) (i32.const 2)) (i32.const 122)) (i32.store8 (i32.add (local.get $ptr) (i32.const 3)) (i32.const 122))
                (local.set $ptr (i32.add (local.get $ptr) (i32.const 4)))
              )
              (else
                (if (i32.eqz (i32.rem_u (local.get $i) (i32.const 5)))
                  (then
                    (i32.store8 (local.get $ptr) (i32.const 66)) (i32.store8 (i32.add (local.get $ptr) (i32.const 1)) (i32.const 117))
                    (i32.store8 (i32.add (local.get $ptr) (i32.const 2)) (i32.const 122)) (i32.store8 (i32.add (local.get $ptr) (i32.const 3)) (i32.const 122))
                    (local.set $ptr (i32.add (local.get $ptr) (i32.const 4)))
                  )
                  (else
                    (if (i32.ge_u (local.get $i) (i32.const 100))
                      (then
                        (i32.store8 (local.get $ptr) (i32.const 49)) (i32.store8 (i32.add (local.get $ptr) (i32.const 1)) (i32.const 48))
                        (i32.store8 (i32.add (local.get $ptr) (i32.const 2)) (i32.const 48)) (local.set $ptr (i32.add (local.get $ptr) (i32.const 3)))
                      )
                      (else
                        (if (i32.ge_u (local.get $i) (i32.const 10))
                          (then
                            (i32.store8 (local.get $ptr) (i32.add (i32.const 48) (i32.div_u (local.get $i) (i32.const 10))))
                            (i32.store8 (i32.add (local.get $ptr) (i32.const 1)) (i32.add (i32.const 48) (i32.rem_u (local.get $i) (i32.const 10))))
                            (local.set $ptr (i32.add (local.get $ptr) (i32.const 2)))
                          )
                          (else
                            (i32.store8 (local.get $ptr) (i32.add (i32.const 48) (local.get $i)))
                            (local.set $ptr (i32.add (local.get $ptr) (i32.const 1)))
                          )
                        )
                      )
                    )
                  )
                )
              )
            )
          )
        )
        (local.set $i (i32.add (local.get $i) (i32.const 1)))
        (br $top)
      )
    )
    local.get $ptr
  )
  (export "fizzbuzz" (func $fizzbuzz))
)`
};

function loadTemplate(key) {
    document.getElementById('watCode').value = templates[key];
    document.getElementById('dynamicFunctions').innerHTML = `<p style="color: #666; font-style: italic;">コードが切り替わりました。「コードをコンパイル」ボタンを押してください。</p>`;
}

loadTemplate('calc');

let currentExports = null;

document.getElementById('compileBtn').addEventListener('click', async () => {
    const watCode = document.getElementById('watCode').value.replace(/ /g, '  ');
    const statusBox = document.getElementById('statusBox');
    const logOutput = document.getElementById('logOutput');
    const dynamicFunctions = document.getElementById('dynamicFunctions');
    if (statusBox) 
      statusBox.style.display = 'none';
    logOutput.innerText = "";
    dynamicFunctions.innerHTML = "";
    try 
    {
        const wabt = await WabtModule();
        let wasmModule = wabt.parseWat("main.wat", watCode);
        let buffer = wasmModule.toBinary({ 
            log: true, 
            canonicalize_lebs: true, 
            relocatable: false, 
            write_debug_names: true 
        });
        wasmModule.destroy();        
        logOutput.innerText = buffer.log || `bytesize: ${buffer.buffer.byteLength}`;        
        const results = await WebAssembly.instantiate(buffer.buffer);
        currentExports = results.instance.exports;        
        const exportedFunctions = [];
        for (const name in currentExports) 
        {
            if (typeof currentExports[name] === 'function') 
            {
                const paramCount = currentExports[name].length;
                exportedFunctions.push({ name, paramCount });
            }
        }
        if (exportedFunctions.length === 0) 
        {
            dynamicFunctions.innerHTML = "関数がエクスポートされていません。";
            return;
        }
        exportedFunctions.forEach(func => {
            if (func.name === 'get_ptr' || func.name === 'get_len') 
              return;
            const card = document.createElement('div');
            card.className = 'func-card';
            const nameSpan = document.createElement('span');
            nameSpan.className = 'func-name';
            nameSpan.innerText = `${func.name} `;
            card.appendChild(nameSpan);
            const inputs = [];
            for (let i = 0; i < func.paramCount; i++) 
            {
                const input = document.createElement('input');
                input.type = 'number';
                input.className = 'param-input';
                input.value = Math.floor(Math.random() * 10) + 1;
                card.appendChild(input);
                inputs.push(input);
            }
            const runBtn = document.createElement('button');
            runBtn.className = 'run-func-btn';
            runBtn.innerText = '実行';
            card.appendChild(runBtn);
            const resultSpan = document.createElement('span');
            resultSpan.className = 'func-result';
            resultSpan.style.marginLeft = '10px';
            card.appendChild(resultSpan);
            dynamicFunctions.appendChild(card);
            runBtn.addEventListener('click', () => {
                const args = inputs.map(input => parseInt(input.value, 10) || 0);
                try 
                {
                    const res = currentExports[func.name](...args);
                    const memory = currentExports.memory || currentExports.mem;
                    if (memory) 
                    {
                        let ptr = 0;
                        let len = 0;
                        let isString = false;
                        if (Array.isArray(res)) 
                        {
                            ptr = res[0];
                            len = res[1];
                            isString = true;
                        } 
                        else if (func.name === 'fizzbuzz') 
                        {
                            ptr = currentExports.get_ptr ? currentExports.get_ptr() : 0;
                            len = res;
                            isString = true;
                        }
                        if (isString) 
                        {
                            const bytes = new Uint8Array(memory.buffer, ptr, len);
                            const text = new TextDecoder('utf-8').decode(bytes);
                            resultSpan.innerText = ` 出力: "${text}"`;
                            return;
                        }
                    }
                    resultSpan.innerText = ` 結果: ${res}`;
                } 
                catch (err) 
                {
                    resultSpan.innerText = ` エラー: ${err.message}`;
                    console.error(err);
                }
            });
        });
    } 
    catch (e) 
    {
        if (statusBox) 
          statusBox.style.display = 'block';
        logOutput.innerText = e.toString();
        console.error(e);
    }
});




成果物

以上。

0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?