The as-if rule
Erlaubt jegliche und alle Code-Transformationen, die das beobachtbare Verhalten des Programms nicht ändern.
Inhaltsverzeichnis |
Erklärung
Beobachtbares Verhalten eines Programms umfasst Folgendes:
|
(bis C++11) |
|
(seit C++11) |
|
(until C++26) |
|
(since C++26) |
- Aufforderungstext, der an interaktive Geräte gesendet wird, wird angezeigt, bevor das Programm auf Eingaben wartet.
-
Falls das ISO C-Pragma
#pragma STDC FENV_ACCESS
unterstützt wird und auf
ONgesetzt ist, werden Änderungen an der Gleitkommaumgebung (Gleitkomma-Ausnahmen und Rundungsmodi) garantiert von den Gleitkomma-Operatoren und Funktionsaufrufen beachtet, als ob sie wie geschrieben ausgeführt würden, außer dass- das Ergebnis jedes Gleitkomma-Ausdrucks außer Typumwandlung und Zuweisung Bereich und Genauigkeit eines Gleitkommatyps haben kann, der vom Typ des Ausdrucks abweicht (siehe FLT_EVAL_METHOD ),
-
ungeachtet des oben Gesagten können Zwischenergebnisse jedes Gleitkomma-Ausdrucks berechnet werden, als ob mit unendlichem Bereich und unendlicher Genauigkeit (es sei denn,
#pragma STDC FP_CONTRACT
ist auf
OFFgesetzt).
|
Dem C++-Compiler ist es erlaubt, beliebige Änderungen am Programm vorzunehmen, solange bei gleicher Eingabe das beobachtbare Verhalten des Programms einem der möglichen beobachtbaren Verhalten entspricht, die zu dieser Eingabe gehören. Wenn jedoch eine bestimmte Eingabe zu undefiniertem Verhalten führt, kann der Compiler kein beobachtbares Verhalten des Programms mit dieser Eingabe garantieren, selbst wenn ein Teil des beobachtbaren Verhaltens vor einem möglichen undefinierten Vorgang stattfindet. |
(bis C++26) |
|
Ein Programm kann beobachtbare Checkpoints enthalten.
Ein Vorgang
Dem C++-Compiler ist es erlaubt, beliebige Änderungen am Programm vorzunehmen, solange bei gleicher Eingabe das beobachtbare Verhalten des definierten Präfixes des Programms einem der möglichen beobachtbaren Verhalten entspricht, die zu diesem definierten Präfix gehören. Wenn eine bestimmte Eingabe zu undefiniertem Verhalten führt, kann der Compiler kein beobachtbares Verhalten des Programms mit dieser Eingabe garantieren, das nicht zum definierten Präfix gehört. |
(seit C++26) |
Hinweise
Da der Compiler (normalerweise) nicht in der Lage ist, den Code einer externen Bibliothek zu analysieren, um festzustellen, ob sie E/A- oder flüchtige Zugriffe durchführt oder nicht, sind Aufrufe von Drittanbieter-Bibliotheken ebenfalls nicht von der Optimierung betroffen. Allerdings können Standardbibliotheksaufrufe während der Optimierung durch andere Aufrufe ersetzt, entfernt oder dem Programm hinzugefügt werden. Statisch verknüpfter Drittanbieter-Bibliothekscode kann einer Linkzeit-Optimierung unterzogen werden.
Programme mit undefiniertem Verhalten ändern oft ihr beobachtbares Verhalten, wenn sie mit unterschiedlichen Optimierungseinstellungen neu kompiliert werden. Beispielsweise wird, wenn ein Test auf vorzeichenbehafteten Integer-Überlauf auf dem Ergebnis dieses Überlaufs basiert, z.B. if ( n + 1 < n ) abort ( ) ; , dieser von einigen Compilern vollständig entfernt , weil vorzeichenbehafteter Überlauf undefiniertes Verhalten ist und der Optimierer davon ausgehen kann, dass dies niemals geschieht, und der Test somit redundant ist.
Copy Elision ist eine Ausnahme von der as-if-Regel: Der Compiler kann Aufrufe von Move- und Copy-Konstruktoren sowie die entsprechenden Aufrufe der Destruktoren temporärer Objekte entfernen, selbst wenn diese Aufrufe beobachtbare Seiteneffekte haben.
|
new expression hat eine weitere Ausnahme von der as-if-Regel: Der Compiler kann Aufrufe der replaceable allocation functions entfernen, selbst wenn eine benutzerdefinierte Ersetzung bereitgestellt wird und beobachtbare Seiteneffekte hat. |
(since C++14) |
Die Anzahl und Reihenfolge von Gleitkomma-Ausnahmen kann durch Optimierung geändert werden, solange der Zustand, wie er von der nächsten Gleitkomma-Operation wahrgenommen wird, so ist, als hätte keine Optimierung stattgefunden:
#pragma STDC FENV_ACCESS ON for (i = 0; i < n; ++i) x + 1; // x + 1 ist toter Code, kann aber FP-Ausnahmen auslösen // (sofern der Optimierer nicht das Gegenteil beweisen kann). Die Ausführung n-mal // wird jedoch dieselbe Ausnahme wiederholt auslösen. Daher kann dies optimiert werden zu: if (0 < n) x + 1;
Beispiel
int& preinc(int& n) { return ++n; } int add(int n, int m) { return n + m; } // volatile input to prevent constant folding volatile int input = 7; // volatile output to make the result a visible side-effect volatile int result; int main() { int n = input; // using built-in operators would invoke undefined behavior // int m = ++n + ++n; // but using functions makes sure the code executes as-if // the functions were not overlapped int m = add(preinc(n), preinc(n)); result = m; }
Ausgabe:
# full code of the main() function as produced by the GCC compiler
# x86 (Intel) platform:
movl input(%rip), %eax # eax = input
leal 3(%rax,%rax), %eax # eax = 3 + eax + eax
movl %eax, result(%rip) # result = eax
xorl %eax, %eax # eax = 0 (the return value of main())
ret
# PowerPC (IBM) platform:
lwz 9,LC..1(2)
li 3,0 # r3 = 0 (the return value of main())
lwz 11,0(9) # r11 = input;
slwi 11,11,1 # r11 = r11 << 1;
addi 0,11,3 # r0 = r11 + 3;
stw 0,4(9) # result = r0;
blr
# Sparc (Sun) platform:
sethi %hi(result), %g2
sethi %hi(input), %g1
mov 0, %o0 # o0 = 0 (the return value of main)
ld [%g1+%lo(input)], %g1 # g1 = input
add %g1, %g1, %g1 # g1 = g1 + g1
add %g1, 3, %g1 # g1 = 3 + g1
st %g1, [%g2+%lo(result)] # result = g1
jmp %o7+8
nop
# in all cases, the side effects of preinc() were eliminated, and the
# entire main() function was reduced to the equivalent of result = 2 * input + 3;
Siehe auch
|
C-Dokumentation
für
as-if rule
|