Betrachten Sie diesen einfachen Code:
void g();
void foo()
{
volatile bool x = false;
if (x)
g();
}
Sie können sehen, dass der potenzielle Anruf an weder optimiert gcc
noch clang
optimiert wird g
. Dies ist nach meinem Verständnis richtig: Die abstrakte Maschine geht davon aus, dass sich volatile
Variablen jederzeit ändern können (z. B. durch Hardware-Zuordnung), sodass ein konstantes Falten der false
Initialisierung in die if
Prüfung falsch wäre.
Aber MSVC eliminiert den Aufruf von g
vollständig (das Lesen und Schreiben bleibt jedoch erhalten volatile
!). Ist das standardkonformes Verhalten?
Hintergrund: Ich verwende gelegentlich diese Art von Konstrukt, um die Debugging-Ausgabe im laufenden Betrieb ein- und ausschalten zu können: Der Compiler muss den Wert immer aus dem Speicher lesen. Wenn Sie also diese Variable / diesen Speicher während des Debuggens ändern, sollte der Steuerungsfluss entsprechend geändert werden . Der MSVC-Ausgang liest den Wert erneut, ignoriert ihn jedoch (vermutlich aufgrund ständiger Faltung und / oder Eliminierung toten Codes), was meine Absichten hier natürlich zunichte macht.
Bearbeitungen:
Die Eliminierung der Lese- und Schreibvorgänge
volatile
wird hier erläutert: Darf ein Compiler eine lokale flüchtige Variable optimieren? (Danke Nathan!). Ich denke, der Standard ist völlig klar, dass diese Lese- und Schreibvorgänge stattfinden müssen . Diese Diskussion behandelt jedoch nicht, ob es für den Compiler legal ist, die Ergebnisse dieser Lesevorgänge als selbstverständlich zu betrachten und auf dieser Grundlage zu optimieren. Ich nehme an, dass dies im Standard unter- / nicht spezifiziert ist , aber ich würde mich freuen, wenn mir jemand das Gegenteil beweisen würde.Ich kann natürlich
x
eine nicht lokale Variable erstellen, um das Problem zu umgehen. Diese Frage ist eher aus Neugier.
quelle
Antworten:
Ich denke, [intro.execution] (Absatznummer variiert) könnte verwendet werden, um das MSVC-Verhalten zu erklären:
Der Standard erlaubt nicht die Eliminierung eines Durchlesens eines flüchtigen Gl-Werts, aber der obige Absatz könnte so interpretiert werden, dass er die Vorhersage des Wertes ermöglicht
false
.Übrigens sagt das der C-Standard (N1570 6.2.4 / 2)
Es ist unklar, ob in einem Objekt mit automatischer Speicherdauer im C-Speicher / Objektmodell ein nicht expliziter Speicher vorhanden sein könnte.
quelle
volatile
Sie durch Hinzufügen kaufen (außer überflüssigem Lesen / Schreiben), wenn es zu Optimierungszwecken ignoriert wird?TL; DR Der Compiler kann bei jedem flüchtigen Zugriff tun, was er will. Die Dokumentation muss Ihnen jedoch Folgendes mitteilen: "Die Semantik eines Zugriffs über einen flüchtigen Wert ist implementierungsdefiniert."
Der Standard definiert für ein Programm zulässige Sequenzen von "flüchtigen Zugriffen" und anderem "beobachtbarem Verhalten" (erreicht durch "Nebenwirkungen"), die eine Implementierung gemäß der "Als-ob" -Regel "einhalten muss.
Aber der Standard sagt (meine kühne Betonung):
Ähnliches gilt für interaktive Geräte (meine Fettdruck-Betonung):
(Wie auch immer welcher spezifische Code für ein Programm generiert wird , ist vom Standard nicht festgelegt.)
Obwohl der Standard besagt, dass flüchtige Zugriffe nicht aus den abstrakten Sequenzen abstrakter Maschineneffekte und daraus resultierenden beobachtbaren Verhaltensweisen herausgelöst werden können, die ein Code (möglicherweise) definiert, können Sie nicht erwarten, dass sich etwas im Objektcode oder in der realen Welt widerspiegelt Verhalten, es sei denn, Ihre Compiler-Dokumentation sagt es Ihnen was einen flüchtigen Zugriff ausmacht . Das Gleiche gilt für interaktive Geräte.
Wenn Sie in volatil vis a vis interessiert sind die abstrakten Sequenzen von abstrakten Maschine Nebenwirkungen und / oder daraus folgenden beobachtbaren Verhalten , dass einige Code (vielleicht) definiert dann so sagen . Wenn Sie jedoch daran interessiert sind, welcher entsprechende Objektcode generiert wird, müssen Sie dies im Kontext Ihres Compilers und Ihrer Kompilierung interpretieren .
Chronisch glauben Menschen fälschlicherweise, dass bei flüchtigen Zugriffen eine abstrakte Maschinenbewertung / -lesung ein implementiertes Lesen und eine abstrakte Maschinenzuweisung / -schreibung ein implementiertes Schreiben verursacht. Es gibt keine Grundlage für diese Annahme, wenn keine Implementierungsdokumentation dies sagt. Wenn / wenn die Implementierung sagt, dass sie bei einem "flüchtigen Zugriff" tatsächlich etwas tut , können die Leute zu Recht etwas erwarten - vielleicht die Erzeugung eines bestimmten Objektcodes.
quelle
Ich glaube, es ist legal, den Scheck zu überspringen.
Der Absatz, den jeder gerne zitiert
bedeutet nicht, dass eine Implementierung davon ausgehen muss, dass solche Speicher jederzeit oder für jede flüchtige Variable möglich sind. Eine Implementierung weiß, welche Speicher möglich sind. Beispielsweise ist es durchaus vernünftig anzunehmen, dass solche impliziten Schreibvorgänge nur für flüchtige Variablen auftreten, die Geräteregistern zugeordnet sind, und dass eine solche Zuordnung nur für Variablen mit externer Verknüpfung möglich ist. Oder eine Implementierung kann annehmen, dass solche Schreibvorgänge nur an wortgroßen, wortausgerichteten Speicherstellen stattfinden.
Trotzdem denke ich, dass MSVC-Verhalten ein Fehler ist. Es gibt keinen realen Grund, den Anruf zu optimieren. Eine solche Optimierung mag konform sein, ist aber unnötig böse.
quelle
volatile
dies ein Hinweis auf die Implementierung sein soll, dass sich der Wert durch Mittel ändern kann, die der Implementierung unbekannt sind.volatile
auf dieser Plattform verwenden können und was nicht. Außerhalb dieser Spezifikation wird es immer ein Mist-Shooting sein.volatile
tut, und sich daran halten. Mein Punkt ist, dass der Code selbst (gemäß der C ++ - Standard- / abstrakten Maschine) es Ihnen nicht erlaubt zu bestimmen, obg
aufgerufen werden kann.