Простой ответ: Я использую cdecl, stdcall и fastcall. Я редко пользуюсь fastcall. stdcall используется для вызова функций Windows API.
Подробный ответ (украдено из Википедия ):
cdecl - В cdecl аргументы подпрограммы передаются в стек. Целочисленные значения и адреса памяти возвращаются в регистре EAX, значения с плавающей запятой в регистре ST0 x87. Регистры EAX, ECX и EDX сохраняются вызывающим абонентом, а остальные сохраняются вызываемым абонентом. Регистры с плавающей запятой x87 ST0-ST7 должны быть пустыми (выдвинуты или освобождены) при вызове новой функции, а ST1-ST7 должны быть пустыми при выходе из функции. ST0 также должен быть пустым, если он не используется для возврата значения.
syscall - Это похоже на cdecl в том, что аргументы передаются справа налево. EAX, ECX и EDX не сохраняются. Размер списка параметров в двойных словах передается в AL.
паскаль - параметры помещаются в стек в порядке слева направо (противоположно cdecl), и вызываемый абонент отвечает за балансировку стека перед возвратом.
stdcall - Соглашение о вызовах stdcall [4] представляет собой вариант соглашения о вызовах Pascal, в котором вызываемый объект отвечает за очистку стека, но параметры помещаются в стек справа от в левом порядке, как в соглашении о вызовах _cdecl. Регистры EAX, ECX и EDX предназначены для использования внутри функции. Возвращаемые значения хранятся в регистре EAX.
fastcall - Соглашение __fastcall (также известное как __msfastcall) передает первые два аргумента (вычисляются слева направо), которые соответствуют ECX и EDX. Остальные аргументы помещаются в стек справа налево.
vectorcall - В Visual Studio 2013 Microsoft представила соглашение о вызовах __vectorcall в ответ на проблемы эффективности со стороны разработчиков игр, графики, видео / аудио и кодеков. [7] Для кода IA-32 и x64 __vectorcall аналогичен __fastcall и исходным соглашениям о вызовах x64 соответственно, но расширяет их для поддержки передачи векторных аргументов с использованием регистров SIMD. Для x64, когда любой из первых шести аргументов является векторным типом (float, double, __m128, __m256 и т. Д.), Они передаются через соответствующие регистры XMM / YMM. Аналогично для IA-32, до шести регистров XMM / YMM распределяются последовательно для аргументов векторного типа слева направо независимо от положения. Кроме того, __vectorcall добавляет поддержку для передачи значений однородного векторного агрегата (HVA), которые являются составными типами, состоящими только из четырех идентичных векторных типов, с использованием одних и тех же шести регистров. После того, как регистры были выделены для аргументов векторного типа, неиспользуемые регистры назначаются для аргументов HVA слева направо независимо от положения. Результирующий тип вектора и значения HVA возвращаются с использованием первых четырех регистров XMM / YMM.
safecall - n Delphi и Free Pascal в Microsoft Windows, соглашение о вызовах safecall инкапсулирует обработку ошибок COM (объектная модель компонентов), поэтому исключения не просачиваются вызывающей стороне, а сообщаются в Возвращаемое значение HRESULT, как требуется COM / OLE. При вызове функции safecall из кода Delphi Delphi также автоматически проверяет возвращенный HRESULT и при необходимости вызывает исключение.
Соглашение о вызовах safecall такое же, как и соглашение о вызовах stdcall, за исключением того, что исключения передаются вызывающей стороне в EAX как HResult (а не в FS: [0]), а результат функции передается по ссылке на стек, как если бы это был последний параметр «out». При вызове функции Delphi из Delphi это соглашение о вызовах будет выглядеть так же, как и любое другое соглашение о вызовах, поскольку, хотя исключения передаются обратно в EAX, они автоматически преобразуются вызывающей стороной в надлежащие исключения. При использовании COM-объектов, созданных на других языках, HResults будут автоматически вызываться как исключения, и результат для функций Get будет результатом, а не параметром. При создании COM-объектов в Delphi с помощью safecall не нужно беспокоиться о HResults, поскольку исключения можно вызывать как обычно, но они будут рассматриваться как HResults на других языках.
Соглашение о вызовах Microsoft X64 - Соглашение о вызовах Microsoft x64 [12] [13] соблюдается в Windows и перед загрузкой UEFI (для длительного режима на x86-64). Он использует регистры RCX, RDX, R8, R9 для первых четырех целочисленных аргументов или указателей (в этом порядке), а XMM0, XMM1, XMM2, XMM3 используются для аргументов с плавающей запятой. Дополнительные аргументы помещаются в стек (справа налево). Целочисленные возвращаемые значения (аналогично x86) возвращаются в формате RAX, если 64 или менее бит. Возвращаемые значения с плавающей точкой возвращаются в XMM0. Параметры длиной менее 64 бит не расширены нулями; старшие биты не обнуляются.
При компиляции для архитектуры x64 в контексте Windows (будь то с помощью инструментов Microsoft или сторонних разработчиков) существует только одно соглашение о вызовах - описанное здесь, так что stdcall, thiscall, cdecl, fastcall и т. Д. теперь все одно и то же.
В соглашении о вызовах Microsoft x64 вызывающий обязан выделить 32 байта «теневого пространства» в стеке непосредственно перед вызовом функции (независимо от фактического количества используемых параметров) и извлечь стек после вызов. Теневое пространство используется для разлива RCX, RDX, R8 и R9, [14], но должно быть доступно всем функциям, даже тем, которые имеют менее четырех параметров.
Регистры RAX, RCX, RDX, R8, R9, R10, R11 считаются энергозависимыми (сохраненными вызывающим абонентом). [15]
Регистры RBX, RBP, RDI, RSI, RSP, R12, R13, R14 и R15 считаются энергонезависимыми (сохраненные вызывающим абонентом). [15]
Например, функция, принимающая 5 целочисленных аргументов, будет принимать регистры с первого по четвертый, а пятый будет помещаться в верхнюю часть теневого пространства. Поэтому, когда вызывается вызываемая функция, стек будет состоять из (в порядке возрастания) обратного адреса, за которым следует теневое пространство (32 байта), за которым следует пятый параметр.
В x86-64 Visual Studio 2008 хранит числа с плавающей запятой в XMM6 и XMM7 (а также с XMM8 по XMM15); следовательно, для x86-64 подпрограммы, написанные пользователем на языке ассемблера, должны сохранять XMM6 и XMM7 (по сравнению с x86, где подпрограммам, написанным пользователем, не нужно было сохранять XMM6 и XMM7). Другими словами, пользовательские подпрограммы на ассемблере должны быть обновлены для сохранения / восстановления XMM6 и XMM7 до / после функции при портировании с x86 на x86-64.