register window
Sparcプロセッサは、register windowと呼ばれる一風変わったアーキテクチャを採用しています。プロセッサ自体は、64〜528個の汎用レジスタを持っており、ソフトウェアからは32個のレジスタにアクセスできます。32個のうち24個は汎用レジスタの一部をregister window経由でアクセス出来ます。残りの8個はグローバルレジスタとして常に同じレジスタにアクセスしています。汎用レジスタのどこにアクセス出来るかは、current window pointer (CWP) レジスタで決まります。このCWPレジスタが、ちょうどスタックポインタのような動きをします。
図の左側が、CWPがある状態でのアクセス出来るレジスタを示しているとします。黄色の部分のグローバルレジスタ8個はCWPの状態に関わらずアクセス出来ます。薄く緑になっている部分が、現在のCWPでアクセスできるレジスタ24個です。CWPが1つ増えると、図の右側の状態になり濃い緑の部分が新しくアクセス出来る24個のレジスタになります。右と左で領域が重なっている部分(赤枠)を、関数呼出時の引数(available for argument passing)、戻り番地(reserved for return address)、フレームポインタ(reserved for %sp / %fp)に使用します。
Standard register modelでは、引数に6個、戻り番地に1個、フレームポインタの保存に1個のレジスタを使用します。
元となるソース
int foo(void); int f6(int a, int b, int c, int d, int e, int f); int f7(int a, int b, int c, int d, int e, int f, int g); extern int dummy(int a); int foo(void) { int a, b; a = f6(1,2,3,4,5,6); b = f7(1,2,3,4,5,6,7); return a - b; } int f6(int a, int b, int c, int d, int e, int f) { dummy(a); return a + b + c + d + e + f; } int f7(int a, int b, int c, int d, int e, int f, int g) { dummy(a); return a + b + c + d + e + f + g; }
extern 宣言されたdummy()関数は、leaf function(他の関数を呼び出さない関数)になるのを避ける為に入れています。ちなみにleaf functionの場合は、register windowを操作することなく処理を行います。
最適化オプションは-O1で、コンパイルしました。
YUKI.N>sparc-elf-gcc -S -O1 func.sparc.c
引数の渡し方(引数が6つ以下)
呼び出し側は、outレジスタに引数を入れていきます。
mov 1, %o0 mov 2, %o1 mov 3, %o2 mov 4, %o3 mov 5, %o4 call f6, 0 mov 6, %o5
呼び出された側では、inputレジスタの値をそのまま使っています。
save %sp, -104, %sp call dummy, 0 mov %i0, %o0 add %i1, %i0, %i1 add %i1, %i2, %i1 add %i1, %i3, %i1 add %i1, %i4, %i1 jmp %i7+8 restore %i1, %i5, %o0
register windowの操作により、o0〜o5がi0〜i5に変わっているところが重要です。他のプロセッサで行われるレジスタの待避、復元がsave、restore命令によって行われスタックメモリを消費しません。register windowがスタックメモリの代わりになっています。
引数の渡し方(引数が7つ以上)
呼び出し側は7つ目の引数をスタックに積んで、関数を呼び出します。
mov 7, %g1 # ここ st %g1, [%sp+92] # ここ mov 1, %o0 mov 2, %o1 mov 3, %o2 mov 4, %o3 mov 5, %o4 call f7, 0 mov 6, %o5
呼び出された側は7つ目の引数をスタックから取り出します。
save %sp, -104, %sp call dummy, 0 mov %i0, %o0 add %i1, %i0, %i1 add %i1, %i2, %i1 add %i1, %i3, %i1 add %i1, %i4, %i1 add %i1, %i5, %i1 ld [%fp+92], %i0 # ここ jmp %i7+8 restore %i1, %i0, %o0
戻りアドレスの処理
i7レジスタが戻り番地の格納先になっているので、i7レジスタを使用してjmpします。あわせて、restore命令を使用して、register windowを復元します。
jmp %i7+8 restore %i1, %i5, %o0
参考
The SPARC Architecture Manual/Version 9
http://www.sparc.com/standards/SPARCV9.pdf
2008/9/2 追加
コメント欄で、m.ukai さんに資料を教えていただきました。ありがとうございます。リンク先のSPARC psABI 3.0 は、The SPARC Architecture Manualよりは具体的に詳しく書かれています。
http://www.sparc.org/specificationsDownload.html