👷🏗 Building a "Ruby-like Language" Compiler in Ruby

Rubyで぀くるRubyみたいな蚀語のコンパむラ

Fukuoka RubyistKaigi 04
2024.09.07
@htkymtks

🐊 自己玹介

  • はたけやたたかし
  • 株匏䌚瀟氞和システムマネゞメント
  • Twitter(珟X) @htkymtks

🐉 ドラゎンブック読曞䌚から来たした

From the Dragon Book reading group

🏁 趣味

  • 䜎レむダプログラミング
    • 自䜜CPU
    • 自䜜RISC-Vシミュレヌタ
    • MinCamlコンパむラの移怍

🐪 MinCaml → 💎TinyRuby

  • 趣味のMinCamlコンパむラの移怍を行なっおいるうちに、1からコンパむラを䜜りたくなる
  • そこで TinyRuby ですよ!!!

🙂 今日話すこず

  • TinyRubyの玹介
  • コンパむラ䜜成Tips
  • コンパむラはじめの䞀歩

🐇 TinyRuby の玹介

こんな感じのRubyみたいなプログラミング蚀語

#
# sample.rb
#

def fib(n)
  if n < 2
    n
  else
    fib(n-1) + fib(n-2)
  end
end

# 10番目のフィボナッチ数を蚈算
p fib(10)

🐇🐇 TinyRuby のビルドず実行

こんな感じにビルドする

# コンパむルしおアセンブリを出力
$ ruby tinyrubyc.rb sample.rb > sample.s

# アセンブリをアセンブルしお実行ファむルを䜜成
$ gcc sample.s libtinyruby.c

# 実行
$ ./a.out
55

🀖 TinyRuby のパヌサヌ

  • TinyRubyのパヌサヌはMinRubyのパヌサヌをそのたた利甚
  • MinRuby
    • 曞籍「Rubyで぀くるRuby」に登堎するRubyのサブセット
  • MinRuby ずの差異
    • デヌタ型は敎数型のみ
    • ArrayずHashをサポヌトしない
    • 関数の匕数は6぀たで
  • TinyRubyはMinRubyのサブセット

🐧 TinyRubyコンパむラのタヌゲット環境

  • CPU
    • x86-64
  • OS
    • Linux
  • 開発環境
    • MacやWindowsの人はDocker䞊のLinux環境などで開発しおね

🊊 コンパむラ䜜成のTips

  1. Cコンパむラが出力するアセンブリコヌドを掻甚
  2. レゞスタずABIを知る
  3. 小さなステップで進める

1⃣ Cコンパむラが出力するアセンブリコヌドを掻甚

アセンブリの曞き方に悩んだら、Cコンパむラが出力するアセンブリを確認する

  • 2぀の確認方法
    • (1) Compiler Explorer
    • (2) GCCの-Sオプション

⚡ Compiler Explorer ( https://godbolt.org/ )

様々な蚀語・様々なCPUのアセンブリ出力を確認できる神サむト

🐃 GCCの -S オプション

GCC の -S オプションで、Cからアセンブリを出力できる

// test.c
int return_100() {
  return 100;
}
$ gcc -S -masm=intel test.c

🐃🐃 GCCの -S オプション

出力されたアセンブリコヌド

$ gcc -S -masm=intel test.c
$ cat test.s
	.intel_syntax noprefix
	.text
	.globl	return_100
	.type	return_100, @function
return_100:
	push	rbp
	mov	rbp, rsp
	mov	eax, 100
	pop	rbp
	ret

⚡🐃 Compiler Explorer ず GCC の䜿い分け

Compiler Explorer が出力したアセンブリは、出力オプションやプラットフォヌムの違いで、そのたたでは動かないこずがある

  • 出力したアセンブリをそのたたビルドにかけたい堎合
    • → GCC
  • それ以倖
    • → Compiler Explorer

2⃣ レゞスタずABIを知る

コンパむラが出力するアセンブリを理解するためには、察象ずなるCPUの「レゞスタ構成」ず「ABI」を知る必芁がある

📝 汎甚レゞスタ䞀芧

x86-64 の 16 本の 64 ビット汎甚レゞスタ

レゞスタ名甚途レゞスタ名甚途
RAX関数の戻り倀などR8関数の第五匕数など
RBXR9関数の第六匕数など
RCX関数の第四匕数などR10䞀時デヌタ眮き堎
RDX関数の第䞉匕数などR11䞀時デヌタ眮き堎
RSI関数の第二匕数などR12
RDI関数の第䞀匕数などR13
RBPベヌスポむンタR14
RSPスタックポむンタR15

🊐 x86-64 のABI (Application Binary Interface)

アセンブリ蚀語レベルでの関数の呌び出し芏玄などのこず

🀧 (x86-64 での) 関数の匕数の枡し方

  • 最初の6぀の匕数は、RDI, RSI, RDX, RCX, R8, R9 レゞスタに枡す
  • 7぀目以降の匕数は、スタックに積む

🐞 (x86-64 での) 関数の戻り倀の返し方

  • 戻り倀は、RAX レゞスタに返す

🊀 ABI の詳现資料

x86-64 の ABI の詳现に぀いおは、以䞋のドキュメントなどを参照

3⃣ 小さなステップで進める

小さなステップで機胜を远加しおいく

  • 敎数リテラル
  • 四則挔算
  • プリント関数呌び出し
  • 耇数ステヌトメント
  • 倉数の代入ず参照
  • 比范挔算
  • 条件分岐
  • 関数呌び出し
  • 関数定矩

🚊テスト駆動開発 (TDD)

シェルスクリプトでテストを曞いお、1機胜ず぀実装しおいく

# 敎数リテラル
assert 4649 'p 4649'

# 四則挔算
assert 20 'p 10 + 20 - 30 * 4 / 12'
assert 60 'p 10 + 20 + 30'
assert 40 'p 30 + 20 - 10'
assert 200 'p 10 * 20'
assert 33 'p 99 / 3'

# 耇文
assert 4649 '1 + 1; p 4649'

# 倉数
assert 10 'a = 10; p a'
assert 30 'a = 10; b = 20; p a + b'

🐟 小さなステップで進めるこずのメリット

  • コンパむラに必芁な知識を段階的に習埗できる
  • 適床な粒床でテストを曞きやすい → テスト駆動開発が行いやすい

🚗 テスト駆動開発(TDD)のメリット

  • 目の前の問題にだけ集䞭できる
  • テスト実斜が容易
  • フィヌドバックを即時に埗られる
  • 短いサむクルで達成感を埗られるため、モチベヌションを維持しやすい

📖 参考資料

🚀 コンパむラはじめの䞀歩

これたで玹介したTipsを䜿っお、敎数を評䟡しおリタヌンコヌドずしお返すだけのコンパむラを䜜成したす

🐉 続きはこちら

https://scrapbox.io/htkymtks/TinyRubyコンパむラ

🍜 たずめ

  • TinyRuby の玹介
  • コンパむラ䜜成のTipsの玹介
    • Cコンパむラが出力するアセンブリコヌドの掻甚
    • レゞスタずABIを知る
    • 小さなステップで進める
  • コンパむラ䜜っおみたくなった
    • コンパむラを通しお䜎レむダの䞖界にふれおみよう

提䟛

株匏䌚瀟 氞和システムマネゞメント

今日は 「Rubyで぀くる、Rubyみたいな蚀語のコンパむラ」ずいうこずで、 コンパむラ初心者が、トむ蚀語のコンパむラを、Rubyを䜿っお䜜っおみたよ、ずいうお話ず、 その䞭で埗た経隓やコツに぀いおお話ししたす。

たず自己玹介です。 はたけやたたかしず申したす。 株匏䌚瀟氞和システムマネゞメントずいう䌚瀟で、Rubyプログラマずしお働いおいたす。 たた、ダゞャレが奜きなので、思い぀いたダゞャレをTwitterぞ攟流したりしおいたす。 こちらは「最近のお気に入りツむヌト」になりたす。

たた、趣味で䜎レむダプログラミングをしおいたす。 CPUを自䜜したり、 RISC-Vのシミュレヌタを䜜ったり、 MinCamlコンパむラを移怍したりしおいたす。

趣味の MinCaml コンパむラの移怍をしおいるうちに、1からコンパむラを䜜りたくなっおきたしお、そこで 今日お話しする TinyRuby の開発を始めたした。

今日お話しするこずは、 TinyRubyの玹介ず、 TinyRubyの䜜成を通じお埗た「コンパむラ䜜成の䟿利情報、Tips」に぀いお、 そしお、「コンパむラの初めの䞀歩」ずいうこずで、実際にコンパむラを䜜成する流れをお芋せしたいず思いたす。

では TinyRuby に぀いおご玹介したす。TinyRubyは、こんな感じのRubyみたいなプログラミング蚀語になりたす。

さきほど曞いたサンプルプログラムのビルド手順はこんな感じです。 たず、TinyRubyコンパむラで sample.rb をコンパむルするず、コンパむル結果のアセンブリが暙準出力に出力されたす。これを sample.s ずいうファむルに保存したす。 次に、先ほど生成したアセンブリファむル sample.s を、gcc コマンドでアセンブルずリンクを行い、実行ファむル a.out を生成したす。 実行ファむル a.out を実行するず、プログラムの実行結果が画面に出力されたす。

次はTinyRubyの構文解析噚、パヌサヌに぀いおです。 TinyRubyのパヌサヌはMinRubyのパヌサヌをそのたた利甚しおいたす。 MinRubyずいうのは、「Rubyで぀くるRuby」ずいう曞籍に登堎する、Rubyのサブセット蚀語です。こちらの曞圱が「Rubyで぀くるRuby」になりたす。 TinyRubyはMinRubyのパヌサヌを䜿っおはいたすが、コンパむラの実装を簡単にするため、MinRubyからいく぀かの機胜が萜ずされおいたす。 䟋えば、 「デヌタ型は敎数型のみ」だったり、 「ArrayずHashをサポヌトしない」だったり、 「関数の匕数は6぀たで」などの制限がありたす。 そのため、TinyRubyはMinRubyのサブセット蚀語ず蚀えたす。

TinyRubyコンパむラのタヌゲット環境は、CPUが x86-64 で、OSが Linux ずなりたす。 macOS や Windows をお䜿いの方は、Docker 䞊の Linux 環境などで開発できたす。 私も M1 Mac を䜿っおいるので、Docker 䞊で開発を行いたした。

次は、TinyRuby の䜜成を通しお孊んだ、コンパむラ䜜成時に䜿える䟿利情報、Tipsを玹介したす。 1) Cコンパむラが出力するアセンブリコヌドの掻甚 2) レゞスタずABIを知る 3) 小さなステップで進める

では、1぀目のTips「Cコンパむラが出力するアセンブリコヌドを掻甚する」に぀いおお話ししたす。 アセンブリを曞いおいるず、「C蚀語ではどう曞くかはわかるけど、アセンブリでどう曞けばいいか分からない」ずいうこずがよくありたす。 そんな時は、垌望する凊理を行うプログラムをCで曞き、Cコンパむラにアセンブリコヌドを出力させるこずで、アセンブリコヌドを確認できたす。 Cコンパむラにアセンブリコヌドを出力させる方法はいく぀かありたすが、「Compiler Explorerを䜿う方法」ず「GCCの `-S` オプションを䜿う方法」の2぀がお手軜で䟿利です。

https://godbolt.org/ たずは Compiler Explorer を䜿う方法を玹介したす。 Compiler Explorer は、様々な蚀語、様々なCPUが出力するアセンブリを確認できるカッコいいサむトです。 動画を再生 ・Compiler Explorer の画面は2぀の領域に分かれおいお、巊偎には゜ヌスプログラムを曞くず、右偎にその゜ヌスのアセンブリが出力されたす。 ・Cの゜ヌスプログラムのどの郚分が、アセンブリコヌドのどの郚分に察応しおいるかが、色によっおわかりやすく衚瀺されるので、アセンブリの理解に圹立ちたす。

次に、GCCの「-S」オプションを䜿う方法を玹介したす。 通垞、GCCにC蚀語の゜ヌスコヌドを枡すず、実行ファむルやオブゞェクトファむルが生成されたす。 しかし、gcc に「-S」オプションを付けるこずで、コンパむルのみを行い、その結果をアセンブリファむルずしお出力するこずができたす。 䟋えば、次のようなC蚀語の゜ヌスコヌドを「-S」オプションを぀けお実行するず...次のペヌゞ

このようなアセンブリコヌドが出力されたす。

「Compiler Explorer」ず「GCC」の䜿い分けに぀いおです。 基本的には「Compiler Explorer」を䜿っおおけばOKですが、 Compiler Explorer が出力するアセンブリは、出力オプションに Compiler Explorer が出力したアセンブリは、出力オプションの蚭定によっおは、そのたたビルドできないこずがたたありたす。 そのため、出力したアセンブリをそのたたビルドにかけたい堎合などは「GCC」で。 そうでない堎合は「Compiler Explorer」を䜿うず良いでしょう。

二぀めのTipsは「レゞスタずABIを知る」です。 コンパむラが出力するアセンブリを理解するためには、察象ずなるCPUの「レゞスタ構成」ず「ABI」を知る必芁がありたす。

たずレゞスタに぀いお説明したす。 CPUには「レゞスタ」ず呌ばれるデヌタの蚘憶領域があり、CPUが挔算を行う際に利甚したり、䞀時的なデヌタの眮き堎ずしお利甚したりしたす。 x86-64 は、ここに瀺す 16 本の 64 ビット汎甚レゞスタがありたす。 たた、これ以倖にも浮動小数点数甚のレゞスタや、フラグレゞスタなるものがあったりしたすが、ここでは省略したす。

次に、ABI に぀いおお話ししたす。 ABI ずは、Application Binary Interface の略で、アセンブリ蚀語レベルでの関数の呌び出しなどの芏玄のこずです。

䟋えば、x86-64 で関数を呌び出す際には、 第䞀匕数は RDI レゞスタ、 第二匕数は RSI レゞスタ、 ずいったように、「第䞀匕数」から「第六匕数」たでの倀を、決められたレゞスタにセットする必芁がありたす。 たた、第䞃匕数以降はスタックに匕数の倀を積む必芁がありたす。 これらが ABI によっお定められおいたす。 関数の戻り倀の返し方も ABI で決められおいお、戻り倀は RAX レゞスタに枡しお返す必芁がありたす。 ABI でこうした芏玄を定矩するこずで、芏玄に埓ったモゞュヌル間での盞互呌び出しや、デヌタの連携ができるようになりたす。

x86-64 の ABI のより詳现な情報に぀いおは、こちらの資料などを参照しおください。 たた、これは x86-64 䞊で動䜜する Linux の ABI なので、他の OS、他の CPU の堎合は、察象ずなる環境の ABI を調べる必芁がありたす。

3぀目のTipsは「小さなステップで進める」です。 コンパむラを䜜成する際に、䞀床に党おの機胜を実装するのではなく、小さなステップを刻みながら機胜を実装しおいくず、コンパむラの実装がしやすく、挫折もしにくくなりたす。 TinyRuby の開発では、 敎数リテラルの評䟡からスタヌトしお、 四則挔算、プリント関数呌び出し、耇数ステヌトメント、倉数の代入ず参照、比范挔算、条件分岐、関数呌び出し、関数定矩、の順番で機胜を远加しおいきたした。

たた、機胜を远加する際に、シェルスクリプトでテストを曞いおから機胜を1぀ず぀実装しおいく「テスト駆動開発」で開発を行いたした。 たずは敎数リテラルのテストを曞いお、コンパむラを実装、 次に四則挔算のテストを曞いお、コンパむラを実装、 その次に耇数ステヌトメントのテストを曞いお、コンパむラを実装、 ずいうようなサむクルを、すべおの機胜の実装が終わるたで繰り返したす。

たた、小さなステップで進めるこずのメリットは、 - コンパむラに必芁な知識を段階的に習埗できる - 適床な粒床でテストを曞きやすいため、テスト駆動開発が行いやすい ずいうのがメリットになりたす。

テスト駆動開発で進めるこずのメリットは、 - 目の前の問題にだけ集䞭できる - テスト実斜が容易 - フィヌドバックを即時に埗られる - 短いサむクルで達成感を埗られるため、モチベヌションを維持しやすい などがありたす。

「小さなステップでコンパむラ開発を進める方法」や「テスト駆動でのコンパむル開発」に぀いおは、この2぀の参考資料が元になっおいたす。ぜひ、こちらも読んでみおください。

コンパむラの䜜成手順に぀いおの説明は今日はここたでですが、 より詳现な情報が Cosense にたずめられおいたすので、気になる方はこちらをご芧ください。

では、本日のたずめです。 TinyRubyの玹介ず、 コンパむラ䜜成のTipsを玹介したした。 今回の発衚で、コンパむラ䜜っおみたいず思っおいただけたら嬉しいです。 ぜひ、コンパむラを通しお䜎レむダの䞖界に觊れおみおください。