** "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.
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.
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;
Das Jit-Objekt definiert Methoden für Parameter und Compilerklassen.
Es gibt drei Arten von Jit-Parametern: Sofortwert, Register und Speicherzugriff. Es wird in der folgenden Form verwendet.
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. |
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.R0 ~ Jit.R5 |
Allzweckregister. Vorübergehend verwendet. Es kann nach dem Aufruf einer anderen Funktion verworfen werden. |
Jit.S0 ~ Jit.S5 |
Allzweckregister. Stellen Sie sicher, dass es nach dem Aufruf einer anderen Funktion nicht zerstört wird. |
Jit.FR0 ~ Jit.FR5 |
Gleitkommaregister. Vorübergehend verwendet. Es kann nach dem Aufruf einer anderen Funktion verworfen werden. |
Jit.FS0 ~ Jit.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) |
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.dst Es 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.reg Speichern unter. |
Jit.Compiler#ret(val) |
Geben Sie den Rückkehrcode aus.val Gib es zurück.val Ist eine GleitkommazahlFR0 Ansonsten registrierenR0 Zurück an der Kasse. |
|
Jit.Compiler#f2i(dst, op1) |
double int64_Geben Sie den zu konvertierenden Code in t aus.dst Ist ein Allzweckregister.op1 Ist ein Gleitkommaregister. |
|
Jit.Compiler#i2f(dst, op1) |
int64_Ausgabecode, der t in double umwandelt.dst Ist ein Gleitkommaregister.op1 Ist ein Allzweckregister. |
|
Jit.Compiler#mov(dst, op1) |
dst Zuop1 Geben Sie den zu ersetzenden Code aus. Gleitkomma und andere Typen werden automatisch erkannt. |
|
Jit.Compiler#neg(dst, op1) |
op1 Das Ergebnis der Vorzeichenumkehrung vondst Geben Sie den Code aus, in dem gespeichert werden soll. |
|
Jit.Compiler#clz(dst, op1) |
op1 Zählen Sie die Anzahl der Bits, die vom Anfang an 0 sinddst Geben Sie den Code aus, in dem gespeichert werden soll. |
|
Jit.Compiler#add(dst, op1, op2) |
op1 Wannop2 Das Ergebnis des Hinzufügensdst Geben Sie den Code aus, in dem gespeichert werden soll. |
|
Jit.Compiler#sub(dst, op1, op2) |
op1 Wannop2 Das Ergebnis des Subtrahierensdst Geben Sie den Code aus, in dem gespeichert werden soll. |
|
Jit.Compiler#mul(dst, op1, op2) |
op1 Wannop2 Das Ergebnis der Multiplikation mitdst Geben Sie den Code aus, in dem gespeichert werden soll. |
|
Jit.Compiler#div(dst, op1, op2) |
Nur Gleitkommazahlen,op1 Wannop2 Das Ergebnis der Teilungdst Geben Sie den Code aus, in dem gespeichert werden soll. |
|
Jit.Compiler#div() |
Der Wert geteilt durch das Allzweckregister als vorzeichenlosR0 Geben Sie den Code aus, der im Register gespeichert werden soll. |
|
Jit.Compiler#sdiv() |
Der Wert geteilt durch das Allzweckregister als signiertR0 Geben Sie den Code aus, der im Register gespeichert werden soll. |
|
Jit.Compiler#divmod() |
Der Wert geteilt durch das Allzweckregister als vorzeichenlosR0 In einem Register speichern und den Rest hinterlassenR1 Geben Sie den Code aus, der im Register gespeichert werden soll. |
|
Jit.Compiler#sdivmod() |
Der Wert geteilt durch das Allzweckregister als signiertR0 In einem Register speichern und den Rest hinterlassenR1 Geben Sie den Code aus, der im Register gespeichert werden soll. |
|
Jit.Compiler#not(dst, op1) |
op1 Das Ergebnis der Bitinversion vondst Geben Sie den Code aus, in dem gespeichert werden soll. |
|
Jit.Compiler#and(dst, op1, op2) |
op1 Wannop2 Bit UND Wert mitdst Geben Sie den Code aus, in dem gespeichert werden soll. |
|
Jit.Compiler#or(dst, op1, op2) |
op1 Wannop2 Bit ODER Wert indst Geben Sie den Code aus, in dem gespeichert werden soll. |
|
Jit.Compiler#xor(dst, op1, op2) |
op1 Wannop2 Bit XOR-Wert indst Geben Sie den Code aus, in dem gespeichert werden soll. |
|
Jit.Compiler#shl(dst, op1, op2) |
op1 Zuop2 Der Wert wird um das Bit nach links verschobendst Geben Sie den Code aus, in dem gespeichert werden soll. |
|
Jit.Compiler#lshr(dst, op1, op2) |
op1 Zuop2 Der Wert verschob sich logischerweise um das Bit nach rechtsdst Geben Sie den Code aus, in dem gespeichert werden soll. |
|
Jit.Compiler#ashr(dst, op1, op2) |
op1 Zuop2 Bits, arithmetischer rechtsverschobener Wertdst Geben 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.label Wenn 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 | jmp Geben Sie den Befehl aus.label Wenn angegeben, muss es später nicht mehr festgelegt werden. |
Jit.Compiler#ijmp(dst) |
JumpTarget | jmp Geben Sie den Befehl aus.dst Ist ein Register, das die Adresse angibt, oder ein unmittelbarer Wert. |
Jit.Compiler#eq(op1, op2) |
JumpTarget | op1 == op2 Geben 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 != op2 Geben 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 < op2 Geben 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 <= op2 Geben 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 > op2 Geben 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 >= op2 Geben 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 < op2 Geben 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 <= op2 Geben 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 > op2 Geben 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 >= op2 Geben 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.
SW
... Signed Word (64bit)UW
... Unsigned Word (64bit)FP
... Floating Point (64bit)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.
Jit.ArgType.SW_SW_SW
Jit.ArgType.SW_SW
Jit.ArgType.SW
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. |
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
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 ...).
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.
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
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:
wir sehen uns.