x86 と x86-64 の呼び出し規約の比較
System V と Microsoft、64 ビットと 32 ビットを並べて示します。引数の表はコンパイラーが生成したコードから読み取ったもので、どの行からもそれを説明するページへ進めます。
x86 のコードのほぼすべてを 4 つの ABI がカバーしています。Linux、macOS、BSD 系向けの System V x86-64、Windows と UEFI 向けの Microsoft x64、そしてそれらの 32 ビット版である System V i386 と 32 ビット Windows の規約です。32 ビット Windows の規約のうち、表ではデフォルトの __cdecl を示しています。どのプラットフォームがどれを使うかは概要にあります。どの表でも、列はその ABI のページにリンクしています。
引数
表 1 と表 2 は手で入力したものではありません。小さな例の各関数が N 番目の引数を返すようにし、ビルド時に ABI ごとに、引数がどこから来るかを関数の最初の命令から読み取っています。スタックスロットは、関数に入った時点、つまりスタックポインターが戻りアドレスを指しているときのスタックポインターからの相対位置で示します。
int 引数が渡される場所 (GCC と Clang の出力から測定)| 引数 | System V x86-64 | Microsoft x64 | System V i386 | Windows x86 |
|---|---|---|---|---|
| 1 | RDI | RCX | ESP+4 | ESP+4 |
| 2 | RSI | RDX | ESP+8 | ESP+8 |
| 3 | RDX | R8 | ESP+12 | ESP+12 |
| 4 | RCX | R9 | ESP+16 | ESP+16 |
| 5 | R8 | RSP+40 | ESP+20 | ESP+20 |
| 6 | R9 | RSP+48 | ESP+24 | ESP+24 |
| 7 | RSP+8 | RSP+56 | ESP+28 | ESP+28 |
| 8 | RSP+16 | RSP+64 | ESP+32 | ESP+32 |
double 引数が渡される場所 (同じ方法で測定)| 引数 | System V x86-64 | Microsoft x64 | System V i386 | Windows x86 |
|---|---|---|---|---|
| 1 | XMM0 | XMM0 | ESP+4 | ESP+4 |
| 2 | XMM1 | XMM1 | ESP+12 | ESP+12 |
| 3 | XMM2 | XMM2 | ESP+20 | ESP+20 |
| 4 | XMM3 | XMM3 | ESP+28 | ESP+28 |
| 5 | XMM4 | RSP+40 | ESP+36 | ESP+36 |
| 6 | XMM5 | RSP+48 | ESP+44 | ESP+44 |
| 7 | XMM6 | RSP+56 | ESP+52 | ESP+52 |
| 8 | XMM7 | RSP+64 | ESP+60 | ESP+60 |
| 9 | RSP+8 | RSP+72 | ESP+68 | ESP+68 |
| 10 | RSP+16 | RSP+80 | ESP+76 | ESP+76 |
2 つの 64 ビット ABI の違いはレジスターだけではありません。System V は独立した 2 つの並びを持ちます。整数は 6 つの汎用レジスターのうち次のものを、浮動小数点値は 8 つのベクトルレジスターのうち次のものを、引数の順序に関係なく使います。Microsoft は最初の 4 つの位置それぞれに 1 つのスロットを割り当てます。2 番目の引数は、整数なら RDX に、double なら XMM1 に入ります。また Microsoft のスタック引数は、呼び出し元が確保するシャドウスペース (shadow space) の分だけ、32 バイト上から始まります。32 ビット x86 ではすべての引数がスタックにあり、double は 2 スロットを占めます。
int の後の double、もう一度は double の後の int です。System V では、その種類の最初のレジスターである XMM0 または EDI で渡されます。Microsoft x64 では、2 番目のスロットである XMM1 または EDX で渡されます。mixed-slots.c
/* Mixed arguments: System V fills integer and vector registers from
separate sequences, Microsoft x64 gives each position one slot. */
double second_double(int a, double b)
{
return b;
}
int second_int(double a, int b)
{
return b;
}
GCC 14.2.0 (Debian 14.2.0-19) x86_64-linux-gnu
AT&T 構文 gcc-14 -O2 -fno-asynchronous-unwind-tables -S mixed-slots.c
second_double:
ret
second_int:
movl %edi, %eax
retIntel 構文 gcc-14 -O2 -fno-asynchronous-unwind-tables -masm=intel -S mixed-slots.c
second_double:
ret
second_int:
mov eax, edi
retClang 23.1.2 x86_64-pc-windows-msvc
AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S mixed-slots.c
second_double:
movaps %xmm1, %xmm0
retq
second_int:
movl %edx, %eax
retqIntel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S mixed-slots.c
second_double:
movaps xmm0, xmm1
ret
second_int:
mov eax, edx
ret戻り値
| 値 | System V x86-64 | Microsoft x64 | System V i386 | Windows x86 |
|---|---|---|---|---|
| 整数とポインター | RAX | RAX | EAX | EAX |
| 64 ビット整数 | RAX | RAX | EDX:EAX | EDX:EAX |
float、double | XMM0 | XMM0 | ST0 | ST0 |
long double | ST0 | double として XMM0 (MSVC)、メモリ (MinGW-w64) | ST0 | ST0 |
| 16 バイトまでの構造体 | クラスに応じて RAX、RDX、XMM0、XMM1 | 1、2、4 または 8 バイトなら RAX、それ以外はメモリ | Linux はメモリ、FreeBSD と OpenBSD は 1、2、4 または 8 バイトなら EAX、EDX | 1、2、4 または 8 バイトなら EAX、EDX、それ以外はメモリ |
| より大きな構造体 | メモリ | メモリ | メモリ | メモリ |
| メモリのアドレス | RDI、RAX で返す | RCX、RAX で返す | スタック上、呼び出し先がポップ | スタック上、呼び出し元に任せる |
呼び出し先保存レジスター
呼び出し先保存レジスターは、関数から戻るときに、呼び出されたときと同じ値になっていなければなりません。関数はそのレジスターを使うなら、保存して復元します。ほかのレジスターはすべてスクラッチ (scratch) で、どの呼び出しでも変更される可能性があります。
| System V x86-64 | Microsoft x64 | System V i386 | Windows x86 | |
|---|---|---|---|---|
| 汎用 | RBX、RBP、R12 から R15 | RBX、RBP、RDI、RSI、R12 から R15 | EBX、ESI、EDI、EBP | EBX、ESI、EDI、EBP |
| ベクトル | なし | XMM6 から XMM15 の下位 128 ビット | なし | なし |
| APX (R16 から R31) | なし | R30、R31 | 32 ビットモードにはない | 32 ビットモードにはない |
| 制御状態 | x87 制御ワード、MXCSR の制御ビット | 同じ | 同じ | 文書化されていない |
最初の 2 列の違いは、ABI をまたいで呼び出すときのコストになります。System V のコードから呼び出された Windows の関数が上書きしてよいレジスターに、呼び出し元が保持を期待するものはありません。しかし Windows のコードから呼び出された System V の関数は、RSI、RDI、XMM6 から XMM15 を上書きすることがあり、呼び出し元はそれらが元のまま戻ることを期待しています。それらを保存するコードは Microsoft x64 のページにあります。
構造体、浮動小数点、ベクトル
| 引数 | System V x86-64 | Microsoft x64 | System V i386 | Windows x86 |
|---|---|---|---|---|
| 16 バイトまでの構造体 | eightbyte ごとに分類: 最大 2 つのレジスター (整数またはベクトル) | 1、2、4 または 8 バイトならレジスター、それ以外は参照渡し | スタック上に値渡し | スタック上に値渡し |
| より大きな構造体 | スタック上に値渡し | コピーへの参照渡し | スタック上に値渡し | スタック上に値渡し |
float、double | XMM0 から XMM7 のうち次のもの | XMM0 から XMM3 のうち自分のスロット | スタック上 | スタック上 |
long double | スタック上、16 バイト | double (MSVC)、参照渡し (MinGW-w64) | スタック上、12 バイト | double (MSVC)、スタック上 (MinGW-w64) |
__m128 | XMM レジスター | 参照渡し | 最初の 3 つは XMM0 から XMM2 | XMM レジスター |
__m256、__m512 | 名前付き引数で CPU にあれば YMM または ZMM レジスター | 参照渡し | 最初の 3 つは YMM0 から YMM2、または ZMM0 から ZMM2 | __vectorcall では値渡し |
__m128 のような 16 バイトのベクトル。Microsoft x64 以外ではどこでも XMM0 と XMM1 で渡されます。Microsoft x64 は両方を参照で、RCX と RDX で渡します。i386 向けの GCC には -msse2 を与えています。それがない場合のコンパイル対象である i686 には XMM レジスターがないためです。vector-args.c
/* A 16-byte vector, like SSE's __m128, as argument and result. */
typedef float v4sf __attribute__((vector_size(16)));
v4sf vadd(v4sf a, v4sf b)
{
return a + b;
}
GCC 14.2.0 (Debian 14.2.0-19) x86_64-linux-gnu
AT&T 構文 gcc-14 -O2 -fno-asynchronous-unwind-tables -S vector-args.c
vadd:
addps %xmm1, %xmm0
retIntel 構文 gcc-14 -O2 -fno-asynchronous-unwind-tables -masm=intel -S vector-args.c
vadd:
addps xmm0, xmm1
retClang 23.1.2 x86_64-pc-windows-msvc
AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S vector-args.c
vadd:
movaps (%rcx), %xmm0
addps (%rdx), %xmm0
retqIntel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S vector-args.c
vadd:
movaps xmm0, xmmword ptr [rcx]
addps xmm0, xmmword ptr [rdx]
retGCC 14.2.0 (Debian 14.2.0-19) i386-linux-gnu
AT&T 構文 gcc-14 -m32 -march=i686 -mtune=generic -O2 -fno-asynchronous-unwind-tables -msse2 -S vector-args.c
vadd:
addps %xmm1, %xmm0
retIntel 構文 gcc-14 -m32 -march=i686 -mtune=generic -O2 -fno-asynchronous-unwind-tables -msse2 -masm=intel -S vector-args.c
vadd:
addps xmm0, xmm1
retClang 23.1.2 i686-pc-windows-msvc
AT&T 構文 clang --target=i686-pc-windows-msvc -march=pentium4 -mtune=generic -O2 -fno-asynchronous-unwind-tables -S vector-args.c
_vadd:
addps %xmm1, %xmm0
retlIntel 構文 clang --target=i686-pc-windows-msvc -march=pentium4 -mtune=generic -O2 -fno-asynchronous-unwind-tables -masm=intel -S vector-args.c
_vadd:
addps xmm0, xmm1
retスタック
各 ABI で関数がフレームをどう配置するかは、スタックフレームのページにあります。
出典
- System V Application Binary Interface, AMD64 Architecture Processor Supplement, version 1.0: コミット
e1ce098331da、2025-03-12 - System V Application Binary Interface, Intel386 Architecture Processor Supplement, version 1.2: コミット
20ec676cd56d、2025-08-24 - Microsoft Learn: x64 calling convention: コミット
f70d88cd5da7、2026-09-24 - Microsoft Learn: Overview of x64 ABI conventions: コミット
f70d88cd5da7、2026-09-24 - Microsoft Learn: Argument passing and naming conventions: コミット
f70d88cd5da7、2026-09-24 - Microsoft Learn: __stdcall: コミット
f70d88cd5da7、2026-09-24