La norme C11 semble impliquer que les instructions d'itération avec des expressions de contrôle constantes ne doivent pas être optimisées. Je suis mon conseil de cette réponse , qui cite spécifiquement la section 6.8.5 du projet de norme:
Une instruction d'itération dont l'expression de contrôle n'est pas une expression constante ... peut être supposée par l'implémentation se terminer.
Dans cette réponse, il mentionne qu'une boucle comme while(1) ;
ne devrait pas faire l'objet d'une optimisation.
Alors ... pourquoi Clang/LLVM optimise-t-il la boucle ci-dessous (compilée avec cc -O2 -std=c11 test.c -o test
)?
#include <stdio.h>
static void die() {
while(1)
;
}
int main() {
printf("begin\n");
die();
printf("unreachable\n");
}
Sur ma machine, cela imprime begin
, puis plante sur une instruction illégale (un piège ud2
Placé après die()
). Sur godbolt , nous pouvons voir que rien n'est généré après l'appel à puts
.
Cela a été une tâche étonnamment difficile pour Clang de produire une boucle infinie sous -O2
- alors que je pourrais tester à plusieurs reprises une variable volatile
, qui implique une lecture de mémoire que je ne veux pas. Et si je fais quelque chose comme ça:
#include <stdio.h>
static void die() {
while(1)
;
}
int main() {
printf("begin\n");
volatile int x = 1;
if(x)
die();
printf("unreachable\n");
}
... Clang affiche begin
suivi de unreachable
comme si la boucle infinie n'avait jamais existé.
Comment faire pour que Clang génère une boucle infinie correcte, sans accès à la mémoire, avec les optimisations activées?
Une implémentation conforme peut, et de nombreuses implémentations pratiques le font, imposer des limites arbitraires sur la durée d'exécution d'un programme ou le nombre d'instructions qu'il exécuterait, et se comporter de manière arbitraire si ces limites sont violées ou - selon la règle du "comme si" - s'il détermine qu'ils seront inévitablement violés. À condition qu'une mise en œuvre puisse traiter avec succès au moins un programme qui exerce nominalement toutes les limites énumérées dans N1570 5.2.4.1 sans atteindre les limites de traduction, l'existence de limites, la mesure dans laquelle elles sont documentées et les effets de leur dépassement sont tous les problèmes de qualité de mise en œuvre hors de la compétence de la norme.
Je pense que l'intention du Standard est très claire que les compilateurs ne devraient pas supposer qu'une boucle while(1) {}
sans effets secondaires ni instructions break
se terminera. Contrairement à ce que certains pourraient penser, les auteurs du Standard n'invitaient pas les rédacteurs de compilateurs à être stupides ou obtus. Une implémentation conforme pourrait utilement décider de mettre fin à tout programme qui, s'il n'est pas interrompu, exécuterait plus d'instructions sans effets secondaires qu'il n'y a d'atomes dans l'univers, mais une implémentation de qualité ne devrait pas effectuer une telle action sur la base d'une hypothèse sur la résiliation, mais plutôt sur la base que cela pourrait être utile et ne serait pas (contrairement au comportement de Clang) pire qu'inutile.