Die as-if-Regel
Erlaubt alle Code-Transformationen, die das beobachtbare Verhalten des Programms nicht ändern.
Inhalt |
[bearbeiten] Erklärung
Das beobachtbare Verhalten eines Programms umfasst Folgendes:
|
(bis C++11) |
|
(seit C++11) |
|
(bis C++26) |
|
(seit C++26) |
- Prompt-Texte, die an interaktive Geräte gesendet werden, werden angezeigt, bevor das Programm auf Eingaben wartet.
- Wenn das ISO C Pragma #pragma STDC FENV_ACCESS unterstützt und auf
ONgesetzt ist, wird garantiert, dass die Änderungen an der Gleitkomma-Umgebung (Gleitkomma-Ausnahmen und Rundungsmodi) von den Gleitkomma-Arithmetik-Operatoren und Funktionsaufrufen beobachtet werden, als ob sie wie geschrieben ausgeführt worden wären, mit Ausnahme von:- Das Ergebnis jedes Gleitkomma-Ausdrucks mit Ausnahme von Casts und Zuweisungen kann die Reichweite und Genauigkeit eines Gleitkomma-Typs haben, der sich vom Typ des Ausdrucks unterscheidet (siehe FLT_EVAL_METHOD).
- Ungeachtet des Vorstehenden können Zwischenergebnisse eines Gleitkomma-Ausdrucks berechnet werden, als ob sie unendliche Reichweite und Genauigkeit hätten (sofern nicht #pragma STDC FP_CONTRACT auf
OFFgesetzt ist).
|
Dem C++-Compiler ist es gestattet, jede Änderung am Programm vorzunehmen, solange bei gleicher Eingabe das beobachtbare Verhalten des Programms eines der möglichen beobachtbaren Verhaltensweisen ist, die dieser Eingabe entsprechen. Wenn jedoch bestimmte Eingaben zu undefiniertem Verhalten führen, kann der Compiler für diese Eingabe kein beobachtbares Verhalten des Programms garantieren, selbst wenn eine Operation des beobachtbaren Verhaltens vor jeder möglichen undefinierten Operation stattfindet. |
(bis C++26) |
|
Ein Programm kann beobachtbare Checkpoints enthalten. Eine Operation Dem C++-Compiler ist es gestattet, jede Änderung am Programm vorzunehmen, solange bei gleicher Eingabe das beobachtbare Verhalten des definierten Präfixes des Programms eines der möglichen beobachtbaren Verhaltensweisen ist, die diesem definierten Präfix entsprechen. Wenn bestimmte Eingaben zu undefiniertem Verhalten führen, kann der Compiler für diese Eingabe kein beobachtbares Verhalten des Programms garantieren, das nicht zum definierten Präfix gehört. |
(seit C++26) |
[bearbeiten] Hinweise
Da der Compiler (normalerweise) nicht in der Lage ist, den Code einer externen Bibliothek zu analysieren, um festzustellen, ob sie E/A oder volatile Zugriffe durchführt, werden auch Aufrufe von Drittanbieter-Bibliotheken nicht von der Optimierung beeinflusst. Aufrufe der Standardbibliothek können jedoch während der Optimierung durch andere Aufrufe ersetzt, eliminiert oder dem Programm hinzugefügt werden. Statisch verlinkter Code von Drittanbieter-Bibliotheken kann Link-Time-Optimierung unterliegen.
Programme mit undefiniertem Verhalten ändern oft ihr beobachtbares Verhalten, wenn sie mit unterschiedlichen Optimierungseinstellungen neu kompiliert werden. Wenn zum Beispiel ein Test auf vorzeichenbehaftete Ganzzahlüberläufe vom Ergebnis dieses Überlaufs abhängt, z. B. if (n + 1 < n) abort();, wird dieser von einigen Compilern vollständig entfernt, da vorzeichenbehaftete Überläufe undefiniertes Verhalten sind und der Optimierer frei davon ausgehen kann, dass sie nie auftreten und der Test redundant ist.
Copy-Elision ist eine Ausnahme von der Als-ob-Regel: Der Compiler darf Aufrufe von Move- und Copy-Konstruktoren sowie die entsprechenden Aufrufe der Destruktoren temporärer Objekte entfernen, auch wenn diese Aufrufe beobachtbare Nebeneffekte haben.
|
Der new Ausdruck hat eine weitere Ausnahme von der Als-ob-Regel: Der Compiler darf Aufrufe der ersetzbaren Speicherfunktionsoperatoren entfernen, auch wenn eine benutzerdefinierte Ersetzung bereitgestellt wird und beobachtbare Nebeneffekte hat. |
(seit C++14) |
Die Anzahl und Reihenfolge von Gleitkomma-Ausnahmen können durch Optimierung geändert werden, solange der Zustand, wie er von der nächsten Gleitkomma-Operation beobachtet wird, so ist, als ob keine Optimierung stattgefunden hätte.
#pragma STDC FENV_ACCESS ON for (i = 0; i < n; ++i) x + 1; // x + 1 is dead code, but may raise FP exceptions // (unless the optimizer can prove otherwise). However, executing it n times // will raise the same exception over and over. So this can be optimized to: if (0 < n) x + 1;
[bearbeiten] 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;[bearbeiten] Siehe auch
| C-Dokumentation für Als-ob-Regel
|