Соглашение о вызовах __cdecl не работает на MSVC x64 - PullRequest
0 голосов
/ 10 апреля 2019

Просто тест для __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 2 и push 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
...

аргументы передаются регистром edx и ecx, а не стеком.

Так почему аргументы не передаются по стеку в x64, даже если я указываю __cdecl, и что мне делать, если я хочу делать то же самое в средах x64.

1 Ответ

3 голосов
/ 10 апреля 2019

x64 имеет свои собственные соглашения о вызовах.

Документы Microsoft __cdecl

На процессорах ARM и x64 __cdecl принимается, но обычно игнорируется компилятором. По соглашению для ARM и x64 аргументы передаются в регистрах, когда это возможно, и последующие аргументы передаются в стек. В коде x64 используйте __cdecl для переопределения параметра компилятора / Gv и используйте соглашение о вызовах x64 по умолчанию.

Microsoft docs x64 calling convention

Двоичный интерфейс приложения x64 (ABI) по умолчанию использует соглашение о быстрых вызовах из четырех регистров. Пространство выделяется в стеке вызовов как хранилище теней для вызываемых, чтобы сохранить эти регистры. Существует строгое однозначное соответствие между аргументами вызова функции и регистрами, используемыми для этих аргументов. Любой аргумент, который не помещается в 8 байтов или не имеет значения 1, 2, 4 или 8 байтов, должен передаваться по ссылке.

...

Целочисленные аргументы передаются в регистрах RCX, RDX, R8 и R9

Вы можете увидеть это, используя ECX и EDX для int a и int b (так как они 32-битные, тогда как полный RCX и RDX 64-битные).

__stdcall, __fastcall и __thiscall также игнорируются. __vectorcall доступен (параметр / Gv делает его по умолчанию) и является еще одним соглашением о вызовах регистров, но по сравнению с x64 по умолчанию он может использовать регистры в большем количестве случаев и имеет некоторые другие различия в правилах.

...