gcc -O0 optimiert weiterhin “unbenutzten” Code. Gibt es ein Compiler-Flag, um das zu ändern?
Lesezeit: 9 Minuten
BurnsBA
Wie ich in dieser Frage angesprochen habe, entfernt gcc (ja, mit -O0) eine Codezeile _mm_div_ss(s1, s2); vermutlich weil das Ergebnis nicht gespeichert wird. Dies jedoch sollte löst eine Fließkomma-Ausnahme aus und löst SIGFPE aus, was nicht passieren kann, wenn der Aufruf entfernt wird.
Frage: Gibt es ein Flag oder mehrere Flags, die an gcc übergeben werden, damit der Code so kompiliert wird, wie er ist? Ich denke so etwas wie fno-remove-unused aber sowas sehe ich nicht. Idealerweise wäre dies ein Compiler-Flag, anstatt meinen Quellcode ändern zu müssen, aber wenn dies nicht unterstützt wird, gibt es stattdessen ein gcc-Attribut/Pragma?
Dinge, die ich versucht habe:
$ gcc --help=optimizers | grep -i remove
keine Ergebnisse.
$ gcc --help=optimizers | grep -i unused
keine Ergebnisse.
Und alle toten Code-/Eliminierungs-Flags explizit deaktivieren – beachten Sie, dass es keine Warnung über unbenutzten Code gibt:
Dies scheint im Zusammenhang mit der __always_inline__ Attribut auf der _mm_div_ssDefinition.
$ cat t.c
int
div(int b)
{
return 1/b;
}
int main()
{
div(0);
return 0;
}
$ gcc -O0 -Wall -Wextra -pedantic -Winline t.c -o t.out
$
(keine Warnungen oder Fehler)
$ ./t.out
Floating point exception
$
vs unten (gleich außer für Funktionsattribute)
$ cat t.c
__inline int __attribute__((__always_inline__))
div(int b)
{
return 1/b;
}
int main()
{
div(0);
return 0;
}
$ gcc -O0 -Wall -Wextra -pedantic -Winline t.c -o t.out
$
(keine Warnungen oder Fehler)
$ ./t.out
$
Hinzufügen des Funktionsattributs __warn_unused_result__ gibt zumindest eine hilfreiche Nachricht:
$ gcc -O0 -Wall -Wextra -pedantic -Winline t.c -o t.out
t.c: In function ‘main’:
t.c:9:5: warning: ignoring return value of ‘div’, declared with attribute warn_unused_result [-Wunused-result]
div(0);
^
bearbeiten:
Einige Diskussionen über die gcc-Mailingliste. Letztendlich denke ich, dass alles wie beabsichtigt funktioniert.
Versuchen Sie es mit __attribute__((used)) mit den beteiligten Variablen.
– Eugen Sch.
3. Oktober 2016 um 13:44 Uhr
Vielleicht hilft es, s1 und s2 als flüchtig zu deklarieren …
– Malkocoglu
3. Oktober 2016 um 13:47 Uhr
Warum gibt gcc die angegebene Anweisung nicht aus?
Ein Compiler erzeugt Code, der die haben muss beobachtbares Verhalten von der Norm vorgegeben. Alles, was nicht beobachtbar ist, kann nach Belieben geändert (und optimiert) werden, da es das Verhalten des Programms (wie angegeben) nicht ändert.
Wie können Sie es in Unterwerfung schlagen?
Der Trick besteht darin, den Compiler glauben zu machen, dass das Verhalten des bestimmten Codeabschnitts tatsächlich beobachtbar ist.
Da dies ein häufig bei Mikro-Benchmarks auftretendes Problem ist, empfehle ich Ihnen, sich anzusehen, wie (zum Beispiel) Google-Benchmark damit umgeht. Aus benchmark_api.h wir bekommen:
zwingt den Compiler, dies zu berücksichtigen s1 und s2 können zwischen ihrer Initialisierung und Verwendung geändert worden sein
zwingt den Compiler zu berücksichtigen, dass das Ergebnis der Operation verwendet wird
Es ist kein Flag erforderlich, und es sollte auf jeder Optimierungsebene funktionieren (ich habe es auf getestet https://gcc.godbolt.org/ bei -O3).
Haben Sie eine Quelle dafür, was gcc als beobachtetes Verhalten entscheidet oder nicht? Ich versuche einzugrenzen, ob das Entfernen dieser Anweisung ohne Vorwarnung ein Fehler, unbeabsichtigt oder beabsichtigt ist.
– BurnsBA
4. Oktober 2016 um 19:19 Uhr
@BurnsBA: Der C++-Standard ist die Referenz dafür, was beobachtbares Verhalten ist oder nicht. Es ist auch ziemlich schwer zu lesen und voller Eckfälle … Im Allgemeinen ist beobachtbares Verhalten für Singlethread-Programme alles, was die Ausgabe (E / A) von Programmen beeinflusst. Bei Multithreading-Programmen wird es komplizierter, da viele Interleaving-Ausgaben möglich sind.
– Matthias M.
5. Oktober 2016 um 6:45 Uhr
Das DoNotOptimize Implementierung ist einer der hinterhältigsten Tricks, die ich seit langem gesehen habe, lang Zeit. Wow.
– Qix – MONICA WURDE MISSHANDELT
7. Oktober 2020 um 23:39 Uhr
GCC “optimiert” hier nichts heraus. Es generiert einfach keinen nutzlosen Code. Es scheint eine weit verbreitete Illusion zu sein, dass es eine reine Form von Code gibt, die der Compiler generieren sollte, und alle Änderungen daran sind eine “Optimierung”. Es gibt keine solche Sache.
Der Compiler erstellt eine Datenstruktur, die darstellt, was der Code bedeutet, wendet dann einige Transformationen auf diese Datenstruktur an und generiert daraus Assembler, der dann zu Anweisungen kompiliert wird. Wenn Sie ohne “Optimierungen” kompilieren, bedeutet dies nur, dass der Compiler nur den geringstmöglichen Aufwand zum Generieren von Code aufwenden wird.
In diesem Fall ist die gesamte Anweisung nutzlos, da sie nichts bewirkt und sofort weggeworfen wird (nach dem Erweitern der Inlines und der Bedeutung der Builtins entspricht sie dem Schreiben a/b;der Unterschied ist, dass das Schreiben a/b; wird eine Warnung ausgeben statement with no effect während die Builtins wahrscheinlich nicht von denselben Warnungen behandelt werden). Dies ist keine Optimierung, der Compiler müsste tatsächlich zusätzliche Anstrengungen unternehmen, um eine Bedeutung für eine bedeutungslose Anweisung zu erfinden, dann eine temporäre Variable vortäuschen, um das Ergebnis dieser Anweisung zu speichern, um sie dann wegzuwerfen.
Was Sie suchen, sind nicht Flags zum Deaktivieren von Optimierungen, sondern Pessimisierungs-Flags. Ich glaube nicht, dass Compiler-Entwickler Zeit damit verschwenden, solche Flags zu implementieren. Außer vielleicht als Aprilscherz.
Wie zeige ich Warnungen zu Aussagen ohne Wirkung an? Da -Wall -Wextra -pedantic zeigt nichts an.
– BurnsBA
3. Oktober 2016 um 14:45 Uhr
@BurnsBA Ich konnte es auch nicht dazu bringen, eine Warnung zu erzeugen. Das ist wahrscheinlich ein Fehler in gcc. Senden Sie ihnen einen Fehlerbericht.
– Kunst
3. Oktober 2016 um 14:51 Uhr
Das Missverständnis hier ist tatsächlich, dass das Erhöhen von SIGFPE von einer Division durch Null ein definierter Effekt ist und dass daher das Optimieren des Anrufs beobachtbares definiertes Verhalten entfernt.
– Random832
3. Oktober 2016 um 15:53 Uhr
@ Random832: Ich würde erwarten, dass es Implementierungen gibt, die tun Definieren Sie dies als Effekt, obwohl die Dokumentation schlampig sein kann, was garantiert ist oder nicht (zu sagen, dass ein Signal ausgelöst werden könnte, wäre bedeutungslos, wenn sich die Implementierung auch auf andere Weise willkürlich verhalten könnte). Es wäre wahrscheinlich am logischsten für eine Implementierung zu spezifizieren, dass eine Division durch Null keine Nebeneffekte hat außer die Ausgabe eines Signals und wird kein ergeben beobachtbar Wert ohne Signal, muss aber kein Signal erzeugen, wenn der Quotient nicht in beobachtbarer Weise verwendet wird.
– Superkatze
3. Oktober 2016 um 18:48 Uhr
Ich akzeptiere diese Antwort, weil ich denken Dies ist eigentlich ein Fehler oder zumindest ein unbeabsichtigtes Verhalten von gcc (Entfernung der Anweisung ohne Warnung). Ich werde aber zuerst ein bisschen mehr recherchieren; Die Leute verstricken sich immer wieder in das Dividieren durch Null, was, soweit ich das beurteilen kann, irrelevant ist, aber der einfachste Weg, um einen Nebeneffekt auf niedriger Ebene zu erzeugen.
– BurnsBA
4. Oktober 2016 um 19:17 Uhr
Ich bin kein Experte mit gcc Interna, aber es scheint, dass Ihr Problem nicht darin besteht, toten Code durch einen Optimierungsdurchgang zu entfernen. Es ist sehr wahrscheinlich, dass der Compiler nicht einmal darüber nachdenkt generieren dieser Code an erster Stelle.
Lassen Sie uns Ihr Beispiel von Compiler-spezifischen Intrinsics auf eine einfache alte Ergänzung reduzieren:
Das Folgende ist nur eine Spekulation, aber aus meiner Erfahrung mit dem Bau von Compilern ist es natürlicher, den Code für nicht verwendete Ausdrücke nicht zu generieren, anstatt diesen Code später zu entfernen.
Meine Empfehlung ist, Ihre Absichten deutlich zu machen und das Ergebnis in einen Ausdruck zu setzen flüchtig (und daher vom Optimierer nicht entfernbar) Variable.
@Matthieu M wies darauf hin, dass es nicht ausreicht, die Vorberechnung des Werts zu verhindern. Für etwas mehr als das Spielen mit Signalen sollten Sie also dokumentierte Methoden verwenden, um genau die Anweisung auszuführen, die Sie möchten (wahrscheinlich, volatile Inline-Montage).
Leider einfach das Ergebnis eingeben volatile reicht nicht aus, um den Compiler daran zu hindern, ihn vorab zu berechnen, ohne tatsächlich die gewünschte Anweisung auszugeben, da alle Parameter zur Kompilierzeit vorhanden sind.
– Matthias M.
3. Oktober 2016 um 16:01 Uhr
In der Tat; Wenn Sie die Null in einen flüchtigen Wert schieben und erneut auslesen, wird der Optimierer geschlossen.
– Josua
3. Oktober 2016 um 16:44 Uhr
@MatthieuM. Ich stimme vollkommen zu, dass es nicht immer ausreichend ist, aber in diesem Fall ist es (der letzte Link). Auch Compiler muss Bevorzugen Sie im allgemeinen Fall einen schnelleren Code, um die gewünschte Anweisung auszugeben, um nützlich zu sein. Dies ist der Fall, wenn eine Inline-Montage verwendet werden sollte.
– Benutzer2512323
3. Oktober 2016 um 16:47 Uhr
@deniss: Leider bin ich aus Erfahrung vorsichtig, an “ausreichend” zu glauben. Compiler sind die kniffligen Biester, die sie sind, und neigen dazu, ihr Verhalten bei der geringsten Änderung ihrer Eingaben zu ändern.
– Matthias M.
3. Oktober 2016 um 17:26 Uhr
14088100cookie-checkgcc -O0 optimiert weiterhin “unbenutzten” Code. Gibt es ein Compiler-Flag, um das zu ändern?yes
Versuchen Sie es mit
__attribute__((used))
mit den beteiligten Variablen.– Eugen Sch.
3. Oktober 2016 um 13:44 Uhr
Vielleicht hilft es, s1 und s2 als flüchtig zu deklarieren …
– Malkocoglu
3. Oktober 2016 um 13:47 Uhr