Luego de mucho batallar hemos hallado el resultado de este enigma que muchos de nuestros usuarios de nuestro sitio web presentan. Si quieres aportar algún detalle no dejes de compartir tu comentario.
Solución:
Propósito general significa que todos estos registros pueden usarse con cualquier instrucción que realice cálculos con registros de propósito general, mientras que, por ejemplo, no puede hacer lo que quiera con el puntero de instrucción (RIP) o el registro de banderas (RFLAGS).
Algunos de estos registros fueron previstos para ser usados para un uso específico, y comúnmente lo son. Los más críticos son el RSP y RBP.
Si necesita usarlos para su propio propósito, debe guardar su contenido antes de guardar algo más en su interior y restaurarlos a su valor original cuando haya terminado.
Si un registro puede ser un operando para add
, o usado en un modo de direccionamiento, es “propósito general”, a diferencia de registros como el FS
registro de segmento o RIP. Los registros GP también se denominan “registros de números enteros”, aunque otros tipos de registros también pueden contener números enteros.
En la arquitectura de la computadora, es común que las CPU manejen internamente registros / instrucciones enteros por separado de los registros / instrucciones FP / SIMD. por ejemplo, las CPU de la familia Intel Sandybridge tienen archivos de registro físico separados para cambiar el nombre de los registros GP integer vs. FP / vector. Estos se denominan simplemente archivos de registro de enteros frente a FP. (Donde FP es la abreviatura de todo lo que un kernel no necesita guardar / restaurar para usar los registros GP mientras deja intacto el estado FPU / SIMD del espacio de usuario). Cada entrada en el archivo de registro FP tiene 256 bits de ancho (para contienen un vector AVX ymm), pero las entradas del archivo de registro de enteros solo tienen que tener un ancho de 64 bits.
En las CPU que cambian el nombre de los registros de segmento (Skylake no lo hace), supongo que eso sería parte del estado entero, al igual que RFLAGS + RIP. Pero cuando decimos “registro entero”, normalmente nos referimos específicamente a un registro de propósito general.
Cada registro tiene alguna especialidad para algunas instrucciones, excepto algunos de los registros completamente nuevos agregados con x86-64: R8-R15. Estos no los descalifican como de uso general. Los (16 bajos de los) 8 originales se remontan a 8086, y hubo usos implícitos de cada uno de ellos incluso en el 8086 original.
Para RSP, es especial para push / pop / call / ret, por lo que la mayoría del código nunca lo usa para nada más. (Y en el modo kernel, se usa de forma asincrónica para las interrupciones, por lo que realmente no puede guardarlo en algún lugar para obtener un registro GP adicional de la forma en que puede hacerlo en el código de espacio de usuario: ¿ESP es tan general como EAX?)
Pero en el condicional controlado (como sin manejadores de señales) no es necesario usar RSP para un puntero de pila. por ejemplo, puede usarlo para leer un array en un bucle con pop, como en esta respuesta de código de golf. (De hecho usé esp
en código de 32 bits, pero la misma diferencia: pop
es más rápido que lodsd
en Skylake, mientras que ambos son de 1 byte).
Usos implícitos y especialidad para cada registro:
Consulte también Ensamblado x86: por qué [e]bx conservado en convenciones de llamadas? para una lista parcial.
Principalmente estoy limitando esto a las instrucciones de espacio de usuario, especialmente a las que un compilador moderno podría emitir desde código C o C ++. No intento ser exhaustivo para las reglas que tienen muchos usos implícitos.
-
rax
: un operando [i]mul / [i]div / cdq / cdqe, string instrucciones (stos),cmpxchg
, etc. etc. Así como codificaciones especiales más cortas para muchas instrucciones inmediatas como 2 bytescmp al, 1
o 5 bytesadd eax, 12345
(sin byte ModRM). Consulte también codegolf.SE Consejos para jugar al golf en código máquina x86 / x64.También hay
xchg
-con-eax que es donde0x90 nop
vino de (antesnop
se convirtió en una instrucción documentada por separado en x86-64, porquexchg eax,eax
cero-extiende eax en RAX y, por lo tanto, no puede usar el0x90
codificación. Peroxchg rax,rax
pueden aún ensamblar a REX.W = 1 0x90.) rcx
: recuentos de turnos,rep
-string cuenta, el lentoloop
instrucciónrdx
:rdx:rax
se usa para dividir y multiplicar, y cwd / cdq / cqo para configurarlos.rdtsc
. IMC2mulx
.-
rbx
: 8086xlatb
.cpuid
utilice los cuatro de EAX..EDX. 486cmpxchg8b
, x86-64cmpxchg16b
. La mayoría de los compiladores de 32 bits emitiráncmpxchg8
porstd::atomic
. (Pure load / pure store puede usar SSE MOVQ o x87 fild / fistp, sin embargo, si apunta a Pentium o posterior). Los compiladores de 64 bits usarán 64 bits::compare_exchange_weak lock cmpxchg
, no cmpxchg8b.Algunos compiladores de 64 bits emitirán
cmpxchg16b
poratomic
. RBX tiene la menor cantidad de usos implícitos de los 8 originales, perolock cmpxchg16b
es uno de los pocos compiladores que realmente utilizará. rsi
/rdi
: string operaciones, incluyendorep movsb
que algunos compiladores a veces están en línea. (gcc también en línearep cmpsb
por string literales en algunos casos, pero probablemente eso no sea óptimo).rbp
:leave
(solo 1 uop más lento quemov rsp, rbp
/pop rbp
. gcc en realidad lo usa en funciones con un puntero de marco, cuando no puede simplementepop rbp
). También el horriblemente lentoenter
que nadie usa nunca.-
rsp
: operaciones de pila: push / pop / call / ret, yleave
. (Yenter
). Y en modo kernel (no espacio de usuario) uso asincrónico por hardware para ahorrar contexto de interrupción. Es por eso que el código del kernel no puede tener una zona roja. -
r11
:syscall
/sysret
utilícelo para guardar / restaurar RFLAGS del espacio de usuario. (Junto con RCX para guardar / restaurar el RIP del espacio de usuario).
Casos especiales de codificación en modo de direccionamiento:
(Consulte también ¿rbp no permitido como base SIB? Que trata solo sobre los modos de direccionamiento, donde copié esta parte de esta respuesta).
rbp
/r13
no puede ser un registro base sin desplazamiento: esa codificación en su lugar significa: (en ModRM) rel32
(Relativo a RIP) o (en SIB) disp32
sin registro base. (r13
utiliza los mismos 3 bits en ModRM / SIB, por lo que esta opción simplifica la decodificación al no hacer que el decodificador de longitud de instrucción mire el bit REX.B para obtener el cuarto bit de registro base). [r13]
se ensambla a [r13 + disp8=0]
. [r13+rdx]
se ensambla a [rdx+r13]
(evitando el problema intercambiando base / índice cuando esa es una opción).
rsp
/r12
como registro base siempre necesita un byte SIB. (La codificación ModR / M de base = RSP es un código de escape para señalar un byte SIB, y nuevamente, más decodificadores tendrían que preocuparse por el REX prefix si r12
se manejó de manera diferente).
rsp
no puede ser un registro de índice. Esto hace posible codificar [rsp]
, que es más útil que [rsp + rsp]
. (Intel podría haber diseñado las codificaciones ModRM / SIB para los modos de direccionamiento de 32 bits (nuevo en 386) por lo que SIB-sin-índice solo era posible con base = ESP. Eso haría [eax + esp*4]
posible y solo excluir [esp + esp*1/2/4/8]
. Pero eso no es útil, por lo que simplificaron el hardware haciendo index = ESP el código para ningún índice, independientemente de la base. Esto permite dos formas redundantes de codificar cualquier modo de direccionamiento base o base + disp: con o sin un SIB.)
r12
pueden ser un registro de índice. A diferencia de los otros casos, esto no afecta la decodificación de la longitud de la instrucción. Además, no se puede solucionar con una codificación más larga como en los otros casos. AMD quería que el registro de AMD64 fuera lo más ortogonal posible, por lo que tiene sentido que gasten algunos transistores adicionales para verificar REX.X como parte de la decodificación de índice / sin índice. Por ejemplo, [rsp + r12*4]
requiere index = r12, por lo que teniendo r12
un propósito no completamente generalizado haría de AMD64 un peor objetivo del compilador.
0: 41 8b 03 mov eax,DWORD PTR [r11]
3: 41 8b 04 24 mov eax,DWORD PTR [r12] # needs a SIB like RSP
7: 41 8b 45 00 mov eax,DWORD PTR [r13+0x0] # needs a disp8 like RBP
b: 41 8b 06 mov eax,DWORD PTR [r14]
e: 41 8b 07 mov eax,DWORD PTR [r15]
11: 43 8b 04 e3 mov eax,DWORD PTR [r11+r12*8] # *can* be an index
A los compiladores les gusta cuando todos los registros pueden se puede utilizar para cualquier cosa, restringiendo únicamente la asignación de registros para unas pocas operaciones de casos especiales. Esto es lo que se entiende por ortogonalidad de registro.
Recuerda que puedes recomendar este artículo si te fue de ayuda.