Dies wurde durch diese Frage / Antwort und anschließende Diskussion in den Kommentaren inspiriert: Ist die Definition von "volatile" dies volatil, oder ist GCC mit einigen Standard-Kompatibilitätsproblemen? . Basierend auf der Interpretation von anderen und meiner Interpretation, was passieren sollte, wie in den Kommentaren besprochen, habe ich es an GCC Bugzilla geschickt: Ссылка Andere relevante Antworten sind immer noch willkommen.
Dieser Thread hat seither auch diese Frage aufgeworfen: Bewirkt der Zugriff auf ein deklariertes nichtflüchtiges Objekt über einen flüchtigen Verweis / Zeiger flüchtige Regeln? auf diese Zugriffe?
Ich weiß, dass volatile
nicht das ist, was die meisten Leute denken, und ist ein Implementierungs-definiertes Nest von Vipern. Und ich möchte die folgenden Konstrukte in keinem echten Code verwenden. Allerdings bin ich völlig verwirrt darüber, was in diesen Beispielen vor sich geht, also würde ich jede Erläuterung sehr begrüßen.
Meine Vermutung ist, dass dies entweder auf eine sehr nuancierte Interpretation des Standards oder (wahrscheinlicher?) nur auf Eckfälle für den verwendeten Optimierer zurückzuführen ist. So oder so, obwohl akademischer als praktischer Art, hoffe ich, dass dies für eine Analyse als wertvoll erachtet wird, insbesondere angesichts der Tatsache, wie häufig missverstandenes volatile
ist. Einige Datenpunkte - oder vielleicht eher Punkte dagegen - müssen gut sein.
Gegeben dieser Code:
%Vor% g++
von gcc (Debian 4.9.2-10) 4.9.2
(Debian stable
a.k.a.Jessie) mit der Befehlszeile g++ -std=c++14 -O3 -S test.cpp
erzeugt die folgende ASM für main()
. Die Version Debian 5.4.0-6
(current unstable
) erzeugt gleichwertigen Code, aber ich habe gerade den älteren zuerst ausgeführt, also hier:
Alle 3 Funktionen sind inline, und beide, die volatile
lokale Variablen zuweisen, tun dies aus ziemlich offensichtlichen Gründen auf dem Stapel. Aber das ist das einzige, was sie teilen ...
f()
stellt sicher, dass% ce_de% bei jeder Iteration gelesen wird, vermutlich aufgrund seines x
- aber nur das Ergebnis wird in volatile
gespeichert, vermutlich weil das Ziel edx
isn ist 't deklariert y
und wird nie gelesen, dh Änderungen daran können unter der as-if -Regel nixiert werden. OK, macht Sinn.
volatile
ist wirklich für Hardware-Register, und eindeutig kann ein lokaler Wert keiner von diesen sein - und kann nicht anders in volatile
way geändert werden, wenn seine Adresse nicht weitergegeben wird ... was es nicht ist. Sieh mal, es gibt einfach nicht viel Sinn von volatile
lokalen Werten. Aber C ++ lässt uns sie deklarieren und versucht etwas mit ihnen zu machen. Und so, wie immer verwirrt, stolpern wir weiter. volatile
: Was. Durch Verschieben der g()
-Quelle in einen Pass-by-Value-Parameter, der immer noch eine andere lokale Variable ist, GCC entscheidet irgendwie, dass es nicht oder weniger volatile
ist, und so muss es nicht jede Iteration lesen ... aber es führt immer noch die Schleife aus, trotz seines Körpers, der jetzt nichts tut .
volatile
: Durch die Übergabe des übergebenen h()
als "pass-by-reference" wird das gleiche effektive Verhalten wie volatile
wiederhergestellt, sodass die Schleife f()
liest.
volatile
tatsächlich Sinn. Zur Erläuterung: Imagine f()
bezieht sich auf ein Hardware-Register, von dem jedes Lesen Nebenwirkungen hat. Sie würden nichts davon auslassen wollen. Das Hinzufügen von x
führt dazu, dass #define volatile /**/
wie erwartet ein No-Op ist. Also, wenn vorhanden, sogar auf eine lokale Variable main()
tut etwas ... Ich habe nur keine Idee was im Fall von volatile
. Was um alles in der Welt geht dort vor?
g()
deklariert. Sie haben auch keine Adresse ausgegeben - und haben keine volatile
-Adresse, die inline-ASM static
ry ausschließt - so dass sie niemals außerhalb der Funktion geändert werden können. Der Compiler kann sehen, dass jede Konstante ist, nie wieder gelesen werden muss, und POKE
ist nicht wahr -
volatile
deklariert) - volatile
lokale Variablen mehr volatile
als andere? Ist das ein seltsamer Eckfall aufgrund der Reihenfolge der Optimierung von Analysen oder dergleichen?Da der Code ein dämlicher Gedankenexperiment ist, würde ich GCC nicht dafür bestrafen, aber es wäre gut, das sicher zu wissen. (Oder ist volatile
die manuelle Timingschleife, von der die Leute in all diesen Jahren geträumt haben?) Wenn wir zu dem Schluss kommen, dass es keinen Standard gibt, werde ich es nur zu ihrer Information in ihren Bugzilla verschieben.
Und natürlich die wichtigere Frage aus einer praktischen Perspektive, obwohl ich nicht möchte, dass das Potenzial für Compiler-Geekery in den Schatten gestellt wird ... Welche, wenn überhaupt, sind nach dem Standard wohldefiniert / korrekt ?
Für f: GCC beseitigt die nichtflüchtigen Speicher (aber nicht die Lasten, die Nebenwirkungen haben können, wenn der Quellort ein speicherabgebildetes Hardwareregister ist). Es ist wirklich nichts überraschend hier.
Für g: Wegen des x86_64 ABI wird der Parameter x
von g
zugewiesen in einem Register (zB rdx
) und hat keinen Speicherplatz im Speicher. Das Lesen eines Allzweckregisters hat keine beobachtbaren Nebenwirkungen, so dass das tote Lesen beseitigt wird.
Tags und Links optimization c++ volatile g++ pass-by-value