Общие сведения о соглашениях ABI ARM64EC
ARM64EC — это двоичный интерфейс приложения (ABI), который позволяет двоичным файлам ARM64 выполняться в собственном коде и взаимодействовать с кодом x64. В частности, ARM64EC ABI следует соглашениям о программном обеспечении x64, включая соглашение о вызовах, использование стека и выравнивание данных, что делает ARM64EC и x64 код взаимодействия. Операционная система эмулирует часть двоичного файла x64. (EC в ARM64EC соответствует совместимости эмуляции.)
Дополнительные сведения о соглашениях X64 и ARM64 ABI см. в разделе "Обзор соглашений ABI x64" и "Обзор соглашений ABI ARM64".
ARM64EC не решает различия в модели памяти между архитектурами на основе x64 и ARM. Дополнительные сведения см. в разделе Распространенные проблемы с миграцией ARM Visual C++.
Определения
- ARM64 — поток кода для процессов ARM64, содержащих традиционный код ARM64.
- ARM64EC — поток кода, использующее подмножество набора регистров ARM64 для обеспечения взаимодействия с кодом x64.
Сопоставление регистров
Процессы x64 могут иметь потоки, работающие ARM64EC коде. Поэтому всегда можно получить контекст регистра x64, ARM64EC использует подмножество основных регистров ARM64, которые сопоставляют 1:1 с эмулированными регистрами x64. Важно, что ARM64EC никогда не использует регистры за пределами этого подмножества, за исключением чтения адреса x18
блока среды потоков (TEB).
Собственные процессы ARM64 не должны регрессии в производительности, когда некоторые или многие функции перекомпилируются как ARM64EC. Для поддержания производительности ABI следует следующим принципам:
Подмножество регистрации ARM64EC включает все регистры, которые являются частью соглашения о вызове функций ARM64.
Соглашение о вызове ARM64EC напрямую сопоставляется с соглашением о вызовах ARM64.
Специальные вспомогательные подпрограммы, такие как __chkstk_arm64ec
использование пользовательских соглашений о вызовах и регистров. Эти регистры также включаются в подмножество регистров ARM64EC.
Регистрация сопоставления для целых регистров
регистрация ARM64EC | Регистрация x64 | соглашение о вызовах ARM64EC | Соглашение о вызовах ARM64 | Соглашение о вызовах для 64-разрядных систем |
---|---|---|---|---|
x0 |
rcx |
volatile | volatile | volatile |
x1 |
rdx |
volatile | volatile | volatile |
x2 |
r8 |
volatile | volatile | volatile |
x3 |
r9 |
volatile | volatile | volatile |
x4 |
r10 |
volatile | volatile | volatile |
x5 |
r11 |
volatile | volatile | volatile |
x6 |
mm1 (низкий 64-разрядный регистр x87 R1 ) |
volatile | volatile | volatile |
x7 |
mm2 (низкий 64-разрядный регистр x87 R2 ) |
volatile | volatile | volatile |
x8 |
rax |
volatile | volatile | volatile |
x9 |
mm3 (низкий 64-разрядный регистр x87 R3 ) |
volatile | volatile | volatile |
x10 |
mm4 (низкий 64-разрядный регистр x87 R4 ) |
volatile | volatile | volatile |
x11 |
mm5 (низкий 64-разрядный регистр x87 R5 ) |
volatile | volatile | volatile |
x12 |
mm6 (низкий 64-разрядный регистр x87 R6 ) |
volatile | volatile | volatile |
x13 |
Н/П | Запрещено | volatile | Неприменимо |
x14 |
Неприменимо | Запрещено | volatile | Н/П |
x15 |
mm7 (низкий 64-разрядный регистр x87 R7 ) |
volatile | volatile | volatile |
x16 |
Высокий 16 бит каждого из регистров x87 R0 -R3 |
volatile(xip0 ) |
volatile(xip0 ) |
volatile |
x17 |
Высокий 16 бит каждого из регистров x87 R4 -R7 |
volatile(xip1 ) |
volatile(xip1 ) |
volatile |
x18 |
GS.base | fixed(TEB) | fixed(TEB) | fixed(TEB) |
x19 |
r12 |
не изменяющийся | не изменяющийся | не изменяющийся |
x20 |
r13 |
не изменяющийся | не изменяющийся | не изменяющийся |
x21 |
r14 |
не изменяющийся | не изменяющийся | не изменяющийся |
x22 |
r15 |
не изменяющийся | не изменяющийся | не изменяющийся |
x23 |
Н/П | Запрещено | не изменяющийся | Неприменимо |
x24 |
Неприменимо | Запрещено | не изменяющийся | Н/П |
x25 |
rsi |
не изменяющийся | не изменяющийся | не изменяющийся |
x26 |
rdi |
не изменяющийся | не изменяющийся | не изменяющийся |
x27 |
rbx |
не изменяющийся | не изменяющийся | не изменяющийся |
x28 |
Н/П | Запрещено | Запрещено | Н/П |
fp |
rbp |
не изменяющийся | не изменяющийся | не изменяющийся |
lr |
mm0 (низкий 64-разрядный регистр x87 R0 ) |
Оба варианта | Оба варианта | Оба варианта |
sp |
rsp |
не изменяющийся | не изменяющийся | не изменяющийся |
pc |
rip |
указатель инструкций | указатель инструкций | указатель инструкций |
PSTATE подмножество: N Z SS /C //V /1, 2 |
RFLAGS подмножество: SF /ZF /CF /OF /TF |
volatile | volatile | volatile |
Н/П | RFLAGS подмножество: PF /AF |
Неприменимо | Неприменимо | volatile |
Н/П | RFLAGS подмножество: DF |
Неприменимо | Неприменимо | не изменяющийся |
1 Избегайте прямого чтения, записи или сопоставления вычислений между PSTATE
и RFLAGS
. Эти биты могут использоваться в будущем и подлежат изменению.
2 Флаг переноса C
ARM64EC является обратным флагом CF
х64 для операций вычитания. Нет специальной обработки, так как флаг является переменным и поэтому корзина при переходе между функциями (ARM64EC и x64).
Регистрация сопоставления для регистров векторов
регистрация ARM64EC | Регистрация x64 | соглашение о вызовах ARM64EC | Соглашение о вызовах ARM64 | Соглашение о вызовах для 64-разрядных систем |
---|---|---|---|---|
v0 -v5 |
xmm0 -xmm5 |
volatile | volatile | volatile |
v6 -v7 |
xmm6 -xmm7 |
volatile | volatile | не изменяющийся |
v8 -v15 |
xmm8 -xmm15 |
переменная 1 | переменная 1 | не изменяющийся |
v16 -v31 |
xmm16 -xmm31 |
Запрещено | volatile | запрещено (эмулятор x64 не поддерживает AVX-512) |
FPCR 2 |
MXCSR[15:6] |
не изменяющийся | не изменяющийся | не изменяющийся |
FPSR 2 |
MXCSR[5:0] |
volatile | volatile | volatile |
1 Эти регистры ARM64 являются особыми в том, что нижние 64-разрядные биты не изменяются, но верхние 64-разрядные биты являются переменными. С точки зрения вызывающего объекта x64 они фактически изменяются, так как вызывающий объект будет мусорить данные.
2 Избегайте прямого чтения, записи или сопоставления вычислений FPCR
и FPSR
. Эти биты могут использоваться в будущем и подлежат изменению.
Упаковка структуры
ARM64EC следует тем же правилам упаковки структур, используемым для x64, чтобы обеспечить взаимодействие между кодом ARM64EC и кодом x64. Дополнительные сведения и примеры упаковки структур x64 см. в разделе "Общие сведения о соглашениях ABI x64".
Вспомогательные подпрограммы ABI эмуляции
ARM64EC коде и thunks используют вспомогательные подпрограммы эмуляции для перехода между функциями x64 и ARM64EC.
В следующей таблице описывается каждая специальная подпрограмма ABI и регистрируются используются ABI. Подпрограммы не изменяют перечисленные сохраненные регистры в столбце ABI. Никаких предположений о незаписанных регистрах не следует делать. На диске указатели обычной процедуры ABI имеют значение NULL. Во время загрузки загрузчик обновляет указатели, указывающие на подпрограммы эмулятора x64.
Имя | Описание | ABI |
---|---|---|
__os_arm64x_dispatch_call_no_redirect |
Вызывается выходным thunk для вызова целевого объекта x64 (либо функции x64 или последовательности x64 fast-forward). Подпрограмма отправляет ARM64EC возвращаемый адрес (в регистре LR ), за которым следует адрес инструкции, которая завершает blr x16 инструкцию, которая вызывает эмулятор x64. Затем он запускает инструкцию blr x16 |
возвращаемое значение (x8 rax ) |
__os_arm64x_dispatch_ret |
Вызывается элементом записи, чтобы вернуться к вызывающей объекту x64. Он выводит адрес возврата x64 из стека и вызывает эмулятор x64, чтобы перейти к нему | Н/П |
__os_arm64x_check_call |
Вызывается ARM64EC кодом с указателем на выходную точку и косвенный ARM64EC целевой адрес для выполнения. Целевой объект ARM64EC считается исправленным, и выполнение всегда возвращается вызывающему объекту с теми же данными, с которыми он был вызван, или с измененными данными. | Аргументы:x9 : целевой адресx10 : адрес выхода из блокаx11 : адрес последовательности быстрого переадресацииВне: x9 : если целевая функция была отложена, она содержит адрес последовательности быстрого переадресации.x10 : адрес выхода из блокаx11 : если функция была удалена, она содержит адрес выхода. В противном случае целевой адрес перескочил наСохраненные регистры: x0 -x8 , x15 (chkstk ). и q0 -q7 . |
__os_arm64x_check_icall |
Вызывается ARM64EC кодом с указателем на выход из thunk, чтобы обрабатывать переход к целевому адресу, который является x64 или ARM64EC. Если целевой объект равен x64, а код x64 не был исправлен, подпрограмма задает целевой регистр адресов. Он указывает на ARM64EC версию функции, если она существует. В противном случае он задает регистр, указывающий на выход, который переходит к целевому объекту x64. Затем он возвращает вызывающий код ARM64EC, который затем переходит к адресу в регистре. Эта подпрограмма представляет собой неоптимизированную версию __os_arm64x_check_call , в которой целевой адрес не известен во время компиляции.Используется на сайте вызова косвенного вызова |
Аргументы:x9 : целевой адресx10 : адрес выхода из блокаx11 : адрес последовательности быстрого переадресацииВне: x9 : если целевая функция была отложена, она содержит адрес последовательности быстрого переадресации.x10 : адрес выхода из блокаx11 : если функция была удалена, она содержит адрес выхода. В противном случае целевой адрес перескочил наСохраненные регистры: x0 -x8 , x15 (chkstk ) и q0 -q7 |
__os_arm64x_check_icall_cfg |
То же самое, что __os_arm64x_check_icall и проверка того, что указанный адрес является допустимым целевым объектом непрямого вызова потока управления |
Аргументы:x10 : адрес выходного фрагментаx11 : адрес целевой функцииВне: x9 : если целевой объект равен x64, адрес функции. В противном случае не определеноx10 : адрес выходного фрагментаx11 : если целевой объект равен x64, он содержит адрес выходного элемента. В противном случае адрес функцииСохраненные регистры: x0 -x8 , x15 (chkstk ) и q0 -q7 |
__os_arm64x_get_x64_information |
Возвращает запрошенную часть контекста динамического регистра x64 | _Function_class_(ARM64X_GET_X64_INFORMATION) NTSTATUS LdrpGetX64Information(_In_ ULONG Type, _Out_ PVOID Output, _In_ PVOID ExtraInfo) |
__os_arm64x_set_x64_information |
Задает запрошенную часть контекста динамического регистра x64 | _Function_class_(ARM64X_SET_X64_INFORMATION) NTSTATUS LdrpSetX64Information(_In_ ULONG Type,_In_ PVOID Input, _In_ PVOID ExtraInfo) |
__os_arm64x_x64_jump |
Используется в сигнатуре без сигнатуры и других thunks, которые напрямую перенаправляютjmp () вызов другой функции, которая может иметь любую сигнатуру, отложив потенциальное применение правого thunk на реальный целевой объект |
Аргументы:x9 : целевой объект для перехода кВсе регистры параметров сохранены (переадресованы) |
Преобразователи
Thunks — это низкоуровневые механизмы для поддержки функций ARM64EC и x64, вызывающих друг друга. Существует два типа: входные thunks для ввода функций ARM64EC и выхода из них для вызова функций x64.
Thunk и встроенные блоки записи: x64 для вызова функции ARM64EC
Для поддержки вызывающих объектов x64 при компиляции функции C/C++ как ARM64EC цепочка инструментов создает одну запись, состоящую из ARM64EC машинного кода. Встроенные компоненты имеют собственный элемент. Все остальные функции совместно используют элемент записи со всеми функциями, имеющими соответствующее соглашение о вызовах, параметры и тип возвращаемого значения. Содержимое thunk зависит от соглашения о вызове функции C/C++.
Помимо обработки параметров и адреса возврата, thunk мостит различия в волатильности между ARM64EC и x64 векторными регистрами, вызванными сопоставлением векторных регистров ARM64EC:
регистрация ARM64EC | Регистрация x64 | соглашение о вызовах ARM64EC | Соглашение о вызовах ARM64 | Соглашение о вызовах для 64-разрядных систем |
---|---|---|---|---|
v6 -v15 |
xmm6 -xmm15 |
переменная, но сохранена или восстановлена в элементе записи (x64 до ARM64EC) | переменная или частично изменяемая верхняя 64-разрядная версия | не изменяющийся |
Элемент записи выполняет следующие действия:
Номер параметра | Использование стека |
---|---|
0–4 | Хранит ARM64EC v6 и v7 в выделенное абонентом домашнее пространствоТак как вызывающий объект ARM64EC, который не имеет понятия о домашнем пространстве, хранимые значения не являются clobbered. Выделяет дополнительные 128 байтов в стеке и храните ARM64EC v8 до v15 . |
5-8 | x4 = 5-й параметр из стекаx5 = 6-й параметр из стекаx6 = 7-й параметр из стекаx7 = 8-й параметр из стекаЕсли параметр является SIMD, v4 -v7 вместо этого используются регистры. |
+9 | Выделяет байты AlignUp(NumParams - 8 , 2) * 8 в стеке. *Копирует 9-й и оставшийся параметр в эту область |
* Выравнивание значения с четным числом гарантирует, что стек остается равным 16 байтам
Если функция принимает 32-разрядный целочисленный параметр, thunk может отправлять только 32 бита вместо 64 бит родительского регистра.
Затем thunk использует инструкцию ARM64 bl
для вызова функции ARM64EC. После возврата функции thunk:
- Отменяет выделение стека
- Вызывает вспомогательный помощник эмулятора
__os_arm64x_dispatch_ret
, чтобы открыть адрес возврата x64 и возобновить эмуляцию x64.
Выход из thunk: ARM64EC вызов функции x64
Для каждого вызова функции ARM64EC C/C++ в потенциальный код x64 цепочка инструментов MSVC создает выход из thunk. Содержимое thunk зависит от параметров вызываемого объекта x64, а также от того, использует ли вызывающий объект стандартную соглашение о вызовах или __vectorcall
. Компилятор получает эти сведения из объявления функции для вызываемого абонента.
Во-первых lr
, thunk отправляет возвращаемый адрес, который находится в регистре ARM64EC и фиктивное 8-байтовое значение, чтобы гарантировать, что стек выровнен по 16 байтам. Во-вторых, thunk обрабатывает параметры:
Номер параметра | Использование стека |
---|---|
0–4 | Выделяет 32 байта домашнего пространства в стеке |
5-8 | Выделяет больше байтов AlignUp(NumParams - 4, 2) * 8 выше на стеке. * Копирует 5-й и все последующие параметры из ARM64EC x4 -x7 в это дополнительное пространство |
+9 | Копирует 9-й и оставшийся параметры в дополнительное пространство |
* Выравнивание значения с четным числом гарантирует, что стек остается равным 16 байтам.
В-третьих, вспомогательный компонент эмулятора вызывает __os_arm64x_dispatch_call_no_redirect
эмулятор x64 для запуска функции x64. Вызов должен быть инструкцией blr x16
(удобно, x16
является переменным регистром). Требуется blr x16
инструкция, так как эмулятор x64 анализирует эту инструкцию как подсказку.
Функция x64 обычно пытается вернуться в вспомогательный элемент эмулятора с помощью инструкции x64 ret
. На этом этапе эмулятор x64 обнаруживает, что он находится в ARM64EC коде. Затем он считывает предыдущий 4-байтовый намек, который происходит в инструкции ARM64 blr x16
. Так как это указание указывает, что обратный адрес находится в этом вспомогательном приложении, эмулятор переходит непосредственно к этому адресу.
Функция x64 разрешена вернуться в вспомогательный эмулятор с помощью любой инструкции ветви, включая x64 jmp
и call
. Эмулятор также обрабатывает эти сценарии.
Когда помощник затем возвращается в thunk, thunk:
- Отменяет выделение стека
- Появляется регистрация ARM64EC
lr
- Выполняет инструкцию ARM64
ret lr
.
ARM64EC украшение имени функции
Имя функции ARM64EC имеет дополнительное украшение, применяемое после любого оформления языка. Для функций с компоновкой C (будь то компилируемый как C или с помощью extern "C"
), предопределено #
к имени. Для декорированных функций $$h
C++ тег вставляется в имя.
foo => #foo
?foo@@YAHXZ => ?foo@@$$hYAHXZ
__vectorcall
В настоящее время цепочка инструментов ARM64EC не поддерживается __vectorcall
. Компилятор выдает ошибку при обнаружении __vectorcall
использования с ARM64EC.
См. также
Общие сведения о ARM64EC ABI и коде сборки
Распространенные проблемы миграции ARM Visual C++
Внутренние имена