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 prends 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 celle- while(1) ;
ci ne devrait pas être soumise à l'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 s'imprime begin
, puis se bloque sur une instruction illégale (un ud2
piège 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 d'obtenir que Clang produise une boucle infinie sous -O2
- alors que je pouvais tester à plusieurs reprises une volatile
variable, ce qui implique une lecture en 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 imprime begin
suivi unreachable
comme si la boucle infinie n'avait jamais existé.
Comment obtenir Clang pour produire une boucle infinie sans accès à la mémoire appropriée avec les optimisations activées?
la source
exit()
, et parce que le code peut avoir découvert une situation où il ne peut garantir que les effets d'une exécution continue ne seraient pas pires qu'inutiles . Une boucle de saut à soi est une façon assez moche de gérer de telles situations, mais elle peut néanmoins être la meilleure façon de gérer une mauvaise situation.Réponses:
La norme C11 le dit, 6.8.5 / 6:
Les deux notes de bas de page ne sont pas normatives mais fournissent des informations utiles:
Dans votre cas,
while(1)
est une expression constante limpide, donc l'implémentation ne peut pas être considérée comme terminée. Une telle implémentation serait désespérément interrompue, car les boucles "pour toujours" sont une construction de programmation courante.Ce qui arrive au "code inaccessible" après la boucle n'est cependant pas, à ma connaissance, bien défini. Cependant, le clang se comporte en effet très étrangement. Comparaison du code machine avec gcc (x86):
gcc 9.2
-O3 -std=c11 -pedantic-errors
clang 9.0.0
-O3 -std=c11 -pedantic-errors
gcc génère la boucle, clang court juste dans les bois et se termine avec l'erreur 255.
Je penche pour que ce soit un comportement non conforme de clang. Parce que j'ai essayé de développer votre exemple comme ceci:
J'ai ajouté C11
_Noreturn
pour essayer d'aider le compilateur plus loin. Il doit être clair que cette fonction va raccrocher, à partir de ce seul mot-clé.setjmp
renverra 0 lors de la première exécution, donc ce programme devrait juste s'écraser sur lewhile(1)
et s'arrêter là, n'imprimant que "begin" (en supposant que \ n vide la sortie standard). Cela se produit avec gcc.Si la boucle a été simplement supprimée, elle devrait imprimer "commencer" 2 fois puis imprimer "inaccessible". Cependant, sur clang ( godbolt ), il imprime "begin" 1 fois puis "inaccessible" avant de retourner le code de sortie 0. C'est tout simplement faux, peu importe comment vous le mettez.
Je ne trouve aucun argument pour revendiquer un comportement indéfini ici, donc je pense que c'est un bogue dans Clang. En tout cas, ce comportement rend clang 100% inutile pour des programmes comme les systèmes embarqués, où vous devez simplement pouvoir compter sur des boucles éternelles suspendant le programme (en attendant un chien de garde, etc.).
la source
6.8.5/6
est sous la forme de si (ces) alors vous pouvez supposer (ceci) . Cela ne signifie pas sinon (ces) vous ne pouvez pas supposer (ceci) . C'est une spécification uniquement lorsque les conditions sont remplies, et non lorsqu'elles ne sont pas remplies, où vous pouvez faire tout ce que vous voulez en respectant les normes. Et s'il n'y a pas d'observables ...int z=3; int y=2; int x=1; printf("%d %d\n", x, z);
il n'y a pas2
dans l'assemblage, donc dans le sens inutile videx
n'a pas été attribué aprèsy
mais après enz
raison de l'optimisation. Donc, à partir de votre dernière phrase, nous suivons les règles habituelles, supposons que le temps soit arrêté (parce que nous n'étions pas mieux contraints), et laissé dans l'impression finale, "inaccessible". Maintenant, nous optimisons cette déclaration inutile (parce que nous ne savons pas mieux).while(1);
la même chose qu'uneint y = 2;
déclaration en termes de ce que la sémantique nous permet d'optimiser, même si leur logique reste à la source. À partir de n1528, j'avais l'impression qu'ils peuvent être les mêmes, mais comme les gens sont beaucoup plus expérimentés que moi, ils argumentent dans l'autre sens, et c'est apparemment un bug officiel, puis au-delà d'un débat philosophique sur la question de savoir si le libellé de la norme est explicite , l'argument est rendu théorique.Vous devez insérer une expression susceptible de provoquer un effet secondaire.
La solution la plus simple:
Lien Godbolt
la source
asm("")
est implicitementasm volatile("");
et donc l'instruction asm doit être exécutée autant de fois qu'elle le fait dans la machine abstraite gcc.gnu.org/onlinedocs/gcc/Basic-Asm.html . (Notez qu'il n'est pas sûr que ses effets secondaires incluent de la mémoire ou des registres; vous avez besoin d'Asm étendu avec un"memory"
clobber si vous voulez lire ou écrire de la mémoire à laquelle vous accédez jamais depuis C.Asm de base n'est sûr que pour des choses commeasm("mfence")
oucli
.)D'autres réponses ont déjà couvert les moyens de faire émettre Clang la boucle infinie, avec le langage d'assemblage en ligne ou d'autres effets secondaires. Je veux juste confirmer qu'il s'agit bien d'un bug du compilateur. Plus précisément, il s'agit d' un bogue LLVM de longue date - il applique le concept C ++ de «toutes les boucles sans effets secondaires doivent se terminer» aux langages où il ne devrait pas, comme C.
Par exemple, le langage de programmation Rust autorise également des boucles infinies et utilise LLVM comme backend, et il a ce même problème.
À court terme, il semble que LLVM continuera de supposer que "toutes les boucles sans effets secondaires doivent se terminer". Pour tout langage autorisant des boucles infinies, LLVM s'attend à ce que le frontal insère des
llvm.sideeffect
opcodes dans ces boucles. C'est ce que Rust prévoit de faire, donc Clang (lors de la compilation du code C) devra probablement le faire aussi.la source
sideeffect
op (en 2017) et s'attend à ce que les frontaux insèrent cet op dans des boucles à leur discrétion. LLVM a dû choisir des valeurs par défaut pour les boucles, et il s'est avéré que celui-ci s'alignait sur le comportement de C ++, intentionnellement ou non. Bien sûr, il reste encore un certain travail d'optimisation à faire, par exemple pour fusionner lessideeffect
opérations consécutives en une seule. (C'est ce qui empêche le frontal de Rust de l'utiliser.) Ainsi, sur cette base, le bogue se trouve dans le frontal (clang) qui n'insère pas l'op dans les boucles.sideeffect
opérations au début de chaque fonction et n'a constaté aucune régression des performances d'exécution. Le seul problème est une régression de la compilation , apparemment en raison du manque de fusion des opérations consécutives comme je l'ai mentionné dans mon commentaire précédent.Ceci est un bug Clang
... lors de l'insertion d'une fonction contenant une boucle infinie. Le comportement est différent lorsqu'il
while(1);
apparaît directement en main, ce qui me sent très bogué.Voir la réponse de @ Arnavion pour un résumé et des liens. Le reste de cette réponse a été écrit avant que je ne confirme que c'était un bug, encore moins un bug connu.
Pour répondre à la question du titre: Comment créer une boucle vide infinie qui ne sera pas optimisée? ? -
créer
die()
une macro, pas une fonction , pour contourner ce bogue dans Clang 3.9 et versions ultérieures. (Les versions antérieures de Clang conservent la boucle ou émettent uncall
vers une version non en ligne de la fonction avec la boucle infinie.) Cela semble être sûr même si laprint;while(1);print;
fonction s'aligne dans son appelant ( Godbolt ).-std=gnu11
vs-std=gnu99
ne change rien.Si vous ne vous souciez que de GNU C, P__J__
__asm__("");
à l'intérieur de la boucle fonctionne également, et ne devrait pas nuire à l'optimisation du code environnant pour les compilateurs qui le comprennent. Les instructions asm GNU C Basic sont implicitementvolatile
, donc cela compte comme un effet secondaire visible qui doit "s'exécuter" autant de fois que dans la machine abstraite C. (Et oui, Clang implémente le dialecte GNU de C, comme documenté par le manuel GCC.)Certaines personnes ont fait valoir qu'il pourrait être légal d'optimiser une boucle infinie vide. Je ne suis pas d'accord 1 , mais même si nous acceptons cela, il ne peut pas également être légal pour Clang de supposer que les instructions après que la boucle soit inaccessible, et de laisser l'exécution tomber de la fin de la fonction dans la fonction suivante, ou dans les ordures qui décode comme des instructions aléatoires.
(Ce serait conforme aux normes pour Clang ++ (mais toujours pas très utile); les boucles infinies sans aucun effet secondaire sont UB en C ++, mais pas C.
Is while (1); comportement indéfini en C? UB permet au compilateur d'émettre pratiquement n'importe quoi pour le code sur un chemin d'exécution qui rencontrera certainement l'UB. Une
asm
instruction dans la boucle éviterait cet UB pour C ++. Mais dans la pratique, la compilation de Clang en C ++ ne supprime pas les boucles vides infinies d'expression constante, sauf en ligne, comme lorsque compilation en C.)L'intégration manuelle
while(1);
modifie la façon dont Clang le compile: boucle infinie présente dans asm. C'est ce que nous attendons d'un PDV de règles-avocat.Sur l'explorateur du compilateur Godbolt , Clang 9.0 -O3 se compile en C (
-xc
) pour x86-64:Le même compilateur avec les mêmes options compile un
main
qui appelleinfloop() { while(1); }
d'abord le mêmeputs
, mais arrête ensuite d'émettre des instructions pourmain
après ce point. Donc, comme je l'ai dit, l'exécution tombe juste de la fin de la fonction, quelle que soit la fonction suivante (mais avec la pile mal alignée pour l'entrée de la fonction, ce n'est donc même pas un appel valide).Les options valables seraient de
label: jmp label
boucle infiniereturn 0
partir demain
.Crasher ou continuer sans imprimer "inaccessible" n'est clairement pas acceptable pour une implémentation C11, sauf s'il y a UB que je n'ai pas remarqué.
Note de bas de page 1:
Pour mémoire, je suis d'accord avec la réponse de @ Lundin qui cite la norme pour prouver que C11 ne permet pas l'hypothèse de terminaison pour les boucles infinies à expression constante, même lorsqu'elles sont vides (pas d'E / S, volatiles, synchronisation, ou autre effets secondaires visibles).
Il s'agit de l'ensemble de conditions permettant de compiler une boucle en une boucle asm vide pour un processeur normal. (Même si le corps n'était pas vide dans la source, les affectations aux variables ne peuvent pas être visibles par les autres threads ou les gestionnaires de signaux sans UB de course de données pendant que la boucle est en cours d'exécution. Une implémentation conforme pourrait donc supprimer ces corps de boucle si elle le voulait Ensuite, cela laisse la question de savoir si la boucle elle-même peut être supprimée. ISO C11 dit explicitement non.)
Étant donné que C11 distingue ce cas comme un cas où l'implémentation ne peut pas supposer que la boucle se termine (et que ce n'est pas UB), il semble clair qu'ils ont l'intention que la boucle soit présente au moment de l'exécution. Une implémentation qui cible les processeurs avec un modèle d'exécution qui ne peut pas faire une quantité infinie de travail en temps fini n'a aucune justification pour supprimer une boucle infinie constante vide. Ou même en général, la formulation exacte consiste à savoir si elles peuvent être "supposées se terminer" ou non. Si une boucle ne peut pas se terminer, cela signifie que le code ultérieur n'est pas accessible, quels que soient les arguments que vous faites sur les mathématiques et les infinis et combien de temps il faut pour effectuer une quantité infinie de travail sur une machine hypothétique.
De plus, Clang n'est pas simplement une DeathStation 9000 conforme à la norme ISO C, il est destiné à être utile pour la programmation de systèmes de bas niveau dans le monde réel, y compris les noyaux et les éléments intégrés. Donc , si vous acceptiez ou non des arguments au sujet de C11 permettant la suppression de
while(1);
, il ne fait pas de sens que Clang voudrait réellement faire cela. Si vous écrivezwhile(1);
, ce n'était probablement pas un accident. La suppression des boucles qui finissent par être infinies par accident (avec des expressions de contrôle de variable d'exécution) peut être utile, et il est logique que les compilateurs le fassent.Il est rare que vous vouliez simplement tourner jusqu'à la prochaine interruption, mais si vous écrivez cela en C, c'est certainement ce que vous attendez. (Et ce qui se passe dans GCC et Clang, sauf pour Clang lorsque la boucle infinie est à l'intérieur d'une fonction wrapper).
Par exemple, dans un noyau de système d'exploitation primitif, lorsque le planificateur n'a aucune tâche à exécuter, il peut exécuter la tâche inactive. Une première implémentation de cela pourrait être
while(1);
.Ou pour le matériel sans aucune fonction d'économie d'énergie, cela pourrait être la seule implémentation. (Jusqu'au début des années 2000, c'était je pense pas rare sur x86. Bien que l'
hlt
instruction existait, IDK si elle économisait une quantité significative d'énergie jusqu'à ce que les processeurs commencent à avoir des états de veille à faible puissance.)la source
-ffreestanding -fno-strict-aliasing
. Cela fonctionne bien avec ARM et peut-être avec les AVR hérités.Juste pour mémoire, Clang se comporte également mal avec
goto
:Il produit le même résultat que dans la question, c'est-à-dire:
Je ne vois aucun moyen de lire ceci comme autorisé dans C11, qui dit seulement:
Comme
goto
n'est pas une « déclaration d'itération » (6.8.5 listeswhile
,do
etfor
) rien indulgences appliquer « pris terminaison » spéciale, mais vous voulez les lire.Le compilateur de liens Godbolt de la question d'origine est x86-64 Clang 9.0.0 et les drapeaux sont
-g -o output.s -mllvm --x86-asm-syntax=intel -S --gcc-toolchain=/opt/compiler-explorer/gcc-9.2.0 -fcolor-diagnostics -fno-crash-diagnostics -O2 -std=c11 example.c
Avec d'autres tels que x86-64 GCC 9.2, vous obtenez le parfait:
Drapeaux:
-g -o output.s -masm=intel -S -fdiagnostics-color=always -O2 -std=c11 example.c
la source
nasty: goto nasty
peut être conforme et ne pas faire tourner le (s) CPU jusqu'à ce que l'épuisement de l'utilisateur ou des ressources intervienne.bar()
intérieurfoo()
traité comme un appel de__1foo
à__2bar
, de__2foo
à__3bar
, etc. et de__16foo
à__launch_nasal_demons
, ce qui permettrait alors à tous les objets automatiques d'être alloués statiquement, et ferait ce qui est généralement une limite "d'exécution" en une limite de traduction.Je jouerai l'avocat du diable et soutiendrai que la norme n'interdit pas explicitement à un compilateur d'optimiser une boucle infinie.
Analysons cela. Une instruction d'itération qui satisfait certains critères peut être supposée se terminer:
Cela ne dit rien sur ce qui se passe si les critères ne sont pas satisfaits et supposer qu'une boucle peut se terminer même alors n'est pas explicitement interdit tant que d'autres règles de la norme sont respectées.
do { } while(0)
ouwhile(0){}
sont après tout des instructions d'itération (boucles) qui ne satisfont pas aux critères qui permettent à un compilateur de simplement supposer sur un coup de tête qu'elles se terminent et pourtant elles se terminent évidemment.Mais le compilateur peut-il tout simplement optimiser
while(1){}
?5.1.2.3p4 dit:
Cela mentionne des expressions, pas des déclarations, donc ce n'est pas convaincant à 100%, mais cela permet certainement des appels comme:
à sauter. Fait intéressant, clang le saute, et gcc ne le fait pas .
la source
while(1){}
y a donc une séquence infinie d'1
évaluations entrelacées avec des{}
évaluations, mais où dans la norme dit-il que ces évaluations doivent prendre un temps différent de zéro ? Le comportement de gcc est plus utile, je suppose, car vous n'avez pas besoin de trucs impliquant un accès à la mémoire ou des trucs en dehors du langage. Mais je ne suis pas convaincu que la norme interdit cette optimisation en clang. Si rendre nonwhile(1){}
optimisable est l'intention, la norme devrait être explicite à ce sujet et le bouclage infini devrait être répertorié comme un effet secondaire observable dans 5.1.2.3p2.1
condition comme un calcul de valeur. Le temps d'exécution n'a pas d'importance - ce qui compte, c'est ce quiwhile(A){} B;
peut ne pas être entièrement optimisé, pas optimiséB;
et non re-séquencéB; while(A){}
. Pour citer la machine abstraite C11, je souligne: "La présence d'un point de séquence entre l'évaluation des expressions A et B implique que chaque calcul de valeur et effet secondaire associé à A est séquencé avant chaque calcul de valeur et effet secondaire associé à B ". La valeur deA
est clairement utilisée (par la boucle).J'ai été convaincu que ce n'est qu'un simple vieux bug. Je laisse mes tests ci-dessous et en particulier la référence à la discussion au sein du comité standard pour certains raisonnements que j'avais précédemment.
Je pense que c'est un comportement indéfini (voir fin), et Clang n'a qu'une seule implémentation. GCC fonctionne en effet comme vous vous y attendez, optimisant uniquement l'
unreachable
instruction print mais laissant la boucle. Certains comment Clang prend étrangement des décisions en combinant la doublure et en déterminant ce qu'elle peut faire avec la boucle.Le comportement est très étrange - il supprime l'impression finale, donc "voir" la boucle infinie, mais aussi se débarrasser de la boucle.
C'est encore pire pour autant que je sache. Suppression de l'inline que nous obtenons:
de sorte que la fonction est créée et l'appel optimisé. C'est encore plus résistant que prévu:
entraîne un assemblage très non optimal pour la fonction, mais l'appel de fonction est à nouveau optimisé! Encore pire:
J'ai fait un tas d'autres tests en ajoutant une variable locale et en l'augmentant, en passant un pointeur, en utilisant un
goto
etc ... À ce stade, j'abandonnerais. Si vous devez utiliser clangFait le travail. Il aspire à l'optimisation (évidemment), et part en finale redondante
printf
. Au moins, le programme ne s'arrête pas. Peut-être GCC après tout?Addenda
Après discussion avec David, je cède que la norme ne dit pas "si la condition est constante, vous ne pouvez pas supposer que la boucle se termine". En tant que tel, et conformément à la norme, il n'y a pas de comportement observable (tel que défini dans la norme), je ne plaiderais que pour la cohérence - si un compilateur optimise une boucle parce qu'il suppose qu'elle se termine, il ne doit pas optimiser les instructions suivantes.
Heck n1528 a ces comportements indéfinis si je lis bien. Plus précisément
À partir de là, je pense que cela ne peut se résumer qu'à une discussion de ce que nous voulons (attendu?) Plutôt que de ce qui est autorisé.
la source
Il semble que ce soit un bug dans le compilateur Clang. S'il n'y a aucune contrainte sur la
die()
fonction d'être une fonction statique, supprimez-lastatic
et faites-lainline
:Il fonctionne comme prévu lorsqu'il est compilé avec le compilateur Clang et est également portable.
Explorateur du compilateur (godbolt.org) - clang 9.0.0
-O3 -std=c11 -pedantic-errors
la source
static inline
?Les éléments suivants semblent fonctionner pour moi:
à godbolt
Dire explicitement à Clang de ne pas optimiser qu'une fonction entraîne l'émission d'une boucle infinie comme prévu. Espérons qu'il existe un moyen de désactiver sélectivement certaines optimisations au lieu de simplement les désactiver toutes comme ça. Clang refuse toujours d'émettre du code pour le second
printf
, cependant. Pour le forcer à le faire, j'ai dû modifier davantage le code à l'intérieurmain
pour:Il semble que vous deviez désactiver les optimisations pour votre fonction de boucle infinie, puis vous assurer que votre boucle infinie est appelée conditionnellement. Dans le monde réel, ce dernier est presque toujours le cas de toute façon.
la source
printf
soit générée si la boucle va réellement pour toujours, car dans ce cas, la secondeprintf
est vraiment inaccessible et peut donc être supprimée. (L'erreur de Clang est à la fois de détecter l'inaccessibilité et de supprimer la boucle de sorte que le code inaccessible soit atteint).__attribute__ ((optimize(1)))
, mais clang l'ignore comme non pris en charge: godbolt.org/z/4ba2HM . gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.htmlUne implémentation conforme peut, et de nombreuses applications pratiques le font, imposer des limites arbitraires sur la durée d'exécution d'un programme ou sur 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 "comme si" - s'il détermine qu'ils seront inévitablement violés. À condition qu'une implémentation 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 aucune limite 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 en dehors de la juridiction de la norme.
Je pense que l'intention de la norme est tout à fait claire que les compilateurs ne devraient pas supposer qu'une
while(1) {}
boucle sans effets secondaires nibreak
instructions se terminera. Contrairement à ce que certains pourraient penser, les auteurs de la norme 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'était pas interrompu, exécuterait plus d'instructions libres d'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 concernant 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.la source
La boucle n'a pas d'effets secondaires et peut donc être optimisée. La boucle est en fait un nombre infini d'itérations de zéro unité de travail. Ceci n'est pas défini en mathématiques et en logique et la norme ne dit pas si une implémentation est autorisée à accomplir un nombre infini de choses si chaque chose peut être faite en un temps nul. L'interprétation de Clang est parfaitement raisonnable en traitant l'infini fois zéro comme zéro plutôt que l'infini. La norme ne dit pas si une boucle infinie peut se terminer si tout le travail dans les boucles est en fait terminé.
Le compilateur est autorisé à optimiser tout ce qui n'est pas un comportement observable tel que défini dans la norme. Cela comprend le temps d'exécution. Il n'est pas nécessaire de conserver le fait que la boucle, si elle n'est pas optimisée, prendrait un temps infini. Il est permis de changer cela en un temps d'exécution beaucoup plus court - en fait, c'est le point de la plupart des optimisations. Votre boucle a été optimisée.
Même si clang a traduit le code naïvement, vous pourriez imaginer un processeur d'optimisation capable de terminer chaque itération en deux fois moins de temps que l'itération précédente. Cela compléterait littéralement la boucle infinie en un temps fini. Un tel processeur optimisant viole-t-il la norme? Il semble assez absurde de dire qu'un processeur d'optimisation violerait la norme s'il est trop bon pour l'optimisation. Il en va de même pour un compilateur.
la source
Je suis désolé si ce n'est absurdement pas le cas, je suis tombé sur ce post et je sais parce que mes années d'utilisation de la distribution Gentoo Linux que si vous voulez que le compilateur n'optimise pas votre code, vous devez utiliser -O0 (Zero). J'étais curieux à ce sujet, et j'ai compilé et exécuté le code ci-dessus, et la boucle va indéfiniment. Compilé avec clang-9:
la source
Une
while
boucle vide n'a aucun effet secondaire sur le système.Par conséquent, Clang le supprime. Il existe de "meilleures" façons de réaliser le comportement prévu qui vous obligent à être plus évident de vos intentions.
while(1);
est baaadd.la source
abort()
ouexit()
. Si une situation survient lorsqu'une fonction détermine que (peut-être à la suite d'une corruption de la mémoire) une exécution continue serait pire que dangereuse, un comportement par défaut courant pour les bibliothèques intégrées consiste à appeler une fonction qui exécute unwhile(1);
. Il peut être utile pour le compilateur d'avoir des options pour remplacer un comportement plus utile , mais tout rédacteur de compilateur qui ne peut pas comprendre comment traiter une construction aussi simple comme une barrière à l'exécution continue du programme est incompétent pour faire confiance à des optimisations complexes.