Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом разделе описаны стандартные процессы и соглашения, которые одна функция (вызывающий объект) использует для выполнения вызовов к другой функции (вызываемый объект) в коде x64.
Дополнительные сведения о соглашении о вызовах __vectorcall
см. в __vectorcall.
Соглашения о вызовах по умолчанию
Двоичный интерфейс (ABI) для 64-разрядных приложений по умолчанию использует соглашение о вызовах с использованием четырех регистров (fast-call). Место в стеке вызовов выделяется как теневой стек для вызываемых процедур с целью сохранения регистров.
Между аргументами вызова функции и регистрами, используемыми для этих аргументов, существует однозначное соответствие. Любой аргумент, который не умещается в 8 байтов или не равен 1, 2, 4 или 8 байтам, должен передаваться по ссылке. Один аргумент никогда не распределяется между несколькими регистрами.
Стек регистров x87 не используется. Он может использоваться вызываемой функцией. Однако он должен рассматриваться как изменяемый в рамках вызовов функций. Все операции с числами с плавающей запятой выполняются с помощью 16 регистров XMM.
Целочисленные аргументы передаются в регистрах РККС, RDX, R8 и R9. Аргументы с плавающей запятой передаются в XMM0L, XMM1L, XMM2L и XMM3L. 16-байтовые аргументы передаются по ссылке. Подробнее о передаче параметров см. в разделе Передача параметров. Эти регистры и RAX, R10, R11, XMM4 и XMM5 считаются переменными или потенциально изменены вызывающим участником по возвращении. Использование регистров подробно описано в использовании регистров x64 и регистраторах, сохраняемых вызывающим/вызываемым.
Для функций с прототипом все аргументы преобразуются в ожидаемые типы вызываемых объектов перед передачей. Вызывающая функция отвечает за выделение пространства для параметров вызываемой функции. Он всегда должен выделять достаточно места для хранения четырех параметров регистров, даже если вызываемый объект не принимает такое количество параметров. Это соглашение упрощает поддержку функций языка C без прототипов и функций vararg C/C++. Для функций vararg и функций без прототипа все значения с плавающей запятой должны дублироваться в соответствующем регистре общего назначения. Все параметры за пределами первых четырех регистров должны храниться в стеке после теневого хранилища перед вызовом. Подробные сведения о функции vararg см. в статье Функции vararg. Подробнее о функции без прототипа см. в разделе Функции без прототипов.
Выравнивание
Большинство структур выровнены в соответствии с естественным расположением. Основные исключения — это указатель стека и память malloc
или alloca
, которые выравниваются до 16 байт, чтобы обеспечить производительность. Выравнивание свыше 16 байт должно выполняться вручную. Поскольку 16 байт — это общий размер выравнивания для операций XMM, это значение должно подходить для большинства кодов. Дополнительные сведения о макете структуры и выравнивании см. в разделе "Тип x64" и макет хранилища. Подробнее о макете стека см. в разделе Использование стека x64.
Разматываемость
Функции-листья — это функции, которые не изменяют неиспаряющиеся регистры. Функция, не являющаяся листовой, может изменить незатухающий RSP, например, вызвав функцию. Это может изменить RSP, выделив дополнительное пространство стека для локальных переменных. Чтобы восстановить неизменяемые регистры при обработке исключения, не-листовые функции аннотируются статическими данными. Эти данные описывают, как правильно очистить функцию в произвольной инструкции. Эти данные хранятся в виде pdata или данных процедуры, которые, в свою очередь, ссылаются на xdata, данные обработки исключений. xdata содержит сведения о распаковке и может указывать на дополнительные pdata, a также функцию обработчика исключений.
Прологи и эпилоги имеют высокий уровень ограничений, чтобы их можно было правильно описать в xdata. Указатель стека должен оставаться выровненным до 16 байт в любом регионе кода, который не является частью эпилога или пролога, за исключением конечных функций. Конечные функции можно развернуть просто путем имитации возврата, поэтому pdata и xdata не требуются. Дополнительные сведения о правильной структуре прологов и эпилогов функции см. в разделе Пролог и эпилог в коде x64. Дополнительные сведения о механизме обработки и раскрутке исключений для pdata и xdata см. в разделе Обработка исключений в коде x64.
Передача параметров
По умолчанию соглашение о вызовах x64 передает первые четыре аргумента функции в регистрах. Регистры, используемые для этих аргументов, зависят от расположения и типа аргумента. Оставшиеся аргументы передаются в стек в порядке справа налево. Все аргументы, передаваемые в стек, выровнены по 8 байтам.
Целочисленные аргументы в первых четырех позициях передаются в порядке слева направо в RCX, RDX, R8 и R9, соответственно. Пятый и следующие аргументы передаются в стек, как описано выше. Все целочисленные аргументы в регистрах выравниваются по правому краю, поэтому вызываемый объект может игнорировать верхние биты регистра и получить доступ только к той части регистра, которая необходима.
Любые аргументы с плавающей запятой и двойной точностью в первых четырех параметрах передаются в XMM0–XMM3 (в зависимости от позиции). Значения с плавающей запятой помещаются в регистры целочисленных значений RCX, RDX, R8 и R9 только при наличии в них аргументов varargs. Дополнительные сведения см. в разделе Varargs. Аналогичным образом регистры XMM0–XMM3 игнорируются, если соответствующий аргумент является целым числом или указателем.
Типы __m128
, массивы и строки никогда не передаются с непосредственным значением. Вместо этого указатель передается в память, выделенную вызывающим объектом. Структуры и объединения размера 8, 16, 32 или 64 бита и __m64
передаются, как если бы они были целыми числами того же размера. Структуры или объединения других размеров передаются в качестве указателя в память, выделенную вызывающим объектом. Для этих агрегатных типов, которые передаются в виде указателя, в том числе __m128
, выделенная вызывающей стороной временная память должна составлять 16 байт.
Встроенные функции, которые не выделяют пространство стека и не вызывают другие функции, иногда используют другие изменяемые регистры для передачи дополнительных аргументов регистра. Такая оптимизация возможна благодаря тесной привязке между компилятором и реализацией внутренней функции.
Вызываемая сторона отвечает за сброс параметров регистров в теневое пространство, если необходимо.
В следующей таблице приведены сводные сведения о передаче параметров, по их типу и порядку слева.
Тип параметра | пятый и выше | четвертая | третий | секунда | крайний левый |
---|---|---|---|---|---|
с плавающей запятой | стек | XMM3 | XMM2 | XMM1 | XMM0 |
целое число | стек | R9 | R8 | RDX | RCX |
Агрегаты (8, 16, 32 или 64 бит) и __m64 |
стек | R9 | R8 | RDX | RCX |
Другие агрегаты, например указатели | стек | R9 | R8 | RDX | RCX |
__m128 как указатель |
стек | R9 | R8 | RDX | RCX |
Пример передачи аргумента 1 — все целые числа
func1(int a, int b, int c, int d, int e, int f);
// a in RCX, b in RDX, c in R8, d in R9, f then e pushed on stack
Пример передачи аргумента 2 — все числа с плавающей запятой
func2(float a, double b, float c, double d, float e, float f);
// a in XMM0, b in XMM1, c in XMM2, d in XMM3, f then e pushed on stack
Пример передачи аргумента 3 — смешение целых чисел и чисел с плавающей запятой
func3(int a, double b, int c, float d, int e, float f);
// a in RCX, b in XMM1, c in R8, d in XMM3, f then e pushed on stack
Пример передачи аргумента 4 — __m64
, __m128
и агрегаты
func4(__m64 a, __m128 b, struct c, float d, __m128 e, __m128 f);
// a in RCX, ptr to b in RDX, ptr to c in R8, d in XMM3,
// ptr to f pushed on stack, then ptr to e pushed on stack
Функции с переменным количеством аргументов (Varargs)
Если параметры передаются через функции vararg (например, аргументы многоточия), применяется стандартное соглашение о передаче параметров регистров. Это соглашение включает в себя перенос пятого и последующих аргументов в стек. Ответственность вызываемой стороны — сбрасывать аргументы, которые были переданы по адресу. Для значений с плавающей запятой как регистр целых чисел, так и регистр чисел с плавающей запятой должны содержать значение, если вызываемый объект ожидает значение в целочисленных регистрах.
Функции без прототипа
Для функций, для которых нет полного прототипа, вызывающий объект передает целочисленные значения как целые числа, а числа с плавающей запятой — в виде чисел двойной точности. Для значений с плавающей запятой как регистр целых чисел, так и регистр чисел с плавающей запятой содержат значение с плавающей запятой, если вызываемый объект ожидает значение в целочисленных регистрах.
func1();
func2() { // RCX = 2, RDX = XMM1 = 1.0, and R8 = 7
func1(2, 1.0, 7);
}
Возвращаемые значения
Скалярное возвращаемое значение, которое может быть представлено в 64 битах, включая тип __m64
, возвращается через RAX. Нескалярные типы, включая float, double и векторные типы, такие как __m128
, __m128i
и __m128d
, возвращаются в XMM0. Состояние неиспользуемых битов в возвращаемом значении в RAX или XMM0 не определяется.
Определенные пользователем типы можно вернуть из глобальных функций и статических функций-членов по значению. Чтобы вернуть определяемый пользователем тип по значению в RAX, он должен иметь длину 1, 2, 4, 8, 16, 32 или 64 бита. В нем также должны отсутствовать заданные пользователем конструктор, деструктор или оператор назначения копирования. Он должен быть без частных или защищенных нестатических элементов данных, без нестатических элементов данных ссылочного типа. В нем не должно быть базовых классов или виртуальных функций. Кроме того, он может содержать только элементы данных, которые также удовлетворяют этим требованиям. (Это определение по сути совпадает с типом POD C++03. Так как определение изменилось в стандарте C++11, мы не рекомендуем использовать std::is_pod
для этого теста.) В противном случае вызывающий объект должен выделить память для возвращаемого значения и передать указатель на него в качестве первого аргумента. Оставшиеся аргументы затем перемещаются на один аргумент вправо. Тот же указатель должен быть возвращен функцией, вызываемой в RAX.
В приведенных ниже примерах показан способ передачи параметров и возвращаемых значений для функций с указанными объявлениями.
Пример возвращаемого значения 1 — результат в 64 бита
__int64 func1(int a, float b, int c, int d, int e);
// Caller passes a in RCX, b in XMM1, c in R8, d in R9, e pushed on stack,
// callee returns __int64 result in RAX.
Пример возвращаемого значения 2 — результат в 128 бит
__m128 func2(float a, double b, int c, __m64 d);
// Caller passes a in XMM0, b in XMM1, c in R8, d in R9,
// callee returns __m128 result in XMM0.
Пример возвращаемого значения 3 — результат типа пользователя по указателю
struct Struct1 {
int j, k, l; // Struct1 exceeds 64 bits.
};
Struct1 func3(int a, double b, int c, float d);
// Caller allocates memory for Struct1 returned and passes pointer in RCX,
// a in RDX, b in XMM2, c in R9, d pushed on the stack;
// callee returns pointer to Struct1 result in RAX.
Пример возвращаемого значения 4 — результат пользовательского типа по значению
struct Struct2 {
int j, k; // Struct2 fits in 64 bits, and meets requirements for return by value.
};
Struct2 func4(int a, double b, int c, float d);
// Caller passes a in RCX, b in XMM1, c in R8, and d in XMM3;
// callee returns Struct2 result by value in RAX.
Регистрируемые вызывающей стороной и сохраняемые вызываемой стороной регистры
В ABI x64 регистры RAX, RCX, RDX, R8, R9, R10, R11 и XMM0–XMM5 рассматриваются как изменяемые. При наличии верхние части YMM0–YMM15 и ZMM0–ZMM15 также являются изменчивыми. В AVX512VL регистры ZMM, YMM и XMM 16–31 также являются изменяемыми. При наличии поддержки AMX регистры плиток TMM являются переменными. Регистр с произвольным доступом следует считать уничтоженными при вызове функций, если анализ, например оптимизация всей программы, не доказывает обратное.
В ABI x64 регистры RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15 и XMM6–XMM15 считаются сохраняющими своё состояние. Они должны быть сохранены и восстановлены с помощью функции, которая их использует.
Указатели функций
Указатели функций — это просто указатели на метку соответствующей функции. Для указателей функций не предусмотрено требование к содержанию (TOC).
Поддержка чисел с плавающей запятой для устаревшего кода
Регистры MMX и стека с плавающей запятой (MM0-MM7/ST0-ST7) сохраняются во всех переключениях контекста. Для этих регистров не предусмотрено явное соглашение о вызовах. Использование этих регистров строго запрещено в коде режима ядра.
FPCSR
Состояние регистра также включает управляющее слово x87 FPU. Соглашение о вызовах определяет, что регистр является неизменяемым.
Регистр управляющего слова x87 FPU задается с использованием следующих стандартных значений в начале выполнения программы.
Регистр[бит] | Параметр |
---|---|
FPCSR[0:6] | Маски исключений все 1 (все исключения маскированы) |
FPCSR[7] | Зарезервировано — 0 |
FPCSR[8:9] | Управление точностью — 10B (двойная точность) |
FPCSR[10:11] | Управление округлением — 0 (округление до ближайшего числа) |
FPCSR[12] | Управление бесконечностью — 0 (не используется) |
Вызываемый объект, который изменяет любое из полей в FPCSR, должен восстановить их перед возвратом в вызывающий объект. Кроме того, вызывающая сторона, изменившая любое из этих полей, должна восстановить их стандартные значения перед вызовом, если только вызываемый не ожидает измененных значений по договоренности.
Существует два исключения из правил, связанных с постоянством управляющих флагов:
В функциях, где документированная цель заданной функции заключается в изменении неизменяемых флагов FPCSR.
Если доказуемо корректно, что нарушение этих правил приводит к тому, что программа выполняется так же, как и программа, в которой эти правила не нарушаются (например, с помощью анализа всей программы).
MXCSR
Состояние регистра также включает MXCSR. Соглашение о вызовах делит этот регистр на изменяемую часть и неизменяемую часть. Переменная часть состоит из шести флагов состояния в MXCSR[0:5], в то время как остальная часть регистра MXCSR[6:15], считается неактивной.
Неизменяемая часть задается со следующими стандартными значениями в начале выполнения программы:
Регистр[бит] | Настройка |
---|---|
MXCSR[6] | Денормализованные числа равны нулю — 0 |
MXCSR[7:12] | Маски исключений установлены в 1 (все исключения замаскированы) |
MXCSR[13:14] | Управление округлением — 0 (округление до ближайшего числа) |
MXCSR[15] | Сброс до нуля для маскированного переполнения — 0 (отключено) |
Функция, которая изменяет любое из неразрушаемых полей в MXCSR, должна восстановить их перед возвратом к вызывающему. Кроме того, вызывающий объект, который изменил любое из этих полей, должен восстановить их стандартные значения перед вызовом вызываемого объекта, если только по соглашению вызываемый объект не требует измененных значений.
Существует два исключения из правил, связанных с постоянством управляющих флагов:
В функциях, где документированная цель заданной функции заключается в изменении неизменяемых флагов MXCSR.
Если доказуемо корректно, что нарушение этих правил приводит к тому, что программа выполняется так же, как и программа, в которой эти правила не нарушаются (например, с помощью анализа всей программы).
Не делайте никаких предположений о состоянии изменчивой части регистра MXCSR на границе функции, если только это не описано в документации функции.
setjmp/longjmp
При включении setjmpex.h или setjmp.h все вызовы к setjmp
или longjmp
приводят к очистке, вызывающей деструкторы и вызовы __finally
. Это поведение отличается от поведения в архитектуре x86, где включение setjmp.h приводит к невозможности вызова предложений __finally
и деструкторов.
Вызов setjmp
сохраняет текущий указатель стека, неизменяемые регистры и регистры MXCSR. Вызовы longjmp
возвращают к самому недавнему месту вызова setjmp
и сбрасывают указатель стека, неизменяемые регистры и регистры MXCSR в состояние, сохраненное последним вызовом setjmp
.