週刊nmosh - SDL2 / プラグインのFFI不要化

nmoshは脱boehm GCにむけてじわじわ進捗中。なんだかんだ言ってBoehmGCもそれなりに早いので、パフォーマンスを求める用途のために現状のVMも残す。
全体をCC0ライセンス(= パブリックドメイン)にするのは手間の面から言っても現実的でないので、まずはstubとかその辺から準備する予定。

SDL2

もちろん、WindowsとXとMacOSで動く。描画はcairo。

XやDarwinのサポートを考えるとやっぱりSDL2に揃えるのが良いような気もしている。SDL2はiOSAndroidでも動くので、一度書いたスクリプトをこれらのプラットフォームでもそのまま動かしたいという魂胆も。
もっとも描画バックエンドがCairoなので、cairoをビルドする手間は残るが。。この辺はGLにすることで解決したいところ。
毎回ハマっている気がするけど、Mac OS Xでは伝統的にメインスレッドしかUIイベントを処理できないので、今のところイベントはポーリングするしかない。Win32以外でもタイマをサポートしないと。。
SDL2は、SDL1と違って、

  • zlibライセンスになった
  • マルチウインドウをサポートした
  • IMEの存在をちゃんと仮定した
  • タッチスクリーンやオンスクリーンキーボードのON/OFFなど携帯端末向けのAPIを揃えた

ただ、SDL1よりもサポートするプラットフォームは減少していて、API後方互換性も絶妙なものになっている。
SDL1の頃もそうだけど、cairoとSDLはAlphaのあるサーフェスの扱いが異なるので、SDLは単にcairoの表示用にしか使わない。cairoはpre multiplied alphaと呼ばれる方法で色をエンコードしているが、SDLはそうでないので、blit操作を行う前に両者を変換してやる必要がある。流石にこれは面倒なので、alphaが関わりそうなビットマップ操作はcairoに集約することにする。

プラグインFFI不要化

従来、プラグイン(.soとか.DLLのいわゆる拡張モジュール)はFFIを使って実現されていた。nmoshはARMやMIPSではFFIをサポートしていないので、特に非同期I/Oの殆どの機能がプラグインで実現されている現在となっては微妙に都合が悪い。
というわけで、呼び出しスタブを生成して一緒にコンパイルすることにした。例えば、intを2つ受け取ってポインタを返す関数(= シグニチャii_pを持つ)を呼ぶには、

typedef void* (*func_ii_p_t)(int, int);
void
callstub_ii_p(func_ii_p_t func, uint64_t* args, void* ret){
    *(void**)ret = func(*(int *)(char *)&args[0], *(int *)(char *)&args[1]);
}

のようなstubを使って呼び出す。stub自体のプロトタイプは(void *, uint64_t*, void*)に固定されており、この固定されたプロトタイプだけは直接VMから呼べるようにしてある。
引数はuint64_tの配列に格納する。つまり、ビッグエンディアンだと困ったことになる。リトルエンディアンでは、値を64bitに拡張してからストアし、32bit長として読みだしても正常に読み出す事ができる。しかし、ビッグエンディアンでは引数のサイズに応じて値の格納位置をズラす必要がある。
引数が64bitを越えることは想定していない。実際long doubleくらいしか例外が無く、基本的に使われることは無いと判断した。
stubを事前に生成するのは、呼び出し規約を手で実装するのを避けるため。呼び出し規約を手で実装するのは不正なテクニック( http://d.hatena.ne.jp/mjt/20111225/p2 )を使用しないかぎりアセンブラで記述する必要があり、インタプリタの移植性を損なってしまう。逆に、任意のシグネチャを持つ関数を呼べるわけではないため、プロジェクトの外でプラグインを書けるようにするにはちゃんとしたFFIが結局必要になる。
あと、無駄なstubを含めてしまうのも考え物。現状では、インタプリタに統合されるstubは各プラットフォーム毎に共通のものなので、余計なものが少なからず含まれることになる。