Información general sobre las convenciones ABI de ARM64EC
ARM64EC es una interfaz binaria de aplicación (ABI) que permite que los archivos binarios ARM64 se ejecuten de forma nativa e interoperable con código x64. En concreto, la ABI de ARM64EC sigue las convenciones de software x64, incluida la convención de llamada, el uso de la pila y la alineación de datos, lo que hace que el código ARM64EC y x64 sean interoperables. El sistema operativo emula la parte x64 del binario. (La CE de ARM64EC significa emulation compatible [compatible con la emulación]).
Para obtener más información sobre las ABI x64 y ARM64, consulte Información general sobre las convenciones ABI de x64 e Información general sobre las convenciones ABI de ARM64.
ARM64EC no resuelve las diferencias del modelo de memoria entre las arquitecturas basadas en x64 y ARM. Para obtener más información, consulte Problemas comunes de migración de ARM en Visual C++.
Definiciones
- ARM64: el flujo de código de los procesos ARM64 que contiene código ARM64 tradicional.
- ARM64EC: flujo de código que utiliza un subconjunto del conjunto de registros ARM64 para proporcionar interoperabilidad con código x64.
Asignación de registros
Los procesos x64 pueden tener subprocesos que ejecutan código ARM64EC. Por lo tanto, siempre es posible recuperar un contexto de registro x64; ARM64EC usa un subconjunto de los registros principales ARM64 que asignan 1:1 a registros x64 emulados. Lo más importante, ARM64EC nunca usa registros ajenos a este subconjunto, excepto para leer la dirección del bloque de entorno de subprocesos (TEB) de x18
.
Los procesos ARM64 nativos no deberían retroceder en rendimiento cuando algunas o muchas funciones se vuelven a compilar como ARM64EC. Para mantener el rendimiento, la ABI sigue estos principios:
El subconjunto de registros ARM64EC incluye todos los registros que forman parte de la convención de llamada de las funciones ARM64.
La convención de llamada ARM64EC se asigna directamente a la convención de llamada ARM64.
Las rutinas auxiliares especiales, como __chkstk_arm64ec
, usan convenciones de llamada y registros personalizados. Estos registros también se incluyen en el subconjunto ARM64EC de registros.
Asignación de registros de enteros
Registro ARM64EC | Registro x64 | Convención de llamadas ARM64EC | Convención de llamadas ARM64 | Convención de llamadas x64 |
---|---|---|---|---|
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 (registro R1 bajo de 64 bits de x87) |
volatile | volatile | volatile |
x7 |
mm2 (registro R2 bajo de 64 bits de x87) |
volatile | volatile | volatile |
x8 |
rax |
volatile | volatile | volatile |
x9 |
mm3 (registro R3 bajo de 64 bits de x87) |
volatile | volatile | volatile |
x10 |
mm4 (registro R4 bajo de 64 bits de x87) |
volatile | volatile | volatile |
x11 |
mm5 (registro R5 bajo de 64 bits de x87) |
volatile | volatile | volatile |
x12 |
mm6 (registro R6 bajo de 64 bits de x87) |
volatile | volatile | volatile |
x13 |
N/D | no permitido | volatile | N/D |
x14 |
N/D | no permitido | volatile | N/D |
x15 |
mm7 (registro R7 bajo de 64 bits de x87) |
volatile | volatile | volatile |
x16 |
16 bits altos de cada uno de los registros R0 -R3 x87 |
volátil(xip0 ) |
volátil(xip0 ) |
volatile |
x17 |
16 bits altos de cada uno de los registros R4 -R7 x87 |
volátil(xip1 ) |
volátil(xip1 ) |
volatile |
x18 |
GS.base | fijo(TEB) | fijo(TEB) | fijo(TEB) |
x19 |
r12 |
no volátil | no volátil | no volátil |
x20 |
r13 |
no volátil | no volátil | no volátil |
x21 |
r14 |
no volátil | no volátil | no volátil |
x22 |
r15 |
no volátil | no volátil | no volátil |
x23 |
N/D | no permitido | no volátil | N/D |
x24 |
N/D | no permitido | no volátil | N/D |
x25 |
rsi |
no volátil | no volátil | no volátil |
x26 |
rdi |
no volátil | no volátil | no volátil |
x27 |
rbx |
no volátil | no volátil | no volátil |
x28 |
N/D | no permitido | no permitido | N/D |
fp |
rbp |
no volátil | no volátil | no volátil |
lr |
mm0 (registro R0 bajo de 64 bits de x87) |
Ambos | Ambos | Ambos |
sp |
rsp |
no volátil | no volátil | no volátil |
pc |
rip |
puntero de instrucción | puntero de instrucción | puntero de instrucción |
Subconjunto PSTATE : N /Z /C /V /SS 1, 2 |
Subconjunto RFLAGS : SF /ZF /CF /OF /TF |
volatile | volatile | volatile |
N/D | Subconjunto RFLAGS : PF /AF |
N/D | N/D | volatile |
N/D | Subconjunto RFLAGS : DF |
N/D | N/D | no volátil |
1 Evite leer, escribir o calcular asignaciones directamente entre PSTATE
y RFLAGS
. Estos bits se pueden usar en el futuro y están sujetos a cambios.
2 La marca de transporte ARM64EC C
es la inversa de la marca de transporte x64 CF
para las operaciones de resta. No hay control especial, ya que la marca es volátil y, por tanto, se borra al realizar la transición entre las funciones (ARM64EC y x64).
Asignación de registros vectoriales
Registro ARM64EC | Registro x64 | Convención de llamadas ARM64EC | Convención de llamadas ARM64 | Convención de llamadas x64 |
---|---|---|---|---|
v0 -v5 |
xmm0 -xmm5 |
volatile | volatile | volatile |
v6 -v7 |
xmm6 -xmm7 |
volatile | volatile | no volátil |
v8 -v15 |
xmm8 -xmm15 |
volátil 1 | volátil 1 | no volátil |
v16 -v31 |
xmm16 -xmm31 |
no permitido | volatile | no permitido (el emulador x64 no admite AVX-512) |
FPCR 2 |
MXCSR[15:6] |
no volátil | no volátil | no volátil |
FPSR 2 |
MXCSR[5:0] |
volatile | volatile | volatile |
1 Estos registros ARM64 son especiales en cuanto a que los 64 bits inferiores son no volátiles, pero los 64 bits superiores son volátiles. Desde el punto de vista de un autor de llamada x64, son realmente volátiles porque el destinatario de la llamada borraría los datos.
2 Evite leer, escribir o calcular asignaciones directamente de FPCR
y FPSR
. Estos bits se pueden usar en el futuro y están sujetos a cambios.
Empaquetado de struct
ARM64EC sigue las mismas reglas de empaquetado de estructura que se usan para x64 a fin de garantizar la interoperabilidad entre los códigos ARM64EC y x64. Para obtener más información y ejemplos de empaquetado de estructuras x64, vea Información general de las convenciones ABI de x64.
Rutinas ABI del asistente de emulación
El código ARM64EC y los códigos thunk usan rutinas auxiliares de emulación para realizar la transición entre las funciones x64 y ARM64EC.
En la tabla siguiente se describe cada rutina ABI especial y se registran los usos de ABI. Las rutinas no modifican los registros conservados enumerados en la columna ABI. No se debe realizar ninguna suposición sobre los registros no registrados en la lista. En disco, los punteros de rutina ABI son null. En el tiempo de carga, el cargador actualiza los punteros para que apunten a las rutinas del emulador x64.
Nombre | Descripción | ABI |
---|---|---|
__os_arm64x_dispatch_call_no_redirect |
Llamado por un código thunk de salida para llamar a un destino x64 (ya sea una función x64 o una secuencia de avance rápido x64). La rutina inserta la dirección de retorno ARM64EC (en el registro LR ), seguida de la dirección de la instrucción que sucede a una instrucción blr x16 que invoca el emulador x64. A continuación, ejecuta la instrucción blr x16 |
valor devuelto en x8 (rax ) |
__os_arm64x_dispatch_ret |
Llamado por una entrada thunk para volver a su autor de llamada x64. Extrae la dirección de retorno x64 de la pila e invoca al emulador x64 para saltar a ella | N/D |
__os_arm64x_check_call |
Llamado por el código ARM64EC con un puntero a un código thunk de salida y la dirección de destino de ARM64EC indirecta que se va a ejecutar. El destino ARM64EC se considera revisable y la ejecución siempre vuelve al autor de la llamada con los mismos datos con los que fue llamado o con datos modificados | Argumentos:x9 : dirección de destinox10 : dirección de código thunk de salidax11 : la dirección de secuencia de avance rápidoOut: x9 : Si la función de destino se ha desviado, contiene la dirección de la secuencia de avance rápidox10 : dirección de código thunk de salidax11 : si la función se ha desviado, contiene la dirección de código thunk de salida. De lo contrario, la dirección de destino saltó aRegistros conservados: x0 -x8 , x15 (chkstk ). y q0 -q7 |
__os_arm64x_check_icall |
Llamado por el código ARM64EC, con un puntero a un código thunk de salida, para controlar un salto a una dirección de destino que sea x64 o ARM64EC. Si el destino es x64 y el código x64 no se ha revisado, la rutina establece el registro de direcciones de destino. Apunta a la versión ARM64EC de la función si existe. De lo contrario, establece el registro para que apunte al código thunk de salida que hace la transición al destino x64. A continuación, vuelve al código ARM64EC que llama, que luego salta a la dirección del registro. Esta rutina es una versión no optimizada de __os_arm64x_check_call , donde la dirección de destino no se conoce en tiempo de compilaciónSe usa en un sitio de llamada de una llamada indirecta |
Argumentos:x9 : dirección de destinox10 : dirección de código thunk de salidax11 : la dirección de secuencia de avance rápidoOut: x9 : Si la función de destino se ha desviado, contiene la dirección de la secuencia de avance rápidox10 : dirección de código thunk de salidax11 : si la función se ha desviado, contiene la dirección de código thunk de salida. De lo contrario, la dirección de destino saltó aRegistros conservados: x0 -x8 , x15 (chkstk ), y q0 -q7 |
__os_arm64x_check_icall_cfg |
Igual que __os_arm64x_check_icall , pero también comprueba que la dirección especificada es un destino de llamada indirecta de Graph de flujo de control válido |
Argumentos:x10 : dirección de código thunk de salidax11 : dirección de la función de destinoOut: x9 : si el destino es x64, la dirección a la función. De lo contrario,indefinidox10 : dirección de código thunk de salidax11 : si el destino es x64, contiene la dirección del código thunk de salida. De lo contrario, la dirección de la funciónRegistros conservados: x0 -x8 , x15 (chkstk ), y q0 -q7 |
__os_arm64x_get_x64_information |
Obtiene la parte solicitada del contexto de registro x64 en directo | _Function_class_(ARM64X_GET_X64_INFORMATION) NTSTATUS LdrpGetX64Information(_In_ ULONG Type, _Out_ PVOID Output, _In_ PVOID ExtraInfo) |
__os_arm64x_set_x64_information |
Establece la parte solicitada del contexto de registro x64 en directo | _Function_class_(ARM64X_SET_X64_INFORMATION) NTSTATUS LdrpSetX64Information(_In_ ULONG Type,_In_ PVOID Input, _In_ PVOID ExtraInfo) |
__os_arm64x_x64_jump |
Se utiliza en el ajustador sin firma y en otros códigos thunk que reenvían directamente (jmp ) una llamada a otra función que puede tener cualquier firma, aplazando la posible aplicación del código thunk correcto al destino real |
Argumentos:x9 : destino al que saltarTodos los registros de parámetros conservados (reenviados) |
Códigos thunk
Los códigos thunk son los mecanismos de nivel inferior para admitir funciones ARM64EC y x64 que se llaman entre sí. Hay dos tipos: códigos thunk de entrada para escribir funciones ARM64EC y códigos thunk de salida para llamar a funciones x64.
Código thunk de entrada y códigos thunk de entrada intrínseca: llamada de función x64 a ARM64EC
Para admitir autores de llamada x64 cuando se compila una función C/C++ como ARM64EC, la cadena de herramientas genera un único código thunk de entrada que consta de código de máquina ARM64EC. Los intrínsecos tienen un código thunk de entrada propio. Todas las demás funciones comparten un código thunk de entrada con todas las funciones que tienen una convención de llamada, unos parámetros y un tipo de valor devuelto coincidentes. El contenido del código thunk depende de la convención de llamada de la función C/C++.
Además de controlar los parámetros y la dirección de retorno, el código thunk puentea las diferencias de volatilidad entre los registros vectoriales ARM64EC y x64 causadas por la asignación de registros vectoriales ARM64EC:
Registro ARM64EC | Registro x64 | Convención de llamadas ARM64EC | Convención de llamadas ARM64 | Convención de llamadas x64 |
---|---|---|---|---|
v6 -v15 |
xmm6 -xmm15 |
volátil, pero guardado o restaurado en el código thunk de entrada (x64 a ARM64EC) | 64 bits superiores volátiles o parcialmente volátiles | no volátil |
El código thunk de entrada realiza las siguientes acciones:
Número de parámetros | Uso de la pila |
---|---|
0-4 | Almacena ARM64EC v6 y v7 en el espacio principal asignado por el autor de la llamadaDado que el destinatario es ARM64EC, que no tiene la noción de un espacio principal, los valores almacenados no se destruyen. Asigna un extra de 128 bytes en la pila y almacena ARM64EC v8 a través de v15 . |
5-8 | x4 = 5.º parámetro de la pilax5 = 6.º parámetro de la pilax6 = 7.º parámetro de la pilax7 = 8.º parámetro de la pilaSi el parámetro es SIMD, se usan los registros v4 -v7 en su lugar |
+9 | Asigna AlignUp(NumParams - 8 , 2) * 8 bytes en la pila. *Copia el 9.º parámetro y los restantes en esta área |
* Alinear el valor con un número par garantiza que la pila permanece alineada a 16 bytes
Si la función acepta un parámetro entero de 32 bits, el código thunk solo puede insertar 32 bits en lugar de los 64 bits completos del registro primario.
A continuación, el código thunk usa una instrucción ARM64 bl
para llamar a la función ARM64EC. Después de que la función vuelva, el código thunk:
- Deshace las asignaciones de pila
- Llama al asistente del emulador
__os_arm64x_dispatch_ret
para mostrar la dirección de devolución x64 y reanudar la emulación x64.
Código thunk de salida: llamada de función ARM64EC a x64
Para cada llamada que realiza una función ARM64EC C/C++ a un código x64 potencial, la cadena de herramientas de MSVC genera un código thunk de salida. El contenido del código thunk depende de los parámetros del destinatario x64 y de si el destinatario usa la convención de llamada estándar o __vectorcall
. El compilador obtiene esta información de una declaración de función para el destinatario.
En primer lugar, el código thunk inserta la dirección de retorno que se encuentra en el registro ARM64EC lr
y un valor ficticio de 8 bytes para garantizar que la pila está alineada a 16 bytes. En segundo lugar, el código thunk controla los parámetros:
Número de parámetros | Uso de la pila |
---|---|
0-4 | Asigna 32 bytes de espacio principal en la pila |
5-8 | Asigna AlignUp(NumParams - 4, 2) * 8 más bytes más arriba en la pila. * Copia el 5.º parámetro y los parámetros posteriores de ARM64EC x4 -x7 en este espacio adicional |
+9 | Copia el 9.º parámetro y los parámetros restantes en este espacio adicional |
* Alinear el valor con un número par garantiza que la pila permanece alineada a 16 bytes.
En tercer lugar, el código thunk llama al asistente del emulador __os_arm64x_dispatch_call_no_redirect
para invocar el emulador x64 y ejecutar la función x64. La llamada debe ser una instrucción blr x16
(convenientemente, x16
es un registro volátil). Se requiere una instrucción blr x16
porque el emulador x64 analiza esta instrucción como una sugerencia.
La función x64 normalmente intenta volver al asistente del emulador mediante una instrucción x64 ret
. En este momento, el emulador x64 detecta que está en código ARM64EC. A continuación, lee la sugerencia de 4 bytes anterior que resulta ser la instrucción blr x16
de ARM64. Dado que esta sugerencia indica que la dirección de devolución está en este asistente, el emulador salta directamente a esta dirección.
La función x64 puede volver al asistente del emulador mediante cualquier instrucción de rama, incluidas jmp
y call
de x64. El emulador también controla estos escenarios.
Cuando el asistente vuelve al código thunk, el código thunk:
- Deshace las asignaciones de pila
- Muestra el registro
lr
de ARM64EC - Ejecuta una instrucción
ret lr
de ARM64.
Decoración de nombres de función ARM64EC
Un nombre de función ARM64EC tiene una decoración secundaria aplicada después de cualquier decoración específica del lenguaje. Para las funciones con vinculación de C (si se compilan como C o mediante extern "C"
), #
se antepone al nombre. Para las funciones decoradas de C++, se inserta una etiqueta $$h
en el nombre.
foo => #foo
?foo@@YAHXZ => ?foo@@$$hYAHXZ
__vectorcall
La cadena de herramientas ARM64EC no admite __vectorcall
actualmente. El compilador emite un error cuando detecta el uso de __vectorcall
con ARM64EC.
Consulte también
Descripción de la ABI y el código de ensamblado de ARM64EC
Problemas comunes de migración de ARM en Visual C++
Nombres representativos