呼出規約とは? わかりやすく解説

呼出規約

出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2020/11/24 14:20 UTC 版)

ナビゲーションに移動 検索に移動

呼出規約(よびだしきやく)ないし呼出慣例(よびだしかんれい)(: calling convention)は、コンピュータ命令セットアーキテクチャごとに取り決められるABIの一部で、サブルーチンが呼出される際に従わねばならない制限などの標準である。名前修飾について、データを渡す「実引数」、戻るべきアドレスである「リターンアドレス」、データを戻す「返戻値」などを、スタックなどに対してどのように格納するのか、また各レジスタを、呼び出し側とサブルーチンのどちらの側が保存するか、等といった取決めの集まりである。言語が同じでも、分割コンパイルされリンカでリンクされる相互のプロシージャ間では、呼出し呼出されるならば同一の呼出規約に従っていなければならない。一方で、違う言語の間でも、同一の呼出規約を経由して相互にプロシージャを呼出すこともできる。

cdecl

インテルx86ベースのシステム上のC/C++では cdecl 呼出規約が使われることが多い。cdeclでは関数への引数は右から左の順でスタックに積まれる。関数の戻り値は EAX(x86のレジスタの一つ)に格納される。呼び出された側の関数ではEAX, ECX, EDXのレジスタの元の値を保存することなく使用してよい。呼び出し側の関数では必要ならば呼び出す前にそれらのレジスタをスタック上などに保存する。スタックポインタの処理は呼び出し側で行う。

例えば、以下のCプログラムの関数呼び出しは、

int function(int, int, int);
int a, b, c, x;
...
x = function(a, b, c);

以下のような機械語を生成する(MASMにおけるx86アセンブリ言語で記述する)。

push c
push b
push a
call function
add esp, 12 ;スタック上の引数を除去
mov x, eax

Cソースコードにてa, b, cの順で記述された引数は、逆の順序c, b, aでスタックに積まれ、call命令でリターンアドレスをスタックに積んだ上でサブルーチンにジャンプする。戻った後に呼び出し側がスタック上の引数データを除去する。

cdecl は通常 x86 Cコンパイラのデフォルトであるが、他のコンパイラも(Delphiを含むPascalコンパイラ等)、cdecl に呼出規約を変更するオプションを持っている。手動で操作するには、例えば、

void _cdecl function(params);

とする。_cdeclはプロトタイプ宣言部ないし関数宣言部に書く必要がある。

OS/2上の Virtual Pascal での例を挙げると、

function MyWndProc(h : HWND; m : ULONG; mp1 : MPARAM; mp2 : MPARAM) : MRESULT; CDECL;

のようにCDECL指令を付ける。このコンパイラは通常下記の Pascal 呼出規約を用いるが、この関数はOS/2のウィンドウシステム (Presentation Manager) から呼ばれるため、システム側に合わせてcdecl呼出規約に変更する必要がある。

Pascal

Pascal 呼出規約はcdeclの逆である。引数は左から右への順序でスタックに積まれ、スタック上の引数データを除去するのは呼ばれた側(サブルーチン側)である。Cと異なり、引数の個数と型がサブルーチン宣言時点で完全に固定であるため、スタック上の引数データのバイト数はサブルーチン内部で判明している。引数データの除去は実際にはサブルーチンからのリターン時のスタックポインタの調整で行われ、x86では"RET n"の一命令で実行可能である。サブルーチンのコードサイズはわずかながら増えることが多いが、呼び出し側でスタックを処理するコードが不要になるため全体としてはわずかながらコードのサイズが小さくなることが多い。本来呼出規約はプログラミング言語とは独立した概念だが、プログラミング言語の影響を受ける例である。

Lisa及び初期のMacintoshのシステム、アプリケーションはPascalで記述されたため、以前のMacintosh Toolboxを利用するにはPascal呼出規約を用いる必要があった。

呼出規約と言語仕様

言語仕様と呼出規約は独立した概念だが、上記の例とは逆に、呼出規約が実装レベルで言語仕様に影響を与える場合がある。fooとbarがどちらも関数であるとし、foobar(foo, bar) のように複数の引数をとる関数呼び出しがあるとする。Pascal 呼出規約では引数を左から右に格納するので、fooを先に、barを後に評価するのが自然で効率が高い。cdeclでは逆にbarを先にfooを後に評価すると自然である。これらの呼出規約に沿って「自然に」コンパイラを実装すると、言語仕様を実装レベルで決定してしまうことになる。

例えば次のPascalコードを考える。

var i : Integer;
...
function foo : Boolean;
begin
    foo := i > 0;
    i := 1
end;

function bar : Boolean;
begin
    bar := i > 0;
    i := -1
end;

function foobar(f, b : Boolean) : Boolean;
begin
    foobar := f < b
end;

begin
   i := 1;
   write(foobar(foo, bar))
end

関数呼び出し foobar(foo, bar) を行う際引数のfooとbarのどちらを先に評価するかで、結果が変わる。fooを先に評価すると変数 i はfoo呼び出し後にも1であるので、fooは真、barも真、foobar(foo, bar)は偽となる。barを先に評価すると、barは真だが、呼び出し後に i は-1になるため、fooは偽となり、foobar(foo, bar)は真となる。なお、標準Pascalでは引数を評価する順序を規定しておらず、結果が引数の評価順に依存するプログラムは良くない例だとされる。この例は良くない例である。

Register (fastcall)

レジスタ呼出規約または fastcall、レジスタ渡し が意味するものは、歴史的な事情からコンパイラ依存であるが、総じて次のようなものである。プロセッサのレジスタのビット幅と個数に合わせて、最初のいくつかの引数を(スタックではなく)レジスタに格納し、サブルーチンに渡す。残りの引数は cdecl と同様に右から左の順にスタックに積まれる[1]。戻り値はAL, AX, EAXレジスタに格納する。関数の引数の個数が可変でない場合は、スタックからの引数データの除去は(Pascal 呼出規約のように)サブルーチン側で行うのが通例である。しかしながら、ほとんどのランタイムライブラリサブルーチンはわずかな個数の引数しか取らないため、スタックにデータを積む必要は(従ってスタックを清掃する必要も)全くない。メモリよりレジスタの方が読み書きが速いため、レジスタ渡しは効率が高い。

レジスタ渡しの例

  • マイクロソフト(以下MS) __fastcall[1][2] 呼出規約(別名 __msfastcall)では、最初の2つの引数をECXおよびEDXに格納する。
  • ボーランド __fastcall[3] 呼出規約では、最初の3つの引数をEAX, EDX, ECXに格納する。
  • Watcom __fastcall 呼出規約では、最初の4つの引数をEAX, EDX, EBX, ECXに格納する。

Watcom C/C++ コンパイラでは #pragma aux[4] コンパイラ指令を用いて、プログラマが自前の呼出規約を指定することができる。取扱説明書によると、「"Very few users are likely to need this method, but if it is needed, it can be a lifesaver"(引用部分につき訳さない。ほとんど不要だが、いざという時には必要なものである、程度の意)」だそうである。

stdcall

MS stdcall [5]Windows APIで利用されている。引数は右から左に渡される。呼び出された側の関数ではEAX, ECX, EDXのレジスタの元の値を保存することなく使用してよい。呼び出し側の関数では必要ならば呼び出す前にそれらのレジスタをスタック上などに保存する。返り値はEAXに格納する。cdeclと異なり、スタックの清掃はサブルーチン側で行う(Pascal 呼出規約と同様)。ただし引数リストが可変長の場合は呼び出し側がスタックの後始末を行う。

safecall

WindowsでのDelphiは、safecallという呼出規約を用いてCOMのエラー処理をカプセル化している。COM/OLE の要求に沿って、例外が呼び出し側に漏れ出すことがなく、戻り値HRESULTに報告される。Delphiのコードからsafecallを行うと、自動的にHRESULTがチェックされ、必要なら例外が発生する。


thiscall

この規約はC++のメンバ関数を呼ぶのに用いられる。二つの主たる版があり、コンパイラ及び、関数が可変長の引数リストを用いるかによっていずれかが用いられる。

GCC (GNUコンパイラコレクション)では、thiscall はほとんど cdecl と同様である。呼び出し側がスタックを清掃し、引数は右から左の順で渡される。相違点は全ての引数を渡した後、最後に this ポインタがスタックに積まれることである。これは Windows で可変引数を用いるときの thiscall と類似している。

Windowsの場合は、関数が可変引数を取らない場合、引数は右から左に渡され、thisポインタはECXレジスタに格納される。スタックを清掃するのは呼ばれた側である。

thiscallがキーワードではないため、thiscallを明示的に指定することはできないが、IDAのような逆アセンブラではそれを指定する必要がある。そのため、__thiscall__というキーワードが用意されている。

clrcall

未稿

vectorcall

浮動小数点型、__m128型、およびそれらのうち同一の型を最大4つメンバーに持つ複合型はXMM0:XMM5に、__m256型およびその複合型はYMM0:YMM5を用いる。収まらなかった分は整数型としてポインタ渡しされる。返り値はEAX、EDX:EAX、XMM0:XMM3、YMM0:YMM3のいずれかに格納される。 それ以外はマイクロソフト fastcallに準ずる。

Intel ABI

インテルアプリケーションバイナリインタフェース (ABI) はほとんどのコンパイラが採用している呼出規約である。呼び出された側の関数ではEAX, ECX, EDXのレジスタの元の値を保存することなく使用してよい。呼び出し側の関数では必要ならば呼び出す前にそれらのレジスタをスタック上などに保存する。

x86-64

マイクロソフト x64呼出規約

x64呼出規約はx86-64(AMD64) / EM64Tで追加されたレジスタ空間を有益に利用する。RCX, RDX, R8, R9レジスタは整数型とポインタ型の引数に、XMM0, XMM1, XMM2, XMM3は浮動小数点型引数に用いられる。残りの引数はスタックに置かれる。__m128型はXMMレジスタを使わずスタックに置かれ整数型としてポインタ渡しされる。呼び出された側の関数ではRAX, RCX, RDX, R8, R9, R10, R11, XMM0:XMM5のレジスタの元の値を保存することなく使用してよい。呼び出し側の関数では必要ならば呼ぶ出す前にそれらのレジスタをスタック上などに保存する。レジスタが足りなくなれば、スタックが用いられる。返り値はRAXかXMM0に格納される。スタックポインタの処理は呼び出し側で行う。

vectorcall

浮動小数点型、__m128型、およびそれらのうち同一の型を最大4つメンバーに持つ複合型はXMM0:XMM5に、__m256型およびその複合型はYMM0:YMM5を用いる。収まらなかった分は整数型としてポインタ渡しされる。返り値はRAX、XMM0:XMM3、YMM0:YMM3のいずれかに格納される。 それ以外はマイクロソフト x64呼出規約に準ずる。

System V AMD64 ABI 呼出規約

Linux, BSD, macOSなどマイクロソフトのOS以外で利用されている。

  • 整数・ポインタ引数 - RDI, RSI, RDX, RCX, R8, R9
  • 浮動小数点引数 - XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7
  • 戻り値 - RAX
  • システムコールでは RCX の代わりに R10 を使用
  • レジスタだけでは引数の数が不足する場合はスタックを利用。

標準サブルーチンプロローグ/エピローグ

サブルーチンに制御が渡った点で、標準的には次のような処理を行う:

_function:
    push ebp       ;ベースポインタを保存
    mov ebp, esp   ;現在のスタックフレームを指すようベースポインタを変更
    sub esp, x     ;局所変数(Cでいう自動変数)の大きさの分スタックポインタを減らす

この結果、EBPはスタック上の引数の頭を指し、局所変数を格納する領域をスタック上に確保することができる。元のEBPの値はスタックに保存されている。このように局所変数は引数と同様にサブルーチン呼び出し毎にスタック上に確保されるので、再帰呼び出しが可能になる。

このサブルーチンから抜け出す際は、次のシーケンスを実行する:

    mov esp, ebp   ;局所変数を除去
    pop ebp        ;ベースポインタを復帰
    ret            ;サブルーチンから戻る

これはcdeclの例であって、Pascal 呼出規約では次のように引数データをサブルーチン側が掃除する。

    ret n          ;nは引数データのバイト数

次のC関数は

int _cdecl MyFunction(int i) { 
    int k;
    return i + k;
}

次のアセンブラコードと同等である。

    ;エントリシーケンス
    push ebp
    mov ebp, esp
    sub esp, 4     ;整数型変数kの領域に相当

    ;関数のコード
    mov eax, [ebp + 8] 
                   ;引数iを加算器(アキュムレータ)に移動
    add eax, [ebp - 4]
                   ;kをiに加える
                   ;結果はEAX(加算器)に残る

    ;終了シーケンス
    mov esp, ebp
    pop ebp
    ret

脚注・出典

[脚注の使い方]
  1. ^ ボーランド __fastcall 呼出規約では、残りの引数は Pascal 呼出規約 と同様にからの順にスタックに積まれる。変数修飾子 - RAD Studio XE2”. Delphi® XE2 および C++Builder® XE2 オンライン ヘルプ. Embarcadero Technologies, Inc. (2012年4月30日). 2013年1月22日閲覧。

関連項目

外部リンク


呼出規約

出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2021/07/16 03:15 UTC 版)

pコードマシン」の記事における「呼出規約」の解説

スタックフレームは以下のようになっている: EP → ローカルスタックSP → ... ローカル変数 ... 引数 ... リターンアドレス(前のPC) 前のEP 動的リンク(前のMP静的リンク外側プロシージャMPMP関数リターンプロシージャ呼び出し次のうになる。まず呼び出し次の命令開始されるmst n ここで、n は入れ子レベル指定するPascalではプロシージャ入れ子になりうることに注意)。この命令スタック印をつける。すなわち現在のスタックフレームの上の5要素ぶんを予約し以前EP動的リンク静的リンクなどを設定する呼び出し側は必要な引数計算してプッシュし、次の命令実行するcup n, p これでユーザープロシージャが呼び出される(n は引数個数、p はプロシージャアドレス)。この命令PCリターンアドレスとしてスタック上にセーブし、そのプロシージャアドレス新たなPCとしてセットする。 ユーザープロシージャは次の2つ命令から開始されるent 1, i ent 2, j 1番目の命令SPMP + i にする。2番目の命令EPSP + j にする。従って、i にはローカル変数用の領域サイズ指定し引数および余分に5要素分も予約する)、j には命令実行ローカル必要なスタック領域指定されるメモリ不足発生していないかはこの時点チェックされる呼び出し側への復帰次の命令行われる。 retC Cにはリターン型(上述基本型 i, r, c, b, a と何も返さない場合の p)が指定されるリターン値は適切なセル格納される。p 以外の各型リターンでは、その値がスタック置かれたまま呼び出し側に復帰する。 ユーザープロシージャの呼び出し(cup)ではなく標準プロシージャ q を呼び出す場合次の命令使用するcsp q 標準プロシージャとは、Pascal標準ライブラリのようなもので、例えば readln()("csp rln")、sin()("csp sin")などがある。ただし、eof()pコードでは1つ命令となっている。

※この「呼出規約」の解説は、「pコードマシン」の解説の一部です。
「呼出規約」を含む「pコードマシン」の記事については、「pコードマシン」の概要を参照ください。

ウィキペディア小見出し辞書の「呼出規約」の項目はプログラムで機械的に意味や本文を生成しているため、不適切な項目が含まれていることもあります。ご了承くださいませ。 お問い合わせ


英和和英テキスト翻訳>> Weblio翻訳
英語⇒日本語日本語⇒英語
  

辞書ショートカット

すべての辞書の索引

「呼出規約」の関連用語

呼出規約のお隣キーワード
検索ランキング

   

英語⇒日本語
日本語⇒英語
   



呼出規約のページの著作権
Weblio 辞書 情報提供元は 参加元一覧 にて確認できます。

   
ウィキペディアウィキペディア
All text is available under the terms of the GNU Free Documentation License.
この記事は、ウィキペディアの呼出規約 (改訂履歴)の記事を複製、再配布したものにあたり、GNU Free Documentation Licenseというライセンスの下で提供されています。 Weblio辞書に掲載されているウィキペディアの記事も、全てGNU Free Documentation Licenseの元に提供されております。
ウィキペディアウィキペディア
Text is available under GNU Free Documentation License (GFDL).
Weblio辞書に掲載されている「ウィキペディア小見出し辞書」の記事は、Wikipediaのpコードマシン (改訂履歴)の記事を複製、再配布したものにあたり、GNU Free Documentation Licenseというライセンスの下で提供されています。

©2025 GRAS Group, Inc.RSS