ZigでWASM
本記事は
すみません、1日目からギリギリになっています。
ZigでWASMをビルドする
Zig言語は、「堅牢、最適、および再利用可能なソフトウェアをメンテナンスするための汎用プログラミング言語およびツールチェイン」です。(公式より引用)
最近では「Bun」を開発するために使われたりしています。
ZigはLLVMを内包しており、C/C++のコンパイルもできます。
LLVMの機能により、WASMを出力もできます。
そこで、簡単なサンプルをもちいてWASMをビルドし、JavaScriptから利用する、というのを試してみよう、としました。
tl;dr
- ZigでWASMをビルドしてみました
さくっと検索した結果
$ mkdir zig-wasm-sample
$ cd zig-wasm-sample
$ zig init
info: created build.zig
info: created build.zig.zon
info: created src/main.zig
info: created src/root.zig
info: see `zig build --help` for a menu of options
main.zig
はmain関数があるファイルになりますが、今回は置いておきます。
root.zig
の内容は、以下の通り。
const std = @import("std");
const testing = std.testing;
export fn add(a: i32, b: i32) i32 {
return a + b;
}
test "basic add functionality" {
try testing.expect(add(3, 7) == 10);
}
簡単な足し算をする関数と、それのテストコードが生成されます。
こいつをWASMにビルドしたい。
検索した結果「DenoからZigで作ったWasmを使う」という記事が見つかりました。
この記事の段階でZigのバージョンは0.12.0
。一方2024/12月現在の最新は0.13.0
。当該記事にもありますが、Zigはまだメジャーバージョンに至っておらず、仕様がバージョンごとにコロコロ変わります。Bunはよくこんな中で開発できているよな。。。
ひとまず、ビルドを試します。
$ zig build-exe src/root.zig -target wasm32-freestanding -fno-entry -rdynamic --import-memory -O ReleaseSmall
$ ls
build.zig build.zig.zon root.wasm root.wasm.o src
wasmファイルができました。
これをJavaScriptから読み込んでみます。ひとまずnode.jsで実行してみましょう。
先程の記事を参考に、index.jsを作ります。
const fs = require('node:fs')
const memory = new WebAssembly.Memory({initial:20, maximum:100})
const wasm_module = new WebAssembly.Module(fs.readFileSync('root.wasm'))
const instance = new WebAssembly.Instance(wasm_module, { env: { memory } })
const add = instance.exports.add
const result = add(3, 7)
console.log(result)
実行してみましょう。
$ node index.js
10
# bunでもやってみる
$ bun run index.js
10
簡単にできましたね。
とはいえこれだけでは先行記事を追いかけただけに過ぎませんので、なにか新規性を出したい。
ビルドスクリプトをちゃんと書いてみよう
build.zigをちゃんと書くと、zig build
だけでビルドできるようになります。
試しに書いてみました。
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{ .default_target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding } });
const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .Debug });
const exe = b.addExecutable(.{
.name = "zig-wasm-sample",
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
b.installArtifact(exe);
}
targetは指定することができましたが、-fno-entry -rdynamic --import-memory
あたりのオプションを指定することはできない模様。
ZigはZig言語そのもので作られているので、VSCodeであればF12キーを押せば定義にジャンプできます。
ExecutableOptionsや、addExecutableの定義を見ると、どんなオプションを定義できるかがわかります。
src/main.zigで、zigコマンドに渡すサブコマンドの分岐がありました。
buildとbuild-exeではだいぶ雰囲気ちがうな。。。
くらいで
すみません、時間切れっぽいです。。。
Zigはバージョンごとに動作がよく変わる一方、ソースを追いかけやすいので、Zig自体がどう動くのかを把握しやすくなっています。
みなさんももっとZigを使って流行らせませんか?
Discussion