現在、私はいくつかのアセンブリ言語の手順を書いています。慣例にあるように、呼び出し元に整数などの値を返したい場合は、EAXレジスタに返す必要があります。ここで、float、double、enum、または複雑な構造体を返したい場合はどうすればよいのでしょうか。これらのタイプの値を返す方法は?
メモリ内の実際の値を指すEAX内のアドレスを返すことを考えることができます。しかし、それは標準的な方法ですか?
どうもありがとう~~~
現在、私はいくつかのアセンブリ言語の手順を書いています。慣例にあるように、呼び出し元に整数などの値を返したい場合は、EAXレジスタに返す必要があります。ここで、float、double、enum、または複雑な構造体を返したい場合はどうすればよいのでしょうか。これらのタイプの値を返す方法は?
メモリ内の実際の値を指すEAX内のアドレスを返すことを考えることができます。しかし、それは標準的な方法ですか?
どうもありがとう~~~
発信者があなたのコードである場合、それはすべてあなた次第です。発信者があなたの管理下にない場合は、既存の規則に従うか、独自の規則を一緒に開発する必要があります。
たとえば、x86プラットフォームでは、浮動小数点演算がFPU命令によって処理される場合、関数の結果がFPUレジスタスタックの最上位値として返されます。(ご存知の場合、x86 FPUレジスタはある種の「循環スタック」に編成されています)。その時点では、それはどちらfloat
でもありません。これは、内部FPU精度で保存された値です(またはdouble
より高い可能性があります)float
double
)そして、FPUスタックの最上位からその値を取得し、それを必要なタイプに変換するのは呼び出し側の責任です。実際、これが一般的なFPU命令の仕組みです。引数をFPUスタックの最上位から取得し、結果をFPUスタックにプッシュします。同じ方法で関数を実装することにより、基本的に「複雑な」FPU命令を関数でエミュレートします。これは、かなり自然な方法です。
浮動小数点演算がSSE命令によって処理される場合、同じ目的でいくつかのSSEレジスタを選択できます(整数に使用するのとxmm0
同じように使用します)。EAX
複雑な構造(つまり、レジスタまたはレジスタのペアよりも大きい構造)の場合、呼び出し元は通常、予約されたバッファへのポインタを関数に渡します。そして、関数は結果をバッファーに入れます。言い換えると、内部では、関数は実際には大きなオブジェクトを「返す」ことはなく、呼び出し元が提供するメモリバッファにそれらを構築します。
もちろん、この「メモリバッファ」メソッドを使用して任意のタイプの値を返すことができますが、値が小さい場合、つまりスカラータイプの値の場合は、メモリの場所よりもレジスタを使用する方がはるかに効率的です。これは、ところで、小さな構造にも当てはまります。
列挙型は通常、整数型の概念的なラッパーにすぎません。したがって、列挙型と整数のどちらを返すかには違いはありません。
ダブルはスタックの最初のアイテムとして返されます。
C ++コードの例(x86)は次のとおりです。
double sqrt(double n)
{
_asm fld n
_asm fsqrt
}
スタックを手動で管理する(CPUサイクルをいくらか節約する)場合:
double inline __declspec (naked) __fastcall sqrt(double n)
{
_asm fld qword ptr [esp+4]
_asm fsqrt
_asm ret 8
}
複合型の場合は、ポインターを渡すか、ポインターを返す必要があります。
呼び出し規約やアセンブリ言語について質問がある場合は、高水準言語で(別のファイルに)単純な関数を記述してください。次に、コンパイラにアセンブリ言語リストを生成させるか、デバッガに「インターリーブアセンブリ」を表示させます。
リストには、コンパイラがコードを実装する方法が示されるだけでなく、呼び出し規約も示されます。SOに投稿するよりもはるかに簡単で、通常は高速です。;-)
C99には、複雑な組み込みデータ型(_Complex
)があります。したがって、C99準拠のコンパイラを使用している場合は、複合体を返す関数をコンパイルして、これをアセンブラにコンパイルできます(通常は-S
オプションを使用)。そこに取られている慣習を見ることができます。
ABIによって異なります。たとえば、Linux on x86は、Intel386 Architecture Processor Supplment、FourthEditionで指定されているSysVABIを使用します。
「関数呼び出しシーケンス」セクションには、値が返される方法に関する情報があります。簡単に言うと、このAPIでは:
%eax
;を使用します。%st(0)
;を使用します。struct
またはunion
型の場合、呼び出し元は戻り値用のスペースを提供し、そのアドレスを非表示の最初の引数として渡します。呼び出し先は、このアドレスをで返します%eax
。通常、スタックを使用します
Cまたは他の高級言語とのインターフェースを計画している場合は、通常、関数の引数としてメモリバッファーのアドレスを受け入れ、そのバッファーにデータを入力して複素数値を返します。これがアセンブリのみの場合は、必要なレジスタのセットを使用して独自の規則を定義できますが、通常は、特定の理由(パフォーマンスなど)がある場合にのみ定義します。