最近、まつもとゆきひろさんが開発されている Spinel を Mac と Linux 機それぞれで試してみました。
RubyKaigi 2026 に参加後少し間が空いてからではあったのですが、実際に試して体験したこと・RubyKaigi 2026 時からの Spinel の進化にすごくワクワクしました!
簡単ではありますが、自分が Spinel について調べたことと試したことをまとめておきたいと思います。
まずは試してみる
SpinelはRubyのコードからネイティブ実行ファイルを生成するコンパイラです。ただ、正直自分の場合は README の説明を読んだだけではあまり具体的なイメージを持つことができなかったため、まずは実際に動かして試してみることにしました。
Spinel のビルドは数ステップでとても簡単で、Mac でも Linux(Gentoo) でも特にエラーにはならずにビルドをすることができました。
git clone https://github.com/matz/spinel.git cd spinel make deps make
最初に試したのは README に記載があるフィボナッチ数列です。
# fib.rb def fib(n) if n < 2 n else fib(n - 1) + fib(n - 2) end end puts fib(34)
これを Spinel に渡します。
$ ./spinel fib.rb
すると fib という実行ファイルが生成されます。
実行してみると、
$ ./fib 5702887
結果を得ることができました!
また、元々が Ruby スクリプトなので、
$ ruby fib.rb 5702887
としても実行できます。 「Ruby をコンパイルする」ということのイメージがなんとなく湧いてきました。
本当に実行ファイルになっている
上で確認したように Spinel で作られた実行ファイルでも元の .rb ファイルでも同じ結果を得られた訳ですが、ファイルの形式が違うんだということを実感するために file コマンドで結果を確認してみました。
Rubyスクリプトの場合は当たり前?かもですが、このように表示されます。
$ file fib.rb fib.rb: Ruby script text, ASCII text
Mac での結果ですが Spinel が生成したファイルはこのようになっています。
$ file fib fib: Mach-O 64-bit executable arm64
Spinel が生成したファイルは Ruby スクリプトではなく、OS が直接実行できる形式になっています。
依存ライブラリも確認してみる
続いて Mac の otool コマンドや Linux の ldd コマンドを使って依存ライブラリを確認したいと思います。
Spinel によって生成された実行ファイルは、
Mac の場合
$ otool -L ./fib
./fib:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)
Linux の場合
ldd ./fib
linux-vdso.so.1 (0x00007ff996b51000)
libm.so.6 => /usr/lib64/libm.so.6 (0x00007ff996a4a000)
libc.so.6 => /usr/lib64/libc.so.6 (0x00007ff996878000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff996b53000)
という結果でした。 (本題とはずれますが、Mac と Linux の結果の違いも個人的には興味深かったです)
通常の Ruby は、
Mac の場合
$ otool -L $(which ruby)
/Users/xxx/.local/share/mise/installs/ruby/4.0.5/bin/ruby:
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 4201.0.0)
/Users/xxx/.local/share/mise/installs/ruby/4.0.5/lib/libruby.4.0.dylib (compatibility version 4.0.0, current version 4.0.5)
/opt/homebrew/opt/gmp/lib/libgmp.10.dylib (compatibility version 16.0.0, current version 16.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
Linux の場合
$ ldd $(which ruby)
linux-vdso.so.1 (0x00007f1500db9000)
libruby.so.4.0 => /home/xxx/.local/share/mise/installs/ruby/4.0.5/lib/libruby.so.4.0 (0x00007f1500400000)
libz.so.1 => /usr/lib64/libz.so.1 (0x00007f1500d74000)
libgmp.so.10 => /usr/lib64/libgmp.so.10 (0x00007f1500ccb000)
libcrypt.so.2 => /usr/lib64/libcrypt.so.2 (0x00007f15003c6000)
libm.so.6 => /usr/lib64/libm.so.6 (0x00007f15002ec000)
libc.so.6 => /usr/lib64/libc.so.6 (0x00007f150011a000)
libgcc_s.so.1 => /usr/lib/gcc/x86_64-pc-linux-gnu/16/libgcc_s.so.1 (0x00007f1500c9b000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1500dbb000)
など複数のライブラリに依存しています。
ここまで確認して Spinel が生成する実行ファイルは libruby などの Ruby ランタイムには依存しないスタンドアロンな実行ファイルになっているんだということを改めて実感しました。
ここでようやく Spinel とは?
README には次のように書かれています。
SpinelはRubyのソースコードをスタンドアローンのネイティブ実行形式にコンパイルするツールです。プログラム全体を対象とした型推論を行い、最適化されたCコードを生成することで、CRubyと比較して大幅な高速化を実現します。
ざっくりまとめると、
- Ruby コードを解析する
- プログラム全体の型を推論する
- 最適化された C コードを生成する
- C コンパイラでネイティブ実行ファイルを作る
という流れになっています。
README には次のような図があります。
Ruby (.rb)
|
v
parse (libprism) Parse with Prism, linked in as a C library
| (a CRuby + Prism-gem path produces the same AST)
v
text AST -> NodeTable Loaded into an in-memory node table
|
v
analyze Whole-program type inference (src/analyze*.c).
| Walks the AST to a fixpoint: param / return / ivar
| types, value-type detection, dead-code markers,
| a per-node inferred-type cache.
v
codegen C code generation (src/codegen*.c). Reads the AST
| plus the analysis just computed in the same process,
| emits one C file.
v
C source (.c)
|
v
cc -O2 -Ilib -lm System C compiler + runtime
|
v
Native binary Standalone, no runtime dependencies
Spinel 自身が直接ネイティブコードを生成するのではなく、一度Cコードを生成し、その後システムのCコンパイラに処理を委ねています。
AOT コンパイラとは?
Spinel は AOT(Ahead-Of-Time)コンパイラと呼ばれるものです。
AOT とは「実行前にまとめてコンパイルする」方式です。
一方で RubyKaigi などでよく耳にする JIT (Just-In-Time) は、プログラムを実行しながらホットな部分だけをコンパイルします。
大きな違いは、「いつコンパイルするか」です。
型推論とは?
Rubyは動的型付け言語なので、コードを書く際に型を宣言しませんが Spinel はプログラム全体を解析して、
- この変数は Integer
- このメソッドは Stringを返す
- このインスタンス変数は Array
のような情報を推論します。
READMEでは Whole-program Type Inference と説明されています。
プログラム全体を解析して型を推論することで、高速なCコードを生成できるようになります。逆に言えば、現時点では Ruby のすべての機能を対象としているわけではありません。この点は RubyKaigi 2026 でも言及されていたのと README にも現在サポートしていない機能や制約がまとめられています。
セルフホスティングから C 実装へ
2026-07-02 追記
こちらの記事を公開後、まつもとゆきひろさんからコメントをいただき、最新の Spinel からはこの後触れている legacy ディレクトリは削除されているそうです。
ちょうどこの記事が書かれたタイミングでlegacyディレクトリを削除しちゃいました。bootstrapできるRuby版はself-hostブランチに残ってます。
— Yukihiro Matz (@yukihiro_matz) 2026年7月1日
個人的に一番興味深かったのがここです。
現在のコンパイラ本体のコード生成部分は C で実装されていますが、RubyKaigi 2026 で発表された当時は Ruby で書かれていました。
Ruby での実装は現在も legacy ディレクトリとしてリポジトリに残っています。
SpinelをCで実装し直した。今回は丸々1週間かかった。セルフホストはコンパイラ書きにとってロマンだけど、8万行まで育ったコンパイラに全体型解析をかけるとビルドに20分くらいかかるので生産性が落ちてしまっていたので。
— Yukihiro Matz (@yukihiro_matz) 2026年6月15日
C 実装への移行は比較的最近行われたようです。
ちなみに Ruby 実装の Spinel も現在試すことは可能です。
make bootstrap を実行すると、
- Ruby 版 Spinelで最初のコンパイラを作る
- 生成したコンパイラでもう一度自分自身をコンパイルする
- 2回目と3回目の出力が一致することを確認する
という一連の流れを見ることができます。 大量のログが流れますが、「コンパイラ自身をコンパイルしている」という様子がログの内容から分かってすごく面白かったのと、コンパイラ自身をRubyで実装していたという点がとにかく興味深くてもっとこのあたりの仕組みについても分かるようになりたいと思ったりしました。
また、現在は C 実装に置き換わっていますが、こうした変遷を知る面白さも同時に感じました。
おわりに
Spinel を試してみて「Ruby ファイルが実行ファイルになる」という体験が一番印象的でした。
ここまで書いてきたようにファイル形式や依存ライブラリの違いなども確認しつつ実際に試してみると Spinel についてより身近に感じることができるようになりました。 また、Spinel を通じて「コンパイラってこんなことをしているんだ」ということも知ることができ、よりワクワクしながら試すことができました。
実は Spinel を試す中で Mac だとコンパイルが通るけれど、Gentoo だとコンパイルが通らないコードがあり、自分でも原因を調べていたら数日後には問題が解消していてどちらの環境でもコンパイルが通るようになっていました。Spinel は日々改善・開発されて進化していっているんだと実感してさらにワクワクした瞬間でもあります。 Spinel を試したことで、Spinel 自身もコンパイラや実行環境の違いなど関連して自分がもっと知りたい!と思う部分が以前よりもちょっと明確になり、プログラミングは楽しいな!やめられない!と再認識しています。
今回の記事がまだ Spinel を試したことがない方にとってなんらかの参考になれば嬉しいです。




