O0 und Debugging

Regeln zum Beibehalten von Berechnungen, konstanten Ausdrücken, Speicherplatz und Debug-Antwortinformationen.

Wave Foundation

Naturschutzmodell

O0 behält die Berechnungen, Variablen und den Kontrollfluss des Originals typed IR für Debugging-Zwecke bei. Außerdem bleiben ungenutzte Ergebnisse und strukturell nicht erreichbare Blöcke erhalten. Die Überprüfung diagnostiziert fehlerhafte IR und löscht keine Blöcke oder vereinfacht Vorgänge.

Die zur Generierung der Maschinensprache erforderlichen Konvertierungen werden in einem separaten Sub-Sub IR durchgeführt. Auch nach der Umstellung muss die Übereinstimmung mit dem Original ID gewahrt bleiben. Die Konservierung von typed IR bedeutet nicht, dass alle IR Vorgänge und Maschinenanweisungen eins zu eins übereinstimmen.

Konvertierung nicht durchgeführt von O0

Konvertierung O0 Betrieb
Inlining Behalten Sie Aufruf- und Funktionsgrenzen bei
Konvertieren Tail-call Behalten Sie die allgemeine Anruf-/Rückgabestruktur bei
Entfernen Sie toten Code Behalten Sie ungenutzte Berechnungen und nicht erreichbare Blöcke bei
Laufzeitkonstanten ausblenden Behalten Sie den ursprünglichen Betrieb bei
Kombinieren Sie gängige Berechnungen Führen Sie getrennte Berechnungen durch
Wiederverwendung des lokalen Variablenspeicherplatzes Behalten Sie jeden Speicherplatz bei
Rahmenzeiger weglassen Rahmenzeiger beibehalten
Zeichenfolgen automatisch zusammenführen Pflegen Sie separate String-Objekte

Beispielsweise bleibt eine Laufzeitoperation, die zwei Konstanten hinzufügt, auch dann addiert, wenn das Ergebnis nicht verwendet wird. Zweige mit konstanten Bedingungen behalten außerdem die ursprüngliche Kontrollflussstruktur bei.

Konstanten zur Kompilierungszeit

Deklarationen von Konstanten zur Kompilierungszeit bewahren sowohl den typed-Initialisierungsausdruck als auch das Auswertungsergebnis. Dies unterscheidet sich vom ständigen Falten in regulären Laufzeitanweisungen.

Eine Deklaration mit einem Initialisierungsausdruck von 1 + 2 bleibt mit einem Additionsausdruck und einem Ergebnis von 3 zurück. Dieses Beispiel veranschaulicht die Bedeutung des Ausdrucks und nicht die deklarative Grammatik der spezifischen Quellsprache. Das Ergebnis kann beim Aufbau statischer Daten verwendet werden und ersetzt den Initialisierungsausdruck nicht durch einen Laufzeitzusatz.

Auch ungenutzte und nicht erreichbare Deklarationen müssen identifiziert werden. Auch wenn Deklarationen mit demselben Namen einander verdecken, müssen sie durch Namen·Deklaration ID·Referenz getrennt werden. Die Validierung lehnt gespeicherte Ergebnisse ab, die ungültige Referenzen, Abhängigkeitszyklen oder ungültige Typen aufweisen oder nicht mit dem ursprünglichen Ausdruck übereinstimmen.

nicht erreichbare Quellanweisung

Auch die Sätze nach return·break·continue bleiben in einem unzusammenhängenden Block. Aufgrund dieser Anweisung sollten Sie den vorangehenden terminator nicht ändern oder einen neuen Ausführungspfad erstellen. Außerdem werden ungültige Ausdrücke in nicht erreichbaren Sätzen diagnostiziert.

Diese Erhaltungsregel ermöglicht die Überprüfung der ursprünglichen Programmstruktur. Dies bedeutet nicht, dass die Anweisung nach terminator tatsächlich ausgeführt wird.

Erhaltenes Beispiel IR

Nachfolgend finden Sie die aktuelle Druckerausgabe für das Modul, das die Prüfung bestanden hat. Zeigt nicht verwendete Berechnungen, Deklarationen zur Kompilierungszeit und nicht verknüpfte Blöcke zusammen.

module {
  format_version 2
  semantics_version 1
  target "x86_64-whale-linux"
  datalayout { ptr=64, endian=little }

  declare @f0 "preserved": whale () -> void, linkage internal

  fn @preserved() -> void, id @f0 {
  entry:
    %v0: i32 = const i32 1
    %v1: i32 = const i32 2
    %v2: i32 = add i32 %v0, %v1
    %v3: i32 = const_decl "count" add(i32 1, i32 2) => const i32 3
    ret void
  unreachable.cont:
    %v4: i32 = const i32 4
    %v5: i32 = const i32 5
    %v6: i32 = add i32 %v4, %v5
    ret void
  }

}

%v2 bleibt als add, auch wenn es keinen Nutzen hat. %v3 ist ein separates const_decl, das den Ausdruck add(i32 1, i32 2) und das Bewertungsergebnis 3 zusammen behält. Da unreachable.cont keine eingehende Kante hat, können wir das Hinzufügen von %v6 überprüfen, ohne nach ret void einen Ausführungspfad hinzuzufügen.

Bei der Überprüfung bleiben diese Anweisungen und Blöcke erhalten. Dieses Beispiel zeigt die Konfiguration/Verifizierung/Ausgabe von IR und impliziert nicht, dass eine Ausführung von native oder eine Ausgabe von DWARF bereitgestellt wird.

Quell- und Stack-Informationen

Die Debug-Schnittstelle verwendet Funktionsinformationen, Quellzeilen, lokale Standardvariablen und Aufrufrahmeninformationen von DWARF 5. Auch bei der Konvertierung in einen Unterausdruck muss die Entsprechung zwischen Funktions-/lokalen Variableninformationen und der ursprünglichen IR-Kennung erhalten bleiben.

Das Profil AMD64 behält den Frame-Zeiger bei und verwendet nicht red zone. Call-Frame-Informationen werden zur Stack-Inspektion verwendet und bedeuten keine Unterstützung für die Ausnahme unwinding. trap beendet die Ausführung, ohne einen Destruktor zu garantieren oder unwinding.

Informationen zur Verfügbarkeit der Ausgabe DWARF und der Ausführung native finden Sie unter Übersicht über die Toolchain. Das Verhalten von O1 und höheren Optimierungen liegt außerhalb des Rahmens dieser O0-Referenz.