ぱたへね

はてなダイアリーはrustの色分けができないのでこっちに来た

関数呼び出しsparc編

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個のレジスタを使用します。

元となるソース

gccの出したアセンブラを見てみましょう。

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