






#define Size 819000
int sieve ( int N ) {
int64_t i , k , prime , count , n ; char flags [ Size ];
for ( n = 0 ; n < N ; n ++ ) {
count = 0 ;
for ( i = 0 ; i < Size ; i ++ )
flags [ i ] = 1 ;
for ( i = 0 ; i < Size ; i ++ )
if ( flags [ i ]) {
prime = i + i + 3 ;
for ( k = i + prime ; k < Size ; k += prime )
flags [ k ] = 0 ;
count ++ ;
}
}
return count ;
}
void ex100 ( void ) {
printf ("sieve (100) = %d", sieve (100));
} m_sieve : module
export sieve
sieve : func i32, i32:N
local i64:iter, i64:count, i64:i, i64:k, i64:prime, i64:temp, i64:flags
alloca flags, 819000
mov iter, 0
loop : bge fin, iter, N
mov count, 0; mov i, 0
loop2 : bge fin2, i, 819000
mov u8:(flags, i), 1; add i, i, 1
jmp loop2
fin2 : mov i, 0
loop3 : bge fin3, i, 819000
beq cont3, u8:(flags,i), 0
add temp, i, i; add prime, temp, 3; add k, i, prime
loop4 : bge fin4, k, 819000
mov u8:(flags, k), 0; add k, k, prime
jmp loop4
fin4 : add count, count, 1
cont3 : add i, i, 1
jmp loop3
fin3 : add iter, iter, 1
jmp loop
fin : ret count
endfunc
endmodule
m_ex100 : module
format : string "sieve (10) = %dn"
p_printf : proto p:fmt, i32:result
p_sieve : proto i32, i32:iter
export ex100
import sieve, printf
ex100 : func v, 0
local i64:r
call p_sieve, sieve, r, 100
call p_printf, printf, format, r
endfunc
endmodulefunc beschreibt die Signatur der Funktion (nimmt ein 32-Bit-Ganzzahlargument mit Vorzeichen und gibt einen 32-Bit-Ganzzahlwert mit Vorzeichen zurück) und das Funktionsargument N , das eine lokale Variable vom Typ 64-Bit-Ganzzahl mit Vorzeichen ist; trennen.string beschreibt Daten in Form eines C-Stringsexport beschreibt die Modulfunktionen oder Daten, die außerhalb des aktuellen Moduls sichtbar sindimport beschreibt die Modulfunktionen oder Daten, die in anderen MIR-Modulen definiert werden sollenproto beschreibt Funktionsprototypen. Seine Syntax ist dieselbe wie func -Syntaxcall sind MIR-Anweisungen zum Aufrufen von Funktionen MIR_load_external ladenm1 und m2 die Module m_sieve und m_e100 , func ist die Funktion ex100 , sieve ist die Funktion sieve ): /* ctx is a context created by MIR_init / MIR_init2 */
MIR_load_module ( ctx , m1 ); MIR_load_module ( ctx , m2 );
MIR_load_external ( ctx , "printf" , printf );
MIR_link ( ctx , MIR_set_interp_interface , import_resolver );
/* or use MIR_set_gen_interface to generate and use the machine code */
/* or use MIR_set_lazy_gen_interface to generate function code on its 1st call */
/* use MIR_gen (ctx, func) to explicitly generate the function machine code */
MIR_interp ( ctx , func , & result , 0 ); /* zero here is arguments number */
/* or ((void (*) (void)) func->addr) (); to call interpr. or gen. code through the interface */binfmt_misc Die mir-bin-run -Binärdatei wird für die Verwendung von binfmt_misc mit der folgenden Zeile (Beispiel) vorbereitet:
line=:mir:M::MIR::/usr/local/bin/mir-bin-run:P
echo $line > /proc/sys/fs/binfmt_misc/registerPassen Sie den mir-bin-run-Binärpfad an Ihr System an, das ist der Standardpfad
Und renne mit
c2m your-file.c -o your-file
chmod +x your-file
./your-file your argsDie ausführbare Datei ist mit Umgebungsvariablen „konfigurierbar“:
MIR_TYPE legt die Schnittstelle für die Codeausführung fest: interp (zur Interpretation), jit (zur Generierung) und lazy (zur verzögerten Generierung, Standard);MIR_LIBS (durch Doppelpunkte getrennte Liste) definiert eine Liste zusätzlicher zu ladender Bibliotheken;MIR_LIB_DIRS oder LD_LIBRARY_PATH (durch Doppelpunkte getrennte Liste) definiert eine zusätzliche Liste von Verzeichnissen, in denen die Bibliotheken durchsucht werden sollen.Aufgrund der Verknüpfung von
mir-bin-runmitbinfmt_misckann es etwas seltsam sein,mir-bin-rundirekt aufzurufen. DasPFlag auf binfmt_misc übergibt ein zusätzliches Argument mit dem vollständigen Pfad zur MIR-Binärdatei.
Sehr kurze Optimierungspipeline für Geschwindigkeit und geringes Gewicht
Nur die wertvollste Optimierungsnutzung:
Verschiedene Optimierungsstufen zur Abstimmung der Kompilierungsgeschwindigkeit im Vergleich zur Leistung des generierten Codes
Die SSA- Form von MIR wird vor der Registerzuweisung verwendet
Einfache Optimierungsimplementierung bei extremer Leistung des generierten Codes
Weitere Details zur vollständigen JIT-Compiler-Pipeline :
Vereinfachen : MIR senken
Inline : Inline-MIR-Aufrufe
CFG erstellen : Kontrollflussdiagramm erstellen (Basisblöcke und CFG-Kanten)
SSA erstellen : Erstellen eines einzelnen statischen Zuweisungsformulars durch Hinzufügen von Phi-Knoten und SSA-Kanten zu Operanden
Adresstransformation : MIR ADDR-Anweisungen entfernen oder ändern
Globale Wertnummerierung : Entfernen redundanter Inns durch GVN. Dazu gehören die ständige Ausbreitung und die Eliminierung redundanter Lasten
Kopierweitergabe : SSA-Kopierweitergabe und Entfernung redundanter Erweiterungsanweisungen
Eliminierung toter Filialen : Entfernen überflüssiger Filialen
Eliminierung von totem Code : Entfernen von Insns mit ungenutzten Ausgängen
Druckentlastung : Verschieben der Inns, um den Registerdruck zu verringern
SSA-Kombination : Kombinieren von Adressen und Vergleichs- und Verzweigungsbefehlspaaren
Außerhalb von SSA : Phi-Knoten und SSA-Kanten werden entfernt
Sprungoptionen : Verschiedene Sprungoptimierungen
Machinisieren : Führen Sie maschinenabhängigen Code aus, der MIR für Anrufe, ABI, 2-Op-Insns usw. umwandelt
Schleifen finden : Natürliche Schleifen finden und Schleifenbaum erstellen
Live-Info erstellen : Berechnung von Live-In und Live-Out für die Basisblöcke
Registerkonflikte erstellen : Erstellen einer Konfliktmatrix für Register, die an Umzügen beteiligt sind. Es wird zur Registerzusammenführung verwendet
Coalesce : Aggressive Registerverschmelzung
Register Allocator (RA) : Prioritätsbasierter linearer Scan-RA mit Live-Bereichsaufteilung
Live-Bereiche erstellen : Programmpunktbereiche für Register berechnen
Zuweisen : schnelle RA für -O0 oder prioritätsbasierte lineare Scan-RA für -O1 und höher
Umschreiben : Transformieren Sie MIR entsprechend der Zuweisung unter Verwendung reservierter Hardregs
Kombinieren (Codeauswahl): Zusammenführen datenabhängiger Insns zu einem
Eliminierung von totem Code : Entfernen von Insns mit ungenutzten Ausgängen
Maschinen-Insns generieren : Führen Sie maschinenabhängigen Code aus, der Maschinen-Insns erstellt
c2m implementiert. Siehe README.mdmir.h und mir.c enthalten den wichtigsten API-Code, einschließlich der Eingabe/Ausgabe der MIR-Binär- und MIR-Textdarstellungmir-dlist.h , mir-mp.h , mir-varr.h , mir-bitmap.h , mir-hash.h , mir-htab.h und mir-reduce.h enthalten generischen Code entsprechend für Doppelverknüpfungen Listen, Speicherpools, Arrays variabler Länge, Bitmaps, Hash-Berechnungen, Hash-Tabellen und Komprimierung/Dekomprimierung von Daten. Die Datei mir-hash.h ist eine allgemeine, einfache und qualitativ hochwertige Hash-Funktion, die von Hashtabellen verwendet wirdmir-interp.c enthält Code zur Interpretation des MIR-Codes. Es ist in mir.c enthalten und wird nie separat kompiliertmir-gen.h , mir-gen.c , mir-gen-x86_64.c , mir-gen-aarch64.c , mir-gen-ppc64.c , mir-gen-s390x.c und mir-gen-riscv64.c enthält Code für den MIR-JIT-Compilermir-gen-x86_64.c , mir-gen-aarch64.c , mir-gen-ppc64.c , mir-gen-s390x.c und mir-gen-riscv64.c sind maschinenabhängiger Code des JIT-Compilersmir-<target>.c enthalten einfachen maschinenabhängigen Code, der für Interpreter und JIT-Compiler üblich istmir-<target>.h enthalten gemeinsame Deklarationen für Interpreter und JIT-Compilermir2c/mir2c.h und mir2c/mir2c.c enthalten Code für den MIR-zu-C-Compiler. Der generierte Code ist möglicherweise nicht portierbarc2mir/c2mir.h , c2mir/c2mir.c , c2mir/c2mir-driver.c und c2mir/mirc.h enthalten Code für den C-zu-MIR-Compiler. Dateien in den Verzeichnissen c2mir/x86_64 und c2mir/aarch64 , c2mir/ppc64 , c2mir/s390x und c2mir/riscv64 enthalten entsprechend x86_64, aarch64, ppc64le, s390x und riscv maschinenabhängigen Code für den C-zu-MIR-Compilermir-bin-run.c enthält den oben beschriebenen Code für mir-bin-runmir-bin-driver.c mit dem Dienstprogramm b2ctab kann für eine portable Möglichkeit zum Generieren von Binärdateien aus MIR-Binärdateien verwendet werdenmir-utils enthält verschiedene Dienstprogramme für die Arbeit mit MIR, z. B. die Umwandlung von binärem MIR in textuelles MIR und umgekehrtadt-tests , mir-tests , c-tests und c-benchmarks enthält Code zum Testen und Benchmarking von MIR und c2m make bench und make test können Sie einige Benchmarks und Tests durchführen Intel i5-13600K mit 64 GB Speicher unter FC37 mit GCC-12.3.1
| MIR-Generator | MIR-Dolmetscher | gcc-O2 | gcc -O0 | |
|---|---|---|---|---|
| Zusammenstellung [1] | 1,0 (249us) | 0,09 (22us) | 109 (27,1 ms) | 105 (26,1 ms) |
| Ausführung [2] | 1,0 (1,74 Sek.) | 13,7 (23,8 Sek.) | 0,92 (1,6 s) | 2,28 (3,97 Sek.) |
| Codegröße [3] | 1.0 (557 KB) | 0,43 (240 KB) | 58 (32,2 MB) | 58 (32,2 MB) |
| LOC [4] | 1,0 (23,4K) | 0,48 (11,3K) | 103 (2420K) | 103 (2402K) |
[1] basiert auf der Zeit für die Kompilierung des C-Siebcodes (ohne Include-Datei und unter Verwendung des Speicherdateisystems für GCC) und des entsprechenden MIR-Siebcodes durch MIR-Interpreter und MIR-Generator mit Optimierungsstufe 2
[2] basiert auf der besten Wandzeit von 10 Läufen mit dem verwendeten MIR-Generator Optimierungsstufe 2
[3] basiert auf abgespeckten Größen von cc1 für GCC und MIR-Kern und Interpreter oder Generator für MIR
[4] Meine Schätzung basiert nur auf Dateien, die für den x86-64 GNU C-Compiler und MIR-Dateien für ein Minimalprogramm zum Erstellen und Ausführen von MIR-Code erforderlich sind
Intel i5-13600K mit 64 GB Speicher unter FC37 mit GCC-12.3.1
| c2m -O2 -eg (Generator) | c2m -ei (Dolmetscher) | gcc-O2 | gcc -O0 | |
|---|---|---|---|---|
| Zusammenstellung [1] | 1,0 (336us) | 1,0 (337us) | 80 (27,1 ms) | 77 (26,1 ms) |
| Ausführung [2] | 1,0 (1,74 Sek.) | 13,7 (23,8 Sek.) | 0,92 (1,6 s) | 2,28 (3,97 Sek.) |
| Codegröße [3] | 1.0 (961 KB) | 1.0 (961 KB) | 34 (32,2 MB) | 34 (32,2 MB) |
| LOC [4] | 1,0 (54,8K) | 1,0 (54,8K) | 44 (2420K) | 44 (2420K) |
[1] basiert auf der Zeit für die Kompilierung des C-Sieve-Codes (ohne Include-Datei und unter Verwendung des Speicherdateisystems für GCC).
[2] basiert auf der besten Wandzeit von 10 Läufen mit dem verwendeten MIR-Generator Optimierungsstufe 2
[3] basiert auf reduzierten Größen von cc1 für GCC und C2MIR, MIR-Kern, Interpreter und Generator für MIR
[4] basiert auf allen Quelldateien mit Ausnahme von Tests
Hier wird die Codeleistung im Zusammenhang mit GCC -O2 für verschiedene C-Compiler auf 15 kleinen C-Benchmarks (aus dem Verzeichnis c-benchmarks ) auf demselben Computer generiert
| Durchschnitt | Geomean | |
|---|---|---|
| gcc-O2 | 1,00 | 1,00 |
| gcc -O0 | 0,63 | 0,57 |
| c2m -eg | 0,96 | 0,91 |
| c2m -eb | 0,92 | 0,85 |
| chibicc | 0,38 | 0,30 |
| klirren -O2 | 1.12 | 1.09 |
| cparser -O3 | 1.02 | 0,98 |
| cproc | 0,68 | 0,65 |
| lacc -O3 | 0,47 | 0,39 |
| pcc -O | 0,80 | 0,78 |
| tcc | 0,54 | 0,50 |
| emcc -O2/wasmer | 0,60 | 0,55 |
| Wasi-O2/Wasmer-Kranlift | 0,60 | 0,54 |
| wasi -O2/wasmer LLVM | 0,78 | 0,72 |
| wasi -O2/wasmer Singlepass | 0,45 | 0,36 |
| wasi -O2/wasmtime | 0,92 | 0,87 |
c2m ) auf ein anderes Ziel portieren