AMD64:¿instrucciones de ensamblaje ahora?

AMD64:¿instrucciones de ensamblaje ahora?


En esta salida del compilador, estoy tratando de entender cómo la codificación de código de máquina del nopw la instrucción funciona:


00000000004004d0 <main>:
4004d0: eb fe jmp 4004d0 <main>
4004d2: 66 66 66 66 66 2e 0f nopw %cs:0x0(%rax,%rax,1)
4004d9: 1f 84 00 00 00 00 00

Hay alguna discusión sobre "nopw" en http://john.freml.in/amd64-nopl. ¿Alguien puede explicar el significado de 4004d2-4004e0? Al mirar la lista de códigos de operación, parece que 66 .. los códigos son expansiones de varios bytes. Siento que probablemente podría obtener una mejor respuesta a esto aquí de lo que lo haría a menos que intentara asimilar la lista de códigos de operación durante unas horas.



Esa salida de asm proviene del siguiente código (loco) en C, que se optimiza hasta un bucle infinito simple:


long i = 0;
main() {
recurse();
}
recurse() {
i++;
recurse();
}

Cuando se compila con gcc -O2 , el compilador reconoce la recursividad infinita y la convierte en un bucle infinito; lo hace tan bien, de hecho, que en realidad hace un bucle en el main() sin llamar al recurse() función.



nota del editor:las funciones de relleno con NOP no son específicas de los bucles infinitos. Aquí hay un conjunto de funciones con un rango de longitudes de NOP, en el explorador del compilador Godbolt.


Respuestas:


Los 0x66 los bytes son un prefijo de "Anulación del tamaño del operando". Tener más de uno de estos es equivalente a tener uno.


El 0x2e es un 'prefijo nulo' en el modo de 64 bits (de lo contrario, es una anulación del segmento CS:por lo que aparece en el mnemotécnico del ensamblado).


0x0f 0x1f es un código de operación de 2 bytes para un NOP que toma un byte ModRM


0x84 es el byte ModRM que en este caso codifica para un modo de direccionamiento que usa 5 bytes más.


Algunas CPU tardan en decodificar instrucciones con muchos prefijos (por ejemplo, más de tres), por lo que un byte ModRM que especifica un SIB + disp32 es una forma mucho mejor de usar 5 bytes adicionales que cinco bytes de prefijo más.




Esencialmente, esos bytes son una instrucción NOP larga que nunca se ejecutará de todos modos. Está ahí para garantizar que la siguiente función esté alineada en un límite de 16 bytes, porque el compilador emitió un .p2align 4 directiva, por lo que el ensamblador completó con un NOP. el valor predeterminado de gcc para x86 es

-falign-functions=16 . Para los NOP que se ejecutarán, la elección óptima de NOP largo depende de la microarquitectura. Para una microarquitectura que se atraganta con muchos prefijos, como Intel Silvermont o AMD K8, dos NOP con 3 prefijos cada uno podrían haber decodificado más rápido.


El artículo del blog al que se vincula la pregunta ( http://john.freml.in/amd64-nopl ) explica por qué el compilador usa una instrucción NOP única complicada en lugar de un montón de instrucciones NOP 0x90 de un solo byte.


Puede encontrar los detalles sobre la codificación de instrucciones en los documentos de referencia técnica de AMD:



  • http://developer.amd.com/documentation/guides/pages/default.aspx#manuals


Principalmente en el "Manual del programador de arquitectura AMD64 Volumen 3:Propósito general e instrucciones del sistema". Estoy seguro de que las referencias técnicas de Intel para la arquitectura x64 tendrán la misma información (e incluso podrían ser más comprensibles).