web-dev-qa-db-fra.com

Pourquoi les compilateurs sont-ils si fiables?

Nous utilisons des compilateurs quotidiennement comme si leur exactitude est une donnée, mais les compilateurs sont aussi des programmes et peuvent potentiellement contenir des bogues. Je me suis toujours interrogé sur cette robustesse infaillible. Avez-vous déjà rencontré un bogue dans le compilateur lui-même? Qu'est-ce que c'était et comment avez-vous réalisé que le problème était dans le compilateur lui-même?

... et comment do rendent-ils les compilateurs si fiables?

67
EpsilonVector

Ils sont testés de manière approfondie via l'utilisation par des milliers voire des millions de développeurs au fil du temps.

De plus, le problème à résoudre est bien défini (par une spécification technique très détaillée). Et la nature de la tâche se prête facilement aux tests unitaires/système. C'est à dire. il s'agit essentiellement de traduire une entrée textuelle dans un format très spécifique pour une sortie dans un autre type de format bien défini (une sorte de bytecode ou de code machine). Il est donc facile de créer et de vérifier des cas de test.

De plus, les bogues sont généralement faciles à reproduire: à part la plate-forme exacte et les informations de version du compilateur, il vous suffit généralement d'un morceau de code d'entrée. Sans oublier que les utilisateurs du compilateur (qui sont eux-mêmes les développeurs) ont tendance à fournir des rapports de bogues bien plus précis et détaillés que tout utilisateur d'ordinateur moyen :-)

101
Péter Török

En plus de toutes les bonnes réponses à ce jour:

Vous avez un "biais d'observation". Vous n'observez pas de bugs, et donc vous supposez qu'il n'y en a pas.

Je pensais comme toi. Ensuite, j'ai commencé à écrire des compilateurs de manière professionnelle, et laissez-moi vous dire qu'il y a beaucoup de bugs là-dedans!

Vous ne voyez pas les bogues parce que vous écrivez du code qui équivaut à 99,999% du reste du code que les gens écrivent. Vous écrivez probablement du code parfaitement normal, simple et clairement correct qui appelle des méthodes et exécute des boucles et ne fait rien d'extraordinaire ou bizarre, car vous êtes un développeur normal qui résout des problèmes commerciaux normaux.

Vous ne voyez aucun bogue du compilateur car les bogues du compilateur ne sont pas dans les scénarios de code normaux simples à analyser; les bogues sont dans l'analyse de code bizarre que vous n'écrivez pas.

D'un autre côté, j'ai le biais d'observation opposé. Je vois du code fou toute la journée tous les jours, et donc pour moi, les compilateurs semblent être pleins de bugs.

Si vous vous êtes assis avec la spécification de langue de n'importe quelle langue, et avez pris n'importe quelle implémentation de compilateur pour cette langue, et avez vraiment essayé de déterminer si le compilateur a implémenté exactement la spécification ou non, en vous concentrant sur les cas de coins obscurs, vous trouverez bientôt bogues du compilateur assez fréquemment. Permettez-moi de vous donner un exemple, voici un bogue du compilateur C # que j'ai trouvé il y a littéralement cinq minutes.

static void N(ref int x){}
...
N(ref 123);

Le compilateur donne trois erreurs.

  • Un argument ref ou out doit être une variable assignable.
  • La meilleure correspondance pour N (ref int x) a des arguments invalides.
  • "Ref" manquant sur l'argument 1.

Évidemment, le premier message d'erreur est correct et le troisième est un bug. L'algorithme de génération d'erreur essaie de comprendre pourquoi le premier argument n'était pas valide, il le regarde, voit qu'il s'agit d'une constante et ne retourne pas au code source pour vérifier s'il a été marqué comme "ref"; il suppose plutôt que personne ne serait assez fou pour marquer une constante comme référence et décide que la référence doit être manquante.

Il n'est pas clair quel est le troisième message d'erreur correct, mais ce n'est pas ça. En fait, il n'est pas clair non plus si le message d'erreur second est correct. La résolution de surcharge doit-elle échouer ou "ref 123" doit-il être traité comme un argument ref du type correct? Je vais maintenant devoir y réfléchir et en discuter avec l'équipe de triage afin que nous puissions déterminer quel est le comportement correct.

Vous n'avez jamais vu ce bogue car vous ne feriez probablement jamais quelque chose d'aussi stupide que d'essayer de passer 123 par réf. Et si vous le faisiez, vous ne remarqueriez probablement même pas que le troisième message d'erreur est absurde, car le premier est correct et suffisant pour diagnostiquer le problème. Mais j'essaie de faire des choses comme ça, parce que je suis essayant pour casser le compilateur. Si vous essayez, vous verrez également les bogues.

66
Eric Lippert

Vous plaisantez j'espère? Les compilateurs ont aussi des bugs, des charges vraiment.

GCC est probablement le plus célèbre des compilateurs open source de la planète et jetez un œil à sa base de données de bogues: http://gcc.gnu.org/bugzilla/buglist.cgi?product=gcc&component=c%2B% 2B & résolution = ---

Entre GCC 3.2 et GCC 3.2.3, jetez un œil au nombre de bogues corrigés: http://gcc.gnu.org/gcc-3.2/changes.html

Quant à d'autres comme Visual C++, je ne veux même pas commencer.

Comment rendre les compilateurs fiables? Eh bien pour commencer, ils ont des charges et des charges de tests unitaires. Et la planète entière les utilise donc pas de pénurie de testeurs.

Sérieusement, les développeurs de compilateurs que j'aime à croire sont des programmeurs supérieurs et bien qu'ils ne soient pas infaillibles, ils sont très efficaces.

54
Fanatic23

J'en ai rencontré deux ou trois dans ma journée. La seule véritable façon d'en détecter un est de regarder le code Assembly.

Bien que les compilateurs soient très fiables pour des raisons soulignées par d'autres affiches, je pense que la fiabilité du compilateur est souvent une évaluation auto-réalisatrice. Les programmeurs ont tendance à considérer le compilateur comme la norme. Quand quelque chose ne va pas, vous supposez que c'est votre faute (parce que c'est 99,999% du temps) et changez votre code pour contourner le problème du compilateur plutôt que l'inverse. Par exemple, le code qui plante sous un paramètre d'optimisation élevé est certainement un bogue du compilateur, mais la plupart des gens le définissent un peu plus bas et continuent sans rapporter le bogue.

21
Karl Bielefeldt

Les compilateurs ont plusieurs propriétés qui conduisent à leur exactitude:

  • Le domaine est très connu et recherché. Le problème est bien défini et les solutions proposées sont bien définies.
  • Les tests automatisés sont suffisants pour prouver que les compilateurs fonctionnent correctement
  • Les compilateurs ont des tests très étendus, généralement publics, automatisés et unitaires, qui se sont accumulés au fil du temps pour couvrir plus d'espace d'erreur que pour la plupart des autres programmes
  • Les compilateurs ont un très grand nombre de globes oculaires regardant leurs résultats
15
blueberryfields

Nous utilisons des compilateurs au quotidien

... et comment rendent-ils les compilateurs si fiables?

Ils ne le font pas. Nous faisons. Parce que tout le monde les utilise tout le temps, les bugs sont trouvés rapidement.

C'est un jeu de nombres. Parce que les compilateurs sont utilisés de manière si omniprésente, il est très probable que tout bug sera déclenché par quelqu'un, mais parce qu'il y a un si grand nombre d'utilisateurs, il est hautement peu probable que cela quelqu'un sera vous spécifiquement.

Cela dépend donc de votre point de vue: sur tous les utilisateurs, les compilateurs sont bogués. Mais il est très probable que quelqu'un d'autre aura compilé un morceau de code similaire avant vous, donc si leur était un bogue, cela les aurait frappés, pas vous, donc de votre individuel point de vue, on dirait que le bug n'a jamais été là.

Bien sûr, en plus de cela, vous pouvez ajouter toutes les autres réponses ici: les compilateurs sont bien documentés, bien compris. Il y a ce mythe selon lequel ils sont difficiles à écrire, ce qui signifie que seuls les programmeurs très intelligents et très bons tentent réellement d'en écrire un et sont extrêmement prudents lorsqu'ils le font. Ils sont généralement faciles à tester et faciles à stresser ou à tester. Les utilisateurs du compilateur ont tendance à être eux-mêmes des programmeurs experts, ce qui conduit à des rapports de bogues de haute qualité. Et l'inverse: les rédacteurs de compilateurs ont tendance à être des utilisateurs de leur propre compilateur.

14
Jörg W Mittag

En plus de toutes les réponses déjà, je voudrais ajouter:

Je crois souvent, les vendeurs mangent leur propre nourriture pour chien. Cela signifie qu'ils écrivent les compilateurs en eux-mêmes.

12
DevSolo

J'ai rencontré des bogues de compilation souvent.

Vous pouvez les trouver dans les coins les plus sombres où il y a moins de testeurs. Par exemple, pour trouver des bogues dans GCC, vous devriez essayer:

  • Construisez un compilateur croisé. Vous trouverez littéralement des dizaines de bogues dans les scripts de configuration et de construction de GCC. Certains entraînent des échecs de génération lors de la compilation de GCC et d'autres entraîneront l'échec du compilateur croisé à créer des exécutables de travail.
  • Créez une version Itanium de GCC en utilisant profile-bootstrap. La dernière fois que j'ai essayé cela sur GCC 4.4 et 4.5, il n'a pas réussi à produire un gestionnaire d'exceptions C++ fonctionnel. La construction non optimisée a bien fonctionné. Personne ne semblait intéressé par la correction du bogue que j'ai signalé et j'ai renoncé à le réparer moi-même après avoir essayé de creuser ce qui cassait les spécifications de la mémoire asm GCC.
  • Essayez de construire votre propre GCJ de travail à partir des derniers éléments sans suivre un script de construction de distribution. Je te défie.
8
Zan Lynx

Plusieurs raisons:

  • Auteurs du compilateur "mangent leur propre nourriture pour chiens".
  • Les compilateurs sont basés sur principes bien compris de CS.
  • Les compilateurs sont construits sur une très spécification claire.
  • Les compilateurs obtiennent testé.
  • Les compilateurs sont pas toujours très fiables.
6
Kramii

Ils sont généralement très bons à -O0. En fait, si nous suspectons un bogue du compilateur, nous comparons -O0 au niveau que nous essayons d'utiliser. Des niveaux d'optimisation plus élevés s'accompagnent d'un risque plus élevé. Certains le sont même délibérément et sont étiquetés comme tels dans la documentation. J'en ai rencontré un grand nombre (au moins une centaine pendant mon temps), mais ils deviennent beaucoup plus rares récemment. Néanmoins, à la recherche de bons numéros de référence (ou d'autres références importantes pour le marketing), la tentation de repousser les limites est grande. Il y a quelques années, nous avons eu des problèmes où un fournisseur (pour ne pas nommer) a décidé de faire une violation de la parenthèse par défaut - plutôt qu'une option de compilation spéciale clairement étiquetée.

Il peut être difficile de diagnostiquer une erreur de compilation par rapport à une référence de mémoire erronée, une recompilation avec différentes options peut simplement brouiller le positionnement relatif des objets de données dans la mémoire, donc vous ne savez pas s'il s'agit du Heisenbug de votre code source ou d'un buggy compilateur. De nombreuses optimisations apportent également des modifications légitimes dans l'ordre des opérations, voire des simplifications algébriques à votre algèbre, et celles-ci auront des propriétés différentes en ce qui concerne l'arrondi à virgule flottante et le dépassement/dépassement. Il est difficile de démêler ces effets des VRAIS bogues. Le calcul en virgule flottante noyau dur est difficile pour cette raison, car les bogues et la sensibilité numérique ne sont souvent pas faciles à démêler.

5
Omega Centauri

Les bogues du compilateur ne sont pas si rares. Le cas le plus courant est qu'un compilateur signale une erreur sur le code qui devrait être accepté, ou qu'un compilateur accepte un code qui aurait dû être rejeté.

5
kevin cline

Avez-vous déjà rencontré un bogue dans le compilateur lui-même? Qu'est-ce que c'était et comment avez-vous réalisé que le problème était dans le compilateur lui-même?

Ouaip!

Les deux plus mémorables ont été les deux premiers que j'ai rencontrés. Ils étaient tous les deux dans le compilateur Lightspeed C pour les Mac 680x0 vers 1985-7.

La première était où, dans certaines circonstances, l'opérateur de post-incrémentation entier ne faisait rien - en d'autres termes, dans un morceau de code particulier, "i ++" ne faisait simplement rien pour "i". Je tirais mes cheveux jusqu'à ce que je regarde un démontage. Ensuite, j'ai fait l'incrémentation d'une manière différente et soumis un rapport de bogue.

La seconde était un peu plus compliquée, et était vraiment une "fonctionnalité" irréfléchie qui tournait mal. Les premiers Mac avaient un système compliqué pour effectuer des opérations sur disque de bas niveau. Pour une raison quelconque, je n'ai jamais compris - ce qui a probablement à voir avec la création de petits exécutables - plutôt que le compilateur générant simplement les instructions d'opération de disque en place dans le code objet, le compilateur Lightspeed appellerait une fonction interne, qui lors de l'exécution générait l'opération de disque instructions sur la pile et a sauté là-bas.

Cela fonctionnait très bien sur les processeurs 68000, mais lorsque vous exécutiez le même code sur un processeur 68020, cela faisait souvent des choses étranges. Il s'est avéré qu'une nouvelle fonctionnalité du 68020 était un cache d'instructions primitif de 256 octets. Ceci étant les premiers jours avec les caches CPU, il n'avait aucune idée que le cache était "sale" et devait être rempli; Je suppose que les concepteurs de CPU de Motorola n'ont pas pensé au code auto-modifiable. Donc, si vous avez effectué deux opérations de disque suffisamment proches l'une de l'autre dans votre séquence d'exécution et que le runtime Lightspeed a construit les instructions réelles au même emplacement sur la pile, le processeur penserait à tort qu'il a un cache d'instruction et exécuter la première opération de disque deux fois.

Encore une fois, comprendre cela a pris un peu de fouille avec un démonteur et beaucoup de pas à pas dans un débogueur de bas niveau. Ma solution de contournement consistait à préfixer chaque opération de disque avec un appel à une fonction qui exécutait 256 instructions "NOP", ce qui inondait (et donc effaçait) le cache d'instructions.

Au cours des 25 dernières années, j'ai vu de moins en moins de bogues de compilation au fil du temps. Je pense qu'il y a plusieurs raisons à cela:

  • Il existe un ensemble toujours croissant de tests de validation pour les compilateurs.
  • Les compilateurs modernes sont généralement divisés en deux parties ou plus, dont l'une génère du code indépendant de la plate-forme (par exemple, les LLVM ciblant ce que vous pourriez considérer comme un processeur imaginaire), et une autre qui traduit cela en instructions pour votre matériel cible réel. Dans les compilateurs multi-plateformes, la première partie est utilisée partout, elle reçoit donc des tonnes de tests en conditions réelles.
4
Bob Murphy

J'ai trouvé une erreur flagrante dans Turbo Pascal il y a 5,5 ans. Une erreur présente ni dans la version précédente (5.0) ni dans la version suivante (6.0) du compilateur. Et celui qui aurait dû être facile à tester, car ce n'était pas du tout une valise (juste un appel qui n'est pas communément utilisé).

En général, les constructeurs de compilateurs commerciaux (plutôt que les projets de loisirs) auront certainement des procédures d'AQ et de test très complètes en place. Ils savent que leurs compilateurs sont leurs projets phares et que les défauts leur sembleront très mauvais, pires qu'ils ne le feraient sur d'autres entreprises fabriquant la plupart des autres produits. Les développeurs de logiciels sont un groupe impitoyable, nos fournisseurs d'outils nous laissent tomber, nous allons probablement chercher des alternatives plutôt que d'attendre une solution du fournisseur, et nous sommes très susceptibles de communiquer ce fait à nos pairs qui pourraient bien suivre notre exemple. Dans de nombreux autres secteurs, ce n'est pas le cas, de sorte que la perte potentielle d'un fabricant de compilateurs à la suite d'un bug grave est beaucoup plus importante que celle d'un fabricant de logiciels de montage vidéo.

4
jwenting

Oui, j'ai rencontré hier un bogue dans le compilateur ASP.NET:

Lorsque vous utilisez des modèles fortement typés dans les vues, le nombre de paramètres que les modèles peuvent contenir est limité. Évidemment, il ne peut pas prendre plus de 4 paramètres de modèle, de sorte que les deux exemples ci-dessous en font trop pour le compilateur:

ViewUserControl<System.Tuple<type1, type2, type3, type4, type5>>

Ne compilerait pas tel quel mais le fera si type5 est retiré.

ViewUserControl<System.Tuple<MyModel, System.Func<type1, type2, type3, type4>>>

Compilerait si type4 est retiré.

Notez que System.Tuple a de nombreuses surcharges et peut prendre jusqu'à 16 paramètres (c'est fou je sais).

3
user8685

Les bogues du compilateur se produisent, mais vous avez tendance à les trouver dans des coins étranges ...

Il y avait un bug étrange dans le compilateur VAX VMS C de Digital Equipment Corporation dans les années 1990

(Je portais un oignon sur ma ceinture, comme c'était la mode à l'époque)

Un point-virgule superflu n'importe où précédant une boucle for serait compilé comme corps de la boucle for.

f(){...}
;
g(){...}

void test(){
  int i;
  for ( i=0; i < 10; i++){
     puts("hello");
  }
}

Sur le compilateur en question, la boucle ne s'exécute qu'une seule fois.

il voit

f(){...}
g(){...}

void test(){
  int i;
  for ( i=0; i < 10; i++) ;  /* empty statement for fun */

  {
     puts("hello");
  }
}

Cela m'a coûté beaucoup de temps.

L'ancienne version du compilateur PIC C que nous (l'habitude d'infliger) aux étudiants en expérience de travail ne pouvait pas générer de code qui utilisait correctement l'interruption de haute priorité. Vous avez dû attendre 2-3 ans et mettre à niveau.

Le compilateur MSVC 6 avait un bug astucieux dans l'éditeur de liens, il segmenterait la faute et mourrait de temps en temps sans raison. Une construction propre l'a généralement corrigé (mais soupir pas toujours).

3
Tim Williscroft

Lorsque le comportement de votre logiciel est différent lors de la compilation avec -O0 et avec -O2, vous avez trouvé un bogue de compilation.

Lorsque le comportement de votre logiciel est simplement différent de ce que vous attendez, il y a de fortes chances que le bogue soit dans votre code.

2
mouviciel

Dans certains domaines, tels que les logiciels avioniques, les exigences de certification sont extrêmement élevées, sur le code et le matériel, ainsi que sur le compilateur. À propos de cette dernière partie, il y a un projet qui vise à créer un compilateur C formellement vérifié, appelé Compcert . En théorie, ce type de compilateur est aussi fiable que possible.

2
Axel

J'ai vu plusieurs bogues de compilation, j'en ai signalé quelques-uns moi-même (en particulier en F #).

Cela dit, je pense que les bogues du compilateur sont rares car les personnes qui écrivent des compilateurs sont généralement très à l'aise avec les concepts rigoureux de l'informatique qui les rendent vraiment conscients des implications mathématiques du code.

La plupart d'entre eux sont vraisemblablement très familiers avec des choses comme le calcul lambda, la vérification formelle, la sémantique dénotationnelle, etc. - des choses qu'un programmeur moyen comme moi ne peut à peine comprendre.

De plus, il y a généralement un mappage assez simple de l'entrée à la sortie dans les compilateurs, donc le débogage d'un langage de programmation est probablement beaucoup plus facile que le débogage, disons, d'un moteur de blog.

2
Rei Miyasaka

J'ai trouvé un bug dans le compilateur C # il n'y a pas si longtemps, vous pouvez voir comment Eric Lippert (qui fait partie de l'équipe de conception C #) a compris ce qu'était le bug ici .

En plus des réponses déjà données, je voudrais ajouter quelques éléments supplémentaires. Les concepteurs de compilateurs sont souvent d'excellents programmeurs. Les compilateurs sont très importants: la plupart des programmes se font à l'aide de compilateurs, il est donc impératif que le compilateur soit de haute qualité. Il est donc dans le meilleur intérêt des entreprises qui fabriquent des compilateurs d'y mettre leurs meilleurs collaborateurs (ou du moins, de très bonnes personnes: les meilleures pourraient ne pas aimer la conception de compilateurs). Microsoft aimerait beaucoup que ses compilateurs C et C++ fonctionnent correctement, ou le reste de l'entreprise ne peut pas faire son travail.

De plus, si vous construisez un compilateur vraiment complexe, vous ne pouvez pas simplement le pirater ensemble. La logique derrière les compilateurs est à la fois très complexe et facile à formaliser. Par conséquent, ces programmes seront souvent construits de manière très "robuste" et générique, ce qui a tendance à entraîner moins de bogues.

2
Alex ten Brink