c++ - __cdecl 调用约定不适用于 msvc x64

标签 c++ windows visual-c++ calling-convention

只是测试 __cdecl 调用约定。

这是一个 cmake 项目,只有 1 个源文件:

#include <stdio.h>

#define CALL_CONVENTION __cdecl

void CALL_CONVENTION f(int a, int b)
{
    printf("%d, %d", a, b);
}

int main()
{
    f(1, 2);

    return 0;
}

我正在使用 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}/FA") 输出汇编代码。

当我使用 cmake -G "Visual Studio 15" 构建时,它构建了一个 32 位应用程序,一切都在预期之中:

...
; Line 12
    push    ebp
    mov ebp, esp
; Line 13
    push    2        ; <------- argument 2
    push    1        ; <------- argument 1
    call    _f       ; <------- call function
    add esp, 8
; Line 15
    xor eax, eax
; Line 16
    cmp ebp, esp
    call    __RTC_CheckEsp
    pop ebp
    ret 0
_main   ENDP
...

可以看到参数是通过push 2push 1指令传递的,是__cdecl调用约定。

但是如果我使用 cmake -G "Visual Studio 15 Win64" 构建 64 位应用程序,__cdecl 注释似乎不起作用(参数未通过堆栈传递):

...
; Line 12
$LN3:
    push    rdi
    sub rsp, 32                 ; 00000020H
    mov rdi, rsp
    mov ecx, 8
    mov eax, -858993460             ; ccccccccH
    rep stosd
; Line 13
    mov edx, 2        ; <------ argument 2
    mov ecx, 1        ; <------ argument 1
    call    f         ; <------ call function
; Line 15
    xor eax, eax
; Line 16
    add rsp, 32                 ; 00000020H
    pop rdi
    ret 0
main    ENDP
...

参数通过寄存器edxecx传递,不通过栈传递。

那么,即使我指定了 __cdecl,为什么在 x64 中参数没有通过堆栈传递,如果我想在 x64 环境中做同样的事情,我应该怎么做。

最佳答案

x64 有自己的调用约定。

Microsoft docs __cdecl

On ARM and x64 processors, __cdecl is accepted but typically ignored by the compiler. By convention on ARM and x64, arguments are passed in registers when possible, and subsequent arguments are passed on the stack. In x64 code, use __cdecl to override the /Gv compiler option and use the default x64 calling convention.

Microsoft docs x64 calling convention

The x64 Application Binary Interface (ABI) uses a four-register fast-call calling convention by default. Space is allocated on the call stack as a shadow store for callees to save those registers. There's a strict one-to-one correspondence between the arguments to a function call and the registers used for those arguments. Any argument that doesn’t fit in 8 bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference.

...

Integer arguments are passed in registers RCX, RDX, R8, and R9

您可以看到它对 int aint b 使用 ECX 和 EDX(因为它们是 32 位,而完整的 RCX 和 RDX 是 64 位)。

__stdcall , __fastcall__thiscall也被忽略。 __vectorcall可用(/Gv 开关使其成为默认值)并且是另一个寄存器调用约定,但与 x64 默认值相比,它可以在更多情况下使用寄存器并且有一些其他规则差异。

关于c++ - __cdecl 调用约定不适用于 msvc x64,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55612188/

相关文章:

java - Tomcat UTF-8 问题

c# - C++ 中的数组<T ^> ^T 是什么?如何从数组中提取值?

c++ - 使用 MSVC 2013 的正则表达式错误

c++ - 我可以在 std::move 之后重新使用像 std::vector 这样的复杂类吗?

c++ - MongoDB C++ 更新数组中的元素

C++17 支持 Eclipse Neon

c++ - 从类到 double 的隐式转换

c++ - printf/snprintf 格式字符 %N 有什么作用? (不是 %n)

Ruby OpenSSL 错误 - 缺少 CA 证书(Justin 是谁?)

windows - 在 Windows 上的 STDIN 上使用 IO::Select