shecc
wurde von Grund auf neu entwickelt und zielt sowohl auf 32-Bit-Arm- als auch auf RISC-V-Architekturen als selbstkompilierender Compiler für eine Teilmenge der C-Sprache ab. Trotz seiner einfachen Natur ist es in der Lage, grundlegende Optimierungsstrategien als eigenständiger Optimierungscompiler durchzuführen.
shecc
ist in der Lage, C-Quelldateien zu kompilieren, die in der folgenden Syntax geschrieben sind:
+=
, -=
, *=
int i = [expr]
#define
, #ifdef
, #elif
, #endif
, #undef
und #error
__VA_ARGS__
Das Backend zielt auf armv7hf mit Linux ABI ab, verifiziert auf Raspberry Pi 3, und unterstützt auch die RISC-V 32-Bit-Architektur, verifiziert mit QEMU.
Die Schritte zur Validierung shecc
Bootstrappings:
stage0
: shecc
Quellcode wird zunächst mit einem gewöhnlichen Compiler kompiliert, der eine native ausführbare Datei generiert. Der generierte Compiler kann als Cross-Compiler verwendet werden.stage1
: Die erstellte Binärdatei liest ihren eigenen Quellcode als Eingabe und generiert eine ARMv7-A/RV32IM-Binärdatei.stage2
: Die generierte ARMv7-A/RV32IM-Binärdatei wird aufgerufen (über QEMU oder läuft auf ARM- und RISC-V-Geräten) mit ihrem eigenen Quellcode als Eingabe und generiert eine weitere ARMv7-A/RV32IM-Binärdatei.bootstrap
: Erstellen Sie die Compiler stage1
und stage2
und stellen Sie sicher, dass sie byteweise identisch sind. Wenn ja, kann shecc
seinen eigenen Quellcode kompilieren und neue Versionen desselben Programms erstellen. Der Codegenerator in shecc
ist nicht auf externe Dienstprogramme angewiesen. Sie benötigen lediglich gewöhnliche C-Compiler wie gcc
und clang
. Allerdings würde sich shecc
selbst booten und eine ARM/RISC-V-ISA-Emulation ist erforderlich. Installieren Sie QEMU für die Arm/RISC-V-Benutzeremulation unter GNU/Linux:
$ sudo apt-get install qemu-user
Es ist weiterhin möglich, shecc
auf macOS oder Microsoft Windows zu erstellen. Das Bootstrapping der zweiten Stufe würde jedoch aufgrund des Fehlens qemu-arm
fehlschlagen.
Um den Snapshot-Test auszuführen, installieren Sie die folgenden Pakete:
$ sudo apt-get install graphviz jq
Konfigurieren Sie das gewünschte Backend. shecc
unterstützt ARMv7-A- und RV32IM-Backend:
$ make config ARCH=arm
# Target machine code switch to Arm
$ make config ARCH=riscv
# Target machine code switch to RISC-V
Führen Sie make
aus und Sie sollten Folgendes sehen:
CC+LD out/inliner
GEN out/libc.inc
CC out/src/main.o
LD out/shecc
SHECC out/shecc-stage1.elf
SHECC out/shecc-stage2.elf
File out/shecc
ist der Compiler der ersten Stufe. Seine Verwendung:
$ shecc [-o output] [+m] [--no-libc] [--dump-ir] < infile.c >
Compiler-Optionen:
-o
: Geben Sie den Namen der Ausgabedatei an (Standard: out.elf
).+m
: Hardware-Multiplikations-/Divisionsanweisungen verwenden (Standard: deaktiviert)--no-libc
: Eingebettete C-Bibliothek ausschließen (Standard: eingebettet)--dump-ir
: Zwischendarstellung (IR) ausgebenBeispiel:
$ out/shecc -o fib tests/fib.c
$ chmod +x fib
$ qemu-arm fib
Stellen Sie sicher, dass die ausgegebenen IRs mit den Snapshots identisch sind, indem Sie beim Aufruf make
das Ziel check-snapshots
angeben:
$ make check-snapshots
shecc
kommt mit Unit-Tests. Um die Tests auszuführen, geben Sie check
als Argument an:
$ make check
Referenzausgabe:
...
int main(int argc, int argv) { exit(sizeof(char)); } => 1
int main(int argc, int argv) { int a; a = 0; switch (3) { case 0: return 2; case 3: a = 10; break; case 1: return 0; } exit(a); } => 10
int main(int argc, int argv) { int a; a = 0; switch (3) { case 0: return 2; default: a = 10; break; } exit(a); } => 10
OK
Um die generierten Compilerdateien zu bereinigen, führen Sie den Befehl make clean
aus. Verwenden Sie zum Zurücksetzen von Architekturkonfigurationen den Befehl make distclean
.
Sobald die Option --dump-ir
an shecc
übergeben wird, wird die Zwischendarstellung (IR) generiert. Nehmen Sie zum Beispiel die Datei tests/fib.c
. Es besteht aus einer rekursiven Fibonacci-Folgenfunktion.
int fib ( int n )
{
if ( n == 0 )
return 0 ;
else if ( n == 1 )
return 1 ;
return fib ( n - 1 ) + fib ( n - 2 );
}
Führen Sie Folgendes aus, um IR zu generieren:
$ out/shecc --dump-ir -o fib tests/fib.c
Zeilenweise Erklärung zwischen C-Quelle und IR:
C Source IR Explanation
-- -- -- -- -- -- -- -- -- + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
int fib ( int n ) def int @ fib ( int % n ) Indicate a function definition
{ {
if ( n == 0 ) const %. t1001 , $0 Load constant 0 into a temporary variable ".t1001"
%. t1002 = eq % n , %. t1001 Test "n" equals ".t1001" or not , and write the result in temporary variable ".t1002"
br %. t1002 , . label . 1177 , . label . 1178 If ".t1002" equals zero , goto false label ".label.1178" , otherwise ,
goto true label ".label.1177"
. label . 1177
return 0 ; const %. t1003 , $0 Load constant 0 into a temporary variable ".t1003"
ret %. t1003 Return ".t1003"
j . label . 1184 Jump to endif label ".label.1184"
. label . 1178
else if ( n == 1 ) const %. t1004 , $1 Load constant 1 into a temporary variable ".t1004"
%. t1005 = eq % n , %. t1004 Test "n" equals ".t1004" or not , and write the result in temporary variable ".t1005"
br %. t1005 , . label . 1183 , . label . 1184 If ".t1005" equals zero , goto false label ".label.1184" . Otherwise ,
goto true label ".label.1183"
. label . 1183
return 1 ; const %. t1006 , $1 Load constant 1 into a temporary variable ".t1006"
ret %. t1006 Return ".t1006"
. label . 1184
return
fib ( n - 1 ) const %. t1007 , $1 Load constant 1 into a temporary variable ".t1007"
%. t1008 = sub % n , %. t1007 Subtract ".t1007" from "n" , and store the result in temporary variable ".t1008"
push %. t1008 Prepare parameter for function call
call @ fib , 1 Call function " fib " with one parameter
+ retval %. t1009 Store return value in temporary variable ". t1009 "
fib ( n - 2 ); const %. t1010 , $2 Load constant 2 into a temporary variable ".t1010"
%. t1011 = sub % n , %. t1010 Subtract ".t1010" from "n" , and store the result in temporary variable ".t1011"
push %. t1011 Prepare parameter for function call
call @ fib , 1 Call function " fib " with one parameter
retval %. t1012 Store return value in temporary variable ". t1012 "
%. t1013 = add %. t1009 , %. t1012 Add ". t1009 " and ". t1012 ", and store the result in temporary variable " . t1013 "
ret %. t1013 Return ".t1013"
} }
verwendet werden. Alternativ können Sie die Implementierung printf
in der lib/cc
auf var_arg
prüfen. shecc
ist unter der BSD-2-Klausel-Lizenz frei weiterverbreitbar. Die Nutzung dieses Quellcodes unterliegt einer BSD-ähnlichen Lizenz, die in der LICENSE
Datei zu finden ist.