2011-07-05 8 views
6

Entiendo que en el ensamblaje x86_64 existe, por ejemplo, el registro de rax (64 bit), pero también se puede acceder como un registro de 32 bits, eax, 16 bit, ax y 8 bit, al. ¿En qué situación no usaría solo los 64 bits completos, y por qué, qué ventaja habría?ensamblado de 64 bits, cuándo utilizar registros de tamaño más pequeño

A modo de ejemplo, con este sencillo programa hola mundo:

section .data 
msg: db "Hello World!", 0x0a, 0x00 
len: equ $-msg 

section .text 
global start 

start: 
mov rax, 0x2000004  ; System call write = 4 
mov rdi, 1    ; Write to standard out = 1 
mov rsi, msg   ; The address of hello_world string 
mov rdx, len   ; The size to write 
syscall     ; Invoke the kernel 
mov rax, 0x2000001  ; System call number for exit = 1 
mov rdi, 0    ; Exit success = 0 
syscall     ; Invoke the kernel 

RDI y RDX, al menos, sólo necesita 8 bits y no 64, ¿verdad? Pero si los cambio a dil y dl, respectivamente (sus equivalentes inferiores de 8 bits), el programa se ensambla y se vincula, pero no genera nada.

Sin embargo, todavía funciona si uso eax, edi y edx, entonces ¿debería usar esos en lugar de los 64 bits completos? ¿Por qué o por qué no?

+1

En realidad, en Linux (¿y probablemente todo lo demás?) Los parámetros de un syscall son de 32 bits de ancho, por lo que debe usar EDI y EDX. http://www.win.tue.nl/~aeb/linux/lk/lk-4.html#ss4.3 –

+0

¿qué pasa con rax, debería cambiar eso a eax también?Traté de cambiar esos 3 y funciona, pero lo que quiero saber es por qué debería hacer esto y cuál es la ventaja. – mk12

+0

En el caso de este programa, la única diferencia apreciable es que los valores literales (4, 1, 0, etc.) son dos veces más grandes cuando son de 64 bits, por lo que su programa tendrá unos bytes más grandes y, en teoría, podría tomar más tiempo cargar en la CPU desde el disco/memoria. –

Respuesta

2

Lo primero y más importante sería cuando cargue un valor más pequeño (por ejemplo, 8 bits) de la memoria (leyendo un carácter, trabajando en una estructura de datos, deserializando un paquete de red, etc.) en un registro.

MOV AL, [0x1234] 

frente

MOV RAX, [0x1234] 
SHR RAX, 56 
# assuming there are actually 8 accessible bytes at 0x1234, 
# and they're the right endianness; otherwise you'd need 
# AND RAX, 0xFF or similar... 

O, por supuesto, la escritura dicho valor a la memoria.


(Editar, al igual que 6 años después):

Dado que este se presenta de nuevo:

MOV AL, [0x1234] 
  • sólo lee un solo byte de memoria en 0x1234 (la inversa haría solo sobrescribir un solo byte de memoria)
  • mantiene lo que haya en los otros 56 bits de RAX
    • Esto crea una dependencia entre los valores pasados ​​y futuros de RAX, por lo que la CPU no puede optimizar la instrucción usando register renaming.

Por el contrario:

MOV RAX, [0x1234] 
  • lee 8 bytes de memoria a partir de 0x1234 (la inversa podría sobrescribir 8 bytes de memoria)
  • sobrescribe todo de RAX
  • supone que los bytes en la memoria tienen el mismo endianness que la CPU (a menudo no es cierto en los paquetes de red, por lo tanto m Hace y SHR años de instrucción)

También es importante destacar:

MOV EAX, [0x1234] 

Entonces, como se ha mencionado en los comentarios, hay:

MOVZX EAX, byte [0x1234] 
  • sólo lee un solo byte de memoria en 0x1234
  • extiende el valor de llenar todos EAX (y por lo tanto RAX) con ceros (eliminando la dependencia y permitiendo el registro de las optimizaciones de cambio de nombre).

En todos estos casos, si quieres escribir de la 'A' registrarse en la memoria tendría que escoger su anchura:

MOV [0x1234], AL ; write a byte (8 bits) 
MOV [0x1234], AX ; write a word (16 bits) 
MOV [0x1234], EAX ; write a dword (32 bits) 
MOV [0x1234], RAX ; write a qword (64 bits) 
+2

Erm ... x86_64 es _always_ little endian, por lo que sus ejemplos arrojarán resultados diferentes. – Ruslan

+1

La mejor opción aquí es 'movzx eax, [0x1234]'. –

+0

Peter cordes tiene razón. El "mov" no rompe la cadena de dependencia. –

1

Si desea trabajar con un solo una cantidad de 8 bits, entonces trabajarías con el registro AL. Lo mismo para AX y EAX.

Por ejemplo, podría tener un valor de 64 bits que contenga dos valores de 32 bits. Puede trabajar en los 32 bits bajos al acceder al registro EAX. Cuando desee trabajar en los 32 bits altos, puede intercambiar las dos cantidades de 32 bits (invierta las DWORD en el registro) para que los bits altos estén ahora en EAX.

+0

¿Cómo podría intercambiar las cantidades de 32 bits? – mk12

+0

Gire RAX a través de 32 bits. –

+0

¿Cuál sería la instrucción real en nasm? Soy algo nuevo en esto. – mk12

1

64-bit es la pieza más grande de memoria con la que puede trabajar como una sola unidad. Eso no quiere decir que es la cantidad que necesita usar.

Si necesita 8 bits, use 8. Si necesita 16, use 16. Si no importa cuántos bits, no importa cuántos use.

Es cierto que cuando se utiliza un procesador de 64 bits, hay muy poca sobrecarga para usar los 64 bits completos. Pero si, por ejemplo, está calculando un valor de byte, trabajar con un byte significará que el resultado ya tendrá el tamaño correcto.

+0

He editado mi pregunta respecto a cómo esto me confunde. – mk12

5

Usted está haciendo varias preguntas aquí.

Si solo carga los 8 bits bajos de un registro, el resto del registro mantendrá su valor anterior. Eso puede explicar por qué su llamada al sistema obtuvo los parámetros incorrectos.

Una razón para usar 32 bits cuando eso es todo lo que necesita es que muchas instrucciones que usan EAX o EBX son un byte más cortas que las que usan RAX o RBX. También podría significar que las constantes cargadas en el registro son más cortas.

¡El conjunto de instrucciones ha evolucionado durante un largo tiempo y tiene bastantes peculiaridades!

2

Si solo necesita registros de 32 bits, puede trabajar con ellos de manera segura, esto está bien por debajo de 64 bits. Pero si solo necesita registros de 16 bits u 8 bits, intente evitarlos o siempre use movzx/movsx para borrar los bits restantes. Es bien sabido que en x86-64, el uso de operandos de 32 bits borra los bits más altos del registro de 64 bits. El objetivo principal de esto es evitar las cadenas de falsa dependencia.

Por favor refiérase a la sección pertinente - 3.4.1.1 - El Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 1 de:

operandos de 32 bits generan un resultado de 32 bits, cero extendieron a un resultado de 64 bits en el destino general- propósito Registrar

Romper las cadenas de dependencia permite a las instrucciones que se ejecutarán en paralelo, en orden aleatorio, por el Out-of-Order algorithm implementado internamente por las CPU Pentium Pro ya en 1995.

AQ uote del Intel® 64 and IA-32 Architectures Optimization Reference Manual, Sección 3.5.1.8:

secuencias de código que modifica el registro parcial pueden experimentar algún retraso en su cadena de dependencias, pero puede ser evitado mediante el uso de dependencia romper modismos. En los procesadores basados ​​en la microarquitectura Intel Core, varias instrucciones pueden ayudar a borrar la dependencia de la ejecución cuando el software usa estas instrucciones para borrar el contenido del registro a cero. Rompe las dependencias en porciones de registros entre instrucciones operando en registros de 32 bits en lugar de registros parciales. Para movimientos, esto se puede lograr con movimientos de 32 bits o usando MOVZX.

Codificación de ensamblaje/compilación Regla 37. (M impacto, generalidad MH): rompe las dependencias en porciones de registros entre instrucciones operando en registros de 32 bits en lugar de registros parciales. Para movimientos, esto se puede lograr con movimientos de 32 bits o usando MOVZX.

El MOVZX y el MOV con operandos de 32 bits para x64 son equivalentes: todos rompen las cadenas de dependencia.

Es por eso que su código se ejecutará más rápido si siempre intenta borrar los bits más altos de registros más grandes cuando usa registros más pequeños. Cuando los bits son siempre claros, si no hay dependencias en el valor anterior del registro, la CPU puede renombrar internamente los registros.

Register renaming es una técnica utilizada internamente por una CPU que elimina las dependencias falsas de datos que surgen de la reutilización de registros mediante sucesivas instrucciones que no tienen ninguna dependencia real de datos entre ellas.

Cuestiones relacionadas