Поделиться через


Общие сведения о соглашениях 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 летучий летучий летучий
x1 rdx летучий летучий летучий
x2 r8 летучий летучий летучий
x3 r9 летучий летучий летучий
x4 r10 летучий летучий летучий
x5 r11 летучий летучий летучий
x6 mm1 (низкий 64-разрядный регистр x87 R1 ) летучий летучий летучий
x7 mm2 (низкий 64-разрядный регистр x87 R2 ) летучий летучий летучий
x8 rax летучий летучий летучий
x9 mm3 (низкий 64-разрядный регистр x87 R3 ) летучий летучий летучий
x10 mm4 (низкий 64-разрядный регистр x87 R4 ) летучий летучий летучий
x11 mm5 (низкий 64-разрядный регистр x87 R5 ) летучий летучий летучий
x12 mm6 (низкий 64-разрядный регистр x87 R6 ) летучий летучий летучий
x13 Н/П Запрещено летучий Н/П
x14 Н/П Запрещено летучий Н/П
x15 mm7 (низкий 64-разрядный регистр x87 R7 ) летучий летучий летучий
x16 Высокий 16 бит каждого из регистров x87 R0-R3 volatile(xip0) volatile(xip0) летучий
x17 Высокий 16 бит каждого из регистров x87 R4-R7 volatile(xip1) volatile(xip1) летучий
x18 GS.base фиксированный(TEB) фиксированный(TEB) фиксированный(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/C/V/SS1, 2 RFLAGS подмножество: SF/ZF/CF/OF/TF летучий летучий летучий
Н/П RFLAGS подмножество: PF/AF Н/П Н/П летучий
Н/П RFLAGS подмножество: DF Н/П Н/П не изменяющийся

1 Избегайте прямого чтения, записи или сопоставления вычислений между PSTATE и RFLAGS. Эти биты могут использоваться в будущем и подлежат изменению.

2 Флаг переноса ARM64EC C инверсирован относительно флага переноса CF x64 для операций вычитания. Нет специальной обработки, так как флаг является переменным и поэтому корзина при переходе между функциями (ARM64EC и x64).

Регистрация сопоставления для регистров векторов

регистрация ARM64EC Регистрация x64 соглашение о вызовах ARM64EC Соглашение о вызовах ARM64 Соглашение о вызовах для 64-разрядных систем
v0-v5 xmm0-xmm5 летучий летучий летучий
v6-v7 xmm6-xmm7 летучий летучий не изменяющийся
v8-v15 xmm8-xmm15 переменная 1 переменная 1 не изменяющийся
v16-v31 xmm16-xmm31 Запрещено летучий запрещено (эмулятор x64 не поддерживает AVX-512)
FPCR 2 MXCSR[15:6] не изменяющийся не изменяющийся не изменяющийся
FPSR 2 MXCSR[5:0] летучий летучий летучий

1 Эти регистры ARM64 являются особыми в том, что нижние 64-разрядные биты не изменяются, но верхние 64-разрядные биты являются переменными. С точки зрения вызывающего объекта x64 они фактически изменяются, так как вызывающий объект будет мусорить данные.

2 Избегайте прямого чтения, записи или сопоставления вычислений FPCR и FPSR. Эти биты могут использоваться в будущем и подлежат изменению.

Упаковка структуры

ARM64EC следует тем же правилам упаковки структур, используемым для x64, чтобы обеспечить взаимодействие между кодом ARM64EC и кодом x64. Дополнительные сведения и примеры упаковки структур x64 см. в разделе "Общие сведения о соглашениях ABI x64".

Исключения для плавающей запятой

Чтобы определить, поддерживает ли ЦП ARM исключения, напишите значение, которое позволяет исключениям регистра FPCR, а затем считывать его обратно. Если ЦП поддерживает исключения с плавающей запятой, биты, соответствующие поддерживаемым исключениям, остаются заданными, а ЦП сбрасывает биты для неподдерживаемых исключений.

В ARM64EC Windows перехватывает исключения с плавающей запятой процессора и отключает их в регистре FPCR. Это обеспечивает согласованное поведение в разных вариантах процессора.

Вспомогательные подпрограммы 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 возвращаемое значение (x8rax)
__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, вызывающих друг друга. Существует два типа: входные заглушки для входа в функции 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:

  1. Отменяет выделение стека
  2. Вызывает вспомогательный помощник эмулятора __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:

  1. Отменяет выделение стека
  2. Появляется регистрация ARM64EC lr
  3. Выполняет инструкцию ARM64 ret lr .

ARM64EC украшение имени функции

Имя функции ARM64EC имеет дополнительное украшение, применяемое после любого оформления языка. Для функций с компоновкой C (будь то компилируемый как C или с помощью extern "C"), предопределено # к имени. Для декорированных функций $$h C++ тег вставляется в имя.

foo         => #foo
?foo@@YAHXZ => ?foo@@$$hYAHXZ

__vectorcall

В настоящее время цепочка инструментов ARM64EC не поддерживается __vectorcall. Компилятор выдает ошибку при обнаружении __vectorcall использования с ARM64EC.

См. также

Общие сведения о ARM64EC ABI и коде сборки
Распространенные проблемы миграции Visual C++ для ARM
Декорированные имена