Cosa significa “rep; nop; “significa nell’assemblaggio x86? È uguale all’istruzione “pausa”?

  • Cosa fa rep; nop rep; nop significa?
  • È uguale all’istruzione di pause ?
  • È lo stesso di rep nop (senza il punto e virgola)?
  • Qual è la differenza con l’istruzione nop semplice?
  • Si comporta diversamente sui processori AMD e Intel?
  • (bonus) Dov’è la documentazione ufficiale per queste istruzioni?

Motivazione per questa domanda

Dopo qualche discussione nei commenti di un’altra domanda , mi sono reso conto che non so quale rep; nop; rep; nop; significa nell’assembly x86 (o x86-64). E anche io non sono riuscito a trovare una buona spiegazione sul web.

So che rep è un prefisso che significa “ripeti l’istruzione successiva cx volte” (o almeno lo era, nel vecchio assembly x86 a 16 bit). Secondo questa tabella riassuntiva su Wikipedia , sembra che il rep possa essere usato solo con movs , cmps , cmps , cmps , cmps (ma forse questa limitazione è stata rimossa sui nuovi processori). Quindi, penserei che il rep nop (senza punto e virgola) ripeterà una operazione di nop cx volte.

Tuttavia, dopo ulteriori ricerche, sono diventato ancora più confuso. Sembra quel rep; nop rep; nop e rep; nop pause mappa nello stesso identico opcode , e la pause ha un comportamento leggermente diverso rispetto al solo nop . Alcuni vecchi messaggi del 2005 dicevano cose diverse:

  • “cerca di non bruciare troppa energia”
  • “È equivalente a ‘nop’ solo con codifica a 2 byte.”
  • “è una magia su Intel. È come ‘nop ma lascia correre l’altro fratello HT'”
  • “è pausa su Intel e riempimento veloce su Athlon”

Con queste diverse opinioni, non riuscivo a capire il significato corretto.

Viene usato nel kernel Linux (su entrambi i386 e x86_64 ), insieme a questo commento: /* 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. */ Viene anche utilizzato in BeRTOS , con lo stesso commento.

rep; nop rep; nop è effettivamente uguale all’istruzione di pause (opcode F390 ). Potrebbe essere usato per gli assemblatori che non supportano ancora l’istruzione di pause . Su processori precedenti, questo semplicemente non ha fatto nulla, proprio come nop ma in due byte. Sui nuovi processori che supportano l’hyperthreading, viene utilizzato come suggerimento per il processore che si sta eseguendo uno spinloop per aumentare le prestazioni. Dal riferimento alle istruzioni di Intel :

Migliora le prestazioni dei cicli di attesa spin. Quando si esegue un ciclo di “spin-wait loop”, un processore Pentium 4 o Intel Xeon subisce una severa penalizzazione delle prestazioni quando si esce dal ciclo perché rileva una ansible violazione dell’ordine di memoria. L’istruzione PAUSE fornisce un suggerimento al processore che la sequenza di codice è un ciclo di attesa-spin. Il processore utilizza questo suggerimento per evitare la violazione dell’ordine di memoria nella maggior parte delle situazioni, il che migliora notevolmente le prestazioni del processore. Per questo motivo, si consiglia di inserire un’istruzione PAUSE in tutti i loop di attesa di spin.

I prefissi che non si applicano a un’istruzione vengono ignorati. Tuttavia, le future CPU possono usare quella sequenza di byte per codificare una nuova istruzione. (sì, lo spazio per l’opcode x86 è così limitato da fare cose pazzesche come questa, e sì rende complicati i decodificatori).

In questo caso, significa che è ansible utilizzare la pause in spinloops senza rompere indietro compat . Le vecchie CPU che non conoscono la pause la decodificheranno come un NOP senza danni. Sulle nuove CPU, si ottiene il vantaggio della semplicità di risparmio energetico / HT e si evita la speculazione errata della memoria quando la memoria su cui si sta ruotando cambia e si esce dal ciclo di rotazione.


Collegamenti ai manuali di Intel e tonnellate di altre cose buone nella pagina delle informazioni del wiki dei tag x86: https://stackoverflow.com/tags/x86/info

Un altro caso di un prefisso di rep senza significato che diventa una nuova istruzione su nuove CPU: lzcnt è F3 0F BD /r . Sulle CPU che non supportano tale istruzione (manca il flag della funzione LZCNT nel proprio CPUID), decodifica come rep bsr , che funziona come bsr . Quindi su vecchie CPU, produce 32 - expected_result , e non è definito quando l’input era zero.


Un caso di prefisso di una rep priva di significato che probabilmente non decodificherà mai in modo diverso: rep ret è usato di default da gcc quando prende di mira CPU “generiche” (cioè non si rivolge a una CPU specifica con -march o -mtune , e non si -mtune a AMD K8 o K10. Ci vorranno decenni prima che qualcuno possa creare una CPU che decodifica il rep ret di qualsiasi altra cosa che non sia ret , perché è presente nella maggior parte dei binari nella maggior parte delle distribuzioni Linux. Vedi cosa significa “rep ret”?