Bei Godbolt Compiler Explorer kann man einen Quelltext mit Compilern für verschiedene Sprachen übersetzen. Es gibt Compiler für die Sprachen Rust, C, C++, Python, Go, Haskel, Ada u. v. m. und jeweils in mehreren Versionen.

Zum einen kann man damit grundlegend seinen Code testen und prüfen, ob er sich übersetzen lässt. Dann kann man auch über verschiedene Compiler-Versionen hinweg sich ansehen, ob der Code compiliert oder nicht. Dies ist zum Beispiel bei Rust interessant, weil einige Konstrukte erst später ermöglicht wurden bzw. andere Konstrukte wieder entfernt wurden; z. B. try! und der ?-Operator.

Zum anderen kann man sich auch den Assembler-Code und die Ergebnisse der Programmausführung ansehen. Interessant ist dann nämlich der Vergleich zwischen Versionen. Damit lässt sich die Entwicklung des Binärcodes innerhalb einer Compiler-Familie und sprachübergreifend vergleichen.

Das Projekt wird auch als Open-Source entwickelt und bei Github veröffentlicht. Es gibt eine Erklärung zur Funktionsweise von Godbolt und ein Wiki mit Installationsanleitungen.

Beispielnutzung

  1. Seite https://godbolt.org/ öffnen
  2. Tab auf der rechten Seite leben den linken Tab zur Übersichtlichkeit ziehen
  3. Im Quelltext bei »+ Add new« einen neuen Compiler-Tab erstellen und dort zum Beispiel »x86-64 clang 11.0.0 (Editor #1, Compiler #2) C++« wählen
  4. Oben auf »Add…« und »Diff View« gehen, um die beiden Assembler-Codes zu vergleichen
  5. Danach oben auf »Add…« und »Source Editor« gehen und im Source-Editor oben rechts »Rust« auswählen. Der Beispielcode sollte sich entsprechend verändern.
  6. Danach wieder Quelltext-Fenster »+ Add new« und Compiler wählen und noch ein Fenster zum Vergleich (oben »Add…« und »Diff View«) hinzufügen. In dem Fenster zum Vergleich Rust und einen der C++-Compiler wählen.

Der Code wird sich in den Registernamen und in den Funktionen für die Stack-Organisation; der C++-Assembler nutzt z. B. den Stack für die Opernenten, während Rust ein Register nutzt. Beide nutzen aber die Anweisung imul zum Multiplizieren. Rust verwendet aber danach noch die Funktion seto, um den Überlauf bei der Multiplikation zu prüfen. Die anschließenden Befehlen zur Prüfung dieses Überlaufs und dem Sprung zum Auslösen des Programmabbruchs (panic) fehlen komplett im C++-Assembler.

Die normalen arithmetischen Operationen in Rust werden für unoptimierten Code mit Prüfungen auf Überläufe erstellt. Daher kann man auch explizit angeben, dass bei der Multiplikation die Überlaufsarithmetik verwendet werden soll. Dies geht mit der Funktion wrapping_mul:

pub fn square_wrap(num: i32) -> i32 {
    num.wrapping_mul(num)
}

Damit dieser Aufruf dann noch in die Funktion eingefügt (Inlining) wird, muss man in dem Compiler-Fenster oben in dem Textfeld die Option -O einfügen, damit der Code optimiert wird. Beim C++-Compiler muss man die Optione -O2 setzen und dann sind beide Assembler-Codes gleich.

Auf die gleiche Weise kann man auch Assembler-Codes mit unterschiedlichen Optimierungszielen vergleichen; bei Rust -C opt-level=2 (Geschwindigkeit) ggü. -C opt-level=s (Codegröße); bei C++ -O2 ggü. -Os.

Weitere Projekte

Es gibt noch C++ Insights zur Analyse von C++-Code und Quick C++ Benchmarks zum Bewerten von C++-Code.