Spinel 將 Ruby 原始碼編譯成獨立的原生執行檔。它執行全程式型別推論並生成優化的 C 程式碼,相較於 CRuby 可達到顯著的速度提升。

此編譯器是一個單一的自包含 C 執行檔:它透過 libprism 解析 Ruby,對整個程式進行型別推論,產生 C 程式碼,並呼叫系統的 cc 編譯器生成原生執行檔——編譯時不需 Ruby 執行時或其他輔助二進位檔。(Spinel 先前是用自我托管的 Ruby 子集撰寫,該後端仍保留於 self-host 分支。)

分析與程式碼生成在同一程序中執行並共享記憶體模型:程式碼生成直接讀取分析階段推論出的型別,無需序列化步驟。解析器輸出並分析器使用的文字 AST 格式可參考 docs/internals/AST.md。

Spinel 從原始碼建置:複製此倉庫並使用 make 指令建置。它不以 RubyGem 形式發佈,無法透過 gem install 安裝。(Spinel 會將部分 gem 編譯進程式,但編譯器本身以原始碼形式提供。)

spin 是日常使用介面,類似 cargo 或 mix。它負責專案架構、相依性解析、驅動編譯器及執行測試,無需 Makefile 或手動設定 -I 標誌。

相依套件(packages)為編譯進二進位檔的原始碼樹,無需執行時載入或擴充套件建置,套件甚至可包含 .c 檔。完整指南與如何撰寫及發佈函式庫請參考 docs/spin.md。

spinel 是底層編譯器,類似 gcc,適合單檔腳本與實驗使用:

./spinel 是單一原生執行檔(build/spinel,倉庫根目錄的 spinel 為方便用的符號連結),負責解析、型別推論、產生 C 程式碼、呼叫 cc 連結並執行結果,無需 shell 包裝器或輔助二進位檔,無網路或清單依賴(此分離確保建置的封閉性;spin 負責狀態管理)。支援完整選項集,包括 --rbs DIR(以 RBS 啟動推論)及 --emit-rbs、--emit-types、--emit-symbol-map 等分析模式。

整數為原生固定寬度字,--int-overflow=MODE 選擇當結果超出寬度時 + / - / * 的行為:

預設模式下,明顯會溢位的整數區域變數(如累加器 q = q * k)會自動提升為 Bigint;promote 模式則將所有整數運算皆提升。

Spinel 可讀取 RBS 檔案以啟動分析器。使用 --rbs DIR 時,spinel 會對該目錄下的 *.rbs 檔(與 rbs 和 Steep 使用的結構相同)執行 spinel_rbs_extract,並將結果作為分析器的啟動種子。啟動種子為建議性質,推論仍會根據觀察結果調整,錯誤或無法表示的種子最多不產生效果。詳見 docs/rbs-extract.md。

目前通過 1,744 個測試與 58 個基準測試。28 項基準測試的幾何平均速度約為 Ruby 4.0.4(啟用 --yjit)約 5.8 倍。基準以 CRuby 4.0.4(穩定版)為基準,執行時禁用 gem 並啟用 --yjit。每次計時取三次中最佳,10 毫秒以下的數據受解譯器與執行時啟動影響,屬於「噪音底限」。

YJIT 在某些整數迴圈工作負載(如 mandelbrot、nqueens、matmul、partial_sums、sieve)中表現與解譯 Ruby 幾乎相同,因為這些基準受限於解譯器已達原生速度的整數/浮點運算。在呼叫密集的程式碼(ackermann、fib、tarai、tak、rbtree)中,YJIT 可帶來 5-8 倍提升,但 Spinel 仍因提前專門化而勝出。最小差距出現在小型遞迴核心(ackermann、tarai、tak、fib),YJIT 對遞迴呼叫內聯優異,Spinel 編譯的 C 程式僅快 1.4-2.5 倍。

Spinel 支援核心 Ruby 語言特性:類別、繼承、super、include(mixin)、attr_accessor、Struct.new、alias、模組常數、內建類別開放。

控制流程:if / elsif / else、unless、case / when、case / in(模式匹配)、while、until、loop、for..in(範圍與陣列)、break、next、return、catch / throw、&(安全導航)。

區塊:yield、block_given?、&block、proc {}、Proc.new、lambda -> x {}、method(:name)。區塊方法包括 each、each_with_index、map、select、reject、reduce、sort_by、any?、all?、none?、times、upto、downto。

例外處理:begin / rescue / ensure / retry、raise、自訂例外類別。

支援類型:Integer、Float、String(不可變與可變)、Symbol、Array、Hash、Range、Time、StringIO、File、Regexp、MatchData、Complex、Rational、Bigint(自動提升)、Enumerator、Set、Fiber、Thread、Mutex、Queue、SizedQueue、ConditionVariable、Marshal(dump/load)。透過標記聯合實現多型值。支援可空物件類型(T?)以處理自我參照資料結構(連結串列、樹)。

Inspect / p:Object#inspect 輸出與 CRuby 完全相同,涵蓋原始型別、型別化及異質陣列、各種 Hash、Range、Struct 及使用者類別實例(#<Name:0x... @ivar=...> 或自訂 inspect),包含透過多型(標記聯合)綁定可達的值。p obj、obj.inspect、obj.to_s 及字串插值皆一致。

全域變數:$name 編譯為靜態 C 變數,編譯時檢測型別不匹配。

字串:<< 自動提升為可變字串(sp_String),實現 O(n) 原地附加。+、插值、tr、ljust / rjust / center 及所有標準方法皆適用。字元比較如 s[i] == "c" 優化為直接字元陣列存取(零配置)。串接 (a + b + c + d) 透過 sp_str_concat4 / sp_str_concat_arr 合併為單一 malloc,減少 N-1 次配置。迴圈內 str.split(sep) 重複使用同一 sp_StrArray(csv_process 範例消除 400 萬次配置)。

Regexp:內建 NFA 正規表達式引擎(無外部依賴)。支援 =~、$1-$9、match?、gsub(/re/, str)、sub(/re/, str)、scan(/re/)、split(/re/)。

Bigint:透過 mruby-bigint 實現任意精度整數。由迴圈乘法模式自動提升(如 q = q * k),或在 --int-overflow=promote 模式下所有整數運算皆提升。以靜態函式庫連結,僅在使用時包含。

Fiber:協作式並發,使用小型可攜式組合語言上下文切換(x86-64 / arm64,無 ucontext 依賴)。支援 Fiber.new、Fiber#resume、Fiber.yield(帶值傳遞)、Fiber#raise / #kill,外部 Enumerator 與 Enumerator::Lazy 共享此機制。捕捉自由變數至堆疊提升的儲存格。每個 Fiber 有獨立儲存空間 Fiber[:k] / Fiber[:k] = v(及 Fiber.current[:k] 別名),符號鍵多型值,延遲配置,於 Fiber.new 時從父 Fiber 繼承淺層快照。

Threads:Thread 支援真正平行且無 GVL,採用 M:N 排程器將綠色執行緒多工至 OS 工作執行緒(每核心一個,受 SPINEL_WORKERS 限制),具工作竊取與約 10 毫秒搶占時間片,搭配停世界 GC。支援 Thread.new/#join/#value/#alive?、Thread.current/main/list/pass、執行緒區域變數及同步原語 Mutex(#synchronize/#lock/#unlock/#try_lock)、Queue / SizedQueue(阻塞 #pop / #push)、ConditionVariable(#wait/#signal/#broadcast)。未同步共享變異為資料競爭,與 JRuby/TruffleRuby 類似。詳見 docs/thread.md。

記憶體管理:標記清除 GC,具大小分級空閒列表、非遞迴標記與黏著標記位。小型類別(≤8 個純量欄位,無繼承,無參數變異)自動堆疊配置為值型別——1 百萬次 5 欄位類別配置時間由 85 毫秒降至 2 毫秒。僅使用值型別的程式不會產生 GC 執行時。

Symbols:獨立 sp_sym 類型,與字串不同(:a != "a")。符號字面量於編譯時內部化(SPS_name 常數);String#to_sym 僅在需要時使用動態池。符號鍵 Hash({a: 1})使用專用 sp_SymIntHash,直接儲存 sp_sym(整數)鍵,無字串比較與動態字串配置。

I/O:puts、print、printf、p、gets、ARGV、ENV[]、File.read/write/open(支援區塊)、system()、反引號。

FFI:無需擴充編譯器的直接 C 呼叫。宣告(ffi_func、ffi_lib、ffi_const、ffi_buffer、ffi_read_*)置於模組內,程式碼生成輸出 extern 宣告,spinel 包裝器從標記註解擷取 -l 標誌。涵蓋純量、字串、不透明指標、整數常數、原始位元組緩衝區與結構欄位讀取。詳見 docs/FFI.md 與 examples/ffi/。

全程式型別推論驅動多項編譯時優化:

分析器與程式碼生成器皆為 C 程式(src/analyze*.c、src/codegen*.c),共享記憶體中的 Compiler 與 NodeTable。管線接受 Spinel 目標的 Ruby 子集:類別、def、attr_accessor、if / case / while、each / map / select、yield、區塊(含 ... 參數轉發)、begin / rescue、String/Array/Hash 操作、File I/O。

不支援動態巨集程式設計或 eval。支援編譯時類別主體宣告與已知字面量輸入的 Struct 風格方法合成。

分析階段(analyze_program)負責全程式型別推論,透過多輪 AST 遍歷逐步填充與精煉靜態模型:

註冊階段:註冊所有類別、模組、頂層方法、實例/類別方法、實例變數、FFI 宣告、正規表達式字面量與常數,解析繼承、include、prepend、attr/alias 宣告與繼承成員。完成後編譯器表格包含程式定義的所有名稱。

呼叫點擴寬:每個作用域的呼叫點將參數型別傳入被呼叫函式的參數槽,統一器僅在兩個呼叫點型別不一致時擴寬為多型,採保守策略。

迭代精煉迴圈:重新推論回傳型別、寫入者的實例變數型別、參數型別(陣列/哈希/字串專門化)、區塊參數型別、預設參數型別及大整數迴圈區域變數,直到簽名不再變化,多數程式於數輪內收斂。

特徵偵測:值型別偵測(小型不可變類別轉為堆疊結構)、GC 需求、符號收集、proc 捕捉標記與可達性分析,設定標誌以控制執行時輔助函式產生與死碼消除。

節點型別註解:最後一輪填充每節點推論型別快取,程式碼生成時直接讀取,避免重複推論。

程式碼生成階段在同一程序與 Compiler 上執行,輸出單一 C 檔案:

輸出前導碼(#include "sp_runtime.h"、依分析標誌產生的執行時輔助函式、類別階層表、符號內部表、類別結構與建構子、前置宣告),遍歷所有可達方法與類別方法主體輸出定義,最後輸出 int main()。

依程式需求產生執行時輔助函式。簡單程式如 puts "hi" 僅輸出少量 C 程式碼;使用 Method 實例、哈希字面量、類別階層等功能的程式則包含相應執行時區塊。

執行時分為標頭檔(lib/sp_runtime.h:GC、陣列/哈希/字串實作、內聯熱路徑)與外部原始碼(lib/sp_*.c、lib/regexp/),封裝於 libspinel_rt.a。產生的 C 程式包含標頭並連結此靜態函式庫;--gc-sections 選項可剔除未使用函式。

解析器為 src/spinel_parse.c,直接連結 libprism(無需 CRuby),輸出文字 AST 供編譯器使用。require_relative 於解析時透過內嵌目標檔案解決。

可透過 make install PREFIX=$HOME/.local 覆寫安裝路徑。

Prism 為 spinel_parse 使用的 Ruby 解析器。make deps 會從 rubygems.org 下載 prism gem 並解壓 C 原始碼至 vendor/prism。若系統已有 prism gem,建置會自動偵測,也可透過 PRISM_DIR 指定自訂路徑。

建置與執行 C 編譯器不需 CRuby,僅作為選用的解析器備援(Prism gem 路徑)及測試工具,測試時會將 Spinel 輸出與 CRuby 輸出比對。

Spinel 可產生不需呼叫 C 編譯器的 C 程式碼,方便在一台機器建置 Ruby 程式,將產生的原始碼傳送至另一台機器編譯:

輸出為單一自包含 .c 檔,使用者可對其與 lib/ 標頭(sp_runtime.h 等)及 libspinel_rt.a(包含 bigint、regexp、GC、fiber、I/O 等執行時)編譯連結;--gc-sections 可剔除未使用程式碼。目標機器不需 CRuby 或 spinel 執行檔。

執行時為 POSIX 風格,目標平台為 POSIX 系統:

每次 PR 都會在 ubuntu-latest / gcc、ubuntu-latest / clang 與 macos-latest / clang 上執行端對端測試(解析器建置、程式碼生成、完整測試與基準測試套件)。不支援原生 Windows(MinGW / MSVC),因執行時依賴 POSIX 特性(pthread、<sys/mman.h>、GCC 的 __attribute__((cleanup))、GCC/Clang 內聯組合語言)。Windows 使用者可於 WSL 執行 Spinel,作為原生 Linux 工具鏈。

部分行為刻意與 CRuby 不同,因 CRuby 行為需 Spinel 未實作的功能(如 Integer#** 負指數會拋例外而非回傳 Rational)。詳見 docs/limitations.md 中「By design」章節。

歡迎貢獻。議題追蹤器同時作為路線圖,任何開放項目皆可參與;最有用的切入點為可重現錯誤的簡短 Ruby 程式(Spinel 失敗但 CRuby 通過)及修正此類錯誤的程式碼生成改進。

相關生態系(社群建置,非本倉庫一部分):

Spinel 最初以 C 實作(c-version 分支),後改用 Ruby(ruby-v1 分支),再改用自我托管 Ruby 子集。現行 master 為分析與程式碼生成階段的全新 C 實作,建置速度遠快於自我托管後端且輸出等效;master 分支即為正式版本並執行 make test / make bench / make optcarrot。自我托管 Ruby 實作保留於 self-host 分支,僅供歷史參考,已不再合併至 master。