#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
endmodule
func
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/register
Passen 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 args
Die 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-run
mitbinfmt_misc
kann es etwas seltsam sein,mir-bin-run
direkt aufzurufen. DasP
Flag 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-.c
enthalten einfachen maschinenabhängigen Code, der für Interpreter und JIT-Compiler üblich istmir-.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-run
mir-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