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


Общие сведения о соглашениях 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подмножество: NZSS/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 возвращаемое значение (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, вызывающих друг друга. Существует два типа: входные 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:

  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 и коде сборки
Распространенные проблемы миграции ARM Visual C++
Внутренние имена