The as-if rule
Permet toutes les transformations de code qui ne modifient pas le comportement observable du programme.
Table des matières |
Explication
Comportement observable d'un programme inclut les éléments suivants :
|
(jusqu'à C++11) |
|
(depuis C++11) |
|
(until C++26) |
|
(since C++26) |
- Le texte d'invite qui est envoyé aux dispositifs interactifs sera affiché avant que le programme n'attende une entrée.
-
Si le pragme ISO C
#pragma STDC FENV_ACCESS
est pris en charge et est défini sur
ON, les modifications apportées à l' environnement en virgule flottante (exceptions en virgule flottante et modes d'arrondi) sont garanties d'être observées par les opérateurs arithmétiques en virgule flottante et les appels de fonction comme s'ils étaient exécutés tels qu'écrits, sauf que- le résultat de toute expression en virgule flottante autre qu'un cast et une assignation peut avoir une plage et une précision d'un type en virgule flottante différent du type de l'expression (voir FLT_EVAL_METHOD ),
-
nonobstant ce qui précède, les résultats intermédiaires de toute expression en virgule flottante peuvent être calculés comme s'ils avaient une plage et une précision infinies (sauf si
#pragma STDC FP_CONTRACT
est
OFF).
|
Le compilateur C++ est autorisé à effectuer toute modification du programme tant que, pour une entrée donnée, le comportement observable du programme correspond à l'un des comportements observables possibles pour cette entrée. Cependant, si une certaine entrée entraîne un comportement indéfini , le compilateur ne peut garantir aucun comportement observable du programme avec cette entrée, même si une opération du comportement observable se produit avant toute opération potentiellement indéfinie. |
(jusqu'en C++26) |
|
Un programme peut contenir des points de contrôle observables .
Une opération
Le compilateur C++ est autorisé à effectuer toute modification du programme tant que, pour une entrée donnée, le comportement observable du préfixe défini du programme correspond à l'un des comportements observables possibles pour ce préfixe défini. Si une certaine entrée entraîne un comportement indéfini , le compilateur ne peut garantir aucun comportement observable du programme avec cette entrée qui n'appartient pas au préfixe défini. |
(depuis C++26) |
Notes
Parce que le compilateur est (généralement) incapable d'analyser le code d'une bibliothèque externe pour déterminer si elle effectue ou non des E/S ou des accès volatiles, les appels aux bibliothèques tierces ne sont pas non plus affectés par l'optimisation. Cependant, les appels à la bibliothèque standard peuvent être remplacés par d'autres appels, éliminés ou ajoutés au programme pendant l'optimisation. Le code des bibliothèques tierces lié statiquement peut être soumis à l'optimisation au moment de l'édition des liens.
Les programmes comportant un comportement indéfini changent souvent de comportement observable lorsqu'ils sont recompilés avec différents paramètres d'optimisation. Par exemple, si un test pour le dépassement d'entier signé repose sur le résultat de ce dépassement, par exemple if ( n + 1 < n ) abort ( ) ; , il est entièrement supprimé par certains compilateurs car le dépassement signé est un comportement indéfini et l'optimiseur est libre de supposer que cela ne se produit jamais et que le test est redondant.
Élimination de copie est une exception à la règle as-if : le compilateur peut supprimer les appels aux constructeurs de déplacement et de copie ainsi que les appels correspondants aux destructeurs des objets temporaires, même si ces appels ont des effets secondaires observables.
|
new expression a une autre exception à la règle as-if : le compilateur peut supprimer les appels aux fonctions d'allocation remplaçables même si un remplacement défini par l'utilisateur est fourni et a des effets secondaires observables. |
(depuis C++14) |
Le nombre et l'ordre des exceptions en virgule flottante peuvent être modifiés par l'optimisation tant que l'état observé par la prochaine opération en virgule flottante est tel qu'aucune optimisation n'a eu lieu :
#pragma STDC FENV_ACCESS ON for (i = 0; i < n; ++i) x + 1; // x + 1 est du code mort, mais peut lever des exceptions en virgule flottante // (sauf si l'optimiseur peut prouver le contraire). Cependant, l'exécuter n fois // lèvera la même exception à plusieurs reprises. Donc cela peut être optimisé en : if (0 < n) x + 1;
Exemple
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; }
Sortie :
# 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;
Voir aussi
|
Documentation C
pour
règle as-if
|