使うのが最難関に近い呼び出し規約の__fastcallであった

C言語にはアセンブリコードに落とす際に、サブルーチンどうしでどうやって引数を受け渡してもらうかというのがあって、これを呼び出し規約と呼ぶ。一番使っていて簡単なのは__cdeclである。引数は一番目がesp+4で二番目がesp+8と連続していてとても扱いやすい。スタックをpushするたびにesp-=4されるのでespの値が変化することを考慮しなくてはならない。

その一方でどうやって使っていいかわからないものがある。それが__fastcallである。高速化に向かってまっしぐらな身としてはこんなネーミングはすごいと思う。なにせ速い呼び出しそのままなのですから。

で、いくつかの少ない情報を回った後、自分でアセンブリコードをアセンブラにかけてみて、C言語からどうやって使えばいいのかわかったのでここに書いておく。

  • fastcall.asm
;; /* __fastcall呼び出しアセンブラ使用 */
;; /* 2005/May/13 */
;; アセンブルのしかた
;; $ nasmw -f win32 fastcall.asm
;; extern "C" int __fastcall foo( int a, int b, int c );
[bits 32]

[section .text]
global @foo@12  ; @function name@decimal num bytes in parameters

;; __fastcall でも返り値はeaxに返すらしい
@foo@12:
	mov	eax, 0
	push	ebp
	mov	ebp,esp
	; a = ecx
	; b = edx
	; c = [ebp+8]
	; eax = return value
	add	eax, ecx
	add	eax, edx
	add	eax, [ebp+8]

	mov	esp,ebp
	pop	ebp
	ret  4       ; callee cleans up stack
  • fastcall.c
/* __fastcall呼び出しアセンブラ使用 */
/* 2005/May/13 */
/* コンパイルのしかた
   $ gcc fastcall.obj fastcall.c
 */

/* extern するかしないかわからないけどとりあえず宣言 */
int __fastcall foo(int i, int j, int k);

int main(void)
{
	puts("__fastcall 使用例");
	printf("foo(12, 34, 56) = %d\n", foo(12, 34, 56));
	return 0;
}