やったことだけ書く備忘録

LuaをWebAssemblyにコンパイルして実行する話

LuaでもWebAssemblyがしたかった(した)



そろそろWebAssemblyやっていくぞと思って手を付け始めたんですが、もう既に色んな言語からwasmにコンパイルできるようになってるんですね。
Goがwasm対応したと聞いたのでGoでやろうと思っていたんですが、なぜかLuaでやり始めました。

作ったのはこちら。まだPoCで全然機能はないけど、Hello Worldはできました:

ysugimoto/webassembly-lua

なお、既に参考実装にwasm_luaというのがあるんですが、これは入力したLua Scriptをwasm上で実行して結果を得る、というもので、これだと自分で書いたスクリプトをwasmにするのとはちょっと違うなーってことで、参考にしつつイチから作りました。



Lua -> C -> emscripten



やってることはほぼ同じで、バンドル対象のLuaファイルのコードをバイナリにして埋め込みつつ、emscriptenでコンパイルできるCのファイルを組み立ててコンパイル、という手順を取っています。
LuaはCから変数操作などわりとやりたい放題っぽくて、Lua manualみながらガチャガチャやってたらいけました。

コンパイル用のCファイルの組み立てはテンプレーティングみたいな感じだったのですぐできたんですが、LuaのRuntimeを混ぜつつコンパイルするのにちょっと手間取りました。

Luaを普通にソースからインストールのは簡単なんですが、emscriptenでコンパイルするときは generic でコンパイルしないといけなくて、これもwasm_luaに方法が書いてあったんですが、




make generic CC="emcc -s WASM=1"


としてemscriptenようにコンパイルしたものを使わないとダメでした。で、実際にwasmを作るときにコンパイル済みの liblua.a をリンクして、ヘッダを読み込ませることでコンパイルが通ります。具体的には、




emcc -I/path/to/lua/src generated.c /path/to/lua/src/liblua.a -s WASM=1


みたいな感じでコンパイルするとLuaの動くwasmが生成されます。C力低すぎ。。。

Hello Worldするサンプルは examplesに置いたのでお試しください。
あと、上記の諸々の環境を手元で作るの結構大変なので、構築済みのDocker Imageも置きました。

Docker Hub - ysugimoto/webassembly-lua

このコンテナ内でやると、手元の環境を汚さずに済みます:




docker pull ysugimoto/webassembly-lua
docker run --rm -v $PWD:/src ysugimoto/webassembly-lua emcc-lua hello_world.lua


詳細はREADMEを見てください。



戻り値・引数の型問題



現状はまだ文字列、つまりconst char*のやりとりしかできません。引数も取れません。
これはWebAssembly側、つまりJavaScript側からも関数呼び出し時には型指定をするようになっていて、




const wasmFunction Module.cwrap('関数名''戻り値の型''引数の型リスト');
const 
ret wasmFunction(...);
console.log(ret)
 


のような呼び出し方法を取ります(直接コールすることもできますが、HEAPに入れたり出したりしないといけないのでこっちのほうが楽かな)。
一方でLuaには型がないので、それをwasmの関数としてCファイルを生成するのにどうすればいいのかな、と考えています。

生成自体は簡単ですが、どうやって型情報をつけるか…TypeScriptみたいな定義ファイルを一緒に食わせるとか、そういう感じになりそうですが、何か良い案があればぜひ教えてください。
あと、別モジュールに分けてrequireしてるものや、luarocksなどでインストールしたライブラリも今はバンドルできないので、これも対応したいと思います。

…実際Luaでwasmやる人がいるのかどうかはわかりませんが…

現場からは以上です。

« 前の記事