[JAVA] Kinx Library-JIT-Compiler-Bibliothek

Kinx Library-JIT-Compiler-Bibliothek

Einführung

** "Sieht aus wie JavaScript, Gehirn (Inhalt) ist Ruby, (Stabilität ist AC / DC)" ** Skriptsprache Kinx ). Ich habe eine Bibliothek für die JIT-Kompilierung erstellt.

Ich möchte JIT machen. Dieses Mal haben wir SLJIT, das auch in Kinx --native verwendet wird, zu einer Bibliothek gemacht, um die Verwendung zu vereinfachen. Da SLJIT selbst nur wenige Dokumente enthält und durch Dekodieren aus der Quelle verwendet wird, habe ich darüber nachgedacht, zu schreiben, wie SLJIT selbst als Memorandum verwendet wird. Diesmal ist es jedoch reserviert. Ich könnte es irgendwo tun.

Natürlich ist es einfacher zu bedienen als SLJIT, also denke ich, dass dies besser ist. ** Die Host-Sprache ist auch ein Skript **, so dass Sie es leicht genießen können.

Wie ist es?

Zunächst werde ich Ihnen ein Beispiel geben, wie das Programm aussehen wird. Es scheint, dass verschiedene Details fortgeführt werden und dieser Punkt nicht erreicht wird. .. ..

using Jit;

var c = new Jit.Compiler();
var entry1 = c.enter();
    var jump0 = c.ge(Jit.S0, Jit.IMM(3));
    c.ret(Jit.S0);
    var l1 = c.label();
    c.sub(Jit.R0, Jit.S0, Jit.IMM(2));
    c.call(entry1);
    c.mov(Jit.S1, Jit.R0);
    c.sub(Jit.R0, Jit.S0, Jit.IMM(1));
    c.call(entry1);
    c.add(Jit.R0, Jit.R0, Jit.S1);
    c.ret(Jit.R0);

jump0.setLabel(l1);
var code = c.generate();

for (var i = 1; i <= 42; ++i) {
    var tmr = new SystemTimer();
    var r = code.run(i);
    System.println("[%8.3f] fib(%2d) = %d" % tmr.elapsed() % i % r);
}

Erstellen Sie ein Jit.Compiler-Objekt, erstellen Sie einen Funktionseintrag mit enter und schreiben Sie Code, der mit verschiedenen Registern spielt, um ret. Wenn Sie es ausführen, wird es zu "generate ()" und "run ()". Sie können die Assembleliste auch anzeigen, indem Sie "generate ()" und "dump ()" ausführen.

Wenn Sie verschiedene Dinge überspringen möchten, gehen Sie zu [Beispiel](# Beispiel)! → Das Beispiel wird auch mit Ruby, Python und PyPy verglichen.

SLJIT

Was ist SLJIT überhaupt?

Kurz gesagt, ** Abstract Assembler ** ist eine Bibliothek, die das Problem des Assemblers löst, dass ein Schreibstil mehrere Umgebungen unterstützen kann, was für jede CPU unterschiedlich ist und neu erstellt werden muss. Folgende Plattformen werden derzeit unterstützt.

Beachten Sie jedoch, dass die hier vorgestellte Kinx-Version der JIT-Bibliothek nur 64-Bit unterstützt und wir nur x64 Windows und x64 Linux bestätigt (hergestellt) haben.

Offizielles erklärendes Dokument

Soweit ich weiß, habe ich nur die folgenden hilfreichen Dokumente gefunden.

Es wird hilfreich sein.

Das GitHub-Repository befindet sich unten.

Jit

Jetzt die JIT-Bibliothek als Kinx-Bibliothek. Es ist bequemer als die Verwendung als C. Natürlich können Sie die C-Bibliothek für mehr Kontrolle verwenden, aber Sie können es tun.

using Jit

Die Jit-Bibliothek ist nicht integriert. Verwenden Sie daher die using-Direktive, um sie explizit zu laden.

using Jit;

Jit Objekt

Das Jit-Objekt definiert Methoden für Parameter und Compilerklassen.

Methode für Jit-Parameter

Es gibt drei Arten von Jit-Parametern: Sofortwert, Register und Speicherzugriff. Es wird in der folgenden Form verwendet.

Sofortiger Wert, Speicherzugriff

Sofortwert und Speicherzugriff werden in den folgenden Methoden verwendet. Jit.VAR () ist eine spezielle Methode zur Verwendung des lokalen Variablenbereichs. Ein lokaler variabler Bereich wird automatisch im Stapelbereich zugewiesen, und dieser Bereich wird verwendet.

Methode Bemerkungen
Jit.IMM(v) Sowohl 64-Bit-Ganzzahlen als auch Gleitkommazahlen werden auf dieselbe Weise geschrieben. Übereinstimmung mit dem Zuweisungszielregister.
Jit.VAR(n) Lokaler variabler Bereich. 1 Variable auf 8 Bytes festgelegt.
Jit.MEM0(address) Der unmittelbare Wert wird als Adresse zugewiesen, kann jedoch nicht aus dem Skript verwendet werden, da die tatsächliche Adresse derzeit nicht aus dem Skript angegeben werden kann.
Jit.MEM1(r1, offset) Das in r1 angegebene Register wird als Adresse angesehen, und die Speicheradresse der Versatzposition (in Bytes) wird angezeigt.
Jit.MEM2(r1, r2, shift) Verschiebung ist 0 für 1 Byte, 1 für 2 Bytes, 2 für 4 Bytes, 3 für 8 Bytes,r1 + r2 * (Durch Verschiebung angezeigte Bytes)Zeigt die Speicheradresse des Speicherorts von an.
registrieren

Die folgenden Register können verwendet werden. Die Anzahl der Register, die in einer Funktion verwendet werden können, wird automatisch berechnet und ändert sich für jede Funktion (Bereich durch "enter ()" getrennt).

registrieren Verwenden
Jit.R0Jit.R5 Allzweckregister. Vorübergehend verwendet. Es kann nach dem Aufruf einer anderen Funktion verworfen werden.
Jit.S0Jit.S5 Allzweckregister. Stellen Sie sicher, dass es nach dem Aufruf einer anderen Funktion nicht zerstört wird.
Jit.FR0Jit.FR5 Gleitkommaregister. Vorübergehend verwendet. Es kann nach dem Aufruf einer anderen Funktion verworfen werden.
Jit.FS0Jit.FS5 Gleitkommaregister. Stellen Sie sicher, dass es nach dem Aufruf einer anderen Funktion nicht zerstört wird.

Da es für "FR" / "FS" insgesamt maximal 6 Register für Gleitkomma gibt, kann nur "FS0" verwendet werden, wenn bis zu "FR4" verwendet wird. Wenn Sie bis zu "FR5" verwenden, können Sie nicht alle "FS *" verwenden. Bitte beachten Sie, dass es wie folgt aussieht.

FR*registrieren FS*registrieren
(Nicht verfügbar) FS0, FS1, FS2, FS3, FS4, FS5
FR0 FS0, FS1, FS2, FS3, FS4
FR0, FR1 FS0, FS1, FS2, FS3
FR0, FR1, FR2 FS0, FS1, FS2
FR0, FR1, FR2, FR3 FS0, FS1
FR0, FR1, FR2, FR3, FR4 FS0
FR0, FR1, FR2, FR3, FR4, FR5 (Nicht verfügbar)

Jit Compiler

Erstellen Sie zum Erstellen einer Jit-Anweisung ein Jit-Compilerobjekt.

var c = new Jit.Compiler();

Der Jit-Compiler verfügt über die folgenden Methoden.

Jit-Compiler-Methode Rückgabewert Überblick
Jit.Compiler#label() label Fügen Sie dem aktuellen Speicherort eine Beschriftung hinzu.
Jit.Compiler#makeConst(reg, init) ConstTarget Gibt einen temporären Definitionscode aus, um den unmittelbaren Wert nach der Codegenerierung festzulegen.
Jit.Compiler#localp(dst, offset) Geben Sie den Code aus, um die tatsächliche Adresse der lokalen Variablen zu erhalten.dstEs wird in dem in gezeigten Register gespeichert. Offset ist die lokale Variablennummer.
Jit.Compiler#enter(argType) label Erstellen Sie einen Funktionseingang. Der Argumenttyp kann angegeben werden (optional).
Jit.Compiler#fastEnter(reg) label Erstellen Sie einen Funktionseingang. Es wird jedoch kein zusätzlicher Epilog oder Prolog ausgegeben, und die Rücksprungadresse wird ausgegeben.regSpeichern unter.
Jit.Compiler#ret(val) Geben Sie den Rückkehrcode aus.valGib es zurück.valIst eine GleitkommazahlFR0Ansonsten registrierenR0Zurück an der Kasse.
Jit.Compiler#f2i(dst, op1) double int64_Geben Sie den zu konvertierenden Code in t aus.dstIst ein Allzweckregister.op1Ist ein Gleitkommaregister.
Jit.Compiler#i2f(dst, op1) int64_Ausgabecode, der t in double umwandelt.dstIst ein Gleitkommaregister.op1Ist ein Allzweckregister.
Jit.Compiler#mov(dst, op1) dstZuop1Geben Sie den zu ersetzenden Code aus. Gleitkomma und andere Typen werden automatisch erkannt.
Jit.Compiler#neg(dst, op1) op1Das Ergebnis der Vorzeichenumkehrung vondstGeben Sie den Code aus, in dem gespeichert werden soll.
Jit.Compiler#clz(dst, op1) op1Zählen Sie die Anzahl der Bits, die vom Anfang an 0 sinddstGeben Sie den Code aus, in dem gespeichert werden soll.
Jit.Compiler#add(dst, op1, op2) op1Wannop2Das Ergebnis des HinzufügensdstGeben Sie den Code aus, in dem gespeichert werden soll.
Jit.Compiler#sub(dst, op1, op2) op1Wannop2Das Ergebnis des SubtrahierensdstGeben Sie den Code aus, in dem gespeichert werden soll.
Jit.Compiler#mul(dst, op1, op2) op1Wannop2Das Ergebnis der Multiplikation mitdstGeben Sie den Code aus, in dem gespeichert werden soll.
Jit.Compiler#div(dst, op1, op2) Nur Gleitkommazahlen,op1Wannop2Das Ergebnis der TeilungdstGeben Sie den Code aus, in dem gespeichert werden soll.
Jit.Compiler#div() Der Wert geteilt durch das Allzweckregister als vorzeichenlosR0Geben Sie den Code aus, der im Register gespeichert werden soll.
Jit.Compiler#sdiv() Der Wert geteilt durch das Allzweckregister als signiertR0Geben Sie den Code aus, der im Register gespeichert werden soll.
Jit.Compiler#divmod() Der Wert geteilt durch das Allzweckregister als vorzeichenlosR0In einem Register speichern und den Rest hinterlassenR1Geben Sie den Code aus, der im Register gespeichert werden soll.
Jit.Compiler#sdivmod() Der Wert geteilt durch das Allzweckregister als signiertR0In einem Register speichern und den Rest hinterlassenR1Geben Sie den Code aus, der im Register gespeichert werden soll.
Jit.Compiler#not(dst, op1) op1Das Ergebnis der Bitinversion vondstGeben Sie den Code aus, in dem gespeichert werden soll.
Jit.Compiler#and(dst, op1, op2) op1Wannop2Bit UND Wert mitdstGeben Sie den Code aus, in dem gespeichert werden soll.
Jit.Compiler#or(dst, op1, op2) op1Wannop2Bit ODER Wert indstGeben Sie den Code aus, in dem gespeichert werden soll.
Jit.Compiler#xor(dst, op1, op2) op1Wannop2Bit XOR-Wert indstGeben Sie den Code aus, in dem gespeichert werden soll.
Jit.Compiler#shl(dst, op1, op2) op1Zuop2Der Wert wird um das Bit nach links verschobendstGeben Sie den Code aus, in dem gespeichert werden soll.
Jit.Compiler#lshr(dst, op1, op2) op1Zuop2Der Wert verschob sich logischerweise um das Bit nach rechtsdstGeben Sie den Code aus, in dem gespeichert werden soll.
Jit.Compiler#ashr(dst, op1, op2) op1Zuop2Bits, arithmetischer rechtsverschobener WertdstGeben Sie den Code aus, in dem gespeichert werden soll.
Jit.Compiler#call(label) JumpTarget enter()Geben Sie den Code aus, der den definierten Funktionsaufruf ausführt. Gibt ein JumpTarget zurück, das später den Angerufenen festlegt.labelWenn angegeben, muss es später nicht mehr festgelegt werden.
Jit.Compiler#fastCall(label) JumpTarget fastEnter()Geben Sie den Code aus, der die in definierte Funktion aufruft. Gibt ein JumpTarget zurück, das später den Angerufenen festlegt.
Jit.Compiler#jmp(label) JumpTarget jmpGeben Sie den Befehl aus.labelWenn angegeben, muss es später nicht mehr festgelegt werden.
Jit.Compiler#ijmp(dst) JumpTarget jmpGeben Sie den Befehl aus.dstIst ein Register, das die Adresse angibt, oder ein unmittelbarer Wert.
Jit.Compiler#eq(op1, op2) JumpTarget op1 == op2Geben Sie den zu bestätigenden Code aus. Gibt ein JumpTarget zurück, das das Sprungziel angibt, wenn die Bedingung erfüllt ist.
Jit.Compiler#neq(op1, op2) JumpTarget op1 != op2Geben Sie den zu bestätigenden Code aus. Gibt ein JumpTarget zurück, das das Sprungziel angibt, wenn die Bedingung erfüllt ist.
Jit.Compiler#lt(op1, op2) JumpTarget Wie nicht signiertop1 < op2Geben Sie den zu bestätigenden Code aus. Gibt ein JumpTarget zurück, das das Sprungziel angibt, wenn die Bedingung erfüllt ist.
Jit.Compiler#le(op1, op2) JumpTarget Wie nicht signiertop1 <= op2Geben Sie den zu bestätigenden Code aus. Gibt ein JumpTarget zurück, das das Sprungziel angibt, wenn die Bedingung erfüllt ist.
Jit.Compiler#gt(op1, op2) JumpTarget Wie nicht signiertop1 > op2Geben Sie den zu bestätigenden Code aus. Gibt ein JumpTarget zurück, das das Sprungziel angibt, wenn die Bedingung erfüllt ist.
Jit.Compiler#ge(op1, op2) JumpTarget Wie nicht signiertop1 >= op2Geben Sie den zu bestätigenden Code aus. Gibt ein JumpTarget zurück, das das Sprungziel angibt, wenn die Bedingung erfüllt ist.
Jit.Compiler#slt(op1, op2) JumpTarget Wie unterschriebenop1 < op2Geben Sie den zu bestätigenden Code aus. Gibt ein JumpTarget zurück, das das Sprungziel angibt, wenn die Bedingung erfüllt ist.
Jit.Compiler#sle(op1, op2) JumpTarget Wie unterschriebenop1 <= op2Geben Sie den zu bestätigenden Code aus. Gibt ein JumpTarget zurück, das das Sprungziel angibt, wenn die Bedingung erfüllt ist.
Jit.Compiler#sgt(op1, op2) JumpTarget Wie unterschriebenop1 > op2Geben Sie den zu bestätigenden Code aus. Gibt ein JumpTarget zurück, das das Sprungziel angibt, wenn die Bedingung erfüllt ist.
Jit.Compiler#sge(op1, op2) JumpTarget Wie unterschriebenop1 >= op2Geben Sie den zu bestätigenden Code aus. Gibt ein JumpTarget zurück, das das Sprungziel angibt, wenn die Bedingung erfüllt ist.
Jit.Compiler#generate() JitCode Code generieren.

Jit.Compiler#enter(argType)

Der Eingang der Funktion wird durch die Methode "enter" definiert. Wenn jedoch "argType" nicht angegeben wird, wird davon ausgegangen, dass "Jit.ArgType.SW_SW_SW" angegeben ist. Bis zu 3 Argumente (Spezifikation) geben jeden Typ an.

Tatsächlich ändern sich "SW" und "UW" nicht, da die Bitfolgen der empfangenen Register gleich sind, aber es kann in Zukunft einen Unterschied machen. Beachten Sie, dass "SW" im letzten Argument weggelassen werden kann. Daher haben die folgenden die gleiche Bedeutung.

Die als Argumente übergebenen Register sind fest und lauten wie folgt.

Schimmel 1. Argument 2. Argument Drittes Argument
Integer Jit.R0 Jit.R1 Jit.R2
Double Jit.FR0 Jit.FR1 Jit.FR2
Schimmel 1. Argument 2. Argument Drittes Argument
Integer Jit.S0 Jit.S1 Jit.S2
Double Jit.FS0 Jit.FS1 Jit.FS2

Beachten Sie, dass das vom Anrufer festgelegte Register und das vom Empfänger empfangene Register unterschiedlich sind.

ConstTarget

Stellen Sie die Label-Adresse mit setLabel () ein. Es wird verwendet, wenn Sie die Etikettenadresse als unmittelbaren Wert in einem Register oder Speicher speichern möchten. Haben Sie viele Möglichkeiten, es zu nutzen? Ich denke, es könnte ein Ersatz für einen Sprungtisch sein, aber ich habe keinen guten Mechanismus für die Herstellung des Tisches vorbereitet.

Übrigens können Sie den Sofortwert mit "setValue ()" festlegen, aber ich habe es möglich gemacht, normalerweise "Jit.IMM (100)" oder sogar Gleitkommazahlen wie "Jit.IMM (0.1)" zu verwenden. Es macht nicht viel Sinn, es zu benutzen.

Ein Beispiel für die Verwendung für eine Sprungtabelle wird später beschrieben.

JumpTarget

Stellen Sie mit setLabel () das Sprungziel oder die Adresse für den Funktionsaufruf ein.

Wenn Sie beispielsweise basierend auf dem Vergleichsergebnis verzweigen, wird dies wie folgt.

var c = new Jit.Compiler();
//Funktionseinstiegspunkt.
c.enter();
//S0 Registerwert>= 3
var jump0 = c.ge(Jit.S0, Jit.IMM(3));
... //Code, wenn die Bedingung falsch ist
var jump1 = c.jmp();
var label0 = c.label();
... //Code, wenn die Bedingung erfüllt ist
var label1 = c.label();
...

jump0.setLabel(label0);
jump1.setLabel(label1);

JitCode

Wenn der Code erfolgreich von der Methode "generate ()" generiert wurde, wird ein JitCode-Objekt zurückgegeben. Die Methoden des JitCode-Objekts sind wie folgt. Beachten Sie, dass Sie nur bis zu 3 Argumente angeben können (Spezifikation). Da es sich um einen abstrakten Assembler handelt, ist eine Spezifikation erforderlich, um verschiedene Architekturen zu unterstützen. Bei Bedarf muss ein lokaler Variablenbereich gesichert und die Startadresse des lokalen Variablenbereichs übergeben werden. Ein Beispiel wird später beschrieben.

Methode Überblick
JitCode#run(a1, a2, a3) Erhalten Sie den Rückgabewert als Integer.
JitCode#frun(a1, a2, a3) Erhalten Sie den Rückgabewert als Double.
JitCode#dump() Geben Sie die generierte Assembleliste aus.

Stichprobe

Fibonacci-Sequenz (rekursive Version)

Schreiben wir nun eine rekursive Version des Codes, der die übliche Fibonacci-Sequenz berechnet. Es ist das gleiche wie das, das ursprünglich als Beispiel präsentiert wurde.

var c = new Jit.Compiler();
var entry1 = c.enter();
    var jump0 = c.ge(Jit.S0, Jit.IMM(3));
    c.ret(Jit.S0);
    var l1 = c.label();
    c.sub(Jit.R0, Jit.S0, Jit.IMM(2));
    c.call(entry1);
    c.mov(Jit.S1, Jit.R0);
    c.sub(Jit.R0, Jit.S0, Jit.IMM(1));
    c.call(entry1);
    c.add(Jit.R0, Jit.R0, Jit.S1);
    c.ret(Jit.R0);

jump0.setLabel(l1);
var code = c.generate();

for (var i = 1; i <= 42; ++i) {
    var tmr = new SystemTimer();
    var r = code.run(i);
    System.println("[%8.3f] fib(%2d) = %d" % tmr.elapsed() % i % r);
}

Das Ergebnis ist wie folgt.

[   0.000] fib( 1) = 1
[   0.000] fib( 2) = 2
[   0.000] fib( 3) = 3
[   0.000] fib( 4) = 5
[   0.000] fib( 5) = 8
[   0.000] fib( 6) = 13
[   0.000] fib( 7) = 21
[   0.000] fib( 8) = 34
[   0.000] fib( 9) = 55
[   0.000] fib(10) = 89
[   0.000] fib(11) = 144
[   0.000] fib(12) = 233
[   0.000] fib(13) = 377
[   0.000] fib(14) = 610
[   0.000] fib(15) = 987
[   0.000] fib(16) = 1597
[   0.000] fib(17) = 2584
[   0.000] fib(18) = 4181
[   0.000] fib(19) = 6765
[   0.000] fib(20) = 10946
[   0.000] fib(21) = 17711
[   0.000] fib(22) = 28657
[   0.000] fib(23) = 46368
[   0.000] fib(24) = 75025
[   0.000] fib(25) = 121393
[   0.001] fib(26) = 196418
[   0.001] fib(27) = 317811
[   0.001] fib(28) = 514229
[   0.002] fib(29) = 832040
[   0.002] fib(30) = 1346269
[   0.004] fib(31) = 2178309
[   0.006] fib(32) = 3524578
[   0.009] fib(33) = 5702887
[   0.016] fib(34) = 9227465
[   0.035] fib(35) = 14930352
[   0.042] fib(36) = 24157817
[   0.066] fib(37) = 39088169
[   0.119] fib(38) = 63245986
[   0.181] fib(39) = 102334155
[   0.289] fib(40) = 165580141
[   0.476] fib(41) = 267914296
[   0.773] fib(42) = 433494437

Übrigens habe ich das Ergebnis von "fib (42)" mit Ruby, Python, PyPy, PHP, HHVM, Kinx, Kinx (nativ) gemessen und verglichen. Da die Version der JIT-Bibliothek nur die Zeit von "run ()" oben misst, wird alles, einschließlich der Skriptinterpretation und der JIT-Codegenerierung, fair von der Benutzerzeit des gesamten Prozesses berechnet.

Es ist wie folgt, wenn es in der Reihenfolge der Geschwindigkeit angeordnet ist. Immerhin ist es bemerkenswert schnell, wenn der native Code direkt von JIT ausgegeben wird. Es ist eine schöne Fehleinschätzung, dass Kinx (native) schneller als PyPy war. Wie viel kostet HHVM? Ruby ist im Skript schneller. Ich bin tief bewegt, wenn ich die Ära 1.8 kenne.

Sprache Versionsnummer Benutzerzeit
Kinx(Jit-Lib) 0.10.0 0.828
HHVM 3.21.0 2.227
Kinx(native) 0.10.0 2.250
PyPy 5.10.0 3.313
PHP 7.2.24 11.422
Ruby 2.5.1p57 14.877
Kinx 0.10.0 27.478
Python 2.7.15+ 41.125

Klicken Sie hier, um die Assemblierungsliste anzuzeigen, die von der vorherigen JIT-Bibliothek generiert wurde. Es ist anders zwischen Windows und Linux, aber diesmal ist es Linux.

       0:   53                                          push rbx
       1:   41 57                                       push r15
       3:   41 56                                       push r14
       5:   48 8b df                                    mov rbx, rdi
       8:   4c 8b fe                                    mov r15, rsi
       b:   4c 8b f2                                    mov r14, rdx
       e:   48 83 ec 10                                 sub rsp, 0x10
      12:   48 83 fb 03                                 cmp rbx, 0x3
      16:   73 0d                                       jae 0x25
      18:   48 89 d8                                    mov rax, rbx
      1b:   48 83 c4 10                                 add rsp, 0x10
      1f:   41 5e                                       pop r14
      21:   41 5f                                       pop r15
      23:   5b                                          pop rbx
      24:   c3                                          ret
      25:   48 8d 43 fe                                 lea rax, [rbx-0x2]
      29:   48 89 fa                                    mov rdx, rdi
      2c:   48 89 c7                                    mov rdi, rax
      2f:   e8 cc ff ff ff                              call 0x0
      34:   49 89 c7                                    mov r15, rax
      37:   48 8d 43 ff                                 lea rax, [rbx-0x1]
      3b:   48 89 fa                                    mov rdx, rdi
      3e:   48 89 c7                                    mov rdi, rax
      41:   e8 ba ff ff ff                              call 0x0
      46:   49 03 c7                                    add rax, r15
      49:   48 83 c4 10                                 add rsp, 0x10
      4d:   41 5e                                       pop r14
      4f:   41 5f                                       pop r15
      51:   5b                                          pop rbx
      52:   c3                                          ret

Const Beispiel

Wenn Sie es als Beispiel für Const wagen, es zu schreiben, sieht es so aus. Ich erstelle eine Sprungtabelle für lokale Variablen, daher kann ich die Tabelle nicht jedes Mal neu erstellen. Es scheint, dass es gelöst wird, wenn Sie eine separate Schnittstelle vorbereiten, mit der Sie nur eine Tabelle erstellen und die Adresse (möglicherweise) übergeben können.

var c = new Jit.Compiler();
c.enter();
    c.mov(Jit.R1, Jit.IMM(-1));
    var jump0 = c.slt(Jit.S0, Jit.IMM(0));
    var jump1 = c.sgt(Jit.S0, Jit.IMM(3));
    var const0 = c.makeConst(Jit.VAR(0));
    var const1 = c.makeConst(Jit.VAR(1));
    var const2 = c.makeConst(Jit.VAR(2));
    var const3 = c.makeConst(Jit.VAR(3));
    //Die Adresse der lokalen Variablen wird durch den Offset des S0-Registers (erstes Argument) erfasst und im R0-Register gespeichert.
    c.localp(Jit.R0, Jit.S0);
    //Ruft den Wert der lokalen Variablen selbst ab.
    c.mov(Jit.R0, Jit.MEM1(Jit.R0));
    //Wechseln Sie, indem Sie den Inhalt lokaler Variablen als Adressen betrachten.
    c.ijmp(Jit.R0);
    var l0 = c.label();
    c.mov(Jit.R1, Jit.IMM(102));
    c.ret(Jit.R1);
    var l1 = c.label();
    c.mov(Jit.R1, Jit.IMM(103));
    c.ret(Jit.R1);
    var l2 = c.label();
    c.mov(Jit.R1, Jit.IMM(104));
    c.ret(Jit.R1);
    var l3 = c.label();
    c.mov(Jit.R1, Jit.IMM(105));
    var l4 = c.label();
    c.ret(Jit.R1);

//Die Sprungadresse wird vor der Codegenerierung festgelegt.
jump0.setLabel(l4);
jump1.setLabel(l4);

var code = c.generate();
//Der const-Wert wird nach der Codegenerierung festgelegt.
const0.setLabel(l0);
const1.setLabel(l1);
const2.setLabel(l2);
const3.setLabel(l3);

for (var i = -1; i < 5; ++i) {
    var r = code.run(i);
    System.println(r);
}

Ergebnis.

-1
102
103
104
105
-1

Die Code-Ausgabe sieht folgendermaßen aus. Ich habe dies unter der Windows-Version versucht.

       0:   53                                          push rbx
       1:   56                                          push rsi
       2:   57                                          push rdi
       3:   48 8b d9                                    mov rbx, rcx
       6:   48 8b f2                                    mov rsi, rdx
       9:   49 8b f8                                    mov rdi, r8
       c:   4c 8b 4c 24 b0                              mov r9, [rsp-0x50]
      11:   48 83 ec 50                                 sub rsp, 0x50
      15:   48 c7 c2 ff ff ff ff                        mov rdx, 0xffffffffffffffff
      1c:   48 83 fb 00                                 cmp rbx, 0x0
      20:   0f 8c 94 00 00 00                           jl 0xba
      26:   48 83 fb 03                                 cmp rbx, 0x3
      2a:   0f 8f 8a 00 00 00                           jg 0xba
      30:   49 b9 95 ff 57 61 89 01 00 00               mov r9, 0x1896157ff95
      3a:   4c 89 4c 24 20                              mov [rsp+0x20], r9
      3f:   49 b9 a7 ff 57 61 89 01 00 00               mov r9, 0x1896157ffa7
      49:   4c 89 4c 24 28                              mov [rsp+0x28], r9
      4e:   49 b9 b9 ff 57 61 89 01 00 00               mov r9, 0x1896157ffb9
      58:   4c 89 4c 24 30                              mov [rsp+0x30], r9
      5d:   49 b9 cb ff 57 61 89 01 00 00               mov r9, 0x1896157ffcb
      67:   4c 89 4c 24 38                              mov [rsp+0x38], r9
      6c:   48 8d 44 24 20                              lea rax, [rsp+0x20]
      71:   48 6b db 08                                 imul rbx, rbx, 0x8
      75:   48 03 c3                                    add rax, rbx
      78:   48 8b 00                                    mov rax, [rax]
      7b:   ff e0                                       jmp rax
      7d:   48 c7 c2 66 00 00 00                        mov rdx, 0x66
      84:   48 89 d0                                    mov rax, rdx
      87:   48 83 c4 50                                 add rsp, 0x50
      8b:   5f                                          pop rdi
      8c:   5e                                          pop rsi
      8d:   5b                                          pop rbx
      8e:   c3                                          ret
      8f:   48 c7 c2 67 00 00 00                        mov rdx, 0x67
      96:   48 89 d0                                    mov rax, rdx
      99:   48 83 c4 50                                 add rsp, 0x50
      9d:   5f                                          pop rdi
      9e:   5e                                          pop rsi
      9f:   5b                                          pop rbx
      a0:   c3                                          ret
      a1:   48 c7 c2 68 00 00 00                        mov rdx, 0x68
      a8:   48 89 d0                                    mov rax, rdx
      ab:   48 83 c4 50                                 add rsp, 0x50
      af:   5f                                          pop rdi
      b0:   5e                                          pop rsi
      b1:   5b                                          pop rbx
      b2:   c3                                          ret
      b3:   48 c7 c2 69 00 00 00                        mov rdx, 0x69
      ba:   48 89 d0                                    mov rax, rdx
      bd:   48 83 c4 50                                 add rsp, 0x50
      c1:   5f                                          pop rdi
      c2:   5e                                          pop rsi
      c3:   5b                                          pop rbx
      c4:   c3                                          ret

Der Punkt ist "jmp rax" in Zeile 7b. Wenn die Tabelle statisch definiert werden kann, fungiert sie als Sprungtabelle (es gibt jetzt keine einfache Möglichkeit, dies zu tun ...).

Beispiel für 4 oder mehr Argumente

Es ist etwas ärgerlich, aber wenn Sie 4 oder mehr Argumente übergeben möchten, speichern Sie den Wert im Bereich der lokalen Variablen und übergeben Sie die Adresse (den Zeiger) als Argument. Im folgenden Beispiel wird zuerst die Hook-Funktion zum Setzen des Arguments auf den lokalen Variablenbereich durchlaufen. Übrigens, da alle lokalen Variablen in 8 Bytes zugeordnet sind, ist zu beachten, dass der Offset beim direkten Zugriff mit Jit.MEM1 () usw. ein Vielfaches von 8 sein muss.

var c = new Jit.Compiler();
var entry1 = c.enter();
    c.mov(Jit.VAR(0), Jit.S0);
    c.mov(Jit.VAR(1), Jit.IMM(3));
    c.mov(Jit.VAR(2), Jit.IMM(2));
    c.mov(Jit.VAR(3), Jit.IMM(1));
    c.localp(Jit.R0);
    var call1 = c.call();
    c.ret(Jit.R0);
var entry2 = c.enter();
    c.mov(Jit.R1, Jit.S0);
    c.mov(Jit.S0, Jit.MEM1(Jit.R1, 0));
    var jump0 = c.ge(Jit.S0, Jit.MEM1(Jit.R1, 8));
    c.ret(Jit.S0);
    var l1 = c.label();
    c.sub(Jit.R3, Jit.S0, Jit.MEM1(Jit.R1, 16));
    c.mov(Jit.VAR(0), Jit.R3);
    c.mov(Jit.VAR(1), Jit.IMM(3));
    c.mov(Jit.VAR(2), Jit.IMM(2));
    c.mov(Jit.VAR(3), Jit.IMM(1));
    c.localp(Jit.R0);
    c.call(entry2);
    c.mov(Jit.S1, Jit.R0);
    c.sub(Jit.R3, Jit.S0, Jit.MEM1(Jit.R1, 24));
    c.mov(Jit.VAR(0), Jit.R3);
    c.mov(Jit.VAR(1), Jit.IMM(3));
    c.mov(Jit.VAR(2), Jit.IMM(2));
    c.mov(Jit.VAR(3), Jit.IMM(1));
    c.localp(Jit.R0);
    c.call(entry2);
    c.add(Jit.R0, Jit.R0, Jit.S1);
    c.ret(Jit.R0);

jump0.setLabel(l1);
call1.setLabel(entry2);
var code = c.generate();

for (var i = 1; i <= 42; ++i) {
    var tmr = new SystemTimer();
    var r = code.run(i);
    System.println("[%8.3f] fib(%2d) = %d" % tmr.elapsed() % i % r);
}

Die Ausgabe ist die gleiche wie zuvor.

Doppeltes Argument und Rückgabewert

Ich habe Double nicht eingeführt, also auch nicht. Gehen wir auch mit Fibonacci. Aber ich liebe Fibonacci. Ich habe es nicht bemerkt. Es ist eine Version mit 0,1 Schritten.

var c = new Jit.Compiler();
var entry1 = c.enter(Jit.ArgType.FP);
    c.mov(Jit.FR0, Jit.IMM(0.3));
    var jump0 = c.ge(Jit.FS0, Jit.FR0);
    c.ret(Jit.FS0);
    var l1 = c.label();
    c.mov(Jit.FR0, Jit.IMM(0.2));
    c.sub(Jit.FR0, Jit.FS0, Jit.FR0);
    c.call(entry1);
    c.mov(Jit.FS1, Jit.FR0);
    c.mov(Jit.FR0, Jit.IMM(0.1));
    c.sub(Jit.FR0, Jit.FS0, Jit.FR0);
    c.call(entry1);
    c.add(Jit.FR0, Jit.FR0, Jit.FS1);
    c.ret(Jit.FR0);

jump0.setLabel(l1);
var code = c.generate();

for (var i = 0.1; i < 3.5; i += 0.1) {
    var tmr = new SystemTimer();
    var r = code.frun(i);
    System.println("[%8.3f] fib(%3.1f) = %.1f" % tmr.elapsed() % i % r);
}

Da der unmittelbare Wert der Gleitkommazahl in der direkten Vergleichsmethode nicht verfügbar gemacht wird (dies sollte erfolgen), muss er vorübergehend im Register gespeichert und verwendet werden.

Sie können einen doppelten Wert erhalten, indem Sie "frun ()" ausführen. Das Ergebnis ist wie folgt.

[   0.000] fib(0.1) = 0.1
[   0.000] fib(0.2) = 0.2
[   0.000] fib(0.3) = 0.3
[   0.000] fib(0.4) = 0.5
[   0.000] fib(0.5) = 0.8
[   0.000] fib(0.6) = 1.3
[   0.000] fib(0.7) = 2.1
[   0.000] fib(0.8) = 3.4
[   0.000] fib(0.9) = 5.5
[   0.000] fib(1.0) = 8.9
[   0.000] fib(1.1) = 14.4
[   0.000] fib(1.2) = 23.3
[   0.000] fib(1.3) = 37.7
[   0.000] fib(1.4) = 61.0
[   0.000] fib(1.5) = 98.7
[   0.000] fib(1.6) = 159.7
[   0.000] fib(1.7) = 258.4
[   0.000] fib(1.8) = 418.1
[   0.000] fib(1.9) = 676.5
[   0.000] fib(2.0) = 1094.6
[   0.000] fib(2.1) = 1771.1
[   0.000] fib(2.2) = 2865.7
[   0.000] fib(2.3) = 4636.8
[   0.000] fib(2.4) = 7502.5
[   0.000] fib(2.5) = 12139.3
[   0.001] fib(2.6) = 19641.8
[   0.001] fib(2.7) = 31781.1
[   0.002] fib(2.8) = 51422.9
[   0.003] fib(2.9) = 83204.0
[   0.004] fib(3.0) = 134626.9
[   0.006] fib(3.1) = 217830.9
[   0.015] fib(3.2) = 352457.8
[   0.020] fib(3.3) = 570288.7
[   0.027] fib(3.4) = 922746.5

Der Ausgabecode lautet wie folgt. Dies ist auch die Windows-Version. Es gibt eine einfache Hook-Funktion, mit der zuerst eine Gleitkommazahl übergeben werden kann. SLJIT kann im Argument am Einstiegspunkt der Funktion keine Gleitkommazahl angeben, daher wird dies auf diese Weise vermieden.

Auch in diesem Sinne ist die Verwendung dieser Option besser als die direkte Verwendung von SLJIT. Da die erforderliche Größe im Bereich der lokalen Variablen automatisch berechnet wird und die erforderliche Anzahl von temporären Speichercodes für zerstörungsfreie Register ebenfalls automatisch berechnet wird.

       0:   53                                          push rbx
       1:   56                                          push rsi
       2:   57                                          push rdi
       3:   48 8b d9                                    mov rbx, rcx
       6:   48 8b f2                                    mov rsi, rdx
       9:   49 8b f8                                    mov rdi, r8
       c:   4c 8b 4c 24 d0                              mov r9, [rsp-0x30]
      11:   48 83 ec 30                                 sub rsp, 0x30
      15:   0f 29 74 24 20                              movaps [rsp+0x20], xmm6
      1a:   f2 0f 10 03                                 movsd xmm0, qword [rbx]
      1e:   48 89 f2                                    mov rdx, rsi
      21:   49 89 f8                                    mov r8, rdi
      24:   48 89 c1                                    mov rcx, rax
      27:   e8 0d 00 00 00                              call 0x39
      2c:   0f 28 74 24 20                              movaps xmm6, [rsp+0x20]
      31:   48 83 c4 30                                 add rsp, 0x30
      35:   5f                                          pop rdi
      36:   5e                                          pop rsi
      37:   5b                                          pop rbx
      38:   c3                                          ret
      39:   53                                          push rbx
      3a:   56                                          push rsi
      3b:   57                                          push rdi
      3c:   48 8b d9                                    mov rbx, rcx
      3f:   48 8b f2                                    mov rsi, rdx
      42:   49 8b f8                                    mov rdi, r8
      45:   4c 8b 4c 24 b0                              mov r9, [rsp-0x50]
      4a:   48 83 ec 50                                 sub rsp, 0x50
      4e:   0f 29 74 24 20                              movaps [rsp+0x20], xmm6
      53:   f2 0f 11 6c 24 38                           movsd [rsp+0x38], xmm5
      59:   f2 0f 10 f0                                 movsd xmm6, xmm0
      5d:   49 b9 33 33 33 33 33 33 d3 3f               mov r9, 0x3fd3333333333333
      67:   4c 89 4c 24 40                              mov [rsp+0x40], r9
      6c:   f2 0f 10 44 24 40                           movsd xmm0, qword [rsp+0x40]
      72:   66 0f 2e f0                                 ucomisd xmm6, xmm0
      76:   73 17                                       jae 0x8f
      78:   f2 0f 10 c6                                 movsd xmm0, xmm6
      7c:   f2 0f 10 6c 24 38                           movsd xmm5, qword [rsp+0x38]
      82:   0f 28 74 24 20                              movaps xmm6, [rsp+0x20]
      87:   48 83 c4 50                                 add rsp, 0x50
      8b:   5f                                          pop rdi
      8c:   5e                                          pop rsi
      8d:   5b                                          pop rbx
      8e:   c3                                          ret
      8f:   49 b9 9a 99 99 99 99 99 c9 3f               mov r9, 0x3fc999999999999a
      99:   4c 89 4c 24 40                              mov [rsp+0x40], r9
      9e:   f2 0f 10 44 24 40                           movsd xmm0, qword [rsp+0x40]
      a4:   f2 0f 10 e6                                 movsd xmm4, xmm6
      a8:   f2 0f 5c e0                                 subsd xmm4, xmm0
      ac:   f2 0f 11 e0                                 movsd xmm0, xmm4
      b0:   48 89 c1                                    mov rcx, rax
      b3:   e8 81 ff ff ff                              call 0x39
      b8:   f2 0f 10 e8                                 movsd xmm5, xmm0
      bc:   49 b9 9a 99 99 99 99 99 b9 3f               mov r9, 0x3fb999999999999a
      c6:   4c 89 4c 24 40                              mov [rsp+0x40], r9
      cb:   f2 0f 10 44 24 40                           movsd xmm0, qword [rsp+0x40]
      d1:   f2 0f 10 e6                                 movsd xmm4, xmm6
      d5:   f2 0f 5c e0                                 subsd xmm4, xmm0
      d9:   f2 0f 11 e0                                 movsd xmm0, xmm4
      dd:   48 89 c1                                    mov rcx, rax
      e0:   e8 54 ff ff ff                              call 0x39
      e5:   f2 0f 58 c5                                 addsd xmm0, xmm5
      e9:   f2 0f 10 6c 24 38                           movsd xmm5, qword [rsp+0x38]
      ef:   0f 28 74 24 20                              movaps xmm6, [rsp+0x20]
      f4:   48 83 c4 50                                 add rsp, 0x50
      f8:   5f                                          pop rdi
      f9:   5e                                          pop rsi
      fa:   5b                                          pop rbx
      fb:   c3                                          ret

abschließend

JIT ist interessant. Wenn Sie es mit einem Parser-Kombinator implementieren und kombinieren, können Sie mit JIT ein kleines Sprachverarbeitungssystem erstellen. Vielleicht können Sie einen solchen Weg anstreben.

Vielleicht gibt es zwei Verwendungsmöglichkeiten:

  1. Wenn Sie eine Kinx-Bibliothek erstellen, lassen Sie sie im Bereich der numerischen Berechnung usw. JIT und beschleunigen Sie sie.
  2. Hostet DSL (domänenspezifische Sprache) und Oleore-Sprache und verwendet sie für die Backend-Ausgabe.

wir sehen uns.

Recommended Posts

Kinx Library-JIT-Compiler-Bibliothek
Kinx Library-JIT-Compiler-Bibliothek (Extra Edition)
Kinx-Bibliothek --REPL
Kinx-Bibliothek - Getopt
Kinx Library --DateTime
Kinx Library - Prozess