Warum wird eine volatile lokale Variable anders als ein volatiles Argument optimiert, und warum erzeugt der Optimierer eine No-Op-Schleife von letzterer?

8

Hintergrund

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?

Einführung

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.

Eingabe

Gegeben dieser Code:

%Vor%

Ausgabe

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:

%Vor%

Analyse

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.

    • Nun, ich meine ... irgendwie . Wie, nicht wirklich, denn 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.

    • Dieser Fall allein macht für mich aus den oben genannten Gründen gegenüber 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?

Fragen

  • Warum erzeugt ein lokaler Wert, der im Körper deklariert wird, andere Ergebnisse als ein by-value-Parameter, wobei die früheren Vermietungslesevorgänge weg optimiert werden? Beide werden als 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 -
    • so (A) ist entweder erlaubt erlaubt unter solchen Einschränkungen? (handeln as-if sie wurden nicht als volatile deklariert) -
    • und (B) warum wird nur einer weggetragen? Sind einige volatile lokale Variablen mehr volatile als andere?
  • Diese Inkonsistenz für einen Moment beiseite legen: Nachdem der Read weg optimiert wurde, warum generiert der Compiler die Schleife noch? Es tut nichts! Warum sollte der Optimierer es nicht als wenn keine Schleife codiert wurde?

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 ?

    
underscore_d 06.07.2016, 22:55
quelle

1 Antwort

2

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.

    
avdgrinten 07.07.2016 14:59
quelle