Qué significa “rep; nop; “¿significa en ensamblaje x86? ¿Es lo mismo que la instrucción de “pausa”?

  • ¿Qué hace rep; nop rep; nop significa?
  • ¿Es lo mismo que la instrucción de pause ?
  • ¿Es lo mismo que rep nop (sin el punto y coma)?
  • ¿Cuál es la diferencia con la instrucción simple nop ?
  • ¿Se comporta de manera diferente en los procesadores AMD e Intel?
  • (bonificación) ¿Dónde está la documentación oficial para estas instrucciones?

Motivación para esta pregunta

Después de un debate en los comentarios de otra pregunta , me di cuenta de que no sé qué rep; nop; rep; nop; significa en ensamblaje x86 (o x86-64). Y tampoco pude encontrar una buena explicación en la web.

Sé que rep es un prefijo que significa “repetir la siguiente instrucción cx veces” (o al menos lo era, en el antiguo ensamblaje x86 de 16 bits). De acuerdo con esta tabla de resumen en Wikipedia , parece que la rep solo se puede usar con movs , cmps , lods , scas , scas (pero tal vez esta limitación se eliminó en los procesadores más nuevos). Por lo tanto, creo que rep nop (sin punto y coma) repetiría una operación de nop cx veces.

Sin embargo, después de buscar más, me confundí aún más. Parece que rep; nop rep; nop y pause map a exactamente el mismo opcode , y pause tiene un comportamiento un poco diferente a simplemente nop . Algunos correos antiguos de 2005 dijeron cosas diferentes:

  • “intenta no quemar demasiado poder”
  • “es equivalente a ‘nop’ solo con encoding de 2 bytes”.
  • “Es magia en Intel. Es como ‘nop pero deja que el otro hermano HT corra'”
  • “es pausa en Intel y relleno rápido en Athlon”

Con estas opiniones diferentes, no pude entender el significado correcto.

Se está utilizando en el kernel de Linux (tanto en i386 como en x86_64 ), junto con este comentario: /* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */ /* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */ También se está utilizando en BeRTOS , con el mismo comentario.

rep; nop rep; nop es de hecho el mismo que la instrucción de pause (código de operación F390 ). Puede usarse para ensambladores que aún no admiten la instrucción de pause . En procesadores anteriores, esto simplemente no hizo nada, al igual que nop pero en dos bytes. En los nuevos procesadores compatibles con hyperthreading, se usa como una pista para el procesador de que está ejecutando un spinloop para boost el rendimiento. De la referencia de instrucciones de Intel :

Mejora el rendimiento de los bucles spin-wait. Al ejecutar un “bucle de espera de centrifugado”, un procesador Pentium 4 o Intel Xeon sufre una grave penalización de rendimiento al salir del bucle porque detecta una posible violación de orden de memoria. La instrucción PAUSE proporciona una pista al procesador de que la secuencia de código es un ciclo de espera de giro. El procesador usa esta sugerencia para evitar la violación de orden de memoria en la mayoría de las situaciones, lo que mejora enormemente el rendimiento del procesador. Por esta razón, se recomienda que se coloque una instrucción PAUSE en todos los bucles spin-wait.

Los prefijos que no se aplican a una instrucción se ignoran. Sin embargo, las futuras CPU pueden usar esa secuencia de bytes para codificar una nueva instrucción. (Sí, el espacio de código de operación x86 es tan limitado que hacen cosas locas como esta, y sí hace que los decodificadores sean complicados).

En este caso, significa que puede usar pause en spinloops sin romper la compatibilidad hacia atrás . Las CPU antiguas que no conocen la pause lo decodificarán como NOP sin daños. En las CPUs nuevas, se obtiene el beneficio de ahorro de energía / compatibilidad con HT, y se evita la mala especulación ordenando la memoria cuando la memoria que está girando cambia y se sale del ciclo de rotación.


Enlaces a los manuales de Intel y a muchas otras cosas buenas en la página de información wiki de la etiqueta x86: https://stackoverflow.com/tags/x86/info

Otro caso de un prefijo rep sin sentido se está convirtiendo en una nueva instrucción en nuevas CPU: lzcnt es F3 0F BD /r . En las CPU que no admiten esa instrucción (falta el indicador de función LZCNT en su CPUID), se decodifica como rep bsr , que se ejecuta igual que bsr . Entonces, en CPUs antiguas, produce 32 - expected_result , y no está definido cuando la entrada era cero.


Un caso de un prefijo rep sin sentido que probablemente nunca se decodificará de manera diferente: rep ret es utilizado por defecto por gcc cuando apunta a CPU “genéricas” (es decir, no apunta a una CPU específica con -march o -mtune , y no apunta a AMD K8 o K10. ) Pasarán décadas antes de que alguien pueda fabricar una CPU que descodifique rep ret como cualquier cosa que no sea ret , porque está presente en la mayoría de los binarios en la mayoría de las distribuciones de Linux. Ver ¿Qué significa `rep ret`?