Elegir cuatro registros de argumentos en x64 - común a UN * X / Win64
Una de las cosas a tener en cuenta acerca de x86 es que el nombre del registro para la codificación del "número de registro" no es obvio; en términos de codificación de instrucciones (el byte MOD R / M , consulte http://www.c-jump.com/CIS77/CPU/x86/X77_0060_mod_reg_r_m_byte.htm ), los números de registro 0 ... 7 son, en ese orden, ?AX
, ?CX
, ?DX
, ?BX
, ?SP
, ?BP
, ?SI
, ?DI
.
Por lo tanto, elegir A / C / D (reglas 0..2) como valor de retorno y los dos primeros argumentos (que es la __fastcall
convención "clásica" de 32 bits ) es una elección lógica. En lo que respecta a ir a 64 bits, las reglas "superiores" están ordenadas, y tanto Microsoft como UN * X / Linux eligieron R8
/ R9
como las primeras.
Teniendo esto en mente, la elección de de Microsoft RAX
(valor de retorno) y RCX
, RDX
, R8
, R9
(arg [0..3]) son una selección comprensible si elige cuatro registros encontrados para argumentos.
No sé por qué el AMD64 UN * X ABI eligió RDX
antes RCX
.
Elección de seis registros de argumentos en x64: específico de UN * X
UN * X, en arquitecturas RISC, tradicionalmente ha hecho pasar argumentos en registros, específicamente, para los primeros seis argumentos (eso es así en PPC, SPARC, MIPS al menos). Lo cual podría ser una de las principales razones por las que los diseñadores de ABI AMD64 (UN * X) eligieron usar seis registros en esa arquitectura también.
Así que si quieres seis registros para pasar argumentos en, y es lógico que elegir RCX
, RDX
, R8
y R9
para cuatro de ellos, que otros dos debe escoger?
Las reglas "superiores" requieren un byte de prefijo de instrucción adicional para seleccionarlas y, por lo tanto, tienen un tamaño de instrucción más grande, por lo que no querrá elegir ninguna de ellas si tiene opciones. De los registros clásicos, debido al significado implícito de RBP
y RSP
estos no están disponibles, y RBX
tradicionalmente tiene un uso especial en UN * X (tabla de compensación global) con el que aparentemente los diseñadores de ABI AMD64 no querían volverse innecesariamente incompatibles.
Ergo, la única opción era RSI
/ RDI
.
Entonces, si tiene que tomar RSI
/ RDI
como registros de argumentos, ¿qué argumentos deberían ser?
Hacerlos arg[0]
y arg[1]
tiene algunas ventajas. Vea el comentario de cHao.
?SI
y ?DI
son operandos de origen / destino de instrucciones de cadena, y como mencionó cHao, su uso como registros de argumentos significa que con las convenciones de llamada AMD64 UN * X, la strcpy()
función más simple posible , por ejemplo, solo consta de las dos instrucciones de CPU repz movsb; ret
porque el origen / destino La persona que llama ha introducido las direcciones en los registros correctos. Existe, particularmente en el código de "pegamento" de bajo nivel y generado por el compilador (piense, por ejemplo, en algunos asignadores de montón de C ++ objetos de relleno cero en la construcción, o las páginas de montón de relleno cero del kernel ensbrk()
, o fallas de página de copia en escritura) una enorme cantidad de copia / relleno de bloque, por lo tanto, será útil para el código que se usa con tanta frecuencia para guardar las dos o tres instrucciones de la CPU que de otra manera cargarían dichos argumentos de dirección de origen / destino en el registros "correctos".
Así pues, en cierto modo, UN * X y Win64 sólo son diferentes en que la ONU * X "antepone" dos argumentos adicionales, a propósito de elegidos RSI
/ RDI
registros, a la elección natural de cuatro argumentos en RCX
, RDX
, R8
y R9
.
Más allá de eso ...
Hay más diferencias entre las ABI de UN * X y Windows x64 que solo la asignación de argumentos a registros específicos. Para obtener una descripción general de Win64, consulte:
http://msdn.microsoft.com/en-us/library/7kcdt6fy.aspx
Win64 y AMD64 UN * X también difieren notablemente en la forma en que se usa el espacio de pila; en Win64, por ejemplo, la persona que llama debe asignar espacio de pila para los argumentos de la función aunque los argumentos 0 ... 3 se pasen en los registros. En UN * X, por otro lado, una función de hoja (es decir, una que no llama a otras funciones) ni siquiera se requiere para asignar espacio de pila en absoluto si no necesita más de 128 bytes (sí, usted posee y puede usar una cierta cantidad de pila sin asignarla ... bueno, a menos que sea el código del kernel, una fuente de errores ingeniosos). Todas estas son opciones de optimización particulares, la mayor parte de la justificación para ellas se explica en las referencias ABI completas a las que apunta la referencia de wikipedia del póster original.