macos - ¿Por qué la Mac ABI requiere una alineación de pila de 16 bytes para x86-32?
stack alignment (10)
¿No está seguro de por qué nadie ha considerado la posibilidad de una fácil portabilidad desde la plataforma heredada basada en PowerPC?
Lee esto:
Y luego se amplió a "convenciones de llamada de función PowerPC de 32 bits" y finalmente esto:
"Estos son los modos de alineación de inserción disponibles en el entorno de PowerPC de 32 bits:
El modo de alineación de potencia se deriva de las reglas de alineación utilizadas por el compilador IBM XLC para el sistema operativo AIX. Es el modo de alineación predeterminado para la versión de Arquitectura de PowerPC de GCC utilizada en AIX y Mac OS X. Debido a que este modo es más probable que sea compatible entre compiladores de arquitecturas PowerPC de diferentes proveedores, por lo general se usa con estructuras de datos compartidas entre diferentes programas ".
En vista de los antecedentes heredados de OSX basados en PowerPC, la portabilidad es una consideración importante: dicta seguir la convención hasta el compilador XLC de AIX. Cuando piense en términos de la necesidad de asegurarse de que todas las herramientas y aplicaciones funcionen junto con una revisión mínima, creo que es importante mantener el mismo ABI heredado en la medida de lo posible.
Eso da la filosofía, y leer más es la regla explícitamente mencionada ("Prolog y Epilog"):
La función llamada es responsable de asignar su propio marco de pila, asegurándose de mantener la alineación de 16 bytes en la pila. Esta operación se lleva a cabo mediante una sección de código llamada prólogo, que el compilador coloca antes del cuerpo de la subrutina. Después del cuerpo de la subrutina, el compilador coloca un epílogo para restaurar el procesador al estado en que se encontraba antes de la llamada a la subrutina.
Puedo entender este requisito para los viejos sistemas PPC RISC e incluso para x86-64, ¿pero para el viejo x86 probado y verdadero? En este caso, la pila debe alinearse solo en los límites de 4 bytes. Sí, algunas de las instrucciones MMX / SSE requieren alineaciones de 16 bytes, pero si eso es un requisito del destinatario, entonces debe garantizar que las alineaciones sean correctas. ¿Por qué cargar a cada persona que llama con este requisito adicional? Esto realmente puede causar algunas caídas en el rendimiento porque cada sitio de llamadas debe administrar este requisito. ¿Me estoy perdiendo de algo?
Actualización: después de algunas investigaciones más sobre esto y algunas consultas con algunos colegas internos, tengo algunas teorías sobre esto:
- Consistencia entre la versión PPC, x86 y x64 del sistema operativo
- Parece que el codegen GCC ahora consistentemente hace un sub esp, xxx y luego "mov" s los datos en la pila en lugar de simplemente hacer una instrucción "push". Esto podría ser más rápido en algunos hardware.
- Si bien esto complica un poco los sitios de llamadas, hay muy poca sobrecarga adicional cuando se utiliza la convención "cdecl" por defecto, donde la persona que llama limpia la pila.
El problema que tengo con el último elemento, es que para las convenciones de llamadas que se basan en la llamada de limpieza de la pila, los requisitos anteriores realmente "uglifies" el codegen. Por ejemplo, ¿qué compilador decidió implementar un estilo de llamada basado en registro más rápido para su propio uso interno (es decir, cualquier código que no esté destinado a ser llamado desde otros idiomas o fuentes)? Esta alineación de pila podría anular algunas de las mejoras de rendimiento logradas al pasar algunos parámetros en los registros.
Actualización: Hasta ahora, las únicas respuestas reales han sido la coherencia, pero para mí eso es demasiado fácil de responder. Tengo más de 20 años de experiencia con la arquitectura x86 y si la consistencia, el rendimiento o algo más concreto, es realmente la razón, entonces sugiero respetuosamente que es un poco ingenuo para los desarrolladores que lo requieran. Están ignorando casi tres décadas de herramientas y soporte. Especialmente si esperan que los proveedores de herramientas adapten rápida y fácilmente sus herramientas para su plataforma (tal vez no ... es Apple ...) sin tener que pasar por varios aros aparentemente innecesarios.
Daré este tema un día más o menos y luego lo cerraré ...
Relacionado
Creo que es para mantenerlo en línea con el ABI x86-64.
De "Intel®64 y IA-32 Architectures Optimization Reference Manual", sección 4.4.2:
"Para obtener el mejor rendimiento, las extensiones Streaming SIMD y Streaming SIMD Extensions 2 requieren que sus operandos de memoria se alineen con los límites de 16 bytes. Los datos no alineados pueden causar penalizaciones de rendimiento significativas en comparación con los datos alineados".
Del apéndice D:
"Es importante asegurarse de que el marco de la pila esté alineado con un límite de 16 bytes al ingresar la función para mantener los datos de __m128 locales, los parámetros y las ubicaciones de derrames del registro XMM alineados a lo largo de una invocación de función".
Este es un problema de eficiencia.
Asegurarse de que la pila esté alineada en 16 bytes en cada función que utiliza las nuevas instrucciones de SSE agrega una gran cantidad de sobrecarga para usar esas instrucciones, lo que reduce el rendimiento.
Por otro lado, mantener el stack de 16 bytes alineado en todo momento asegura que puede usar las instrucciones de SSE libremente sin penalización de rendimiento. No hay ningún costo para esto (el costo se mide en instrucciones al menos). Solo implica cambiar una constante en el prólogo de la función.
Perder el espacio de pila es barato, es probablemente la parte más caliente de la memoria caché.
Hmm, ¿OS X ABI tampoco hizo RISC divertido como cosas como pasar pequeñas estructuras en los registros?
Entonces eso apunta a la coherencia con la teoría de otras plataformas.
Ahora que lo pienso, la api syscall de FreeBSD también alinea los valores de 64 bits. (como por ejemplo lseek y mmap)
No estoy seguro ya que no tengo pruebas de primera mano, pero creo que la razón es SSE. SSE es mucho más rápido si sus buffers ya están alineados en un límite de 16 bytes (movps vs movups), y cualquier x86 tiene al menos sse2 para mac os x. Puede ser atendido por el usuario de la aplicación, pero el costo es bastante significativo. Si el costo total para hacerlo obligatorio en el ABI no es demasiado significativo, puede valer la pena. SSE se usa de manera general en mac os X: acelerar framework, etc ...
Para mantener la consistencia en kernel. Esto permite que el mismo núcleo se inicie en múltiples arquitecturas sin modificación.
Primero, tenga en cuenta que la alineación de 16 bytes es una excepción introducida por Apple al Sistema V IA-32 ABI.
La alineación de la pila solo es necesaria cuando se invocan funciones del sistema, ya que muchas bibliotecas del sistema utilizan extensiones SSE o Altivec que requieren la alineación de 16 bytes. Encontré una referencia explícita en la página de manual de libgmalloc .
Puede manejar perfectamente su estructura de pila de la forma que desee, pero si intenta llamar a una función del sistema con una pila desalineada, terminará con un mensaje misaligned_stack_error .
Editar: para el registro, puede deshacerse de los problemas de alineación al compilar con GCC utilizando la opción mstack-realign .
Si bien no puedo responder a su pregunta de POR QUÉ, puede encontrar útiles los manuales en el siguiente sitio:
http://www.agner.org/optimize/
Con respecto al ABI, eche un vistazo especialmente a:
http://www.agner.org/optimize/calling_conventions.pdf
Espero que sea útil.
Supongo que Apple cree que todos solo usan XCode (gcc) que alinea la pila para usted. Por lo tanto, es necesario que la pila esté alineada para que el kernel no tenga que hacerlo, solo es una microoptimización.