呼び出し規約

ウィキペディアから、無料の百科事典
ナビゲーションにジャンプ 検索にジャンプ

コンピュータサイエンスでは呼び出し規約は、サブルーチンが呼び出し元からパラメータを受け取る方法と、結果を返す方法の実装レベル(低レベル)のスキームです。さまざまな実装の違いには、パラメーター、戻り値、戻りアドレススコープリンクの配置場所(レジスタスタックメモリなど)や、関数呼び出しの準備とその後の環境の復元のタスクが呼び出し元と呼び出し元の間でどのように分割されるかなどがあります。呼び出し先。

呼び出し規約は特定のプログラミング言語の評価戦略に関連している場合がありますが、評価戦略は通常、より高い抽象化レベルで定義され、言語ではなく言語の一部と見なされるため、ほとんどの場合、その一部とは見なされません(またはその逆)。特定の言語のコンパイラの低レベルの実装の詳細として。

バリエーション

呼び出し規約は次の点で異なる場合があります。

  • パラメータ、戻り値、および戻りアドレスが配置される場所(レジスタコールスタック、両方の組み合わせ、または他のメモリ構造)
  • メモリに渡されるパラメータの場合、仮パラメータの実際の引数が渡される順序(または大きな引数または複雑な引数の一部)
  • (おそらく長いまたは複雑な)戻り値が呼び出し先から呼び出し元に返される方法(スタック、レジスター、またはヒープ内)
  • 関数呼び出しのセットアップとクリーンアップのタスクが、呼び出し元と呼び出し先の間でどのように分割されるか
  • 引数を説明するメタデータが渡されるかどうか、および渡される方法
  • フレームポインタの以前の値が格納されている場所。これは、ルーチンが終了したときにフレームポインタを復元するために使用されます(スタックフレームまたは一部のレジスタ)
  • ルーチンの非ローカルデータアクセス用の静的スコープリンクが配置されている場所(通常はスタックフレームの1つ以上の位置にありますが、場合によっては汎用レジスター、または一部のアーキテクチャーでは専用レジスターにあります)
  • ローカル変数がどのように割り当てられるかは、呼び出し規約の一部になることもあります(呼び出し元が呼び出し先に割り当てる場合)

場合によっては、違いには次のものも含まれます。

  • レジスターが保存されずに呼び出し先によって直接使用される可能性のある規則
  • どのレジスタが揮発性であると見なされ、揮発性の場合は、呼び出し先が復元する必要がないか

多くのアーキテクチャには、広く使用されている呼び出し規約が1つしかなく、アーキテクトによって提案されることがよくあります。SPARC、MIPS、およびRISC-Vを含むRISCの場合、この呼び出し規約に基づくレジスタ名がよく使用されます。たとえば、MIPSレジスタは、標準の呼び出し規約でのパラメータの受け渡しに使用されていることを反映して、「ABI名」からを持っています。(RISC CPUには同等の汎用レジスタが多数あるため、通常、番号以外の名前を付ける理由はハードウェアにありません。) $4$7$a0$a3

いくつ[どれ?] プログラミング言語は、言語仕様で呼び出しシーケンスを部分的に指定する場合があります。または、極めて重要な実装では、そのような言語のさまざまな実装(つまり、さまざまなコンパイラ)がさまざまな呼び出し規約を使用する場合があり、実装では複数の呼び出し規約を選択できます。 。この理由は、パフォーマンス、技術的な理由の有無にかかわらず、他の一般的な言語の規則に頻繁に適応すること、およびさまざまな「コンピューティングプラットフォーム」によって課される制限または規則です。

アーキテクチャ

x86(32ビット)

x86アーキテクチャは、さまざまな呼び出し規約で使用されます。アーキテクチャレジスタの数が少なく、歴史的に単純さと小さなコードサイズに重点が置かれているため、多くのx86呼び出し規約はスタックに引数を渡します。戻り値(またはそれへのポインタ)はレジスタに返されます。一部の規則では、最初のいくつかのパラメーターにレジスターを使用します。これにより、特に頻繁に呼び出される短くて単純なリーフルーチン(つまり、他のルーチンを呼び出さないルーチン)のパフォーマンスが向上する可能性があります。

呼び出し例:

  EAX            をプッシュします; いくつかのレジスタ結果を
 渡すpushdword  [ EBP + 20 ] ; いくつかのメモリ変数(FASM / TASM構文)を渡すpush 3 ; 定数呼び出しcalcを渡します; 返された結果はEAXになりました  
                
             

典型的な呼び出し先の構造:(以下の手順の一部またはすべて(retを除く)は、簡単な手順で最適化できます)。一部の規則では、パラメータスペースが割り当てられたままになり、のret代わりにプレーンが使用されret imm16ます。その場合、add esp,12この例では、呼び出し元はESPへの変更に対処できます。

calc:
  プッシュ EBP             ; 古いフレームポインタを保存
  するmovEBP  ESP ; 新しいフレームポインタサブESP localsizeを取得します; 地元の人のためにスタックスペースを予約します; 計算を実行し、結果をEAXに残しますmov ESP EBP ; 地元の人々のための空きスペースポップEBP ; 古いフレームポインタを復元するretparamsize ; パラメータスペースを解放して戻ります。         
      
  
                     
  
            
                
          

ARM(A32)

標準の32ビットARM呼び出し規約では、15個の汎用レジスタが次のように割り当てられます。

  • r15:プログラムカウンター(命令セットの仕様による)。
  • r14:リンクレジスタ。サブルーチン呼び出しで使用されるBL命令は、このレジスタにリターンアドレスを格納します。
  • r13:スタックポインタ。「サム」動作モードのプッシュ/ポップ命令は、このレジスタのみを使用します。
  • r12:手順内-スクラッチレジスタを呼び出します。
  • r4からr11:ローカル変数。
  • r0〜r3:サブルーチンに渡される引数値と、サブルーチンから返される結果。

返される値のタイプが大きすぎてr0からr3に収まらない場合、またはコンパイル時にサイズを静的に決定できない場合、呼び出し元は実行時にその値にスペースを割り当て、r0でそのスペースへのポインターを渡す必要があります。

サブルーチンは、r4からr11の内容とスタックポインタを保持する必要があります(おそらく、関数プロローグのスタックに保存し、スクラッチスペースとして使用して、関数エピローグのスタックから復元します)。特に、他のサブルーチンを呼び出すサブルーチンは、他のサブルーチンを呼び出す前に、リンクレジスタr14のリターンアドレスをスタックに保存する必要があります。ただし、このようなサブルーチンは、その値をr14に返す必要はありません。返すには、その値をプログラムカウンターであるr15にロードするだけです。

ARM呼び出し規約では、完全な降順のスタックを使用する必要があります。[1]

この呼び出し規約により、「一般的な」ARMサブルーチンは次のようになります。

  • プロローグで、r4からr11をスタックにプッシュし、r14のリターンアドレスをスタックにプッシュします(これは単一のSTM命令で実行できます)。
  • 渡された引数(r0からr3)をローカルのスクラッチレジスタ(r4からr11)にコピーします。
  • 他のローカル変数を残りのローカルスクラッチレジスタ(r4からr11)に割り当てます。
  • r0からr3、r12、およびr14が保持されないと仮定して、計算を実行し、必要に応じてBLを使用して他のサブルーチンを呼び出します。
  • 結果をr0に入れます。
  • エピローグで、スタックからr4からr11をプルし、リターンアドレスをプログラムカウンターr15にプルします。これは、単一のLDM命令で実行できます。

ARM(A64)

64ビットARM(AArch64)呼び出し規約は、31個の汎用レジスタを次のように割り当てます。[2]

  • x31(SP):コンテキストに応じて、スタックポインタまたはゼロレジスタ。
  • x30(LR):サブルーチンから戻るために使用されるプロシージャリンクレジスタ。
  • x29(FP):フレームポインタ。
  • x19からx29:Callee-saved。
  • x18(PR):プラットフォームレジスタ。オペレーティングシステム固有の特別な目的、または追加の呼び出し元が保存したレジスタに使用されます。
  • x16(IP0)およびx17(IP1):手順内-スクラッチレジスタを呼び出します。
  • x9からx15:ローカル変数、呼び出し元が保存されました。
  • x8(XR):間接戻り値アドレス。
  • x0〜x7:サブルーチンに渡される引数値と、サブルーチンから返される結果。

xで始まるすべてのレジスタには、対応する32ビットレジスタの前にwが付いています。したがって、32ビットのx0はw0と呼ばれます。

同様に、32個の浮動小数点レジスタは次のように割り当てられます。[3]

  • v0からv7:サブルーチンに渡された引数値と、サブルーチンから返された結果。
  • v8からv15:呼び出し先が保存されますが、保持する必要があるのは下位64ビットのみです。
  • v16からv31:ローカル変数、呼び出し元が保存されました。

PowerPC

PowerPCアーキテクチャには多数のレジスタがあるため、ほとんどの関数は、単一レベルの呼び出しのためにレジスタ内のすべての引数を渡すことができます追加の引数はスタックに渡され、レジスタベースの引数用のスペースも、マルチレベル呼び出しが使用され(再帰的またはその他)、レジスタを保存する必要がある場合に、呼び出される関数の便宜のために常にスタックに割り当てられます。これは、関数の引数に配列としてアクセスする必要があるなどの可変個引数関数でも役立ちます。printf()すべての手続き型言語には、単一の呼び出し規約が使用されます。

MIPS

O32 [4] ABIは、 MIPS用の元のSystem V ABIとしてのステータスがあるため最も一般的に使用されるABIです。[5]厳密にスタックベースであり、引数を渡すために使用できるレジスタは4つだけです。この認識された速度低下は、16個のレジスタのみを備えた旧式の浮動小数点モデルとともに、他の多くの呼び出し規約の急増を促しています。ABIは1990年に形になり、1994年以降更新されていません。32ビットMIPSに対してのみ定義されていますが、GCCはO64と呼ばれる64ビットのバリエーションを作成しました。[6]$a0-$a3

64ビットの場合、SiliconGraphicsのN64ABI(Nintendo 64とは関係ありません)が最も一般的に使用されます。最も重要な改善点は、8つのレジスタが引数の受け渡しに使用できるようになったことです。また、浮動小数点レジスタの数を32に増やします。n32と呼ばれるILP32バージョンもあります。これは、x32 ABIに類似した、より小さなコードに32ビットポインタを使用します。どちらもCPUの64ビットモードで実行されます。[6]

O32をN32に似た32ビットABIに置き換える試みがいくつか行われています。1995年の会議でMIPSEABIが考案されましたが、32ビットバージョンは非常に似ていました。[7] EABIは、MIPS Technologiesに影響を与え、戻り値に引数レジスタを追加で再利用する、より根本的な「NUBI」ABIを提案しました。[8] MIPS EABIはGCCでサポートされていますが、LLVMではサポートされていません。どちらもNUBIをサポートしていません。

O32およびN32 / N64のすべてについて、リターンアドレスは$raレジスタに格納されます。これは、JAL(ジャンプとリンク)またはJALR(ジャンプとリンクレジスタ)命令を使用して自動的に設定されます。スタックは下向きに成長します。

SPARC

SPARCアーキテクチャは、ほとんどのRISCアーキテクチャとは異なり、レジスタウィンドウ上に構築されています各レジスタウィンドウには24個のアクセス可能なレジスタがあります。8個は「in」レジスタ(%i0-%i7)、8個は「ローカル」レジスタ(%l0-%l7)、8個は「out」レジスタ(% o0-%o7)。「in」レジスタは、呼び出されている関数に引数を渡すために使用され、追加の引数はスタックにプッシュする必要がありますただし、スペースは常に呼び出された関数によって割り当てられ、潜在的なレジスタウィンドウのオーバーフロー、ローカル変数を処理し、(32ビットSPARCでは)値で構造体を返します。関数を呼び出すには、呼び出す関数の引数を「out」レジスタに配置します。関数が呼び出されると、「out」レジスタが「in」レジスタになり、呼び出された関数が「in」レジスタの引数にアクセスします。呼び出された関数が完了すると、戻り値は最初の「in」レジスタに配置されます。これは、呼び出された関数が戻ったときに最初の「out」レジスタになります。

最新のUnixライクなシステムが従うSystemV ABI [9]は、"in"レジスタ%i0から%i5の最初の6つの引数を渡し、フレームポインタ用に%i6、リターンアドレス用に%i7を予約します。

IBM System / 360および後継機

IBM System / 360は、ハードウェアスタックのない別のアーキテクチャーです。以下の例は、64ビットz / Architectureが導入される前にOS / 360以降で使用されていた呼び出し規約を示しています。System / 360の他のオペレーティングシステムでは、呼び出し規約が異なる場合があります。

呼び出しプログラム:

     LA 1、ARGS引数リストアドレスをロード
     L 15、= A(SUB)サブルーチンアドレスをロードします
     BALR14,15呼び出されたルーチン1への分岐
     ..。
ARGS DC A(FIRST)最初の引数のアドレス
     DC A(2番目)
     ..。
     DC A(THIRD)+ X'80000000 '最後の引数2

呼び出されたプログラム:

SUB EQU *これはサブプログラムのエントリポイントです

標準入力シーケンス:

     USING *、15 3 
     STM 14,12,12(13)レジスタの保存4
     ST 13、SAVE +4発信者のsaveareaアドレスを保存
     LA 12、SAVEチェーンsaveareas
     ST 12,8(13)
     LR 13,12
     ..。

標準の戻りシーケンス:

     L 13、SAVE + 4 5
     LM 14,12,12(13)
     L 15、RETVAL 6
     BR14発信者に戻る
SAVE DS 18F Savearea 7

ノート:

  1. BALR命令は、次の命令のアドレス(リターンアドレス)を最初の引数で指定されたレジスタ(レジスタ14)に格納し、レジスタ15の2番目の引数アドレスに分岐します
  2. 呼び出し元は、レジスタ1の引数アドレスのリストのアドレスを渡します。最後のアドレスには、リストの終わりを示すために設定された上位ビットがあります。これにより、この規則を使用するプログラムが31ビットアドレス指定に制限されます。
  3. 呼び出されたルーチンのアドレスはレジスタ15にあります。通常、これは別のレジスタにロードされ、レジスタ15はベースレジスタとして使用されません。
  4. 命令は、STMレジスター14、15、および0から12を、レジスター13が指す保管域と呼ばれる呼び出し元によって提供される72バイトの域に保管します。呼び出されたルーチンは、呼び出すサブルーチンが使用する独自の保管域を提供します。この領域のアドレスは通常、ルーチン全体を通してレジスター13に保持されます。次の手順STMでは、この保存領域を呼び出し元の保存領域にリンクする順方向および逆方向のチェーンを更新します。
  5. リターンシーケンスは、呼び出し元のレジスタを復元します。
  6. レジスター15は通常、戻り値を渡すために使用されます。
  7. 呼び出されたルーチンでsaveareaを静的に宣言すると、再入可能および再帰的ではなくなります。再入可能プログラムは、オペレーティングシステムから取得され、戻ったときに解放されるか、呼び出し側プログラムによって渡されたストレージにある動的保存領域を使用します。

Linuxで使用される System / 390 ABI [10]およびz / Architecture ABI [11]では、次のようになります。

  • レジスタ0と1は揮発性です
  • レジスター2および3は、パラメーターの受け渡しと戻り値に使用されます。
  • レジスター4および5は、パラメーターの受け渡しにも使用されます。
  • レジスター6はパラメーターの受け渡しに使用され、呼び出し先が保存および復元する必要があります
  • レジスター7から13は、呼び出し先が使用するためのものであり、呼び出し先が保存および復元する必要があります。
  • レジスター14は差出人住所に使用されます
  • レジスタ15はスタックポインタとして使用されます
  • 浮動小数点レジスタ0および2は、パラメータの受け渡しと戻り値に使用されます
  • 浮動小数点レジスター4および6は、呼び出し先が使用するためのものであり、それらによって保存および復元する必要があります。
  • z / Architectureでは、浮動小数点レジスター1、3、5、および7から15は、呼び出し先が使用するためのものです。
  • アクセスレジスタ0はシステム用に予約されています
  • アクセスレジスタ1〜15は、呼び出し先が使用するためのものです。

SuperH

登録 Windows CE 5.0 gcc ルネサス
R0 戻り値。アセンブリの疑似命令を拡張するための一時的なものです。8/16ビット演算の暗黙の送信元/宛先。保存されません。 戻り値、呼び出し元は保存します 変数/一時的。保証されません
R1..R3 一時レジスタとして機能します。保存されません。 発信者はスクラッチを保存しました。構造アドレス(デフォルトでは呼び出し元の保存) 変数/一時的。保証されません
R4..R7 整数引数の最初の4ワード。引数ビルド領域は、引数を保持しているR4からR7がこぼれる可能性のあるスペースを提供します。保存されません。 パラメータの受け渡し、呼び出し元の保存 引数。保証されません。
R8..R13 永続的なレジスタとして機能します。保存されます。 Callee Saves 変数/一時的。保証されています。
R14 デフォルトのフレームポインタ。(R8〜R13はフレームポインタとしても機能し、リーフルーチンはR1〜R3をフレームポインタとして使用できます。)保存されます。 フレームポインタ、FP、呼び出し先が保存 変数/一時的。保証されています。
R15 スタックポインタまたは永続レジスタとして機能します。保存されます。 スタックポインタ、SP、呼び出し先が保存 スタックポインタ。保証されています。

注:「保存された」予約は、呼び出し先の保存に使用されます。「保証付き」についても同様です。

68k

Motorola68000シリーズの最も一般的な呼び出し規約は次のとおりです。[12] [13] [14] [15]

  • d0、d1、a0、a1はスクラッチレジスタです
  • 他のすべてのレジスタは呼び出し先に保存されます
  • a6はフレームポインタであり、コンパイラオプションで無効にできます
  • パラメータは、右から左にスタックにプッシュされます
  • 戻り値はd0に格納されます

IBM 1130

IBM 1130は、16ビットのワードアドレス可能な小型のマシンでした6つのレジスタと条件インジケータのみがあり、スタックはありませんでした。レジスタは、命令アドレスレジスタ(IAR)アキュムレータ(ACC)アキュムレータ拡張(EXT)、および3つのインデックスレジスタX1〜X3です。呼び出し側プログラムは、ACC、EXT、X1、およびX2を保存する責任があります。[16]サブルーチンを呼び出すには、メインプログラムに直接リンクされた再配置不可能なサブルーチンをコーディングするためと、転送ベクトルを介して再配置可能なライブラリサブルーチンを呼び出すための2つの疑似操作があります。[17]両方の疑似操作がブランチおよびストアIARに解決されますCALLLIBFBSI)次の命令のアドレスを実効アドレス(EA)に格納し、EA +1に分岐するマシン命令。

引数は、BSI‍—‌通常、これらは引数の1ワードアドレスです‍—‌呼び出されたルーチンは、戻るときにスキップできるように、予想される引数の数を知っている必要があります。または、引数をレジスタに渡すこともできます。関数ルーチンは、実数引数のACC、または実数疑似アキュムレータ(FAC)と呼ばれるメモリ位置に結果を返しました。引数と差出人住所は、サブルーチンの最初の場所に格納されているIAR値へのオフセットを使用してアドレス指定されました。

  * 1130サブルーチンの例
     ENTSUB外部エントリポイント「SUB」を宣言します
 SUB DC 0エントリポイントの予約語、通常は「DC *-*」とコード化
 *サブルーチンコードはここから始まります
 *引数があった場合、アドレスはリターンアドレスから間接的にロードできます
     LDX I 1 SUB最初の引数のアドレスをX1にロードします(例)
 ..。
 *シーケンスを返す
     LDRES整数の結果をACCにロードします
 *引数が指定されていない場合、保存されている差出人住所への間接分岐
     BISUB引数が指定されていない場合
     END SUB

IBM 1130、CDC 6600、およびPDP-8(3台のコンピューターはすべて1965年に導入されました)のサブルーチンは、サブルーチンの最初の場所に戻りアドレスを保管します。[18]

実装に関する考慮事項

複数の言語で記述されたモジュールを組み合わせる場合、またはオペレーティングシステムやライブラリAPIを、それらが記述されている言語以外の言語から呼び出す場合は、この変動性を考慮する必要があります。このような場合、発信者と着信者が使用する呼び出し規約を調整するために特別な注意を払う必要があります。単一のプログラミング言語を使用するプログラムでさえ、コードの最適化のためにコンパイラーによって選択されるか、プログラマーによって指定される複数の呼び出し規約を使用する場合があります。

スレッドコード

スレッド化されたコードは、呼び出されたコードに対する関数呼び出しのセットアップとクリーンアップのすべての責任を負います。呼び出し元のコードは、呼び出されるサブルーチンをリストするだけです。これにより、関数が呼び出される多くの場所ではなく、すべての関数のセットアップとクリーンアップのコードが1つの場所(関数のプロローグとエピローグ)に配置されます。これにより、スレッドコードが最もコンパクトな呼び出し規約になります。

スレッドコードは、スタック上のすべての引数を渡します。すべての戻り値はスタックに返されます。これにより、ナイーブな実装は、レジスタに多くの値を保持する規約を呼び出すよりも遅くなります。ただし、レジスタの最上位スタック値のいくつか(特にリターンアドレス)をキャッシュするスレッドコード実装は、通常、リターンアドレスを常にスタックにプッシュおよびポップするサブルーチン呼び出し規約よりも高速です。[19] [20] [21]

PL / I

PL / I言語で記述されたプログラムのデフォルトの呼び出し規約は、すべての引数を参照によって渡しますが、他の規約をオプションで指定することもできます。引数はコンパイラやプラットフォームごとに異なる方法で処理されますが、通常、引数アドレスはメモリ内の引数リストを介して渡されます。戻り値を含む領域を指す最終的な非表示のアドレスを渡すことができます。PL / Iでサポートされるデータ型は多種多様であるため、データ記述子を渡して、たとえば、文字列またはビット文字列の長さ、配列(ドープベクトル)の次元と境界、またはレイアウトと内容を定義することもできます。データ構造ダミーの議論定数であるか、呼び出されたプロシージャが期待する引数のタイプと一致しない引数に対して作成されます。

も参照してください

参考文献

  1. ^ 「ARMアーキテクチャのプロシージャコール標準」2021年。
  2. ^ 「汎用レジスタのパラメータ」ARMCortex-ARMv8-A用のシリーズプログラマーズガイド2020年11月12日取得
  3. ^ 「NEONおよび浮動小数点レジスタのパラメータ」developer.arm.com 2020年11月13日取得
  4. ^ 「MIPS32命令セットクイックリファレンス」
  5. ^ スウィートマン、ドミニク。MIPS Run(2 ed。)を参照してください。モーガンカウフマン出版社ISBN 0-12088-421-6
  6. ^ a b "MIPSABIヒストリー"
  7. ^ クリストファー、エリック(2003年6月11日)。「mipeabiドキュメント」[email protected](メーリングリスト)2020年6月19日取得
  8. ^ 「NUBI」
  9. ^ システムVアプリケーションバイナリインターフェイスSPARCプロセッササプリメント(3版)。
  10. ^ 「S / 390ELFアプリケーションバイナリインターフェイスサプリメント」
  11. ^ 「zSeriesELFアプリケーションバイナリインターフェイスサプリメント」
  12. ^ スミス、マイク博士。「SHARC(21k)と68kレジスタの比較」
  13. ^ XGCC:組み込み開発用のGnu C / C ++言語システム(PDF)エンベデッドサポートツールズコーポレーション。2000.p。59。
  14. ^ 「COLDFIRE / 68K:FreescaleColdFireファミリー用のThreadX」2015-10-02にオリジナルからアーカイブされました。
  15. ^ Moshovos、Andreas。「サブルーチンの続き:引数の受け渡し、値の戻り、ローカル変数の割り当て」d0、d1、a0、a1、およびa7を除くすべてのレジスタは、呼び出し全体で保持する必要があります。
  16. ^ IBM Corporation(1967)。IBM 1130ディスク・モニター・システム、バージョン2システムの概要(C26-3709-0)(PDF)p。67 2014年12月21日取得
  17. ^ IBM Corporation(1968)。IBM 1130アセンブラー言語(C26-5927-4)(PDF)pp。24–25。
  18. ^ Smotherman、Mark(2004)。「サブルーチンとプロシージャコールのサポート:初期の履歴」
  19. ^ ロドリゲス、ブラッド。「フォースの移動、パート1:フォースカーネルの設計上の決定」6809またはZilogSuper8では、DTCはSTCよりも高速です。
  20. ^ Ertl、アントン。「様々な通訳派遣技術のスピード」
  21. ^ Zaleski、Mathew(2008)。「第4章:効率的な解釈の設計と実装」YETI:段階的に拡張可能なトレースインタープリター直接スレッドインタプリタは分岐予測プロパティが不十分であることが知られていますが、呼び出しとリターンのレイテンシは間接ジャンプよりも長くなる可能性があります。

外部リンク