CコンパイラをGoで自作した話

CコンパイラをGoで自作した話

date: Sun, 22 Mar 2026 author: repunit11
#compiler #go

こんにちは!学部3年のレプです!
CコンパイラをGo言語で実装したのでその話を書きます!

はじめに

2025年12月後半ごろにCコンパイラを作成し始めました。
大学でOSを作るという実験がありました。その実験が4か月ほどあったんですけど、難しくてでも楽しくてまだまだ学ぶべきことがあるな~と思いました。
そんな時に冬休みというある程度まとまった時間ができました。
年末年始になにか作りたいなと思ってChatGPTに相談してみたら、コンパイラとかインタプリタとかOSとかの自作を提案されました。

最初は『Go言語でつくるインタプリタ』をやろうと思っていたんですよ。
大学でこの本を借りようと思い、調べたらすでに貸出中になってました…
ということで自作コンパイラについてはやっている人がネット上にはたくさんいて、資料もあったのでやってみるか~ということでやってみました!
あと、コンパイラは大学の講義でちょうど学んでいる最中だったのでアウトプットになりそうだなって感じでした。

今回利用させていただいた資料はコチラです。 また、chibiccを参考に実装しました。
これらは植山類さん (rui314)が作成したものです。すごくわかりやすくて助かりました!ありがとうございます!

Go言語で書いたのはGoがただ好きだからです
ひとまず資料の内容が終わったのでブログを書いてみようと思いました。まだ、実装を進めることはできそうですが、一回目はこの程度で終わろうかなと。

できたもの

今回できたもの:gominicc

正直きれいなコードを書けている自信はないので少しでも参考になればいいなーという程度です
ちなみにこの程度のプログラムならコンパイルできます~!

int fib(int n) {
    if (n <= 1) return 1;
    return fib(n - 1) + fib(n - 2);
}

int main() {
    int a[2][3];
    int *p = a;
    int i = 0;
    for (i = 0; i < 6; i = i + 1) p[i] = fib(i);
    if ("gominicc"[0] == 103) {
        return a[1][2] + *(p + 4) + sizeof(a);
    }
    return 0;
}

今動く機能はこんな感じです!

  • 型:int、char、ポインタ、配列、二次元配列、関数
  • リテラル:整数リテラル、文字列リテラル
  • 変数:ローカル変数、グローバル変数
  • 演算:四則演算、比較演算、間接参照
  • 制御構文:return, if, else, while, for
  • 関数:関数定義、関数呼び出し、再帰

コンパイラの解説

コンパイラとは、高水準プログラミング言語をコンピュータが解釈・実行できる形式に変換するソフトウェアのことです。
今回作成したコンパイラは、C言語→アセンブリ(x86-64向け)を行うソフトウェアです。

コンパイラは大きく字句解析、構文解析、意味解析、最適化、コード生成で表現されます。最適化は小規模であれば持たないことも多いです。
細かくはもっと分かれることが多いですが、ここでは5ステップで説明します。
コンパイラのステップ分解

字句解析

ここではプログラムをトークン化します。
例えば、tmp = 1 + 2;tmp, =, 1, +, 2, ;の6つに分割できます。 このステップでは、文字列を意味があるトークンという要素に分割して、要素の並びという形に変換します。

構文解析

ここではトークン化された並びを抽象構文木(AST)とよばれるものに変換します。
ASTを構築する理由としては、演算の順番が重要であるからです。
例えば、1+2*3のようなものである場合、2*3を先に演算してその後1+(2*3)をするということになります。
これは、構文木を作ると処理しやすいです。下の画像を見たらわかりやすいかなと思います。
これをもう少し応用してC言語の文法を作っていきます。
詳しくはオートマトン、文脈自由文法などを調べてみるといいです!
ASTの例

意味解析

ここでは作成されたASTをもとに入力されたプログラムが意味的に正しいかどうかを検証します。
例えば以下のような内容です。

  • 変数や関数が宣言済みなのか
  • 型が正しいか
  • スコープが正しいか

構文として正しいとしても仕様として正しいかどうかはここで判定します。

最適化

ここではその名の通り最適化を行います。
使わない変数を削除したり、コンパイル時点で演算できる部分を事前に評価したりします。
一般的には、ASTを中間言語とよばれるものに変換して最適化することが多いです。
例えば、int a; int b = 3; b = 1 + b;のようなプログラムだと、aを使わないものとして削除します。
最適化は小規模なコンパイラでは入らないこともあります。必須のステップではありません。

コード生成

ここでは中間言語やASTをもとに目的言語を生成します。
今回のコンパイラでは、x86-64のアセンブリを生成します。
ASTのそれぞれのノードごとに生成するアセンブリを定義する感じで進めます。

気を付けたこと、苦労したこと

現在は生成AIが発展していることもあり、Codexにわからないことを聞きながら進めていました。
生成AIが発展しているからコンパイラを作成することは比較的容易になっています。
今回の目的は、コンパイラを作ることというよりはコンパイラの仕組みを知ることであったので、生成AIに実装させることはなく進めました。
手段の目的化にならないように注意しました。
ただ、どうしてもわからない部分があったり、エラーが出た部分はAIを使って解説してもらったりしました。
LLVMとの違いとか解説してもらってました。

今回は一人で進めていたので、モチベーションが鍵になりそうだと考えてました。
身の回りに低レイヤに興味がある人が少なかったので、外部の技術イベントに行って話すとか、大学で作業するなど工夫して進めていました

学び

今回、Go言語を使って実装したのはGo言語が好きだからという理由と、C言語よりも頭を使って実装できそうだったからという理由でした。
現代は生成AIが発展していてわからないことがあったらAIに聞くということができました。
なので、実装で困るということはほとんどありませんでした。ただ、それが必ずしもいいものではないと思います。
なぜなら、生成AIを使うことによって動かない理由をすぐに聞いてしまうということをしていました。
これは、コンパイラの大事な部分を学ぶだけなら問題ないと思うのですが、機能を追加していくうえでだんだんどのようなプログラムになっているのかがわからなくなっていきました。
守破離が重要なのかもと。
つまり、まずはCで写経する→Goで実装しなおす→自作言語や最適化など詳しく学ぶの流れがよかったのかもしれません。
適度に学んだことをまとめるのは大事かもと思いました!

今後やりたいこと

これからは、Goのコンパイラについて学んでいきたいと思います。
Goのコンパイラに関しては英語の記事がすでにあるので日本語に翻訳して、わかりやすくまとめたいと考えてます。
また、言語処理系に関してもっと学びたいと思ったので最適化、LLVMなど調べて使って発信していきたいです!

また、全く別ですが自作OSとか低レベルコンテナランタイムとかTCP/IPを実装したいですね~

おわりに

コンパイラの実装は楽しかったです!
ただ、一人でずっと実装していて人に話すことがほとんどなかったのでここ最近はモチベーションが下がり気味でした…(友達がいないわけではないです!)
Webエンジニア志望の学生はいるんですけど、低レイヤに興味がある友達が少なくて寂しいです
うちの大学で低レイヤ好きな人を探してます
もしいたら話したいなー

最後に、植山類さんについて少し触れさせてください。
植山類さんは、LLVM lld の初期開発者であり、さらに高速リンカである mold の作者でもある方です。
今回参考にした資料や実装も本当にわかりやすく、自分のような初学者にとって低レイヤの世界に入る大きな 助けになりました。
技術的にすごいのはもちろんですが、難しい内容を学びやすい形で発信してくださっていることにも強くリス ペクトしています。
また、Turing Complete FMというポッドキャストを以前までやっておられま した。
低レイヤのすごい話がたくさん聞けるのでぜひ聞いてみてください。
正直4割くらいしかわかっていないんですけど賢くなった気分になります笑
続きが出ることをひそかに楽しみにしています

最後まで読んでいただきありがとうございました!

サムネイルには、Renee French による Go Gopher を元に、生成AIを用いて一部改変したイラストを使用しています。
元デザインのライセンスは CC BY 4.0 です。

Share