it-swarm-eu.dev

Wie kommt es, dass Compiler so zuverlässig sind?

Wir verwenden Compiler täglich so, als ob ihre Richtigkeit gegeben wäre, aber Compiler sind auch Programme und können möglicherweise Fehler enthalten. Ich habe mich immer über diese unfehlbare Robustheit gewundert. Haben Sie jemals einen Fehler im Compiler selbst festgestellt? Was war das und wie haben Sie festgestellt, dass das Problem im Compiler selbst liegt?

... und wie do machen sie Compiler so zuverlässig?

67
EpsilonVector

Sie werden im Laufe der Zeit durch die Verwendung durch Tausende oder sogar Millionen von Entwicklern gründlich getestet.

Auch das zu lösende Problem ist genau definiert (durch eine sehr detaillierte technische Spezifikation). Und die Art der Aufgabe eignet sich leicht für Unit-/Systemtests. Das heißt, Grundsätzlich werden Texteingaben in ein ganz bestimmtes Format übersetzt, um sie in einem anderen genau definierten Format (einer Art Bytecode oder Maschinencode) auszugeben. So ist es einfach, Testfälle zu erstellen und zu überprüfen.

Darüber hinaus sind die Fehler normalerweise auch leicht zu reproduzieren: Abgesehen von den genauen Informationen zur Plattform- und Compiler-Version benötigen Sie normalerweise nur einen Eingabecode. Ganz zu schweigen davon, dass die Compiler-Benutzer (die selbst Entwickler sind) dazu neigen, weitaus präzisere und detailliertere Fehlerberichte zu liefern als jeder durchschnittliche Computerbenutzer :-)

101
Péter Török

Neben all den tollen Antworten bisher:

Sie haben eine "Beobachter-Voreingenommenheit". Sie beobachten keine Fehler und gehen daher davon aus, dass es keine gibt.

Ich habe immer so gedacht wie du. Dann habe ich angefangen, Compiler professionell zu schreiben, und ich möchte Ihnen sagen, dass es dort viele Fehler gibt!

Sie sehen die Fehler nicht, weil Sie Code schreiben, der 99,999% des gesamten restlichen Codes entspricht, den die Leute schreiben. Sie schreiben wahrscheinlich ganz normalen, unkomplizierten, klar korrekten Code, der Methoden aufruft und Schleifen ausführt und nichts Besonderes oder Seltsames tut, weil Sie ein normaler Entwickler sind, der normale Geschäftsprobleme löst.

Sie sehen keine Compiler-Fehler, da die Compiler-Fehler nicht in den einfach zu analysierenden einfachen normalen Codeszenarien enthalten sind. Die Fehler liegen in der Analyse von seltsamem Code, den Sie nicht schreiben.

Ich dagegen habe die entgegengesetzte Beobachter-Voreingenommenheit. Ich sehe jeden Tag den ganzen Tag verrückten Code, und so scheinen mir die Compiler voller Fehler zu sein.

Wenn Sie sich mit der Sprachspezifikation einer Sprache zusammengesetzt und eine Compiler-Implementierung für diese Sprache verwendet haben und sich wirklich bemüht haben, festzustellen, ob der Compiler die Spezifikation genau implementiert hat oder nicht, und sich auf obskure Eckfälle konzentrieren, werden Sie ziemlich bald feststellen Compiler-Fehler ziemlich häufig. Lassen Sie mich ein Beispiel geben. Hier ist ein C # -Compiler-Fehler, den ich vor fünf Minuten buchstäblich gefunden habe.

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

Der Compiler gibt drei Fehler aus.

  • Ein ref- oder out-Argument muss eine zuweisbare Variable sein.
  • Die beste Übereinstimmung für N (ref int x) hat ungültige Argumente.
  • Fehlendes "ref" zu Argument 1.

Offensichtlich ist die erste Fehlermeldung korrekt und die dritte ist ein Fehler. Der Fehlergenerierungsalgorithmus versucht herauszufinden, warum das erste Argument ungültig war. Er betrachtet es, stellt fest, dass es eine Konstante ist, und kehrt nicht zum Quellcode zurück, um zu überprüfen, ob es als "ref" markiert wurde. Vielmehr wird davon ausgegangen, dass niemand dumm genug wäre, eine Konstante als ref zu markieren, und es wird entschieden, dass der ref fehlen muss.

Es ist nicht klar, was die richtige dritte Fehlermeldung ist, aber das ist es nicht. Tatsächlich ist auch nicht klar, ob die Fehlermeldung zweite korrekt ist. Sollte die Überlastungsauflösung fehlschlagen oder sollte "ref 123" als ref-Argument des richtigen Typs behandelt werden? Ich muss jetzt darüber nachdenken und mit dem Triage-Team darüber sprechen, damit wir das richtige Verhalten feststellen können.

Sie haben diesen Fehler noch nie gesehen, weil Sie wahrscheinlich nie etwas so Dummes tun würden, um zu versuchen, 123 per Ref zu übergeben. Und wenn Sie dies tun, werden Sie wahrscheinlich nicht einmal bemerken, dass die dritte Fehlermeldung unsinnig ist, da die erste korrekt und ausreichend ist, um das Problem zu diagnostizieren. Aber ich versuche solche Sachen zu machen, weil ich versuche den Compiler zu brechen. Wenn Sie es versuchen würden, würden Sie auch die Fehler sehen.

66
Eric Lippert

Willst du mich veräppeln? Compiler haben auch Fehler, lädt wirklich.

GCC ist wahrscheinlich der berühmteste Open-Source-Compiler der Welt und werfen Sie einen Blick auf seine Fehlerdatenbank: http://gcc.gnu.org/bugzilla/buglist.cgi?product=gcc&component=c%2B% 2B & Auflösung = ---

Sehen Sie sich zwischen GCC 3.2 und GCC 3.2.3 an, wie viele Fehler behoben wurden: http://gcc.gnu.org/gcc-3.2/changes.html

Was andere wie Visual C++ betrifft, möchte ich nicht einmal anfangen.

Wie machen Sie Compiler zuverlässig? Zunächst einmal haben sie jede Menge Unit-Tests. Und der ganze Planet nutzt sie, so dass es nicht an Testern mangelt.

Im Ernst, Compiler-Entwickler, von denen ich gerne glaube, dass sie überlegene Programmierer sind, und obwohl sie nicht unfehlbar sind, packen sie doch einen ziemlichen Durchschlag.

54
Fanatic23

Ich habe zwei oder drei in meinem Tag getroffen. Der einzige wirkliche Weg, einen zu erkennen, besteht darin, sich den Assembly-Code anzusehen.

Obwohl Compiler aus Gründen, auf die andere Poster hingewiesen haben, sehr zuverlässig sind, denke ich, dass die Zuverlässigkeit von Compilern oft eine sich selbst erfüllende Einschätzung ist. Programmierer neigen dazu, den Compiler als Standard anzusehen. Wenn etwas schief geht, nehmen Sie an, dass es Ihre Schuld ist (weil dies in 99,999% der Fälle der Fall ist), und ändern Sie Ihren Code, um das Compiler-Problem zu umgehen, und nicht umgekehrt. Zum Beispiel ist das Abstürzen von Code unter einer hohen Optimierungseinstellung definitiv ein Compiler-Fehler, aber die meisten Leute setzen ihn nur ein wenig niedriger und fahren fort, ohne den Fehler zu melden.

21
Karl Bielefeldt

Compiler haben mehrere Eigenschaften, die zu ihrer Richtigkeit führen:

  • Die Domain ist sehr bekannt und erforscht. Das Problem ist genau definiert, und die angebotenen Lösungen sind genau definiert.
  • Automatisierte Tests reichen aus, um zu beweisen, dass Compiler ordnungsgemäß funktionieren
  • Compiler verfügen über sehr umfangreiche, in der Regel öffentliche, automatisierte und Unit-Tests, die sich im Laufe der Zeit angesammelt haben, um mehr Fehler als bei den meisten anderen Programmen abzudecken
  • Compiler haben eine sehr große Anzahl von Augäpfeln, die ihre Ergebnisse beobachten
15
blueberryfields

Wir verwenden täglich Compiler

... und wie machen sie Compiler so zuverlässig?

Sie tun es nicht. Wir tun es. Weil jeder benutzt sie die ganze Zeit, Fehler werden schnell gefunden.

Es ist ein Spiel mit Zahlen. Da Compiler so allgegenwärtig verwendet werden, ist es sehr wahrscheinlich, dass ein Fehler wird von jemandem ausgelöst wird, aber weil es so viele Benutzer gibt, ist es sehr nwahrscheinlich, dass dies der Fall ist jemand wird Sie speziell sein.

Es hängt also von Ihrer Sichtweise ab: Über alle Benutzer hinweg sind Compiler fehlerhaft. Aber es ist sehr wahrscheinlich, dass jemand anderes vor Ihnen einen ähnlichen Code kompiliert hat. Wenn also sein war ein Fehler wäre, hätte er sie getroffen, nicht Sie, also von Ihrem individuell Sicht sieht es so aus, als wäre der Bug nie da gewesen.

Darüber hinaus können Sie hier natürlich alle anderen Antworten hinzufügen: Compiler sind gut recherchiert, gut verstanden. Es gibt diesen Mythos, dass sie schwer zu schreiben sind, was bedeutet, dass nur sehr kluge, sehr gute Programmierer tatsächlich versuchen, einen zu schreiben, und dabei besonders vorsichtig sind. Sie sind im Allgemeinen leicht zu testen und leicht zu Stresstest oder Fuzz-Test. Compiler-Benutzer sind in der Regel selbst erfahrene Programmierer, was zu qualitativ hochwertigen Fehlerberichten führt. Und umgekehrt: Compiler-Autoren sind in der Regel Benutzer ihres eigenen Compilers.

14
Jörg W Mittag

Zusätzlich zu allen Antworten möchte ich hinzufügen:

Ich glaube oft essen die Verkäufer ihr eigenes Hundefutter. Das heißt, sie schreiben die Compiler in sich.

12
DevSolo

Ich bin oft auf Compiler-Fehler gestoßen.

Sie finden sie in den dunkleren Ecken, wo es weniger Tester gibt. Um beispielsweise Fehler in GCC zu finden, sollten Sie Folgendes versuchen:

  • Erstellen Sie einen Cross-Compiler. Sie werden buchstäblich Dutzende von Fehlern in GCCs Konfigurations- und Build-Skripten finden. Einige führen zu Buildfehlern während der GCC-Kompilierung, andere dazu, dass der Cross-Compiler keine funktionierenden ausführbaren Dateien erstellt.
  • Erstellen Sie eine Itanium-Version von GCC mit Profile-Bootstrap. Das letzte Mal, als ich dies unter GCC 4.4 und 4.5 versuchte, konnte kein funktionierender C++ - Ausnahmebehandler erstellt werden. Der nicht optimierte Build hat gut funktioniert. Niemand schien daran interessiert zu sein, den von mir gemeldeten Fehler zu beheben, und ich gab es auf, ihn selbst zu beheben, nachdem ich versucht hatte, die Fehler in den GCC-ASM-Speicherspezifikationen zu untersuchen.
  • Versuchen Sie, Ihren eigenen funktionierenden GCJ aus den neuesten Informationen zu erstellen, ohne einem Distribution-Build-Skript zu folgen. Du traust dich ja nicht.
8
Zan Lynx

Mehrere Gründe:

  • Compiler-Autoren "essen ihr eigenes Hundefutter".
  • Compiler basieren auf gut verstandenen Prinzipien von CS.
  • Compiler sind auf eine sehr klare Spezifikation gebaut.
  • Compiler erhalten getestet.
  • Compiler sind nicht immer sehr zuverlässig.
6
Kramii

Sie sind normalerweise sehr gut bei -O0. Wenn wir einen Compiler-Fehler vermuten, vergleichen wir -O0 mit dem Level, das wir verwenden möchten. Höhere Optimierungsstufen gehen mit einem höheren Risiko einher. Einige sind sogar absichtlich so und in der Dokumentation als solche gekennzeichnet. Ich habe sehr viele getroffen (mindestens hundert während meiner Zeit), aber sie werden in letzter Zeit viel seltener. Trotzdem ist die Versuchung, die Grenzen zu überschreiten, groß, um gute Kennzahlen (oder andere für das Marketing wichtige Benchmarks) zu erreichen. Wir hatten vor ein paar Jahren Probleme, als ein Anbieter (um nicht genannt zu werden) beschloss, einen Verstoß gegen die Standardeinstellung in Klammern zu machen - anstatt einer speziellen, klar gekennzeichneten Kompilierungsoption.

Es kann schwierig sein, einen Compilerfehler im Vergleich zu einer Streuspeicherreferenz zu diagnostizieren. Eine Neukompilierung mit verschiedenen Optionen kann einfach die relative Position von Datenobjekten im Speicher verschlüsseln, sodass Sie nicht wissen, ob es sich um den Heisenbug Ihres Quellcodes oder um einen Buggy handelt Compiler. Viele Optimierungen führen auch legitime Änderungen in der Reihenfolge der Operationen oder sogar algebraische Vereinfachungen Ihrer Algebra durch, und diese haben unterschiedliche Eigenschaften in Bezug auf Gleitkomma-Rundung und Unter-/Überlauf. Es ist schwer, diese Effekte von REAL Bugs zu trennen. Hardcore-Gleitkomma-Computing ist aus diesem Grund schwierig, da Fehler und numerische Empfindlichkeit oft nicht leicht zu entwirren sind.

5
Omega Centauri

Compiler-Fehler sind nicht allzu selten. Der häufigste Fall ist, dass ein Compiler einen Fehler in Code meldet, der akzeptiert werden soll, oder dass ein Compiler Code akzeptiert, der abgelehnt werden sollte.

5
kevin cline

Haben Sie jemals einen Fehler im Compiler selbst festgestellt? Was war das und wie haben Sie festgestellt, dass das Problem im Compiler selbst liegt?

Yup!

Die beiden denkwürdigsten waren die ersten beiden, denen ich jemals begegnet bin. Sie waren beide ungefähr 1985-7 im Lightspeed C-Compiler für 680x0-Macs.

Das erste war, wo unter bestimmten Umständen der Operator für die Ganzzahl-Nachinkrementierung nichts tat - mit anderen Worten, in einem bestimmten Code hat "i ++" einfach nichts mit "i" gemacht. Ich zog mir die Haare aus, bis ich eine Demontage sah. Dann habe ich das Inkrement einfach anders durchgeführt und einen Fehlerbericht eingereicht.

Das zweite war etwas komplizierter und ein wirklich unüberlegtes "Feature", das schief ging. Die frühen Macs hatten ein kompliziertes System für Festplattenoperationen auf niedriger Ebene. Aus irgendeinem Grund habe ich nie verstanden - wahrscheinlich weil ich kleinere ausführbare Dateien erstellt habe -, anstatt dass der Compiler nur die Anweisungen für den Plattenbetrieb im Objektcode generiert. Der Lightspeed-Compiler hat eine interne Funktion aufgerufen, die zur Laufzeit die Plattenoperation generiert hat Anweisungen auf dem Stapel und sprang dort.

Das hat auf 68000-CPUs hervorragend funktioniert, aber wenn Sie denselben Code auf einer 68020-CPU ausgeführt haben, hat es oft seltsame Dinge bewirkt. Es stellte sich heraus, dass ein neues Merkmal des 68020 ein 256-Byte-Befehls-Cache für primitive Befehle war. Da dies noch frühe Tage mit CPU-Caches waren, hatte es keine Ahnung, dass der Cache "schmutzig" ist und nachgefüllt werden muss. Ich denke, die CPU-Designer bei Motorola haben nicht über selbstmodifizierenden Code nachgedacht. Wenn Sie also zwei Festplattenoperationen in Ihrer Ausführungssequenz nahe genug beieinander ausgeführt haben und die Lightspeed-Laufzeit die tatsächlichen Anweisungen an derselben Stelle auf dem Stapel erstellt hat, würde die CPU fälschlicherweise glauben, dass ein Befehls-Cache getroffen wurde, und die erste Festplattenoperation zweimal ausführen.

Um das herauszufinden, musste man sich wieder mit einem Disassembler umsehen und in einem Low-Level-Debugger viel Einzelschritt machen. Meine Problemumgehung bestand darin, jeder Plattenoperation einen Aufruf einer Funktion voranzustellen, die 256 "NOP" -Anweisungen ausführte, die den Anweisungscache überfluteten (und damit löschten).

In den 25 Jahren seitdem habe ich im Laufe der Zeit immer weniger Compiler-Fehler gesehen. Ich denke, dafür gibt es mehrere Gründe:

  • Es gibt immer mehr Validierungstests für Compiler.
  • Moderne Compiler sind in der Regel in zwei oder mehr Teile unterteilt, von denen einer plattformunabhängigen Code generiert (z. B. LLVMs, die auf eine imaginäre CPU abzielen), und ein anderer, der dies in Anweisungen für Ihre eigentliche Zielhardware übersetzt. Bei Multi-Plattform-Compilern wird der erste Teil überall verwendet, sodass unzählige Tests in der Praxis durchgeführt werden.
4
Bob Murphy

Vor 5,5 Jahren wurde in Turbo Pascal ein eklatanter Fehler gefunden. Ein Fehler, der weder in der vorherigen (5.0) noch in der nächsten (6.0) Version des Compilers vorhanden ist. Und eine, die leicht zu testen sein sollte, da es überhaupt kein Eckfall war (nur ein Anruf, der nicht so häufig verwendet wird).

Im Allgemeinen werden sicherlich die kommerziellen Compiler-Hersteller (und nicht die Hobbyprojekte) über sehr umfangreiche QS- und Testverfahren verfügen. Sie wissen, dass ihre Compiler ihre Vorzeigeprojekte sind und dass Fehler für sie sehr schlimm aussehen werden, schlimmer als für andere Unternehmen, die die meisten anderen Produkte herstellen. Softwareentwickler sind ein unversöhnlicher Haufen, unsere Werkzeuglieferanten lassen uns im Stich, dass wir wahrscheinlich nach Alternativen suchen, anstatt auf eine Lösung durch den Lieferanten zu warten, und wir werden diese Tatsache sehr wahrscheinlich unseren Kollegen mitteilen, die uns möglicherweise folgen Beispiel. In vielen anderen Branchen ist dies nicht der Fall. Daher ist der potenzielle Verlust für einen Compilerhersteller infolge eines schwerwiegenden Fehlers weitaus größer als der eines Herstellers von Videobearbeitungssoftware.

4
jwenting

Ja, ich habe erst gestern einen Fehler im ASP.NET-Compiler festgestellt:

Wenn Sie stark typisierte Modelle in Ansichten verwenden, ist die Anzahl der Parametervorlagen begrenzt. Offensichtlich können nicht mehr als 4 Vorlagenparameter benötigt werden, sodass beide Beispiele für den Compiler zu viel sind:

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

Würde nicht so kompilieren wie es ist, wird aber wenn type5 ist entfernt.

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

Würde kompilieren, wenn type4 ist entfernt.

Beachten Sie, dass System.Tuple hat viele Überladungen und kann bis zu 16 Parameter aufnehmen (es ist verrückt, ich weiß).

3
user8685

Compiler-Fehler treten auf, aber Sie finden sie in merkwürdigen Ecken ...

In den 90er Jahren gab es einen seltsamen Fehler im VAX VMS C-Compiler der Digital Equipment Corporation

(Ich trug eine Zwiebel am Gürtel, wie es damals Mode war)

Ein fremdes Semikolon irgendwo vor einer for-Schleife würde als Hauptteil der for-Schleife kompiliert.

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

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

Auf dem betreffenden Compiler wird die Schleife nur einmal ausgeführt.

es sieht

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

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

  {
     puts("hello");
  }
}

Das hat mich viel Zeit gekostet.

Die ältere Version des PIC C-Compilers, die wir (früher) Arbeitserfahrungsschülern zufügten, konnte keinen Code generieren, der den Interrupt mit hoher Priorität korrekt verwendete. Sie mussten 2-3 Jahre warten und ein Upgrade durchführen.

Der MSVC 6-Compiler hatte einen raffinierten Fehler im Linker, er segmentierte Fehler und starb von Zeit zu Zeit ohne Grund. Ein sauberer Build hat es im Allgemeinen behoben (aber seufz nicht immer).

3
Tim Williscroft

Wenn sich das Verhalten Ihrer Software beim Kompilieren mit -O0 und -O2 unterscheidet, haben Sie einen Compiler-Fehler gefunden.

Wenn sich das Verhalten Ihrer Software nur von dem unterscheidet, was Sie erwarten, ist der Fehler wahrscheinlich in Ihrem Code enthalten.

2
mouviciel

In einigen Bereichen, wie z. B. in der Avionik-Software, gelten extrem hohe Zertifizierungsanforderungen für Code und Hardware sowie für den Compiler. Zu diesem letzten Teil gibt es ein Projekt, das darauf abzielt, einen formal verifizierten C-Compiler namens Compcert zu erstellen. Theoretisch ist diese Art von Compiler so zuverlässig wie sie kommen.

2
Axel

Ich habe mehrere Compiler-Fehler gesehen und einige selbst gemeldet (speziell in F #).

Trotzdem denke ich, dass Compiler-Fehler selten sind, weil Leute, die Compiler schreiben, im Allgemeinen mit den strengen Konzepten der Informatik sehr vertraut sind, die sie für die mathematischen Implikationen von Code wirklich bewusst machen.

Die meisten von ihnen sind vermutlich sehr vertraut mit Dingen wie Lambda-Kalkül, formaler Verifikation, Denotationssemantik usw. - Dinge, die ein durchschnittlicher Programmierer wie ich kaum verstehen kann.

Außerdem gibt es in Compilern normalerweise eine recht einfache Zuordnung von Eingabe zu Ausgabe, sodass das Debuggen einer Programmiersprache wahrscheinlich viel einfacher ist als das Debuggen beispielsweise einer Blog-Engine.

2
Rei Miyasaka

Ich habe vor nicht allzu langer Zeit einen Fehler im C # -Compiler gefunden. Sie können sehen, wie Eric Lippert (der im C # -Design-Team ist) herausgefunden hat, was der Fehler war hier .

Zusätzlich zu den bereits gegebenen Antworten möchte ich noch ein paar Dinge hinzufügen. Compiler-Designer sind oft sehr gute Programmierer. Compiler sind sehr wichtig: Die meisten Programmierungen werden mit Compilern durchgeführt, daher ist es unerlässlich, dass der Compiler von hoher Qualität ist. Es ist daher im besten Interesse von Unternehmen, die Compiler herstellen, ihre besten Leute darauf zu setzen (oder zumindest sehr gute: Die besten mögen möglicherweise kein Compiler-Design). Microsoft möchte sehr, dass die C- und C++ - Compiler ordnungsgemäß funktionieren, oder der Rest des Unternehmens kann seine Aufgaben nicht erledigen.

Wenn Sie einen wirklich komplexen Compiler erstellen, können Sie ihn nicht einfach zusammen hacken. Die Logik hinter Compilern ist sowohl sehr komplex als auch leicht zu formalisieren. Daher werden diese Programme häufig sehr "robust" und allgemein aufgebaut, was tendenziell zu weniger Fehlern führt.

2
Alex ten Brink