スピネル:ルビーAOTネイティブコンパイラ
9時間前原文(github.com)
概要
SpinelはRubyコードをネイティブ実行ファイルにコンパイルするAOTコンパイラ。
全プログラム型推論とCコード最適化でCRubyより高速な実行を実現。
自己ホスティング対応:自身のバックエンドもRubyで記述し自身でコンパイル。
多くのRuby機能をサポートし、独自の最適化や組み込みランタイムを搭載。
依存性最小、高速ビルド、豊富なベンチマーク結果を公開。
Spinelの概要
- SpinelはRubyソースコードをスタンドアロンなネイティブバイナリに変換するAOTコンパイラ。
- 全プログラム型推論を用い、最適化されたCコードを生成。
- CRubyと比較して、計算負荷の高い処理で大幅な高速化を実現。
- 自己ホスティング:Spinel自身のバックエンドもRubyで記述し、Spinelでコンパイル可能。
- 依存性最小:生成バイナリはlibcとlibmのみ必要、外部ランタイム不要。
動作フロー
- Rubyファイル(.rb)をPrismでパースし、ASTをテキスト形式で出力。
- spinel_codegenで型推論とCコード生成を実施。
- 標準Cコンパイラ(gccなど)でネイティブバイナリをビルド。
- 生成されたバイナリは完全スタンドアロン、Ruby環境不要。
クイックスタート
make depsでlibprism取得。makeで全体ビルド。- Rubyプログラム(例:hello.rb)を作成。
./spinel hello.rbでバイナリ生成、./helloで即実行。- オプションで出力ファイル名指定(-o)、Cコードのみ生成(-c)、Cコード出力(-S)など。
自己ホスティングとブートストラップ
- Spinelは自身のバックエンド(spinel_codegen.rb)を自己コンパイル。
- ブートストラッププロセス:
- CRuby+spinel_parse.rbでAST生成。
- CRuby+spinel_codegen.rbでCコード生成→bin1。
- bin1でASTからgen2.c生成→bin2。
- bin2でASTからgen3.c生成し、gen2.cとgen3.cが一致すればブートストラップ完了。
ベンチマーク結果
- 74の機能テスト、55の性能ベンチマークを完走。
- miniruby(Ruby 4.1.0dev)比で平均約11.6倍高速。
- 例:
- life(Conway's GoL):86.7倍
- ackermann:74.8倍
- mandelbrot:58.1倍
- fib(再帰):34.2倍
- rbtree(赤黒木):22.6倍
- json_parse:10.1倍
- ao_render(レイトレーサー):8.0倍
サポートされるRuby機能
- クラス・継承・モジュール・Struct・alias・定数・オープンクラス
- 制御構文:if/elsif/else, unless, case/when, while, until, loop, for..in, break, next, return, catch/throw, safe navigation(&.)
- ブロック・クロージャ:yield, block_given?, &block, proc, lambda, method(:name)
- 例外処理:begin/rescue/ensure/retry, raise, カスタム例外
- 主要型:Integer, Float, String(イミュータブル・ミュータブル両対応), Array, Hash, Range, Time, StringIO, File, Regexp, Bigint, Fiber
- タグ付きユニオンによる多態値、Nullable型(T?)で自己参照構造体対応
- グローバル変数:静的C変数に変換、型不一致はコンパイル時検出
- 文字列操作:<<でミュータブル昇格、+や補間、tr、ljust/rjust/centerなど標準メソッド全対応
- 正規表現:組み込みNFAエンジン、=~, $1-$9, match?, gsub, sub, scan, split
- Bigint:mruby-bigint利用、必要時のみ静的リンク
- Fiber:ucontext_tによる協調型コンカレンシー、値受け渡し対応
- メモリ管理:マーク&スイープGC、サイズ分割フリーリスト、非再帰マーク、スティッキービット
- 値型最適化:小規模クラスは値型C構造体に昇格しGC不要
- シンボル:sp_sym型、リテラルはコンパイル時インターン、Hashはシンボル専用実装
- I/O:puts, print, printf, p, gets, ARGV, ENV[], File.read/write/open, system(), バッククォート
主な最適化内容
- 型推論駆動の最適化:
- 値型昇格:小規模イミュータブルクラスはC構造体でスタック配置
- 定数伝播:リテラル定数は直接インライン展開
- ループ長キャッシュ:配列や文字列長をループ前に計算・再利用
- メソッドインライン化:短い非再帰メソッドはstatic inline化
- 文字列連結最適化:複数連結は一括mallocで中間生成抑制
- Bigint自動昇格:自己参照加算/乗算パターンで自動昇格
- 静的シンボルインターン:リテラルto_symはコンパイル時定数参照
- split再利用:ループ内splitは同一配列を再利用しアロケーション削減
- デッドコード除去:未使用関数はリンク時に自動削除
- 型推論ループの早期終了:多くのプログラムで1-2回で収束し高速化
- ASTフィールドパースのバイトウォーク最適化
- 警告ゼロビルド:全テスト・ベンチマークで-Werrorをクリア
アーキテクチャ構成
- spinel:ワンコマンドラッパースクリプト(POSIX shell)
- spinel_parse.c:libprismを直結したCフロントエンド
- spinel_codegen.rb:AST→Cコード生成(約2万行、自己ホスティング可能)
- lib/sp_runtime.h:GCや配列・ハッシュ・文字列等のランタイムライブラリ
- lib/sp_bigint.c:任意精度整数(mruby-bigintベース)
- lib/regexp/:組み込み正規表現エンジン
- test/:機能テスト
- benchmark/:ベンチマーク
- Makefile:ビルド自動化
ビルドと依存性
make deps:libprism取得make:パーサ・ランタイム・ブートストラップコンパイラ構築make test:74機能テスト実行make bench:55ベンチマーク実行make bootstrap:自己ホスティング再構築sudo make install:/usr/localへインストールmake clean:ビルド成果物削除- Prism:Rubyパーサとして利用、libprism(C)またはprism gem(CRuby)対応
- CRuby:初回ブートストラップ時のみ必要
- ランタイム依存性なし:生成バイナリはlibc + libmのみ
制限事項
- eval・instance_eval・class_eval未対応
- メタプログラミング(send, method_missing, define_method動的生成)未対応
- スレッド・Mutex未対応(Fiberはサポート)
- エンコーディング:UTF-8/ASCII前提
- ラムダ計算:深いネストのlambda+[]は非対応
ライセンス
- MIT License採用
- 詳細はLICENSEファイル参照