8ccを基にして簡単なCコンパイラを作ったので過程を晒す

rui314さんが作った8ccと言うCコンパイラ(rui314/8cc: A Small C Compiler)を基に自作Cコンパイラを作ったので、その過程を紹介していきます。

今回の最終目標はセルフコンパイルできるCコンパイラの自作でしたが、コンパイラを自作していると、色々な前提知識の理解が不足していることに気づき、一旦コンパイラの自作を中止しました。

ただ、「これからコンパイラ自作したいけど、どこから手をつけるべきか?」と言う人もいると思うので、今回はそのような方達を対象に記事を書いていきます。

どんなCコンパイラを作ったのか?

2cc(toumasuxp/2cc: mini c compiler)と言う簡単なCコンパイラです。

Linux64bit環境(開発ではUbuntu)で動くことができ、下記のものがコンパイルできます。Linuxの環境はDockerを使って実現しています。

  • グローバル、ローカル変数の宣言、初期化
  • 四則演算、比較演算(!とかはない)
  • 引数なしの関数宣言、呼び出し
  • 配列、配列の要素へのアクセス、代入
  • ポインタ
  • 文字列リテラル

具体的にできることは、GitHubのテストコードtest.shを見れば分かります。ただし、以下のことはできません。

  • 構造体
  • プリプロセッサ全般
  • 引数ありの関数呼び出し

コンパイラを作るためにやったこと

僕の場合は、コンパイラを自作するために以下の2つのステップを踏みました。

1、コンパイラの入門書を読む

2020年4月現在だと、先ほど紹介した8cc(rui314/8cc: A Small C Compiler)や9cc(rui314/9cc: A Small C Compiler)といったコードを読みながら理解して、自分で実装すると言うのが王道になります。

ただ、8ccの説明は非常に素っ気なく、「初めてコンパイラ作るぜ!」と言う人がいきなりコードを読んでも理解できないと思うので、まずはコンパイラの入門書を読んで、ざっくりとコンパイラの全体図を掴むのが良いです。

具体的には、「コンパイラの理論と実現 (計算機科学・ソフトウェア技術講座)」と言う本が良書で、この本からコンパイラに入門すると良いです。

「コンパイラの理論と実現」の入門書といっても結構専門よりも入門書なので、必要とあれば離散数学とかオートマトンとかをざっくりとで良いので勉強する必要があります。

それでも本の内容が分からなければ、本の巻末に簡単なC言語っぽいインタプリタのソースコードが載っているので、先にこのソースコードを写経&理解をしたほうが分かりやすいです。

2,アセンブリの勉強をする

コンパイラ自作で必須になるのが、アセンブリの勉強です。

アセンブリはnasmとかgnu asとか色々な種類がありますが、今回はCコンパイラを自作するのでgccが採用しているgnu as(AT&T記法)の方のアセンブリを勉強する必要があります。

しかし、ネットで「assembly tutorial」とか「アセンブリ 入門」と検索すると、gnu asのサイトがほとんどなく、大半がnasmのサイトで、gnu as単体を勉強するのはかなり困難な状況になっています。

nasmとgnu asは根本の原理の部分は同じで書き方が少し違うだけなので、まずはnasmから勉強して、少しずつgnu asが書けるようになる、と言う手順を取った方がスムーズに学習できます。

一応、gnu asの公式ドキュメント(Top (Using as))はあるのですが、「初心者は絶対わからんだろ」と言うぐらい説明が素っ気ないですが、現状信用できる勉強物がこれしかないので、そこは頑張る必要があります。

セルフホストできるCコンパイラを作るために、これからやること

ここからは僕自身の話になりますが、今回の目標がセルフホストできるCコンパイラの自作でしたが達成はできませんでした。

この原因を僕なりに分析した所、下記が原因ではないかと予想しています。

  • アセンブリの理解が浅い
  • アセンブリよりも低レイヤな部分(リンカとかOSとか)の部分の理解が浅い
  • C言語の構文の理解が浅い
  • gdbの理解が浅い
  • makeの使い方忘れている

とにかく「全体的に理解が浅い」と言う事に尽きますが、今回コンパイラを作ってみて自分なりの問題点や課題が浮き彫りになったのが良い点だと感じています。

一方で、C言語の構文とか挙動の理解が深まったことも実感しています。例えば、配列の構文とかポインタの構文とか結構何でもありなんだなと気づいたり、

// これが動く
int foo    [   10 ] = {2, 4};

プリプロセッサが、コンパイラの前に処理しているかと思いきや、コンパイラと同時に処理している等が知れて良かったです。

一応、6月までのセルフホストを目指しており、そのためにも理解が不足している部分を勉強していこうと思います。