#navi_header|C言語系| 2010年現在、OpenWatcomでサポートされている。 2010年の時点のMSDNによると、現在は "__fortran" 呼び出し規約はサポートされていない。(obsoleted) - Obsolete Calling Conventions -- http://msdn.microsoft.com/ja-jp/library/wda6h6df%28VS.80%29.aspx ただし、サポートされていないのはあくまでも "__fortran" という宣言であり、次のMSDNを見る限りでは "__stdcall" と "__cdecl" が Fortran <> C,C++ との間で使えるようである。 - Mixed-Language Programming Topics -- http://msdn.microsoft.com/en-us/library/aa249078%28VS.60%29.aspx --- MSDN -> MSDN Library -> Development Tools and Languages -> Visual Studio 6.0 -> Visual C and C++ 6.0 -> Product Documentation -> Visual C++ Programmer's Guide -> Adding Program Functionality -> Details -> Mixed-Language Programming Topics "__cdecl", "__stdcall" 共に引数を渡す順序は "右→左" である。ではFortran <> C,C++間はそれでOKかと思いきや、以下の記事では「"__pascal"呼び出し規約と同じ」とあり、これだけ見ると「左→右」の順序と読み取れる。 - The Old New Thing : The history of calling conventions, part 1 -- http://blogs.msdn.com/oldnewthing/archive/2004/01/02/47184.aspx MSDNの記事の場合は"Visual Studio 6.0"時代の内容であるし、"The Old New Thing"とともにFortran処理系を特に指定していない。従って、実際にどのFortranでどの呼び出し規約をC,C++との間で使えるのかは、手持ちの資料だけでは正確には何も言えない。 ''2010-12-24追記:'' "The Old New Thing"からFORTRANとCの相互運用について新しい記事が登場した。FORTRANでは関数の引数をリファレンス渡しにする必要があったり、配列のインデックスが1始まりになっていたり、複素数の扱い方などが紹介されている。 - The __fortran calling convention isn't the calling convention used by FORTRAN -- http://blogs.msdn.com/b/oldnewthing/archive/2010/12/22/10108152.aspx #more|| ---- #outline|| ---- * サンプルコード ** callee.c (呼ばれる関数側) #code|c|> int foo1(int a) { return a * 2; } int foo2(int a, int b) { return a + b; } int foo3(int a, int b, int c) { return a + b + c; } int foo4(int a, int b, int c, int d) { return a + b + c + d; } int foo5(int a, int b, int c, int d, int e) { return a + b + c + d + e; } int foo6(int a, int b, int c, int d, int e, int f) { return a + b + c + d + e + f; } ||< ** caller.c (呼ぶ main() 側) #code|c|> #include extern int foo1(int a); extern int foo2(int a, int b); extern int foo3(int a, int b, int c); extern int foo4(int a, int b, int c, int d); extern int foo5(int a, int b, int c, int d, int e); extern int foo6(int a, int b, int c, int d, int e, int f); int __cdecl main(int argc, char *argv[]) { printf("foo1() = %d\n", foo1(10)); printf("foo2() = %d\n", foo2(10, 20)); printf("foo3() = %d\n", foo3(10, 20, 30)); printf("foo4() = %d\n", foo4(10, 20, 30, 40)); printf("foo5() = %d\n", foo5(10, 20, 30, 40, 50)); printf("foo6() = %d\n", foo6(10, 20, 30, 40, 50, 60)); return 0; } ||< * OpenWatcom Compiler(16bit) : 指定方法 : #block||> int __fortran foobar(); typedef int (__fortran *ptr)(); ||< : コンパイラオプション : "-ecr" : 装飾名 : 関数名の英字が全て大文字に変換される。 OpenWatcomのDOS用コンパイラ(wcc.exe)で今回のサンプルコードをコンパイルしたところ、次のような引数の渡し方になっていることが分かった。 - 左から4つまでの引数は、 ''左から順にAX, DX, BX, CXレジスタ経由で'' 渡される。 - 左から5つ目以降の引数は、 ''右から左へ'' スタックへ積まれる。 - スタックのクリーンアップは、関数(callee)側で "RET imm16" 命令により実現している。 AX, BXまで使われている点が驚きだが、コード中でEAXやEBXを使うようなより実際のコードの場合、どのように変化するかまでは不明。 コンパイル&リンク&実行 #pre||> > wcc -od -d0 -ecr callee.c > wcc -od -d0 -ecr caller.c > wcl -fe=fortran.exe caller.obj callee.obj > fortran.exe foo1() = 20 foo2() = 30 foo3() = 60 foo4() = 100 foo5() = 150 foo6() = 210 ||< アセンブラ生成 #pre||> > wdis -a -l=callee.asm callee.obj > wdis -a -l=caller.asm caller.obj ||< ** callee.c のアセンブラ出力(callee.asm) #pre||> .387 PUBLIC FOO1 PUBLIC FOO2 PUBLIC FOO3 PUBLIC FOO4 PUBLIC FOO5 PUBLIC FOO6 EXTRN __STK:BYTE EXTRN _small_code_:BYTE DGROUP GROUP CONST,CONST2,_DATA _TEXT SEGMENT BYTE PUBLIC USE16 'CODE' ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP FOO1: push ax mov ax,2 call near ptr __STK pop ax shl ax,1 ret FOO2: push ax mov ax,2 call near ptr __STK pop ax add ax,dx ret FOO3: push ax mov ax,2 call near ptr __STK pop ax add ax,dx add ax,bx ret FOO4: push ax mov ax,2 call near ptr __STK pop ax add ax,dx add ax,bx add ax,cx ret FOO5: push ax mov ax,4 call near ptr __STK pop ax push bp mov bp,sp add ax,dx add ax,bx add ax,cx add ax,word ptr 4[bp] pop bp ret 2 FOO6: push ax mov ax,4 call near ptr __STK pop ax push bp mov bp,sp add ax,dx add ax,bx add ax,cx add ax,word ptr 4[bp] add ax,word ptr 6[bp] pop bp ret 4 _TEXT ENDS CONST SEGMENT WORD PUBLIC USE16 'DATA' CONST ENDS CONST2 SEGMENT WORD PUBLIC USE16 'DATA' CONST2 ENDS _DATA SEGMENT WORD PUBLIC USE16 'DATA' _DATA ENDS END ||< ** caller.c のアセンブラ出力(caller.asm) #pre||> .387 PUBLIC main_ EXTRN __STK:BYTE EXTRN FOO1:BYTE EXTRN printf_:BYTE EXTRN FOO2:BYTE EXTRN FOO3:BYTE EXTRN FOO4:BYTE EXTRN FOO5:BYTE EXTRN FOO6:BYTE EXTRN __argc:BYTE EXTRN _small_code_:BYTE EXTRN _cstart_:BYTE DGROUP GROUP CONST,CONST2,_DATA _TEXT SEGMENT BYTE PUBLIC USE16 'CODE' ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP main_: push ax mov ax,0aH call near ptr __STK pop ax push bx push cx mov ax,0aH call near ptr FOO1 push ax mov ax,offset DGROUP:L$1 push ax call near ptr printf_ add sp,4 mov dx,14H mov ax,0aH call near ptr FOO2 push ax mov ax,offset DGROUP:L$2 push ax call near ptr printf_ add sp,4 mov bx,1eH mov dx,14H mov ax,0aH call near ptr FOO3 push ax mov ax,offset DGROUP:L$3 push ax call near ptr printf_ add sp,4 mov cx,28H mov bx,1eH mov dx,14H mov ax,0aH call near ptr FOO4 push ax mov ax,offset DGROUP:L$4 push ax call near ptr printf_ add sp,4 mov ax,32H push ax mov cx,28H mov bx,1eH mov dx,14H mov ax,0aH call near ptr FOO5 push ax mov ax,offset DGROUP:L$5 push ax call near ptr printf_ add sp,4 mov ax,3cH push ax mov ax,32H push ax mov cx,28H mov bx,1eH mov dx,14H mov ax,0aH call near ptr FOO6 push ax mov ax,offset DGROUP:L$6 push ax call near ptr printf_ add sp,4 xor ax,ax pop cx pop bx ret _TEXT ENDS CONST SEGMENT WORD PUBLIC USE16 'DATA' L$1: DB 66H, 6fH, 6fH, 31H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$2: DB 66H, 6fH, 6fH, 32H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$3: DB 66H, 6fH, 6fH, 33H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$4: DB 66H, 6fH, 6fH, 34H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$5: DB 66H, 6fH, 6fH, 35H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$6: DB 66H, 6fH, 6fH, 36H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 CONST ENDS CONST2 SEGMENT WORD PUBLIC USE16 'DATA' CONST2 ENDS _DATA SEGMENT WORD PUBLIC USE16 'DATA' _DATA ENDS END ||< * OpenWatcom Compiler(32bit) : 指定方法 : #block||> int __fortran foobar(); typedef int (__fortran *ptr)(); ||< : コンパイラオプション : "-ecr" : 装飾名 : 関数名の英字が全て大文字に変換される。 OpenWatcomのDOS用コンパイラ(wcc386.exe)で今回のサンプルコードをコンパイルしたところ、次のような引数の渡し方になっていることが分かった。 - 左から4つまでの引数は、 ''左から順にEAX, EDX, EBX, ECXレジスタ経由で'' 渡される。 - 左から5つ目以降の引数は、 ''右から左へ'' スタックへ積まれる。 - スタックのクリーンアップは、関数(callee)側で "RET imm16" 命令により実現している。 EAX, EBXまで使われている点が驚きだが、コード中でEAXやEBXを使うようなより実際のコードの場合、どのように変化するかまでは不明。 コンパイル&リンク&実行 #pre||> > wcc386 -od -d0 -ecr callee.c > wcc386 -od -d0 -ecr caller.c > wcl386 -fe=fortran.exe caller.obj callee.obj > fortran.exe foo1() = 20 foo2() = 30 foo3() = 60 foo4() = 100 foo5() = 150 foo6() = 210 ||< アセンブラ生成 #pre||> > wdis -a -l=callee.asm callee.obj > wdis -a -l=caller.asm caller.obj ||< ** callee.c のアセンブラ出力(callee.asm) #pre||> .387 .386p .model flat PUBLIC FOO1 PUBLIC FOO2 PUBLIC FOO3 PUBLIC FOO4 PUBLIC FOO5 PUBLIC FOO6 EXTRN __CHK:BYTE DGROUP GROUP CONST,CONST2,_DATA _TEXT SEGMENT BYTE PUBLIC USE32 'CODE' ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP FOO1: push 4 call near ptr FLAT:__CHK add eax,eax ret FOO2: push 4 call near ptr FLAT:__CHK add eax,edx ret FOO3: push 4 call near ptr FLAT:__CHK add eax,edx add eax,ebx ret FOO4: push 4 call near ptr FLAT:__CHK add eax,edx add eax,ebx add eax,ecx ret FOO5: push 4 call near ptr FLAT:__CHK add eax,edx add ebx,eax lea eax,[ebx+ecx] add eax,dword ptr 4[esp] ret 4 FOO6: push 4 call near ptr FLAT:__CHK add eax,edx add eax,ebx add eax,ecx add eax,dword ptr 4[esp] add eax,dword ptr 8[esp] ret 8 _TEXT ENDS CONST SEGMENT DWORD PUBLIC USE32 'DATA' CONST ENDS CONST2 SEGMENT DWORD PUBLIC USE32 'DATA' CONST2 ENDS _DATA SEGMENT DWORD PUBLIC USE32 'DATA' _DATA ENDS END ||< ** caller.c のアセンブラ出力(caller.asm) #pre||> .387 .386p .model flat PUBLIC main_ EXTRN __CHK:BYTE EXTRN FOO1:BYTE EXTRN printf_:BYTE EXTRN FOO2:BYTE EXTRN FOO3:BYTE EXTRN FOO4:BYTE EXTRN FOO5:BYTE EXTRN FOO6:BYTE EXTRN __argc:BYTE EXTRN _cstart_:BYTE DGROUP GROUP CONST,CONST2,_DATA _TEXT SEGMENT BYTE PUBLIC USE32 'CODE' ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP main_: push 14H call near ptr FLAT:__CHK push ebx push ecx mov eax,0aH call near ptr FLAT:FOO1 push eax push offset FLAT:L$1 call near ptr FLAT:printf_ add esp,8 mov edx,14H mov eax,0aH call near ptr FLAT:FOO2 push eax push offset FLAT:L$2 call near ptr FLAT:printf_ add esp,8 mov ebx,1eH mov edx,14H mov eax,0aH call near ptr FLAT:FOO3 push eax push offset FLAT:L$3 call near ptr FLAT:printf_ add esp,8 mov ecx,28H mov ebx,1eH mov edx,14H mov eax,0aH call near ptr FLAT:FOO4 push eax push offset FLAT:L$4 call near ptr FLAT:printf_ add esp,8 push 32H mov ecx,28H mov ebx,1eH mov edx,14H mov eax,0aH call near ptr FLAT:FOO5 push eax push offset FLAT:L$5 call near ptr FLAT:printf_ add esp,8 push 3cH push 32H mov ecx,28H mov ebx,1eH mov edx,14H mov eax,0aH call near ptr FLAT:FOO6 push eax push offset FLAT:L$6 call near ptr FLAT:printf_ add esp,8 xor eax,eax pop ecx pop ebx ret _TEXT ENDS CONST SEGMENT DWORD PUBLIC USE32 'DATA' L$1: DB 66H, 6fH, 6fH, 31H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$2: DB 66H, 6fH, 6fH, 32H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$3: DB 66H, 6fH, 6fH, 33H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$4: DB 66H, 6fH, 6fH, 34H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$5: DB 66H, 6fH, 6fH, 35H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$6: DB 66H, 6fH, 6fH, 36H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 CONST ENDS CONST2 SEGMENT DWORD PUBLIC USE32 'DATA' CONST2 ENDS _DATA SEGMENT DWORD PUBLIC USE32 'DATA' _DATA ENDS END ||< #navi_footer|C言語系|