「低レイヤを知りたい人のためのCコンパイラ作成入門」の進捗

低レイヤを知りたい人のためのCコンパイラ作成入門をまたぼちぼちやっていました。

github.com

前回から結構色々実装しました。

難しかったところとかコメントします。

コメント

関数呼び出し

呼び出すときのrspが16の倍数でないといけないので、その調整に苦労しました。 結局関数呼び出しのときにいちいち計算して調整しています。

ただこれはコンパイル時に決定できる気がする(関数先頭からスタックにpushした量を決めておけばいい)ので、実行時に分岐するのはやめにしたいです。

変数宣言

int型を追加して、型による変数定義を行えるようにしました。 ただ未だにrccではint型のサイズが8byteになっています。(64bitレジスタを使ったコンパイラをこれまで作ってきていて、変え方がわからないため)

どうも、raxレジスタの代わりにeaxレジスタとかを使ってやればいいみたいなんですが、まだ着手できていないです。

ポインタの計算

前述の通り、8byteになっちゃったint型しかないのでここは楽でした。(変数のサイズが8byte固定なので)。intがちゃんと4byteになれば、intかポインタかでアドレス計算の仕方を切り分ける必要があります。

たとえばアドレスは64bitなので、8byteですが、intは4byteなので、ポインタのポインタを計算するときと、intのポインタを計算するときで、+1したときの挙動が変わります。

配列

配列の伸び方をアドレスの負方向に伸ばしていたので、アドレス計算に手こずってました。スタックは負方向、ヒープは正方向に伸びているような計算の仕方していてうまくいきませんでした。

これはスタックとしては負方向に伸ばすけど、配列は正方向に伸びるようにすることで解決しました。

ex.)100番地から長さ4のintの配列を確保したとき、a[0]は112番地、a[1]は108番地…というように、スタック上の配列要素の配置箇所をどんどん若くしていく。

おわり

だんだんコンパイラっぽく動くようになってきて楽しいです。 たとえば↓のようなコードがコンパイルできます。

int main()
{
    int a[10];
    a[0] = 3;
    a[1] = 4;
    a[1+2*2] = 123;
    return a[5];
}
int add2(int a,int b)
{
    return a + b;
}
 
int main()
{
    return add2(10, 15);
}