Microsoft の x64 規約は、MSVC、clang-cl、MinGW-w64 のどれでビルドされたかにかかわらず 64 ビット Windows 上のすべてのプログラムが使い、x64 マシンの UEFI ファームウェアも使います。Cygwin と MSYS2 の MSYS 環境も使いますが、これらは規約を保ちつつ long を 64 ビットにしています。Linux と macOS が使う System V x86-64 より単純で、位置で埋まる 4 つの引数スロットがあり、8 バイトより大きいものはレジスターに入りません。

このページのリストは、x86_64-pc-windows-msvc ターゲット向けの Clang と MinGW-w64 GCC を、どちらも -O2 で使ったものです。このターゲットの Clang は MSVC の ABI に従い、clang-cl が内部で使うコンパイラーです。各リストの横のボタンで AT&T 構文と Intel 構文を切り替えられます。

レジスター

最初の 4 つの引数は RCX、RDX、R8、R9 に入り、浮動小数点値なら XMM0 から XMM3 に入ります。関数は RAX、RCX、RDX、R8 から R11、XMM0 から XMM5 を変更して構いませんが、RBX、RBP、RDI、RSI、RSP、R12 から R15、XMM6 から XMM15 は保存しなければなりません。RDI、RSI と 6 つの XMM レジスターが呼び出し先保存であることが、2 つの規約が出会うときに最も重要になる System V との違いです。

Microsoft x64 ABI のレジスター
レジスター役割保存される
RCX、RDX、R8、R9整数、ポインター、小さな構造体の引数 1 から 4いいえ
XMM0 から XMM3浮動小数点の引数 1 から 4いいえ
RAX整数の戻り値いいえ
XMM0浮動小数点、ベクトル、__int128 の戻り値いいえ
R10、R11スクラッチ (scratch)いいえ
XMM4、XMM5スクラッチ。__vectorcall の引数 5 と 6いいえ
RBX、RDI、RSI、R12 から R15呼び出し先保存はい
RBP呼び出し先保存。関数がフレームポインターを使う場合はフレームポインターはい
RSPスタックポインターはい
XMM6 から XMM15呼び出し先保存 (下位 128 ビットのみ)はい
YMM0 から YMM15 の上位半分、ZMMスクラッチいいえ
XMM16 から XMM31 (AVX-512)スクラッチいいえ
R16 から R29 (APX)スクラッチいいえ
R30、R31 (APX)呼び出し先保存はい
TMM0 から TMM7 (AMX)スクラッチいいえ
ST0 から ST7、MM0 から MM7スクラッチ。引数には使わないいいえ

System V と同じく、関数は方向フラグをクリアしたままにし、MXCSR と x87 制御ワードの制御ビットを保ちます。それらのステータスビットはスクラッチです。

引数の渡し方

最初の 4 つの引数にはそれぞれスロットがあり、レジスターは位置だけで決まります。2 番目の引数は、最初の引数が何であっても、整数なら RDX に、double なら XMM1 に入ります。スロットのもう一方のレジスターは使われません。5 番目以降の引数は、右から左へ 8 バイトずつスタックに置かれます。

呼び出し元は、呼び出される関数の引数が 4 つより少ない場合でも、戻りアドレスの上に常に 32 バイトのシャドウスペース (shadow space) を確保します。これはホーム領域 (home space) とも呼ばれ、レジスター引数ごとに 8 バイトのホームが 1 つあります。この領域は呼び出された関数のもので、関数はそこにレジスター引数を格納しても、ほかの何を格納しても構いません。そのため、関数に入った時点で 5 番目の引数は、戻りアドレスとシャドウスペースの後の 40(%rsp) から始まります。

6 つの整数引数。sum6 は 5 番目と 6 番目をシャドウスペースの上の 40(%rsp) と 48(%rsp) に見つけます。call6 は 56 バイトを確保します。シャドウスペースに 32 バイト、2 つのスタック引数に 16 バイト、呼び出し時に RSP を 16 にアラインしておくために 8 バイトです。

win-int-args.c

/* Six integer arguments: four in registers, two on the stack above
   the 32-byte shadow space. */
long long sum6(long long a, long long b, long long c, long long d, long long e, long long f)
{
	return a + b + c + d + e + f;
}

extern long long take6(long long, long long, long long, long long, long long, long long);

long long call6(void)
{
	return take6(1, 2, 3, 4, 5, 6) + 1;
}

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S win-int-args.c

sum6:
        addq    %rdx, %rcx
        leaq    (%r8,%r9), %rax
        addq    %rcx, %rax
        addq    40(%rsp), %rax
        addq    48(%rsp), %rax
        retq

call6:
        subq    $56, %rsp
        movq    $6, 40(%rsp)
        movq    $5, 32(%rsp)
        movl    $1, %ecx
        movl    $2, %edx
        movl    $3, %r8d
        movl    $4, %r9d
        callq   take6
        incq    %rax
        addq    $56, %rsp
        retq

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-int-args.c

sum6:
        add     rcx, rdx
        lea     rax, [r8 + r9]
        add     rax, rcx
        add     rax, qword ptr [rsp + 40]
        add     rax, qword ptr [rsp + 48]
        ret

call6:
        sub     rsp, 56
        mov     qword ptr [rsp + 40], 6
        mov     qword ptr [rsp + 32], 5
        mov     ecx, 1
        mov     edx, 2
        mov     r8d, 3
        mov     r9d, 4
        call    take6
        inc     rax
        add     rsp, 56
        ret

MinGW-w64 GCC 14.2.0 (Debian 14.2.0-19+27+b1) x86_64-w64-mingw32

AT&T 構文 x86_64-w64-mingw32-gcc -O2 -fno-asynchronous-unwind-tables -S win-int-args.c

sum6:
        addq    %rdx, %rcx
        addq    %r8, %rcx
        leaq    (%rcx,%r9), %rax
        addq    40(%rsp), %rax
        addq    48(%rsp), %rax
        ret

call6:
        subq    $56, %rsp
        movl    $4, %r9d
        movl    $3, %r8d
        movl    $2, %edx
        movq    $6, 40(%rsp)
        movl    $1, %ecx
        movq    $5, 32(%rsp)
        call    take6
        addq    $56, %rsp
        addq    $1, %rax
        ret

Intel 構文 x86_64-w64-mingw32-gcc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-int-args.c

sum6:
        add     rcx, rdx
        add     rcx, r8
        lea     rax, [rcx+r9]
        add     rax, QWORD PTR 40[rsp]
        add     rax, QWORD PTR 48[rsp]
        ret

call6:
        sub     rsp, 56
        mov     r9d, 4
        mov     r8d, 3
        mov     edx, 2
        mov     QWORD PTR 40[rsp], 6
        mov     ecx, 1
        mov     QWORD PTR 32[rsp], 5
        call    take6
        add     rsp, 56
        add     rax, 1
        ret
位置によるスロット。a は ECX、b は XMM1、c は R8D、d は XMM3 に入り、RDX、XMM0、R9、XMM2 は使われません。e と f はスタックの 32(%rsp) と 40(%rsp) に置かれます。

win-mixed-args.c

/* The position decides the register: the second argument goes to
   RDX or XMM1, whatever came before it. */
extern double mix(int a, double b, int c, float d, char *e, double f);

double call_mix(char *p)
{
	return mix(1, 2.0, 3, 4.0f, p, 5.0) * 2;
}

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S win-mixed-args.c

__real@4000000000000000:
        .quad   0x4000000000000000

__real@40800000:
        .long   0x40800000

call_mix:
        subq    $56, %rsp
        movabsq $4617315517961601024, %rax
        movq    %rax, 40(%rsp)
        movq    %rcx, 32(%rsp)
        movsd   __real@4000000000000000(%rip), %xmm1
        movss   __real@40800000(%rip), %xmm3
        movl    $1, %ecx
        movl    $3, %r8d
        callq   mix
        addsd   %xmm0, %xmm0
        addq    $56, %rsp
        retq

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-mixed-args.c

__real@4000000000000000:
        .quad   0x4000000000000000

__real@40800000:
        .long   0x40800000

call_mix:
        sub     rsp, 56
        movabs  rax, 4617315517961601024
        mov     qword ptr [rsp + 40], rax
        mov     qword ptr [rsp + 32], rcx
        movsd   xmm1, qword ptr [rip + __real@4000000000000000]
        movss   xmm3, dword ptr [rip + __real@40800000]
        mov     ecx, 1
        mov     r8d, 3
        call    mix
        addsd   xmm0, xmm0
        add     rsp, 56
        ret

呼び出し先は、より小さい引数を保持するレジスターの上位ビットについて何も仮定してはなりません。ECX の int では、RCX の上位半分は未定義です。

構造体、ベクトル、その他の型

値がレジスターで渡されるのは、1、2、4 または 8 バイトの場合だけです。それ以外の構造体や共用体、__m128 ベクトル、16 バイト以上の値はすべて参照渡しです。呼び出し元はコピーを作り、そのアドレスを引数のスロットで渡します。

Microsoft x64 ABI での引数の型ごとの渡し方
C の型サイズ渡し方示すリスト
struct { int a, b; }8値を汎用レジスターでリスト 3
struct { int a, b, c; }12コピーへのポインターリスト 3
float、double4、8値を XMM レジスターでリスト 2
long double (MSVC、Clang)8値を double としてリスト 4
long double (MinGW-w64)16コピーへのポインターリスト 4
__int128 (Clang、GCC)16コピーへのポインターリスト 5
__m128 とその他の 16 バイトのベクトル16コピーへのポインターリスト 9
8 バイトの構造体は RCX で渡され、12 バイトの構造体は RCX のポインターとして渡されます。pass_triple は自分のフレームにコピーを作り、そのアドレスを渡します。

win-struct-args.c

/* Structs of 1, 2, 4 or 8 bytes travel like integers; any other size
   is passed as a pointer to a copy the caller makes. */
struct pair32 { int a, b; };		/* 8 bytes: in a register */
struct triple32 { int a, b, c; };	/* 12 bytes: by reference */

int pair_sum(struct pair32 p)
{
	return p.a + p.b;
}

int triple_sum(struct triple32 t)
{
	return t.a + t.b + t.c;
}

extern int take_triple(struct triple32 t);

int pass_triple(int x)
{
	struct triple32 t = { x, 2 * x, 3 * x };
	return take_triple(t) + 1;
}

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S win-struct-args.c

pair_sum:
        movq    %rcx, %rax
        shrq    $32, %rax
        addl    %ecx, %eax
        retq

triple_sum:
        movl    4(%rcx), %eax
        addl    (%rcx), %eax
        addl    8(%rcx), %eax
        retq

pass_triple:
        subq    $56, %rsp
        leal    (%rcx,%rcx), %eax
        leal    (%rcx,%rcx,2), %edx
        movl    %ecx, 44(%rsp)
        movl    %eax, 48(%rsp)
        movl    %edx, 52(%rsp)
        leaq    44(%rsp), %rcx
        callq   take_triple
        incl    %eax
        addq    $56, %rsp
        retq

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-struct-args.c

pair_sum:
        mov     rax, rcx
        shr     rax, 32
        add     eax, ecx
        ret

triple_sum:
        mov     eax, dword ptr [rcx + 4]
        add     eax, dword ptr [rcx]
        add     eax, dword ptr [rcx + 8]
        ret

pass_triple:
        sub     rsp, 56
        lea     eax, [rcx + rcx]
        lea     edx, [rcx + 2*rcx]
        mov     dword ptr [rsp + 44], ecx
        mov     dword ptr [rsp + 48], eax
        mov     dword ptr [rsp + 52], edx
        lea     rcx, [rsp + 44]
        call    take_triple
        inc     eax
        add     rsp, 56
        ret

MinGW-w64 GCC 14.2.0 (Debian 14.2.0-19+27+b1) x86_64-w64-mingw32

AT&T 構文 x86_64-w64-mingw32-gcc -O2 -fno-asynchronous-unwind-tables -S win-struct-args.c

pair_sum:
        movq    %rcx, %rax
        shrq    $32, %rax
        addl    %ecx, %eax
        ret

triple_sum:
        movl    4(%rcx), %eax
        addl    (%rcx), %eax
        addl    8(%rcx), %eax
        ret

pass_triple:
        pushq   %rbp
        leal    (%rcx,%rcx), %eax
        movq    %rsp, %rbp
        andq    $-16, %rsp
        subq    $64, %rsp
        movl    %ecx, 48(%rsp)
        movl    %eax, 52(%rsp)
        movq    48(%rsp), %rdx
        addl    %ecx, %eax
        leaq    32(%rsp), %rcx
        movl    %eax, 40(%rsp)
        movq    %rdx, 32(%rsp)
        call    take_triple
        leave
        addl    $1, %eax
        ret

Intel 構文 x86_64-w64-mingw32-gcc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-struct-args.c

pair_sum:
        mov     rax, rcx
        shr     rax, 32
        add     eax, ecx
        ret

triple_sum:
        mov     eax, DWORD PTR 4[rcx]
        add     eax, DWORD PTR [rcx]
        add     eax, DWORD PTR 8[rcx]
        ret

pass_triple:
        push    rbp
        lea     eax, [rcx+rcx]
        mov     rbp, rsp
        and     rsp, -16
        sub     rsp, 64
        mov     DWORD PTR 48[rsp], ecx
        mov     DWORD PTR 52[rsp], eax
        mov     rdx, QWORD PTR 48[rsp]
        add     eax, ecx
        lea     rcx, 32[rsp]
        mov     DWORD PTR 40[rsp], eax
        mov     QWORD PTR 32[rsp], rdx
        call    take_triple
        leave
        add     eax, 1
        ret

Microsoft は、呼び出し元のコピーを 16 バイトにアラインするよう求めています。MinGW-w64 GCC は andq $-16 で RSP を切り下げてアラインします。Clang は リスト 3 の 12 バイトのコピーを 44(%rsp) に置き、これは 4 バイトにしかアラインされていません。呼び出される関数は、型そのもののアラインメントより大きなアラインメントに依存すべきではありません。

long double を見ると、Windows の 2 つのツールチェーンの違いがわかります。MSVC はこれを double と同じ型にし、MSVC ターゲット向けの Clang も同様です。MinGW-w64 は GCC の x87 の 80 ビット形式を保ちます。この形式は 16 バイトなので、参照で渡され、参照で返されます。

MSVC ターゲットでは、long double は XMM0 の double です。MinGW-w64 GCC は値へのポインターを RDX で受け取り、RCX のポインターを通して結果を格納し、そのポインターを RAX で返します。

win-long-double.c

/* long double is double for MSVC, and the 80-bit x87 type for
   MinGW-w64, which passes it by reference. */
long double ld_twice(long double x)
{
	return x * 2;
}

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S win-long-double.c

ld_twice:
        addsd   %xmm0, %xmm0
        retq

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-long-double.c

ld_twice:
        addsd   xmm0, xmm0
        ret

MinGW-w64 GCC 14.2.0 (Debian 14.2.0-19+27+b1) x86_64-w64-mingw32

AT&T 構文 x86_64-w64-mingw32-gcc -O2 -fno-asynchronous-unwind-tables -S win-long-double.c

ld_twice:
        fldt    (%rdx)
        pushq   %rbp
        movq    %rcx, %rax
        fadd    %st(0), %st
        movq    %rsp, %rbp
        andq    $-16, %rsp
        fstpt   (%rcx)
        leave
        ret

Intel 構文 x86_64-w64-mingw32-gcc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-long-double.c

ld_twice:
        fld     TBYTE PTR [rdx]
        push    rbp
        mov     rax, rcx
        fadd    st, st(0)
        mov     rbp, rsp
        and     rsp, -16
        fstp    TBYTE PTR [rcx]
        leave
        ret
Windows では Clang と GCC だけが提供する __int128。両方の引数が参照渡しで、結果は XMM0 で返ります。

win-int128.c

/* __int128 (Clang and GCC only): by reference as an argument. */
__int128 add128(__int128 a, __int128 b)
{
	return a + b;
}

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S win-int128.c

add128:
        movq    (%rdx), %rax
        addq    (%rcx), %rax
        movq    8(%rdx), %rdx
        adcq    8(%rcx), %rdx
        movq    %rax, %xmm0
        movq    %rdx, %xmm1
        punpcklqdq      %xmm1, %xmm0
        retq

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-int128.c

add128:
        mov     rax, qword ptr [rdx]
        add     rax, qword ptr [rcx]
        mov     rdx, qword ptr [rdx + 8]
        adc     rdx, qword ptr [rcx + 8]
        movq    xmm0, rax
        movq    xmm1, rdx
        punpcklqdq      xmm0, xmm1
        ret

MinGW-w64 GCC 14.2.0 (Debian 14.2.0-19+27+b1) x86_64-w64-mingw32

AT&T 構文 x86_64-w64-mingw32-gcc -O2 -fno-asynchronous-unwind-tables -S win-int128.c

add128:
        pushq   %rbp
        movq    %rsp, %rbp
        andq    $-16, %rsp
        subq    $16, %rsp
        movq    (%rdx), %rax
        movq    8(%rdx), %rdx
        addq    (%rcx), %rax
        adcq    8(%rcx), %rdx
        movq    %rax, (%rsp)
        movq    %rdx, 8(%rsp)
        movdqu  (%rsp), %xmm0
        leave
        ret

Intel 構文 x86_64-w64-mingw32-gcc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-int128.c

add128:
        push    rbp
        mov     rbp, rsp
        and     rsp, -16
        sub     rsp, 16
        mov     rax, QWORD PTR [rdx]
        mov     rdx, QWORD PTR 8[rdx]
        add     rax, QWORD PTR [rcx]
        adc     rdx, QWORD PTR 8[rcx]
        mov     QWORD PTR [rsp], rax
        mov     QWORD PTR 8[rsp], rdx
        movdqu  xmm0, XMMWORD PTR [rsp]
        leave
        ret

値の返し方

整数、ポインター、1、2、4 または 8 バイトの構造体は RAX で返り、float、double、16 バイトのベクトルは XMM0 で返ります。それ以外のものについては呼び出し元がメモリを用意します。呼び出し元はそのアドレスを最初の隠し引数として RCX で渡し、本来の引数はスロット 1 つ分後ろにずれます。関数は同じアドレスを RAX で返します。C++ では、ユーザー定義のコンストラクター、デストラクター、またはコピー代入を持つ型は、サイズにかかわらず常にメモリを通して返ります。

8 バイトの構造体は ECX と EDX から組み立てられて RAX で返ります。12 バイトの構造体は RCX の指すメモリを通して返るので、引数 x は EDX で渡されます。

win-returns.c

/* A struct of 8 bytes returns in RAX; a larger one through memory the
   caller provides, whose address arrives in RCX. */
struct pair32 { int a, b; };
struct triple32 { int a, b, c; };

struct pair32 ret_pair(int a, int b)
{
	return (struct pair32){ a, b };
}

struct triple32 ret_triple(int x)
{
	return (struct triple32){ x, x, x };
}

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S win-returns.c

ret_pair:
        shlq    $32, %rdx
        movl    %ecx, %eax
        orq     %rdx, %rax
        retq

ret_triple:
        movq    %rcx, %rax
        movl    %edx, (%rcx)
        movl    %edx, 4(%rcx)
        movl    %edx, 8(%rcx)
        retq

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-returns.c

ret_pair:
        shl     rdx, 32
        mov     eax, ecx
        or      rax, rdx
        ret

ret_triple:
        mov     rax, rcx
        mov     dword ptr [rcx], edx
        mov     dword ptr [rcx + 4], edx
        mov     dword ptr [rcx + 8], edx
        ret

MinGW-w64 GCC 14.2.0 (Debian 14.2.0-19+27+b1) x86_64-w64-mingw32

AT&T 構文 x86_64-w64-mingw32-gcc -O2 -fno-asynchronous-unwind-tables -S win-returns.c

ret_pair:
        salq    $32, %rdx
        movl    %ecx, %eax
        orq     %rdx, %rax
        ret

ret_triple:
        movd    %edx, %xmm0
        movl    %edx, 8(%rcx)
        movq    %rcx, %rax
        pshufd  $0xe0, %xmm0, %xmm1
        movq    %xmm1, (%rcx)
        ret

Intel 構文 x86_64-w64-mingw32-gcc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-returns.c

ret_pair:
        sal     rdx, 32
        mov     eax, ecx
        or      rax, rdx
        ret

ret_triple:
        movd    xmm0, edx
        mov     DWORD PTR 8[rcx], edx
        mov     rax, rcx
        pshufd  xmm1, xmm0, 0xe0
        movq    QWORD PTR [rcx], xmm1
        ret

呼び出し先保存レジスター

RSI、RDI、XMM6 から XMM15 は、Windows のコードが呼び出しをまたいで保ち、System V のコードが保たないレジスターです。呼び出しを越えて残る必要がある値はこれらのどれかに置いておけ、それを使う関数は先にそのレジスターを保存します。XMM6 から XMM15 では下位 128 ビットだけが対象です。

x は f の呼び出しを越えて残る必要があるので、呼び出し先保存レジスターである XMM6 に移されます。keep は先に XMM6 を自分のフレームに保存します。

win-callee-saved.c

/* RSI, RDI and XMM6 to XMM15 are callee-saved on Windows, unlike in
   System V: a double that must survive a call can stay in XMM6. */
extern double f(double);

double keep(double x)
{
	return f(x) + x;
}

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S win-callee-saved.c

keep:
        subq    $56, %rsp
        movaps  %xmm6, 32(%rsp)
        movapd  %xmm0, %xmm6
        callq   f
        addsd   %xmm6, %xmm0
        movaps  32(%rsp), %xmm6
        addq    $56, %rsp
        retq

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-callee-saved.c

keep:
        sub     rsp, 56
        movaps  xmmword ptr [rsp + 32], xmm6
        movapd  xmm6, xmm0
        call    f
        addsd   xmm0, xmm6
        movaps  xmm6, xmmword ptr [rsp + 32]
        add     rsp, 56
        ret

MinGW-w64 GCC 14.2.0 (Debian 14.2.0-19+27+b1) x86_64-w64-mingw32

AT&T 構文 x86_64-w64-mingw32-gcc -O2 -fno-asynchronous-unwind-tables -S win-callee-saved.c

keep:
        subq    $56, %rsp
        movups  %xmm6, 32(%rsp)
        movupd  %xmm0, %xmm6
        call    f
        addsd   %xmm6, %xmm0
        movups  32(%rsp), %xmm6
        addq    $56, %rsp
        ret

Intel 構文 x86_64-w64-mingw32-gcc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-callee-saved.c

keep:
        sub     rsp, 56
        movups  XMMWORD PTR 32[rsp], xmm6
        movupd  xmm6, xmm0
        call    f
        addsd   xmm0, xmm6
        movups  xmm6, XMMWORD PTR 32[rsp]
        add     rsp, 56
        ret

スタック

RSP はすべての呼び出しの時点で 16 の倍数なので、関数に入った時点では 16 の倍数より 8 大きい値です。ほかの関数を呼び出す各関数は、フレームの最下部、つまりローカル変数より下で、自分が行う呼び出しの戻りアドレスのすぐ上に、パラメーター領域を確保します。これはシャドウスペースと、その関数が行う最大の呼び出しのスタック引数のための領域からなります。関数は呼び出しのたびに引数をプッシュするのではなく、リスト 1 の call6 のように、この領域をプロローグで一度だけ確保します。

レッドゾーン (red zone) はありません。RSP より下のメモリは、オペレーティングシステム、割り込み、デバッガーによっていつでも上書きされる可能性があるので、関数はスタックに何かを格納する前に RSP を動かします。System V のリーフ関数は RSP より下にデータを置きますが、Windows のリーフ関数は呼び出し元が用意したシャドウスペースを使います。

Microsoft は、関数がプロローグとエピローグで RSP を変更したりレジスターを保存したりする方法も制限しています。.pdata と .xdata のアンワインドデータがそれらを記述しなければならないためです。そのデータについてはアンワインドのページで説明しています。

可変長引数関数

可変長引数関数はほかの関数と同じように呼び出されますが、1 つ追加があります。4 つのレジスタースロットのどれかにある浮動小数点引数は、呼び出し先がその型を知ることができないので、その XMM レジスターと、対応する汎用レジスターの両方で渡されます。プロトタイプのない関数の呼び出しでも同じです。

残りはシャドウスペースのおかげで単純です。可変長引数関数は RDX、R8、R9 を、スタック引数のすぐ下にあるそれぞれのホームに格納するので、すべての引数が 8 バイトのスロットからなる 1 つの配列になります。va_list はその配列をたどる単純な char * で、va_arg は 8 バイトずつ取り出します。

log_msg は RDX、R8、R9 を 56(%rsp) 以上にあるそれぞれのホームに格納し、va_list はその最初を指します。call_double は x を XMM1 と RDX の両方で渡します。

win-varargs.c

/* A variadic function stores RDX, R8 and R9 in the shadow space, right
   below the stack arguments, and va_list is a plain pointer into it. A
   variadic call puts a double in both XMM1 and RDX. */
#include <stdarg.h>

extern int vlog(const char *fmt, va_list ap);

int log_msg(const char *fmt, ...)
{
	va_list ap;
	int r;

	va_start(ap, fmt);
	r = vlog(fmt, ap);
	va_end(ap);
	return r;
}

int call_double(double x)
{
	return log_msg("%f", x);
}

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S win-varargs.c

log_msg:
        subq    $40, %rsp
        movq    %rdx, 56(%rsp)
        movq    %r8, 64(%rsp)
        movq    %r9, 72(%rsp)
        leaq    56(%rsp), %rdx
        movq    %rdx, 32(%rsp)
        callq   vlog
        addq    $40, %rsp
        retq

call_double:
        subq    $40, %rsp
        leaq    "??_C@_02NJPGOMH@?$CFf?$AA@"(%rip), %rcx
        movdqa  %xmm0, %xmm1
        movq    %xmm0, %rdx
        callq   log_msg
        addq    $40, %rsp
        retq

"??_C@_02NJPGOMH@?$CFf?$AA@":
        .asciz  "%f"

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-varargs.c

log_msg:
        sub     rsp, 40
        mov     qword ptr [rsp + 56], rdx
        mov     qword ptr [rsp + 64], r8
        mov     qword ptr [rsp + 72], r9
        lea     rdx, [rsp + 56]
        mov     qword ptr [rsp + 32], rdx
        call    vlog
        add     rsp, 40
        ret

call_double:
        sub     rsp, 40
        lea     rcx, [rip + "??_C@_02NJPGOMH@?$CFf?$AA@"]
        movdqa  xmm1, xmm0
        movq    rdx, xmm0
        call    log_msg
        add     rsp, 40
        ret

"??_C@_02NJPGOMH@?$CFf?$AA@":
        .asciz  "%f"

MinGW-w64 GCC 14.2.0 (Debian 14.2.0-19+27+b1) x86_64-w64-mingw32

AT&T 構文 x86_64-w64-mingw32-gcc -O2 -fno-asynchronous-unwind-tables -S win-varargs.c

log_msg:
        subq    $56, %rsp
        movq    %rdx, 72(%rsp)
        leaq    72(%rsp), %rdx
        movq    %r8, 80(%rsp)
        movq    %r9, 88(%rsp)
        movq    %rdx, 40(%rsp)
        call    vlog
        addq    $56, %rsp
        ret

.LC0:
        .ascii "%f\0"

call_double:
        movq    %xmm0, %rdx
        movupd  %xmm0, %xmm1
        leaq    .LC0(%rip), %rcx
        jmp     log_msg

Intel 構文 x86_64-w64-mingw32-gcc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-varargs.c

log_msg:
        sub     rsp, 56
        mov     QWORD PTR 72[rsp], rdx
        lea     rdx, 72[rsp]
        mov     QWORD PTR 80[rsp], r8
        mov     QWORD PTR 88[rsp], r9
        mov     QWORD PTR 40[rsp], rdx
        call    vlog
        add     rsp, 56
        ret

.LC0:
        .ascii "%f\0"

call_double:
        movq    rdx, xmm0
        movupd  xmm1, xmm0
        lea     rcx, .LC0[rip]
        jmp     log_msg

__vectorcall

__vectorcall は、ベクトルコードのためにこの規約を拡張します。最初の 6 つの中の浮動小数点引数とベクトル引数は、XMM0 から XMM5 (__m256 なら YMM0 から YMM5) で値渡しされ、最大 4 つのベクトルからなる同種集合体 (homogeneous aggregate) は空いているベクトルレジスターを使えます。整数は 4 つのスロットを保ちます。__vectorcall 関数の名前は、@@ と引数のバイト単位のサイズで装飾されます。

2 つの 16 バイトのベクトル。デフォルトでは RCX と RDX で参照渡しされ、__vectorcall では XMM0 と XMM1 で値渡しされます。その場合、名前は add_vectorcall@@32 になります。

win-vectorcall.c

/* Vectors of 16 bytes are passed by reference by default; __vectorcall
   passes them in XMM registers. */
typedef float v4sf __attribute__((vector_size(16)));

v4sf add_default(v4sf a, v4sf b)
{
	return a + b;
}

v4sf __vectorcall add_vectorcall(v4sf a, v4sf b)
{
	return a + b;
}

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S win-vectorcall.c

add_default:
        movaps  (%rcx), %xmm0
        addps   (%rdx), %xmm0
        retq

add_vectorcall@@32:
        addps   %xmm1, %xmm0
        retq

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-vectorcall.c

add_default:
        movaps  xmm0, xmmword ptr [rcx]
        addps   xmm0, xmmword ptr [rdx]
        ret

add_vectorcall@@32:
        addps   xmm0, xmm1
        ret

Windows と System V のコードの間の呼び出し

GCC と Clang には、ターゲットにかかわらず 1 つの関数の規約を選ぶ 2 つの関数属性 ms_abi と sysv_abi があります。Wine は Linux で Windows のプログラムを動かすために ms_abi を使います。Wine のヘッダーは x86-64 で __stdcall を ms_abi として定義しているので、Wine が実装するすべての Windows API 関数も ms_abi になります。EDK II も同じ方法で、GCC と Clang で x64 の UEFI ファームウェアをビルドします。

Windows の方法で呼び出され、通常の System V のコードを呼び出す関数は、Windows の約束を守らなければなりません。RSI、RDI、XMM6 から XMM15 は、その関数の呼び出し元にとっては呼び出し先保存ですが、その関数が呼び出す関数にとってはスクラッチです。

Linux 上の ms_abi 関数。引数は RCX と RDX で渡され、System V の呼び出しのために RDI と RSI に移されます。関数はその呼び出しの前後で RSI、RDI、XMM6 から XMM15 の 176 バイトを保存します。

win-ms-abi.c

/* GCC's ms_abi attribute on Linux: this function receives its
   arguments the Windows way, then calls a System V function, which may
   change RSI, RDI and XMM6 to XMM15; the Windows caller expects them
   back, so they are saved and restored. */
extern long sysv_callee(long a, long b);

__attribute__((ms_abi)) long from_windows(long a, long b)
{
	return sysv_callee(a, b) + 1;
}

GCC 14.2.0 (Debian 14.2.0-19) x86_64-linux-gnu

AT&T 構文 gcc-14 -O2 -fno-asynchronous-unwind-tables -S win-ms-abi.c

from_windows:
        pushq   %rdi
        movq    %rcx, %rdi
        pushq   %rsi
        movq    %rdx, %rsi
        subq    $168, %rsp
        movaps  %xmm6, (%rsp)
        movaps  %xmm7, 16(%rsp)
        movaps  %xmm8, 32(%rsp)
        movaps  %xmm9, 48(%rsp)
        movaps  %xmm10, 64(%rsp)
        movaps  %xmm11, 80(%rsp)
        movaps  %xmm12, 96(%rsp)
        movaps  %xmm13, 112(%rsp)
        movaps  %xmm14, 128(%rsp)
        movaps  %xmm15, 144(%rsp)
        call    sysv_callee@PLT
        movaps  (%rsp), %xmm6
        movaps  16(%rsp), %xmm7
        movaps  32(%rsp), %xmm8
        movaps  48(%rsp), %xmm9
        addq    $1, %rax
        movaps  64(%rsp), %xmm10
        movaps  80(%rsp), %xmm11
        movaps  96(%rsp), %xmm12
        movaps  112(%rsp), %xmm13
        movaps  128(%rsp), %xmm14
        movaps  144(%rsp), %xmm15
        addq    $168, %rsp
        popq    %rsi
        popq    %rdi
        ret

Intel 構文 gcc-14 -O2 -fno-asynchronous-unwind-tables -masm=intel -S win-ms-abi.c

from_windows:
        push    rdi
        mov     rdi, rcx
        push    rsi
        mov     rsi, rdx
        sub     rsp, 168
        movaps  XMMWORD PTR [rsp], xmm6
        movaps  XMMWORD PTR 16[rsp], xmm7
        movaps  XMMWORD PTR 32[rsp], xmm8
        movaps  XMMWORD PTR 48[rsp], xmm9
        movaps  XMMWORD PTR 64[rsp], xmm10
        movaps  XMMWORD PTR 80[rsp], xmm11
        movaps  XMMWORD PTR 96[rsp], xmm12
        movaps  XMMWORD PTR 112[rsp], xmm13
        movaps  XMMWORD PTR 128[rsp], xmm14
        movaps  XMMWORD PTR 144[rsp], xmm15
        call    sysv_callee@PLT
        movaps  xmm6, XMMWORD PTR [rsp]
        movaps  xmm7, XMMWORD PTR 16[rsp]
        movaps  xmm8, XMMWORD PTR 32[rsp]
        movaps  xmm9, XMMWORD PTR 48[rsp]
        add     rax, 1
        movaps  xmm10, XMMWORD PTR 64[rsp]
        movaps  xmm11, XMMWORD PTR 80[rsp]
        movaps  xmm12, XMMWORD PTR 96[rsp]
        movaps  xmm13, XMMWORD PTR 112[rsp]
        movaps  xmm14, XMMWORD PTR 128[rsp]
        movaps  xmm15, XMMWORD PTR 144[rsp]
        add     rsp, 168
        pop     rsi
        pop     rdi
        ret

Cygwin、MSYS2、UEFI

Cygwin と、その上に構築された MSYS2 の MSYS 環境は、この呼び出し規約を Linux の LP64 データモデルとともに使います。long は 64 ビットです。リスト 11 では、long 値の diff が RCX と RDX 全体を操作します。

Windows、Cygwin、UEFI 向けの同じ関数。レジスターは同じで、コードを変えるのは Cygwin の 64 ビットの long だけです。Clang は UEFI ターゲットではフレームポインターを保持します。

every-abi.c

/* Two functions, compiled for every ABI: where do the arguments
   arrive, and where does the result go? */
long diff(long a, long b)
{
	return a - b;
}

double axpy(double a, double x, double y)
{
	return a * x + y;
}

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S every-abi.c

diff:
        movl    %ecx, %eax
        subl    %edx, %eax
        retq

axpy:
        mulsd   %xmm1, %xmm0
        addsd   %xmm2, %xmm0
        retq

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S every-abi.c

diff:
        mov     eax, ecx
        sub     eax, edx
        ret

axpy:
        mulsd   xmm0, xmm1
        addsd   xmm0, xmm2
        ret

Clang 23.1.2 x86_64-pc-cygwin

AT&T 構文 clang --target=x86_64-pc-cygwin -O2 -fno-asynchronous-unwind-tables -S every-abi.c

diff:
        movq    %rcx, %rax
        subq    %rdx, %rax
        retq

axpy:
        mulsd   %xmm1, %xmm0
        addsd   %xmm2, %xmm0
        retq

Intel 構文 clang --target=x86_64-pc-cygwin -O2 -fno-asynchronous-unwind-tables -masm=intel -S every-abi.c

diff:
        mov     rax, rcx
        sub     rax, rdx
        ret

axpy:
        mulsd   xmm0, xmm1
        addsd   xmm0, xmm2
        ret

Clang 23.1.2 x86_64-unknown-uefi

AT&T 構文 clang --target=x86_64-unknown-uefi -O2 -fno-asynchronous-unwind-tables -S every-abi.c

diff:
        pushq   %rbp
        movq    %rsp, %rbp
        movl    %ecx, %eax
        subl    %edx, %eax
        popq    %rbp
        retq

axpy:
        pushq   %rbp
        movq    %rsp, %rbp
        mulsd   %xmm1, %xmm0
        addsd   %xmm2, %xmm0
        popq    %rbp
        retq

Intel 構文 clang --target=x86_64-unknown-uefi -O2 -fno-asynchronous-unwind-tables -masm=intel -S every-abi.c

diff:
        push    rbp
        mov     rbp, rsp
        mov     eax, ecx
        sub     eax, edx
        pop     rbp
        ret

axpy:
        push    rbp
        mov     rbp, rsp
        mulsd   xmm0, xmm1
        addsd   xmm0, xmm2
        pop     rbp
        ret

x64 の UEFI ファームウェアは、ブートサービスとランタイムサービスをこの規約と LLP64 データモデルで呼び出します。これらすべてのプラットフォームでの C の型のサイズは ABI の概要にあります。

出典

  1. Microsoft Learn: x64 calling convention: コミット f70d88cd5da7、2026-09-24
  2. Microsoft Learn: Overview of x64 ABI conventions: コミット f70d88cd5da7、2026-09-24
  3. Microsoft Learn: x64 stack usage: コミット f70d88cd5da7、2026-09-24
  4. Microsoft Learn: __vectorcall: コミット f70d88cd5da7、2026-09-24
  5. GCC 14.2 manual: x86 Function Attributes: 2026-09-25 の公開版
  6. Wine 11.0: include/msvcrt/corecrt.h: コミット db11d0fe6a16、2026-01-13
  7. EDK II: BaseTools/Conf/tools_def.template: コミット 2970e5699ba6、2026-08-12
  8. Cygwin User's Guide: Building applications for 64 bit Cygwin: 2026-09-25 の公開版
  9. Clang 23.1.2: clang/lib/CodeGen/Targets/X86.cpp: コミット 85ac56026243、2026-09-20