Der Unterschied zwischen asm, asm volatile und clobbering memory

Lesezeit: 6 Minuten

Bei der Implementierung lockfreier Datenstrukturen und Timing-Codes ist es oft notwendig, die Optimierungen des Compilers zu unterdrücken. Normalerweise machen die Leute das mit asm volatile mit memory in der clobber liste, aber manchmal sieht man eben asm volatile oder einfach nur eine Ebene asm schlagende Erinnerung.

Welche Auswirkungen haben diese unterschiedlichen Anweisungen auf die Codegenerierung (insbesondere in GCC, da es unwahrscheinlich ist, dass es portierbar ist)?

Nur als Referenz, dies sind die interessanten Variationen:

asm ("");   // presumably this has no effect on code generation
asm volatile ("");
asm ("" ::: "memory");
asm volatile ("" ::: "memory");

  • Jemand scheint viel zu nahe am Metall herumzuspielen 🙂 (Und woanders tippt @Mystcial eine lächerlich detaillierte Antwort weg…)

    – Kerrek SB

    21. Januar 2013 um 23:39 Uhr


Benutzeravatar von Matthew Slattery
Matthew Slattery

Siehe die Seite “Extended Asm” in der GCC-Dokumentation.

Sie können eine verhindern asm Anweisung nicht gelöscht werden, indem das Schlüsselwort geschrieben wird volatile nach dem asm. […] Das volatile Das Schlüsselwort weist darauf hin, dass die Anweisung wichtige Nebeneffekte hat. GCC löscht a nicht volatile asm, wenn es erreichbar ist.

und

Ein asm Anweisungen ohne Ausgangsoperanden werden genauso behandelt wie eine flüchtige asm Anweisung.

Keines Ihrer Beispiele hat Ausgabeoperanden angegeben, also die asm und asm volatile Formulare verhalten sich identisch: Sie erzeugen einen Punkt im Code, der nicht gelöscht werden darf (es sei denn, er ist nicht erreichbar).

Das ist nicht ganz dasselbe wie Nichtstun. Siehe diese Frage für ein Beispiel für einen Dummy asm was die Codegenerierung ändert – in diesem Beispiel wird Code, der 1000 Mal eine Schleife durchläuft, in Code vektorisiert, der 16 Iterationen der Schleife auf einmal berechnet; aber das Vorhandensein eines asm innerhalb der Schleife verhindert die Optimierung (die asm muss 1000 Mal erreicht werden).

Das "memory" clobber lässt GCC davon ausgehen, dass jeder Speicher beliebig gelesen oder beschrieben werden kann asm block, so dass der Compiler daran gehindert wird, Lade- oder Speichervorgänge neu zu ordnen:

Dies führt dazu, dass GCC keine Speicherwerte in Registern über den Assembler-Befehl zwischenspeichert und das Speichern oder Laden in diesen Speicher nicht optimiert.

(Das hindert eine CPU jedoch nicht daran, Lade- und Speichervorgänge in Bezug auf eine andere CPU neu zu ordnen; dafür benötigen Sie echte Speicherbarriereanweisungen.)

  • Das ist eigentlich sehr interessant, nicht zu wissen, dass gcc behandelt asm Blöcke ohne Ausgänge als flüchtig war eine riesige Lücke in meinem Wissen.

    – jleahy

    24. Januar 2013 um 9:59 Uhr

  • So volatile = Performance-Killer, egal in welchem ​​Kontext es verwendet wird (Variable oder Asm). Archivieren Sie es mit der goto Schlüsselwort – nur verwenden, wenn es absolut notwendig ist.

    – Äther

    25. Mai 2013 um 21:51 Uhr

  • “beliebiger Speicher” bedeutet irgendein Objekt im Speicher?

    – Neugieriger

    21. Juni 2018 um 4:47 Uhr

  • EIN "memory" Clobber gilt nur für global erreichbaren Speicher oder Speicher, der über Zeigereingaben auf die erreichbar ist asm Aussage. Soweit es darum geht, welche C-Objekte im Speicher “synchron” sein müssen und welche noch in Registern sein können, ist es wie bei einem Nicht-Inline-Funktionsaufruf. Daher können lokale Variablen, deren Adresse nie außerhalb der Funktion übergeben wurde (z. B. Schleifenzähler), dank normalerweise immer noch in Registern bleiben Fluchtanalyse.

    – Peter Cordes

    23. Juli 2019 um 18:04 Uhr

  • Diese Optimierung ist sicher, weil es bereits nicht sicher / erlaubt ist, so etwas zu tun asm("incl -16(%%rbp)" ::: "memory") um auf den Stackspace zuzugreifen, wo gcc zufällig eine lokale Datei ablegt (ohne eine "+m" Operanden, um den Compiler dazu zu bringen, einen Adressierungsmodus zu generieren). Über das Stack-Frame-Layout können Sie keine Annahmen treffen. verschiedene Compiler-Optionen ändern es. Also wie auch immer, a "memory" Clobber tut, was diese Antwort sagt, aber mit einer Leistungseinbuße, die das nicht ist ziemlich so schlecht wie.

    – Peter Cordes

    23. Juli 2019 um 18:10 Uhr

asm ("") tut nichts (oder zumindest soll es nichts tun.

asm volatile ("") bringt auch nichts.

asm ("" ::: "memory") ist ein einfacher Compilerzaun.

asm volatile ("" ::: "memory") AFAIK ist das gleiche wie das vorherige. Das volatile Schlüsselwort teilt dem Compiler mit, dass es nicht erlaubt ist, diesen Assembly-Block zu verschieben. Beispielsweise kann es aus einer Schleife gehoben werden, wenn der Compiler entscheidet, dass die Eingabewerte bei jedem Aufruf gleich sind. Ich bin mir nicht sicher, unter welchen Bedingungen der Compiler entscheiden wird, dass er genug über die Assembly versteht, um zu versuchen, ihre Platzierung zu optimieren, aber die volatile Schlüsselwort unterdrückt das vollständig. Allerdings wäre ich sehr überrascht, wenn der Compiler versuchen würde, an zu verschieben asm -Anweisung, die keine deklarierten Eingaben oder Ausgaben hatte.

Übrigens, volatile verhindert auch, dass der Compiler den Ausdruck löscht, wenn er entscheidet, dass die Ausgabewerte nicht verwendet werden. Dies kann jedoch nur passieren, wenn Ausgabewerte vorhanden sind, also gilt es nicht für asm ("" ::: "memory").

  • Die Antwort von Matthew Slattery weist darauf hin asm volatile ("") ist nicht ganz dasselbe wie Nichtstun, da es drastische Auswirkungen auf die Compiler-Optimierung haben kann. Die gleichen Auswirkungen auf die Leistung würden für die Verwendung gelten asm volatile ("" ::: "memory") als Compilerzaun.

    – Äther

    25. Mai 2013 um 21:33 Uhr

  • Der Compiler versteht keine Assemblersprache!

    – Neugieriger

    6. Oktober 2015 um 0:26 Uhr

  • @curiousguy nein, aber es versteht, wann ein asm block hat Ein-/Ausgänge deklariert, die dem Compiler mitteilen, von welchen Registern er abhängt und welche er ändern wird, und daher kann der Compiler bestimmte Berechnungen verschieben, wenn sie die Ein-/Ausgänge nicht beeinflussen.

    – Lily Ballard

    7. Oktober 2015 um 0:58 Uhr

  • Zumindest für GCC, asm volatile tut nicht allgemeine Anweisungsneuordnung verhindern, es verhindert nur eine Erreichbarkeit asm Block aufgrund (offensichtlicher) fehlender sinnvoller Nebeneffekte gelöscht wird (und verhindert bei neueren GCCs, dass er aus einer Schleife gehoben wird, selbst wenn der Compiler feststellt, dass die Eingaben immer gleich sind). Andernfalls wird die Neuordnung von Befehlen nur durch deklarierte Ein- und Ausgänge (und Pseudo-Ausgänge, wie z "memory"). Lesen Sie mehr in die Dokumente.

    – ShadowRanger

    23. Juli 2019 um 17:50 Uhr

  • Ja, asm Aussagen sind implizit volatile wenn sie keine Ausgabebeschränkungen haben. (gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html). So asm("":::"memory") ist exakt identisch mit asm volatile("":::"memory"). Eine nichtflüchtige asm-Anweisung könnte gelöscht werden, wenn das Ergebnis nie verwendet oder gehisst (oder auf andere Weise) wird CSEd) wenn es wiederholt mit denselben Eingaben ausgeführt wird. Sie müssten also explizit volatile so etwas zu wickeln rdtsc oder rdrand wegen dir tun erhalten Sie eine andere Ausgabe mit demselben (leeren) Satz von Eingaben.

    – Peter Cordes

    27. Dezember 2020 um 3:19 Uhr


Benutzeravatar von James
James

Nur zur Vollständigkeit der Antwort von Lily Ballard bietet Visual Studio 2010 _ReadBarrier(), _WriteBarrier() und _ReadWriteBarrier() um dasselbe zu tun (VS2010 erlaubt keine Inline-Assemblierung für 64-Bit-Apps).

Diese erzeugen keine Anweisungen, beeinflussen aber das Verhalten des Compilers. Ein schönes Beispiel ist hier.

MemoryBarrier() erzeugt lock or DWORD PTR [rsp], 0

1420340cookie-checkDer Unterschied zwischen asm, asm volatile und clobbering memory

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy