__fastcall呼び出し規約:
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; }
#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); 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)); return 0; }
int __fastcall foobar(); typedef int (__fastcall *ptr)();
コンパイル&リンク&実行
> cl /FAs /TC /Od /nologo /c /Gr /Focallee_fastcall.obj callee.c > cl /FAs /TC /Od /nologo /c /Gr /Focaller_fastcall.obj caller.c > link /SUBSYSTEM:CONSOLE /NOLOGO /OUT:fastcall.exe caller_fastcall.obj callee_fastcall.obj > fastcall.exe foo1() = 20 foo2() = 30 foo3() = 60 foo4() = 100
; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00.30729.01 TITLE C:\in_vitro\c\calling_conventions\callee.c .686P .XMM include listing.inc .model flat INCLUDELIB LIBCMT INCLUDELIB OLDNAMES PUBLIC @foo1@4 ; Function compile flags: /Odtp ; File c:\in_vitro\c\calling_conventions\callee.c _TEXT SEGMENT _a$ = -4 ; size = 4 @foo1@4 PROC ; _a$ = ecx ; 1 : int foo1(int a) { return a * 2; } push ebp mov ebp, esp push ecx mov DWORD PTR _a$[ebp], ecx mov eax, DWORD PTR _a$[ebp] shl eax, 1 mov esp, ebp pop ebp ret 0 @foo1@4 ENDP _TEXT ENDS PUBLIC @foo2@8 ; Function compile flags: /Odtp _TEXT SEGMENT _b$ = -8 ; size = 4 _a$ = -4 ; size = 4 @foo2@8 PROC ; _a$ = ecx ; _b$ = edx ; 2 : int foo2(int a, int b) { return a + b; } push ebp mov ebp, esp sub esp, 8 mov DWORD PTR _b$[ebp], edx mov DWORD PTR _a$[ebp], ecx mov eax, DWORD PTR _a$[ebp] add eax, DWORD PTR _b$[ebp] mov esp, ebp pop ebp ret 0 @foo2@8 ENDP _TEXT ENDS PUBLIC @foo3@12 ; Function compile flags: /Odtp _TEXT SEGMENT _b$ = -8 ; size = 4 _a$ = -4 ; size = 4 _c$ = 8 ; size = 4 @foo3@12 PROC ; _a$ = ecx ; _b$ = edx ; 3 : int foo3(int a, int b, int c) { return a + b + c; } push ebp mov ebp, esp sub esp, 8 mov DWORD PTR _b$[ebp], edx mov DWORD PTR _a$[ebp], ecx mov eax, DWORD PTR _a$[ebp] add eax, DWORD PTR _b$[ebp] add eax, DWORD PTR _c$[ebp] mov esp, ebp pop ebp ret 4 @foo3@12 ENDP _TEXT ENDS PUBLIC @foo4@16 ; Function compile flags: /Odtp _TEXT SEGMENT _b$ = -8 ; size = 4 _a$ = -4 ; size = 4 _c$ = 8 ; size = 4 _d$ = 12 ; size = 4 @foo4@16 PROC ; _a$ = ecx ; _b$ = edx ; 4 : int foo4(int a, int b, int c, int d) { return a + b + c + d; } push ebp mov ebp, esp sub esp, 8 mov DWORD PTR _b$[ebp], edx mov DWORD PTR _a$[ebp], ecx mov eax, DWORD PTR _a$[ebp] add eax, DWORD PTR _b$[ebp] add eax, DWORD PTR _c$[ebp] add eax, DWORD PTR _d$[ebp] mov esp, ebp pop ebp ret 8 @foo4@16 ENDP _TEXT ENDS END
; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00.30729.01 TITLE C:\in_vitro\c\calling_conventions\caller.c .686P .XMM include listing.inc .model flat INCLUDELIB LIBCMT INCLUDELIB OLDNAMES _DATA SEGMENT $SG2496 DB 'foo1() = %d', 0aH, 00H ORG $+3 $SG2497 DB 'foo2() = %d', 0aH, 00H ORG $+3 $SG2498 DB 'foo3() = %d', 0aH, 00H ORG $+3 $SG2499 DB 'foo4() = %d', 0aH, 00H _DATA ENDS PUBLIC _main EXTRN @foo4@16:PROC EXTRN @foo3@12:PROC EXTRN @foo2@8:PROC EXTRN _printf:PROC EXTRN @foo1@4:PROC ; Function compile flags: /Odtp ; File c:\in_vitro\c\calling_conventions\caller.c _TEXT SEGMENT _argc$ = 8 ; size = 4 _argv$ = 12 ; size = 4 _main PROC ; 9 : { push ebp mov ebp, esp ; 10 : printf("foo1() = %d\n", foo1(10)); mov ecx, 10 ; 0000000aH call @foo1@4 push eax push OFFSET $SG2496 call _printf add esp, 8 ; 11 : printf("foo2() = %d\n", foo2(10, 20)); mov edx, 20 ; 00000014H mov ecx, 10 ; 0000000aH call @foo2@8 push eax push OFFSET $SG2497 call _printf add esp, 8 ; 12 : printf("foo3() = %d\n", foo3(10, 20, 30)); push 30 ; 0000001eH mov edx, 20 ; 00000014H mov ecx, 10 ; 0000000aH call @foo3@12 push eax push OFFSET $SG2498 call _printf add esp, 8 ; 13 : printf("foo4() = %d\n", foo4(10, 20, 30, 40)); push 40 ; 00000028H push 30 ; 0000001eH mov edx, 20 ; 00000014H mov ecx, 10 ; 0000000aH call @foo4@16 push eax push OFFSET $SG2499 call _printf add esp, 8 ; 14 : return 0; xor eax, eax ; 15 : } pop ebp ret 0 _main ENDP _TEXT ENDS END
int __fastcall foobar(); typedef int (__fastcall *ptr)();
アセンブラコードを生成
> bcc32 -Od -pr -S callee.c > bcc32 -Od -pr -S caller.c
コンパイル&リンク&実行
> bcc32 -Od -pr -c -ocallee_fastcall.obj callee.c > bcc32 -Od -pr -c -ocaller_fastcall.obj caller.c > ilink32 /c /ap /Tpe c0x32 caller_fastcall callee_fastcall, fastcall, , cw32 import32 > fastcall.exe foo1() = 20 foo2() = 30 foo3() = 60 foo4() = 100
.386p ifdef ??version if ??version GT 500H .mmx endif endif model flat ifndef ??version ?debug macro endm endif ?debug S "callee.c" ?debug T "callee.c" _TEXT segment dword public use32 'CODE' _TEXT ends _DATA segment dword public use32 'DATA' _DATA ends _BSS segment dword public use32 'BSS' _BSS ends DGROUP group _BSS,_DATA _TEXT segment dword public use32 'CODE' @foo1 proc near ?live1@0: ; ; int foo1(int a) { return a * 2; } ; push ebp mov ebp,esp push ecx mov dword ptr [ebp-4],eax @1: mov eax,dword ptr [ebp-4] add eax,eax @3: @2: pop ecx pop ebp ret @foo1 endp @foo2 proc near ?live1@32: ; ; int foo2(int a, int b) { return a + b; } ; push ebp mov ebp,esp add esp,-8 mov dword ptr [ebp-8],edx mov dword ptr [ebp-4],eax @4: mov eax,dword ptr [ebp-4] add eax,dword ptr [ebp-8] @6: @5: pop ecx pop ecx pop ebp ret @foo2 endp @foo3 proc near ?live1@64: ; ; int foo3(int a, int b, int c) { return a + b + c; } ; push ebp mov ebp,esp add esp,-12 mov dword ptr [ebp-12],ecx mov dword ptr [ebp-8],edx mov dword ptr [ebp-4],eax @7: mov eax,dword ptr [ebp-4] add eax,dword ptr [ebp-8] add eax,dword ptr [ebp-12] @9: @8: mov esp,ebp pop ebp ret @foo3 endp @foo4 proc near ?live1@96: ; ; int foo4(int a, int b, int c, int d) { return a + b + c + d; } ; push ebp mov ebp,esp add esp,-12 mov dword ptr [ebp-12],ecx mov dword ptr [ebp-8],edx mov dword ptr [ebp-4],eax @10: mov eax,dword ptr [ebp-4] add eax,dword ptr [ebp-8] add eax,dword ptr [ebp-12] add eax,dword ptr [ebp+8] @12: @11: mov esp,ebp pop ebp ret 4 @foo4 endp _TEXT ends public @foo1 public @foo2 public @foo3 public @foo4 ?debug D "callee.c" 15473 24064 end
.386p ifdef ??version if ??version GT 500H .mmx endif endif model flat ifndef ??version ?debug macro endm endif ?debug S "caller.c" ?debug T "caller.c" _TEXT segment dword public use32 'CODE' _TEXT ends _DATA segment dword public use32 'DATA' _DATA ends _BSS segment dword public use32 'BSS' _BSS ends DGROUP group _BSS,_DATA _TEXT segment dword public use32 'CODE' _main proc near ?live1@0: ; ; int __cdecl main(int argc, char *argv[]) ; push ebp mov ebp,esp ; ; { ; printf("foo1() = %d\n", foo1(10)); ; @1: mov eax,10 call @foo1 push eax push offset s@ call _printf add esp,8 ; ; printf("foo2() = %d\n", foo2(10, 20)); ; mov edx,20 mov eax,10 call @foo2 push eax push offset s@+13 call _printf add esp,8 ; ; printf("foo3() = %d\n", foo3(10, 20, 30)); ; mov ecx,30 mov edx,20 mov eax,10 call @foo3 push eax push offset s@+26 call _printf add esp,8 ; ; printf("foo4() = %d\n", foo4(10, 20, 30, 40)); ; push 40 mov ecx,30 mov edx,20 mov eax,10 call @foo4 push eax push offset s@+39 call _printf add esp,8 ; ; return 0; ; xor eax,eax ; ; } ; @3: @2: pop ebp ret _main endp _TEXT ends _DATA segment dword public use32 'DATA' s@ label byte ; s@+0: db "foo1() = %d",10,0 ; s@+13: db "foo2() = %d",10,0 ; s@+26: db "foo3() = %d",10,0 ; s@+39: db "foo4() = %d",10,0 align 4 _DATA ends _TEXT segment dword public use32 'CODE' _TEXT ends public _main extrn __setargv__:near extrn _printf:near extrn @foo1:near extrn @foo2:near extrn @foo3:near extrn @foo4:near ?debug D "C:\in_vitro\apps\borland\bcc55\include\_nfile.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\_null.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\_defs.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\_stddef.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\stdio.h" 10339 10240 ?debug D "caller.c" 15473 24232 end
int CALLINGCONV foo(int a, int b, int c, int d, int e) { return a + b + c + d + e; } int bar() { return foo(10, 20, 30, 40, 50); }
アセンブラコードを生成
>bcc32 -S -DCALLINGCONV=__fastcall foo.c
foo.asmのfoo()呼び出し部分を見てみると次のようになっている。
push 40 push 50 mov ecx,30 mov edx,20 mov eax,10 call @foo
順番を変えれば、「左→右」に EAX, EDX, ECX, スタックの順に詰まれていくのが分かる。
mov eax,10 mov edx,20 mov ecx,30 push 40 push 50 call @foo
foo.asm全体:
.386p ifdef ??version if ??version GT 500H .mmx endif endif model flat ifndef ??version ?debug macro endm endif ?debug S "foo.c" ?debug T "foo.c" _TEXT segment dword public use32 'CODE' _TEXT ends _DATA segment dword public use32 'DATA' _DATA ends _BSS segment dword public use32 'BSS' _BSS ends DGROUP group _BSS,_DATA _TEXT segment dword public use32 'CODE' @foo proc near ?live1@0: ; ; int CALLINGCONV foo(int a, int b, int c, int d, int e) { return a + b + c + d + e; } ; push ebp mov ebp,esp @1: add edx,eax add ecx,edx add ecx,dword ptr [ebp+12] add ecx,dword ptr [ebp+8] mov eax,ecx @3: @2: pop ebp ret 8 @foo endp _bar proc near ?live1@32: ; ; int bar() { ; push ebp mov ebp,esp ; ; return foo(10, 20, 30, 40, 50); ; @4: push 40 push 50 mov ecx,30 mov edx,20 mov eax,10 call @foo ; ; } ; @6: @5: pop ebp ret _bar endp _TEXT ends public @foo public _bar ?debug D "foo.c" 15473 33047 end
装飾名や引数渡しに使うレジスタ、スタックPUSH方向などはVC++2008と同じ。
int __msfastcall foobar(); typedef int (__msfastcall *ptr)();
.386p ifdef ??version if ??version GT 500H .mmx endif endif model flat ifndef ??version ?debug macro endm endif ?debug S "callee.c" ?debug T "callee.c" _TEXT segment dword public use32 'CODE' _TEXT ends _DATA segment dword public use32 'DATA' _DATA ends _BSS segment dword public use32 'BSS' _BSS ends DGROUP group _BSS,_DATA _TEXT segment dword public use32 'CODE' @foo1@4 proc near ?live1@0: ; ; int foo1(int a) { return a * 2; } ; push ebp mov ebp,esp push ecx mov dword ptr [ebp-4],ecx @1: mov eax,dword ptr [ebp-4] add eax,eax @3: @2: pop ecx pop ebp ret @foo1@4 endp @foo2@8 proc near ?live1@32: ; ; int foo2(int a, int b) { return a + b; } ; push ebp mov ebp,esp add esp,-8 mov dword ptr [ebp-8],edx mov dword ptr [ebp-4],ecx @4: mov eax,dword ptr [ebp-4] add eax,dword ptr [ebp-8] @6: @5: pop ecx pop ecx pop ebp ret @foo2@8 endp @foo3@12 proc near ?live1@64: ; ; int foo3(int a, int b, int c) { return a + b + c; } ; push ebp mov ebp,esp add esp,-8 mov dword ptr [ebp-8],edx mov dword ptr [ebp-4],ecx @7: mov eax,dword ptr [ebp-4] add eax,dword ptr [ebp-8] add eax,dword ptr [ebp+8] @9: @8: pop ecx pop ecx pop ebp ret 4 @foo3@12 endp @foo4@16 proc near ?live1@96: ; ; int foo4(int a, int b, int c, int d) { return a + b + c + d; } ; push ebp mov ebp,esp add esp,-8 mov dword ptr [ebp-8],edx mov dword ptr [ebp-4],ecx @10: mov eax,dword ptr [ebp-4] add eax,dword ptr [ebp-8] add eax,dword ptr [ebp+8] add eax,dword ptr [ebp+12] @12: @11: pop ecx pop ecx pop ebp ret 8 @foo4@16 endp _TEXT ends public @foo1@4 public @foo2@8 public @foo3@12 public @foo4@16 ?debug D "callee.c" 15473 24064 end
.386p ifdef ??version if ??version GT 500H .mmx endif endif model flat ifndef ??version ?debug macro endm endif ?debug S "caller.c" ?debug T "caller.c" _TEXT segment dword public use32 'CODE' _TEXT ends _DATA segment dword public use32 'DATA' _DATA ends _BSS segment dword public use32 'BSS' _BSS ends DGROUP group _BSS,_DATA _TEXT segment dword public use32 'CODE' _main proc near ?live1@0: ; ; int __cdecl main(int argc, char *argv[]) ; push ebp mov ebp,esp ; ; { ; printf("foo1() = %d\n", foo1(10)); ; @1: mov ecx,10 call @foo1@4 push eax push offset s@ call _printf add esp,8 ; ; printf("foo2() = %d\n", foo2(10, 20)); ; mov edx,20 mov ecx,10 call @foo2@8 push eax push offset s@+13 call _printf add esp,8 ; ; printf("foo3() = %d\n", foo3(10, 20, 30)); ; push 30 mov edx,20 mov ecx,10 call @foo3@12 push eax push offset s@+26 call _printf add esp,8 ; ; printf("foo4() = %d\n", foo4(10, 20, 30, 40)); ; push 40 push 30 mov edx,20 mov ecx,10 call @foo4@16 push eax push offset s@+39 call _printf add esp,8 ; ; return 0; ; xor eax,eax ; ; } ; @3: @2: pop ebp ret _main endp _TEXT ends _DATA segment dword public use32 'DATA' s@ label byte ; s@+0: db "foo1() = %d",10,0 ; s@+13: db "foo2() = %d",10,0 ; s@+26: db "foo3() = %d",10,0 ; s@+39: db "foo4() = %d",10,0 align 4 _DATA ends _TEXT segment dword public use32 'CODE' _TEXT ends public _main extrn __setargv__:near extrn _printf:near extrn @foo1@4:near extrn @foo2@8:near extrn @foo3@12:near extrn @foo4@16:near ?debug D "C:\in_vitro\apps\borland\bcc55\include\_nfile.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\_null.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\_defs.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\_stddef.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\stdio.h" 10339 10240 ?debug D "caller.c" 15473 24232 end