はじめに
下記の内容が分からない場合は第1回に戻りましょう
・ジャンプ、アドレスとは?
・コンパイル、アセンブルするとは何をさすか?
https://www.thehub.co.jp/blogs/making-c-compiler-study-1st
本ブログの執筆ルールも第1回の内容を引き継ぎます。
第2回はこちら
https://www.thehub.co.jp/blogs/making-c-compiler-study-2nd
ざっくりいうと「あるCコードをコンパイルした結果と、それに対応するアセンブリプログラムをアセンブリした結果は当然一致するよね」ということを確認した回でした。
今回の学習範囲
Cとそれに対応するアセンブラ(関数呼び出しを含む例)〜ステップ1:整数1個をコンパイルする言語の作成(コンパイラ本体の作成)
関数呼び出しを含む例
関数がある場合のアセンブラのコードの流れについて解説しています。
第2回でされたアセンブラのコードの流れの説明の続きです。
新しくでてきた言葉をプリングルスで説明。
スタック・・・後入先出しをするデータ構造。プリングルスでいうところのプリングルスの筒。データはプリングルス1枚1枚。
スタックポインタ・・・スタック内で次のデータが格納されるところ。プリングルスでいうところの、一番上のプリングルス。
プッシュ・・・プリングルスを1枚追加して、スタックポインタを1つ上に移動すること。
ポップ・・・プリングルスを1枚とりだして、スタックポインタを1つ下に移動すること。
コードの実行がなかったため完結な説明に留めます。
電卓レベルの実装
Cコンパイラの最初のステップとして四則演算の式をコンパイルできるようにするのが目標。
ステップ1:整数1個をコンパイルする言語の作成
1個の数字の入力を受け付けて、その数字をプログラムの終了として返すアセンブリを出力するコンパイラを作成する。
実行した流れ
1:数字を入力してアセンブリを出力するコンパイラを作成
2:コンパイルを実行して、アセンブリを出力する
3:アセンブリを実行して、結果を出力する
1. 数字を入力してアセンブリを出力するコンパイラ作成
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "引数の個数が正しくありません\n");
return 1;
}
printf(".intel_syntax noprefix\n");
printf(".globl main\n");
printf("main:\n");
printf(" mov rax, %d\n", atoi(argv[1]));
printf(" ret\n");
return 0;
}
#include <stdio.h>はprintfなどC言語の関数を使うために必要なおまじない(厳密には違うがこの勉強会ではこの理解でよいと思う)
printfは単に出力するコマンド
2. コンパイルを実行して、アセンブリを出力する
$ cc -o 9cc 9cc.c
macの場合は
$ docker run -it -v $HOME/9cc:/9cc -w /9cc compilerbook cc -o 9cc 9cc.c
ccは拡張子で言語を判定してコンパイルやアセンブルするコマンド。
9cc.cをコンパイルして9ccという実行ファイルを生成。
第二回にあるように、macの場合はdocker経由でコマンドを実行する必要がある。(長いので以降は省略)
$ ./9cc 123 > tmp.s
9ccを使って123を引数にしてtmp.sというファイルを生成。.sはアセンブリの拡張子。
実行した結果(アセンブリ)は下記のとおり。
$ cat tmp.s
.intel_syntax noprefix
.globl main
main:
mov rax, 123
ret
3. アセンブリを実行して、結果を出力する
$ cc -o tmp tmp.s
$ ./tmp
$ echo $?
123
ccでtmp.sをアセンブルしてtmpファイルを生成。
シェルでは直前のコマンドの終了コードが$?という変数でアクセスできる。
9ccで与えた引数である123が出力されればうまくいっているということになる。
やったことまとめ
- 関数を実行する時のアセンブラのコードの流れを確認しました
- 整数を入力して、その数を出力するコンパイラを作成しました
所感&メモ
- 専門単語が多く、すぐには理解できませんでしたが、第1回第2回の復習をしてから読み直したら理解できました。本書内で必要な言葉は全て解説されています。