コンピュータ・ハードウェアの演算に関するCPUとレジスタ周りについて
コンピュータサイエンスの基礎となるCPUとレジスタ周りについて調べました。結構忘れやすいので備忘録がわりに残しておくことにしました。ほとんど本を読んでまとめただけな個人的なメモです。
CPUとレジスタ
汎用レジスタ(general-purpose)
条件フラグ(condition flag)
- 比較命令の結果を確認する1ビット長のレジスタで、ゼロフラグ、zf,sfなどがある。
命令ポインタ(instruction pointer)
- CPUが次に実行すべき命令の格納場所を指す32ビット長のレジスタeipで、プログラムカウンタ(program counter)とも呼ばれる。CPUは、eipの指す位置から命令を一つ取り出すと同時に、その直後の命令の位置を指すようにeipを更新しておく。その後取り出した命令を実行する。
コード生成関連
コード生成にあたって基本となる事項ををまとめる。処理系について勉強したい人は理解していたほうがいいです。
メモリについて
メモリはメモリ領域(memory area)という連続した領域に分けて利用される。以下の図のように、OS領域、コード領域、大域領域、ヒープ、スタックに分類される。
OS領域 | コード領域 | 大域領域 | ヒープ | スタック |
OS領域:
OS専用の領域。ここのコンピュータごとにその大きさが一定。
コード領域:
プログラムのコードを格納するための領域。プログラムごとにきまっており、プログラム実行中は変化しない。
レジスタ
呼び出した関数がどのレジスタを使用中であるかは、呼び出された関数にはわからない。そのためコンパイラ設計時には、汎用レジスタを2種類に分けておく方法がとられる。
呼び出し後保存レジスタ(callee-saved register)
呼び出す側が使用中かもしれないレジスタ。呼び出される関数がこれらのレジスタを使用したい場合は、本体の実行前に値を保存しておき、実行後にもとに戻さなければならない
呼び出し前保存レジスタ(caller-saved register)
呼び出される関数は、これらのレジスタの値を保存することなく、自由に使用して良い。ただし、呼び出された関数がこれらのレジスタを使用中にさらに別の関数を呼び出すときは、あらかじめ値を保存しておかなければならない。
関数本体の実行時に、どの呼び出し後保存レジスタを使用するかは、本体のコードを生成してみないとわからない。そこで本体のコード生成時には呼び出し後保存レジスタの値を退避するための場所を、割り当てると良い。汎用レジスタの個数は限られているし、使用できる数も数個から数十個ときわめて少ない。pentiumプロセッサの場合は、esp,ebpを除くと6個しかない。そのため、汎用レジスタすべてについて「使用中」、「空き」を記憶するテーブルを用意し、レジスタを割り当てるときに、そのテーブルを検索して空きレジスタをさがすの方法が単純で効果的となる。汎用レジスタの個数はきわめて少ないので、これらを有効的に利用することはコード生成の基本となる。レジスタを有効に利用して、できるだけ一時変数への退避をさけるようなコードを生成することが、算術式のコード生成で最も重要な課題である。