2016-12-06 18 views
4

アセンブリでf1関数の3番目の引数で渡された関数(* f2)を呼び出すにはどうすればよいですか? 宣言は以下のようになります。関数ポインタを引数として渡して関数を呼び出す方法はありますか?

extern float f1(int v1, float v2, float (*f2)(int v3, float v4)); 

私がV3にV1を渡したい、V4、コール関数f2、および戻り値にv2の

f1: 
    push rbp   
    mov rbp, rsp 

    mov rdx, rdi ; v1 to v3 
    mov xmm1, xmm0 ; v2 to v4 
    call ??? 
    mov xmm0, xmm1 

    mov rsp, rbp  
    pop rbp  
ret 

私は疑問符の代わりに何を入れますか?

+0

現在使用しているプラ​​ットフォーム上のアプリケーションバイナリインターフェイスは何ですか?それは、パラメータがどこにあり、呼び出し規約が何であるかを定義します。 –

+0

@DavidHoelzer Abi64 – helloFromTheOtherSide

+0

これは何ですか?そのようなABIはありません。 –

答えて

5

「Abi64」のようなものはありません。 MASMの質問にタグを付けたので、あなたはWindowsプラットフォームを使用していると推測できます。明らかに "64"はこれが64ビットコードであることを意味するため、可能性を大幅に絞り込んでいます。しかし、Windows上の64ビットコードには、依然として2つの共通呼び出し規約があります。そのうちの1つは__vectorcallで、もう1つはMicrosoft x64呼び出し規約です(他のすべての呼び出し規約は廃止され、&hellipにするために考案されたものです)。

Microsoft x64呼び出し規約が最も一般的で、この特定のケースでは__vectorcallを使用しても何も変更されないため、使用していると仮定します。そして、必要なコードは絶対に些細なことになります。スタックが同じように設定されるので、f1からf2にジャンプするだけです。 f1の最初の2つのパラメータは、f2に渡すべき2つのパラメータであり、戻り値f2f1の戻り値です。したがって、

これは書き込みが簡単であるだけでなく、サイズと速度の両方にとって最適な実装です。
したい場合でも例えば、事前にv1またはv2パラメータを変更することができます:あなたはもっと複雑なものをやってみたかった場合

f1: 
    inc  ecx  ; increment v1 (passed in ecx) 

    ; multiply v2 (xmm1) by v1 (ecx) 
    movd  xmm0, ecx 
    cvtdq2ps xmm0, xmm0 
    mulss xmm1, xmm0 

    rex_jmp r8 ; third parameter (pointer to f2) is passed in r8 

を、ここではそれがうまくいく方法は次のとおりです。

f1: 
    sub rsp, 40  ; allocate the required space on the stack 
    call r8   ; call f2 through the pointer, passed in r8 
    add rsp, 40  ; clean up the stack 
    ret 

に留意されたいです。質問に表示されているプロローグ/エピローグコードは必要ありませんが、それを含めると何かを傷つけることはありません。

ただし、質問に表示されているサンプルコードで行っていたパラメータのシャッフルは、が間違っています! Microsoft x64呼び出し規約では、最初の4つまでの整数引数は、RCX、RDX、R8、およびR9の左から右へレジスタで渡されます。その他の整数引数はすべてスタックに渡されます。最初の4つまでの浮動小数点値は、左から右、XMM0、XMM1、XMM2、およびXMM3のレジスタにも渡されます。残りはレジスタには大きすぎる構造体と共にスタックに渡されます。

しかし、変わったことは、スロットが「固定」であるため、整数とfpの引数が混在している場合でも、合計4つのレジスタのargだけが使用できることです。したがって:

╔═══════════╦══════════════════════════╗ 
║   ║   TYPE   ║ 
║ PARAMETER ╠═════════╦════════════════╣ 
║   ║ Integer ║ Floating-Point ║ 
╠═══════════╬═════════╬════════════════╣ 
║ First  ║ RCX ║  XMM0  ║ 
╠═══════════╬═════════╬════════════════╣ 
║ Second ║ RDX ║  XMM1  ║ 
╠═══════════╬═════════╬════════════════╣ 
║ Third  ║ R8 ║  XMM2  ║ 
╠═══════════╬═════════╬════════════════╣ 
║ Fourth ║ R9 ║  XMM3  ║ 
╠═══════════╬═════════╩════════════════╣ 
║ (rest) ║   on stack   ║ 
╚═══════════╩══════════════════════════╝ 

第2パラメータは、渡される最初の浮動小数点値ではありません。最初の浮動小数点値なのでXMM0には入りません。これは2番目のパラメータであり、2番目の「スロット」にあるためXMM1に入ります。 (これはthe x86-64 System V ABIとは異なります。最初の6つの整数引数がレジスタに入りますが、FP引数があるかどうかは関係ありません)。

Windowsパラメータの受け渡しに関する詳細なドキュメントは、例を含めてhereです。

+1

*サイズとスピードの両方に最適な実装*:サイズについては、[CVTSI2SS](http://www.felixcloutier.com/x86/CVTSI2SS.html)を使用し、XMM0の古い値に対する誤った依存性を無視します(PIII、Intelの近視眼的な設計に感謝します)。私はgccがスピードに最適なもの( 'pxor xmm0、xmm0' /' cvtsi2ss xmm0、ecx')を期待していましたが、実際にはMOVDのように見えますし、パックされた変換は同じ低い待ち時間と少ないuopです。 MOVDからCVTDQ2PSへの1cバイパス遅延が存在する可能性があります。 (AgnerはそれをNehalemで3 + 2cと表示しているので、CVTDQ2PSはそこにフロート入力をしたいと考えています) –

+0

私はその上にあるコードに適用するコメントを意味しました。必ずしも浮動小数点値の変換ではありません。私はスピードのためにそれを書いていない!しかし、情報と研究をお寄せいただきありがとうございます!私が示したコードは、基本的にMSVCが生成するコードです。 Agnerは後世代のプロセッサに同じペナルティを課しているわけではないので、おそらく64ビットビルドの最も可能性の高いターゲットとして最適化する方が望ましいでしょう。少なくとも、私の選択であろう。間違いなくGCCのコードは読みやすいですが。 –

+0

なぜ「__fastcall」と「Microsoft x64呼び出し規約」と言うのですか?あなたは、vectorcallとfastcallは(ほとんど同じですが)他の呼び出し規約とは違うと言っているようです。しかし、私は、x64 '__fastcall'は、XMM/YMM regsでベクトルを渡さない元のMicrosoft x64呼び出し規約のまれな用語であると考えました。 ([Windows上のasmのインテルチュートリアルガイド](https://software.intel.com/en-us/articles/introduction-to-x64-assembly)には、呼び出し規約の詳細な説明があり、すべて - 周りの良い。) –

-2

アセンブリコードは、使用されているマイクロコントローラによって異なります。

ないあなたが探しているが、インテルCore I7を持つWindowsプラットフォーム上で生成されたアセンブリコードを以下の通りまさに: -

Cコード: -

extern float f1(int v1, float v2, float (*f2)(int v3, float v4)) 
{ 
    float a = 10.0; 
    int b = 12; 

    f2(b, a); 
    return a+ 12.5; 
} 

アセンブリコード: -

_f1: 
pushl %ebp 
movl %esp, %ebp 
subl $40, %esp 
movl LC0, %eax 

movl %eax, -12(%ebp) 
movl $12, -16(%ebp) 
movl -12(%ebp), %eax 
movl %eax, 4(%esp) 
movl -16(%ebp), %eax 
movl %eax, (%esp) 
movl 16(%ebp), %eax 
call *%eax 
fstp %st(0) 
flds -12(%ebp) 
flds LC1 
faddp %st, %st(1) 
leave 
ret 

これが役に立ちます。

+1

明らかに、最適化が有効になっていない状態でこのコードをコンパイルしたのは間違いです。また、Microsoftのx64呼び出し規約を使用しない*コンパイラでコンパイルしていると明らかにコンパイルされているため、疑問に尋ねた人には分かりにくく、役に立たないでしょう。確かに、問題は不明であったが、彼がMASMを使ってx64をターゲットにしていることは確かだ。これはMASM構文ではなく、Windowsでのx64呼び出し規約では、最初の4つの整数または浮動小数点パラメーターがスタックに渡されることはありません。 –

関連する問題