2010年現在、OpenWatcomでサポートされている。
2010年の時点のMSDNによると、現在は "__fortran" 呼び出し規約はサポートされていない。(obsoleted)
ただし、サポートされていないのはあくまでも "__fortran" という宣言であり、次のMSDNを見る限りでは "__stdcall" と "__cdecl" が Fortran <> C,C++ との間で使えるようである。
"__cdecl", "__stdcall" 共に引数を渡す順序は "右→左" である。ではFortran <> C,C++間はそれでOKかと思いきや、以下の記事では「"__pascal"呼び出し規約と同じ」とあり、これだけ見ると「左→右」の順序と読み取れる。
MSDNの記事の場合は"Visual Studio 6.0"時代の内容であるし、"The Old New Thing"とともにFortran処理系を特に指定していない。従って、実際にどのFortranでどの呼び出し規約をC,C++との間で使えるのかは、手持ちの資料だけでは正確には何も言えない。
2010-12-24追記:
"The Old New Thing"からFORTRANとCの相互運用について新しい記事が登場した。FORTRANでは関数の引数をリファレンス渡しにする必要があったり、配列のインデックスが1始まりになっていたり、複素数の扱い方などが紹介されている。
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; }
#include <stdio.h> 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; }
int __fortran foobar(); typedef int (__fortran *ptr)();
OpenWatcomのDOS用コンパイラ(wcc.exe)で今回のサンプルコードをコンパイルしたところ、次のような引数の渡し方になっていることが分かった。
AX, BXまで使われている点が驚きだが、コード中でEAXやEBXを使うようなより実際のコードの場合、どのように変化するかまでは不明。
コンパイル&リンク&実行
> 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
アセンブラ生成
> wdis -a -l=callee.asm callee.obj > wdis -a -l=caller.asm caller.obj
.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
.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
int __fortran foobar(); typedef int (__fortran *ptr)();
OpenWatcomのDOS用コンパイラ(wcc386.exe)で今回のサンプルコードをコンパイルしたところ、次のような引数の渡し方になっていることが分かった。
EAX, EBXまで使われている点が驚きだが、コード中でEAXやEBXを使うようなより実際のコードの場合、どのように変化するかまでは不明。
コンパイル&リンク&実行
> 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
アセンブラ生成
> wdis -a -l=callee.asm callee.obj > wdis -a -l=caller.asm caller.obj
.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
.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