呼出規約とは呼出側(caller)と呼び出された側(callee)の受け渡し規約のこと。
・callerの選択肢はクリーンアップするか、しないか
・calleeの選択肢はクリーンアップするか、しないか
stdcallの特徴は
・calleeはクリーンアップする
・callerはクリーンアップしない
cdeclの特徴は
・calleeはクリーンアップしない
・callerはクリーンアップする
考えてみてほしい。
callerとcalleeのどちらでクリーンアップするほうが良いか
・calleeでやる場合の特徴はクリーンアップが呼出回数に比例し
プログラムサイズは小さくなる。
・callerでやる場合の特徴は下記のような場合、
クリーンアップしなくてもいいという選択肢が生まれる。
しかし、プログラムサイズは大きくなる
func1(1)
func2(1, 2)
[0回]
●gccの呼出規約の縮小とは
stdcallの特徴は
・calleeはクリーンアップする
cdeclの特徴は
・calleeはクリーンアップしない
とし、callerの選択肢を限定しないことにある。
一番融通が効くのはcalleeもcallerもクリーンアップしないである。
クリーンアップしないことの問題は
pushが連続してスタックオーバーフローを引き起こす
gccは逆転の発想で最初に、その関数で呼び出す関数の引数の上限だけ
espレジスタを減算してスタック領域を確保している。
室内で一番大きい置物の高さに天井を合わせればその上の階も立てるという仕組みらしい。
void func()
{
int p1_wrk, p2_wrk; //←これが上の階までの距離でコンパイラが確保
int p1, p2, p3;
func2(p1, p2);//p1_wrk←p1, p2_wrk←p2
func3(p3);//p1_wrk←p3
}
さらに言うと、もはやpushでなくてもいいから
引数の個数だけespレジスタをずらす必要もない。
逆に言うと、上の階の人に何もしてほしくないため
stdcallにすると天井を降ろされた場合、上げ直して
上限を確保しなおすというオーバーヘッドが発生する。
逆転の発想は・・コストまで逆転する。
stdcallはクリーンアップが呼出回数×2回発生し
cdeclは発生しなくなる。
いつからなんだろう。
あまりにもpush&popが出回りすぎてこの大きな違いに気付くのが難しかった。
PR