tags | |
---|---|
|
以前,"低レイヤを知りたい人のための Cコンパイラ作成入門" を読み始めたという記事を書いた.
前回の記事では,括弧や演算子の優先順位を考慮した四則演算に対応した.
例えば,(1 + 2) * 3
や, 1 + 2 * 3
といった式をコンパイルできる.
結果として,その式の評価結果を返すアセンブリを出力する,といった感じだ.
このCコンパイラについて,まとまった進捗が得られたので,ここにまとめようと思う.
前回からの変更点は以下の通りだ.
- 1文字のローカル変数を実装
- 複数の文に対応
- ソースファイルの分割
変数名は1文字のみ,アルファベット26文字が使用可能,という制限付きでローカル変数が使用できるようになった.
ローカル変数とはいえ,現状別のスコープを作る手段はないのだが.
これにより,a = b = 2; a + b;
のようなコードをコンパイルできるようになった.
実は,セミコロンで区切られた複数の文にも対応したのだが,それについては次の章で述べる.
実装について,代入式は二項演算と同様のASTになる.
例えば a = b = 2
という代入式の場合は,以下のようなASTが構築される.
=
/ \
a =
b 2
また,ローカル変数は,スタック領域にそのメモリ領域を確保している. 今の所,ローカル変数は最大26個,データ型は整数のみ,といった制限があるので, プロローグで8 * 26バイト分の領域をスタックに確保するようにしている. ローカル変数を参照したい際は,変数名からその領域を計算すれば良い.
セミコロンで区切ることで,複数の文を書けるようになった.
例えば,a = b = 2; a + b; a * b
のようなコードをコンパイルできるようになった.
これを実現するために,コンパイルする際にノードの配列を確保するようにした.
今までは,コードをASTに変換する際,必ず1つの木構造で表現できた.
しかし,複数の文に対応するには,複数の木を用いる必要があったのだ.
今の所,以下のように固定長の配列を使う実装になっている.
Node *code[100];
しかし,本書のあとの方でベクタとマップを実装する箇所があるので,その実装タイミングで他の箇所と一緒に書き換えることになるだろう.
コンパイラ自体のソースファイルを分割した.
今までは, 9cc.c
の1ファイルにすべてのコードを書いていた.
本書の流れで,そろそろコードを分けても良いだろう,とのことで,以下のファイルに分割した.
- 9cc.h
- codegen.c
- parse.c
- tokenize.c
- main.c
さて,本書も大分終わりが見えてきた.残すところは以下のような内容になっている.
- 比較演算の実装
- ベクタ・マップの実装
そろそろこの2つを実装した後のことを考えても良いだろう.
この後は,
- C で書いた関数の呼び出し
- 浮動小数点型への対応
と言ったことを考えている.
というのも,大学の授業内課題で電卓を作る必要がある,というのが本書を読みはじめたきっかけである.それを達成するためには,Cで書かれた三角関数や指数・対数関数の実装を呼び出せる必要がある.(それらは他の生徒によって実装されている)
そうなると,最低限関数呼び出しの文法と,倍精度浮動小数点型への対応が必要なのだ.
実験は,x86 32bitのマシンで行っており,インターネットの利用は禁止,USB等の外部情報を持ち出せるものも禁止されている.
この制限を考慮すると,おおよそ呼び出し規約や,x86のFPU関係の命令についての調査・実装で苦戦することだろう.
また,調査・実装を行った内容については,私が実験用のマシンに手動で打ち込むことになるので,ミニマルな実装が望ましい.
なにかと疑問に思う部分は多いが,去年も同様のことを指摘して改善されていないことを考えると,あまり無駄な体力は使わないほうが良いだろう.
なにはともあれ,上記の内容は授業期間内に終わらせてしまいたい.