PowerPCにおいて、ユーザレベルで触れる可能性があるレジスタは以下のようなものがある。
見逃したレジスタが他にもあるかもしれないが、普通は上6つの種類のレジスタで十分なはずだ。
| レジスタ名 | レジスタ名 | bit数 | 備考 |
|---|---|---|---|
| 汎用レジスタ | r0 ~ r31 | 32 | 64bitモードではサイズが64bitである。 |
| 浮動小数レジスタ | f0 ~ f31 | 64 | 単精度も倍精度もこのレジスタを使う。浮動小数演算用。 |
| ベクトルレジスタ | v0 ~ v31 | 128 | AltiVecを搭載したCPU(G4, G5など)にしか存在しない。AltiVec演算用。 |
| 状態レジスタ | cr0 ~ cr7 | 4 | 比較命令の結果を保存するレジスタ。 |
| リンクレジスタ | LR | 32 | 分岐に利用。関数を呼ぶ時、戻り先アドレスが与えられる。関数から戻るのに使われることが多い。64bitモードではサイズが64bitである。 |
| カウンタレジスタ | CTR | 32 | 繰り返し条件や、関数ポインタへのジャンプに使われることが多い。64bitモードではサイズが64bitである。 |
| 整数演算例外レジスタ | XER | 32 | キャリー、オーバーフロー、文字列ロード/ストア命令に使用される。64bitモードではサイズが64bit。 |
| 浮動小数演算の状態およびコントロールレジスタ | FPCSR | 32 | 浮動小数演算の例外などに使用。 |
| ベクトル特殊レジスタ | VRSAVE | 32 | AltiVec搭載CPUのみ。AltiVec演算の補助。 |
PowerPCにはたくさんのレジスタが存在するので、それらに役割づけをしなければ使いづらい。また、関数呼び出しが行われた時に、どのレジスタをスタックに積まねばならないかもわからないと、壊してはいけないデータを壊してしまう可能性もある。MacOSXでは(おそらくほとんどのOSでも)、以下のような役割分担になっている。
| レジスタ名 | レジスタ内容の特性 | 備考 |
|---|---|---|
| 汎用レジスタ | ||
| r0 | 揮発性 | PowerPCの命令によっては、特殊な扱いになる。 |
| r1 | 非揮発性 | スタックポインタ。 |
| r2 | 揮発性 | Mac OSX以外のOSではTOC(Table Of Contents。グローバルデータへのアクセス参照用)に使用されるが、OSXでは揮発性レジスタなので、r3などと同様に扱える。 |
| r3 ~ r10 | 揮発性 | 引数/返り値渡しに利用される。関数内で使用可能。 |
| r11 | 揮発性 | 関数内で使用可能。 |
| r12 | 揮発性 | 関数ジャンプに利用される。通常のプログラム部分では使用すべきではない。 |
| r13 ~ r31 | 非揮発性 | 関数内で使う場合、必ず元の内容をスタックに保存すること。 |
| 浮動小数レジスタ | ||
| f0 | 揮発性 | 関数内で使用可能。 |
| f1 ~ f13 | 揮発性 | 引数/返り値の渡しに利用される。関数内で使用可能。 |
| f14 ~ f31 | 非揮発性 | 関数内で使う場合、必ず元の内容をスタックに保存すること。 |
| ベクトルレジスタ | ||
| v0 ~ v1 | 揮発性 | 関数内で使用可能。 |
| v2 ~ v13 | 揮発性 | 引数/返り値の渡しに利用される。関数内で使用可能。 |
| v14 ~ v19 | 揮発性 | 関数内で使用可能。 |
| v20 ~ v31 | 非揮発性 | 関数内で使う場合、必ず元の内容をスタックに保存すること。 |
| 状態レジスタ | ||
| cr0 | 揮発性 | 汎用レジスタ命令のデフォルトの条件結果格納先。関数内で使用可能。 |
| cr1 | 揮発性 | 浮動小数演算命令の例外条件の結果格納先。関数内で使用可能。 |
| cr2 ~ cr4 | 非揮発性 | 関数内で使用する場合、状態レジスタの内容をスタックに保存すること。 |
| cr5 | 揮発性 | 関数内で使用可能。 |
| cr6 | 揮発性 | ベクトル演算命令のデフォルトの条件結果格納先。関数内で使用可能。 |
| cr7 | 揮発性 | 関数内で使用可能。 |
| その他のレジスタ | ||
| VRSAVE | 非揮発性 | ベクトル演算命令を使うのなら、保存すること。 |
| LR | 揮発性 | 内部で関数呼び出しを行う関数は、必ずスタックに保存すること。 |
| CTR | 揮発性 | 関数を呼ばないループのカウンタに利用することが多い。 |
| XER | 揮発性 | |
スタックを有効に使うために、PowerPC用のスタックフレーム構造が定義されている。スタックフレームは、負の方向に、16byte単位で伸びる。スタックポインタはr1に格納されている。
なお、関数へ入った時にリンクレジスタに格納されていたアドレスが、関数から戻る先である。
| 呼び出し側のスタックエリア | 引数エリア |
| 連結エリア | |
| 呼び出された関数のスタックエリア | 退避エリア |
| 局所変数エリア | |
| 引数エリア | |
| 連結エリア | |
| 未使用スタック領域 | |
なお、もし関数が内部で関数呼び出しを行わない葉の関数で、スタックに対し必要としているメモリサイズが224byteを上回らないのであれば、スタックフレームを構築せずに未使用領域を直接使用してよい。
連結エリアは、スタックポインタ、リンクレジスタ、状態レジスタを保存しておく、サイズが24byteのエリアである。ただし、リンクレジスタと状態レジスタの保存先は呼び出し側関数のスタックフレーム内の連結エリアであり、それらを保存する連結エリアにあるスタックポインタは呼び出し側関数がセットしたものである。
なお、関数内部でリンクレジスタや状態レジスタの内容を変更しないのなら、別に保存する必要はない。
| 呼び出し側引数エリア | |
| 呼び出し側連結エリア | TOCレジスタ内容保存先(スタックポインタ+20のアドレス)※Mac OSXでは未使用 |
| 予約領域 | |
| 呼び出された側のリンクレジスタ保存先(スタックポインタ+8のアドレス) | |
| 呼び出された側の状態レジスタ保存先(スタックポインタ+4のアドレス) | |
| 呼び出し側のスタックポインタ | |
| 呼び出された側のスタックフレーム | |
連結エリアの構築と破棄の例を、アセンブラを使って表すと、以下のようになる。
HogeHoge:;関数の先頭 mflrr0;リンクレジスタを保存 stwr0, 8(r1) mfcrr0;状態レジスタを保存 stwr0, 4(r1) stwur1, -STACK_FRAME_SIZE(r1);スタックフレームの構築 ...;関数内部 lar1, STACK_FRAME_SIZE(r1);スタックフレームの破棄 lwzr0, 4(r1);状態レジスタの内容の復帰 mtcrr0 lwzr0, 8(r1);リンクレジスタの内容の復帰 mtlr0 blr;関数から抜ける
64bitモードでは、リンクレジスタやスタックポインタのサイズが異なるので、連結エリアの構成が異なると思われる。
退避する必要があるレジスタがある場合、この領域に内容を退避すること。
| 浮動小数保存領域 |
| 汎用レジスタ保存領域 |
| VRSAVEレジスタ保存領域(4byte) |
| 空(スタックのアドレス値を16の倍数に合わせる、パディング領域) |
| ベクトルレジスタ保存領域 |
局所変数はこの領域に作成すること。
引数エリアは、内部で関数呼び出しを行う関数に必要な領域で、内部で呼び出す関数の中で一番引数が多いものを引数エリアに置いても大丈夫なサイズを確保する必要がある。なお、最低サイズは、r3 ~ r10を格納できるサイズ(32byte)である。
この領域は、主に以下のことに使われる。
整数やアドレスは、汎用レジスタr3 ~ r10に渡される。
浮動小数に関しては、単精度でも倍精度でも、浮動小数レジスタf1 ~ f13に渡す。その際、その浮動小数データのサイズ分だけの汎用レジスタを、使用しているものと見なしてとばす。つまり、単精度の場合1つ、倍精度の場合2つの汎用レジスタをとばす。これは汎用レジスタが足りなくなり、引数エリアにはみ出る時でも同じである。
なお、可変長引数をとる関数へ浮動小数を引数として渡す場合、呼び出される関数は、次の引数がどのレジスタに入っているかわからないので、全ての引数を汎用レジスタに渡す。その際、浮動小数は全て倍精度扱いにする。
ベクトルデータは浮動小数と異なり、汎用レジスタをとばす必要はなく、単純にv2 ~ v13に渡される。ただし、ベクトルレジスタが足りなくなった時には、引数エリアを利用せざるを得ない。
関数宣言がされていない関数を呼び出す場合、可変長引数関数に渡す場合と固定長引数関数を渡す場合との、両方の場合を考えて引数を渡す。
固定長引数関数の例: void fooFunc (int i1, float f1, double d1, short s1, double d2, unsigned char c1, unsigned short s2, float f2, int i2);
この関数の呼び出しの為に用意する引数エリアは以下の通り。
struct params {
int p_i1; r3に当てられる
float p_f1; f1(r4の代わり)
double p_d1; f2(r5, r6の代わり)
short p_s1; r7。サイズはintと同じと見なす。
double p_d2; f3(r8, r9の代わり)
unsigned char p_c1; r10。サイズはintと同じと見なす。
unsigned short p_s2; 引数エリアに保存。サイズはintと同じとみなす。
float p_f2; f4(引数エリアも必要)
int p_i2; 引数エリアに保存。
};
これを引数エリア上で見ると、このようになる。
| 引数名 | 割当て場所 | 使用byte数 | 備考 |
| 呼び出し側連結エリア | |||
| i1 | r3 | 4 | |
| f1 | f1 | 4 | 代わりにr4をスキップ |
| d1 | f2 | 8 | 代わりにr5, r6をスキップ |
| s1 | r7 | 4 | スタック内では、4byteの内の下2byteにデータを格納 |
| d2 | f3 | 8 | 代わりにr8, r9をスキップ |
| c1 | r10 | 4 | スタック内では、4byteの内の下1byteにデータを格納 |
| s2 | 引数エリア | 4 | スタック内では、4byteの内の下2byteにデータを格納 |
| f2 | f4 | 4 | f4に値は格納されるが、引数エリアに領域を用意すること |
| i2 | 引数エリア | 4 | |
| 局所変数エリア | |||
引数エリアの先頭は、スタックが負の方向に伸びることから、連結エリアに近い方(下の方)ということになる。