2013/07/22

glibc の関数の引数の数について調べてみた

Open Design Computer Project で作っている mist32 プロセッサの、関数呼び出し規則と割り込みについて考えてる間に、あるひとつの疑問が...

ふつうの関数は、どれぐらいの引数を使うのか

平均と言うよりは、最大でどれぐらいまでの引数が使われることが普通なのか。

何故こんなデータが欲しいかというと、関数呼び出し規則 (Calling conventions) でレジスタに引数を乗せることは確定だが、どの程度引数レジスタを用意すればいいのか、という事を決めたい。

関数呼び出し規則では、レジスタに呼び出し元か呼び出し先か、どちらで保存するかを定義しなければならない。基本的には、呼び出し先保存 (caller-saved) なレジスタの方が、効率が良いのである。実際に使われたレジスタしか退避することがないから。

全部引数に載せればいいじゃん?って思うかもしれないけど、レジスタが許す限り全部引数をレジスタに乗せることに決めた場合、すべてのレジスタが呼び出し元保存 (callee saved) なレジスタになるので、問題が起きる。呼び出した先の関数がどのレジスタを使ってるのかなどわからないので、呼び出し元の関数で使ってる変数レジスタをすべて退避する必要がある。その先の関数でそれを使っていないかもしれないのに。また、割り込みの際にも、呼び出し元保存のレジスタは、基本的に割り込みハンドラで保存しなければいけないし、多すぎるといいことはない。

つまり、どの程度の数の引数が一般的に使われるかがわかれば、ほとんどの数の引数をレジスタ渡しで実現しつつ、無駄なレジスタ退避を少なくすることができる、理想の引数レジスタの個数をなんとなく予測することができる。


glibc の関数における引数の個数

glibc の関数を調べてみたらうまくいくかなと思って、以下のようにして、調べてみた。scrapy を使っている。
$ scrapy shell http://www.gnu.org/software/libc/manual/html_mono/libc.html
>>> argc = [0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> for div in hxs.select('//div[@class="defun"]'):
...   if "Function" in div.select('text()').extract()[0]:
...     args = div.select("var/text()").extract()
...     c = len(args[0].split(',')) if (args and args[0] != "void") else 0
...     argc[c] += 1
... 
>>> argc
[75, 379, 280, 177, 55, 24, 13, 1, 1]

というわけで、結果が出た。すごーい。
(ただし、可変長引数などもあるので、それは考慮していないのでなんとも言えない)

1, 2, 3 引数の関数がほとんどであり、それ以上の引数を持つ関数は殆ど無い。

ちなみに、他のアーキテクチャを見てみると amd64 の System V ABI では7個、Windows だと4個、MIPS や ARM も4個になっているようです。

つまり、多くても6個ぐらいあれば、満足できるようです。という事で、mist32 は6個にする方向で、呼び出し規約を定義してみようかと思います。

0 件のコメント: