Autor Thema: blubbOS Design  (Gelesen 22151 mal)

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #20 am: 16. November 2005, 15:29 »
Zitat von: Osbios
X = sin(y)/pi+pi*360
if Bedingung1 then A = X

kann vom Compiler ganz einfach in
Code:
if Bedingung1 then A = sin(y)/pi+pi*360
umgewandelt werden.

was machst du mit folgendem?
X = sin(y)/pi+pi*360
if Bedingung1 then A = X
print(X)

und diesem hier?
X = sin(y)/pi+pi*360
if X < 100 then A = X


und woher weisst du dass sin(y) nicht vielleicht mehr macht, als nur einen wert zurückzugeben? vielleicht schreibt es was auf den monitor oder so.

und was machst du hiermit:
X = 1/sin(y)
if Bedingung1 then A = X

unter bestimmten bedingungen wird sin(y) gleich 0 und es gibt eine divide-by-zero-exceptionen. was, wenn das programm in diesem fall auch genau eine exception braucht?
if Bedingung1 then A = 1/sin(y)
dieser code kann nur dann eine divide-by-zero-exception auslösen, wenn auch Bedingung1 erfüllt ist. die beiden code abschnitte sind also nicht gleichwertig.

Zitat von: Osbios
Aber was ist in einem solchen Fall:X = sin(y)/pi+pi*360
if Bedingung1 then A = X
if Bedingung2 then B = X


if Bedingung1 or Bedingung2 then X = sin(y)/pi+pi*360
if Bedingung1 then A = X
if Bedingung2 then B = X

oder besser:
if Bedingung1 or Bedingung2 then
     X = sin(y)/pi+pi*360
     if Bedingung1 then A = X
     if Bedingung2 then B = X
end if



aber was wenn Bedingung1 oder Bedingung2 sehr kompliziert sind? z.B. Bedingung1 ist "(x < 500 * y - sin(alpha) + cos(beta))" und Bedingung2 ist "(customfunction(a, b, c) == d)"

if (x < 500 * y - sin(alpha) + cos(beta)) or (customfunction(a, b, c) == d) then X = sin(y)/pi+pi*360
if (x < 500 * y - sin(alpha) + cos(beta))then A = X
if (customfunction(a, b, c) == d) then B = X


optimiert sähe das so aus:

bool TempBedingung1 =  (x < 500 * y - sin(alpha) + cos(beta))
bool TempBedingung2 = (customfunction(a, b, c) == d)
if TempBedingung1 or TempBedingung2 then
     X = sin(y)/pi+pi*360
     if TempBedingung1 then A = X
     if TempBedingung2 then B = X
end if


oder noch besser:

if (x < 500 * y - sin(alpha) + cos(beta)) then
     A = sin(y)/pi+pi*360
     if (customfunction(a, b, c) == d) then B = A
else if (customfunction(a, b, c) == d) then B = A
     B = sin(y)/pi+pi*360
end if


was aber tust du bei folgendem code (ich nehm den weil er simpler als die anderen ist):
X = customfunction(y)*sin(y)/pi+pi*360
if Bedingung1 then A = X

wenn du den so optimierst:
if Bedingung1 then A = customfunction(y)*sin(y)/pi+pi*360
wird customfunction() nur noch aufgerufen, wenn Bedingung1 erfüllt ist. was ist wenn customfunction() mehr macht als nur den rückgabewert zu schreiben?

sowas?
if Bedingung1 then A = customfunction(y)*sin(y)/pi+pi*360 else (void)customfunction(y)

oder das?
temp = customfunction(y)
if Bedingung1 then A = temp*sin(y)/pi+pi*360


wie du siehst gibt es milliarden von sonderfällen. du hast ein paar erwischt, die vielleicht optimierbar sind. aber wirst du die auch in der echten welt an den stellen, die wirklich performancekritisch sind, antreffen?

ich habe mir auch ein paar gedanken gemacht, wie ich einen (normalen) compiler optimieren könnte, und bin zu dem entschluss gekommen, dass meine ideen höchstens den code von unfähigen programmieren optimieren könnten (wenn überhaupt) und _wirkliche_ optimierungen nicht vollbringen werden können. ich habe mich nur soweitgehend damit beschäftigt, bis ich davon überzeugt war, dass sowas anderen zu überlassen und mich mit interessanteren dingen zu beschäftigen sinnvoller ist. vielleicht hast du ja bessere ideen und mehr ausdauer als ich. mit deinen beispielen wirst du allerdings nicht sehr weit kommen. sie beschränken sich auf ein paar mathematische formeln. die wenigsten programme bestehen nur aus formeln. es gibt noch schleifen, globale variablen, funktionsübergreifene strukturen (einfachstes beispiel: parameter), rekursionen, etc. diese sachen sind deutlich schwerer zu optimieren. richtig spaßig wirds auch erst, wenn du für multicore-cpus optmierst. ;)
Dieser Text wird unter jedem Beitrag angezeigt.

Osbios

  • Beiträge: 247
    • Profil anzeigen
Gespeichert
« Antwort #21 am: 16. November 2005, 18:27 »
Zitat von: SSJ7Gohan
Naja, ich würde erstmal die Sprache selbst festlegen, bevor ich das Ausgabeformat festlege.

Ach, und bei x86 Prozessoeren hat man auch erst die Sprachen festgelgt und dann die CPU entwickelt?  :wink:
Nein kommt nicht in Frage, zuerst ein solides Ausgabeformat nach dem sich dann die Sprachen zu richten haben!

Zitat von: SSJ7Gohan
Die VM würde ich auf einer virtuellen Maschine mit beliebig vielen Registern aufbauen und nicht auf einem Stack wie in Java oder .NET. Das hat den Vorteil, das man einige Optimierungen besser vornehmen kann, wie z.B. Typchecks zur Compilierzeit. Die Register kann man dann teilweise 1:1 auf die realen Register übertragen und muss nicht den Umweg über Stackzugriffe nehmen.

Ich denke mehr an eine System das ohne beides auskommt.

Zitat von: SSJ7Gohan
Ob du Little oder Big Endian benutzt ist nicht wirklich wichtig, der JIT muss dann zwar eventuell beim Laden der Anwendung etwas rumshiften, aber das solle keinen bemerkbaren Performanceunterschied geben.

Hmm ok, dass dürfte eigentlich nicht soviel Zeit benötigen.

Zitat von: Legend
@Osbios: Für den 2. Fall hast du wahrscheinlich die optimale Möglichkeit (ohne das man irgendwelche Verteilungen vom Eintreten der Bedingungen kennt) schon hingeschrieben!

Nein habe ich nicht, aber sie währe (meine Meinung nach):if Bedingung1 then
{
 X = sin(y)/pi+pi*360
 A = X
 if Bedingung2 then B = X
}
else
 if Bedingung2 then B = sin(y)/pi+pi*360



Zitat von: Legend
Du hast aber einen grundlegenden Unterschied schon mal zum Java Bytecode drin der schon mal ein potenzieller Grund für ein neues Format darstellt: Du willst alles für maximales Tempo drin. Der Java Bytecode sollte in erster Linie kompakt sein, damit die Programme schnell übers Internet übertragen werden können.

Allerdings ist deine Forderung nach einem halbwegs universellem, sicherem Bytecode ähnlich der Anforderungen des Bytecodes von .NET.

Nunja, ich will damit auch Treiber Schreiben und die im Ring 0 laufen lassen. Da muss schon etwas Geschwindigkeit und Sicherhheit vorhanden sein.

Zitat von: Legend
Nun ja, vieles kann man zur Ladezeit machen. Programme die Daten über das Internet verschicken wollen werden aber sich aber darauf verlassen müssen das die Daten die sie zur Laufzeit erzeugen die richtige Reihenfolge haben.

Dafür gibt es in standard Netzerk Libs, Systemunabhängige Funktionen, die dafür sorgen, dass für bestimmte dinge nur eine Bytereihenfolge im Netzwerk benutzt wird.

Zitat von: Legend
Ich denke optimales Tempo kann man wahrscheinlich erreichen indem man Instruktionen die möglichst nahe an klassichen CPUs liegen anbietet, jedoch ohne die Sicherheit zu gefährden, sonst kann man sich ne VM auch praktisch sparen!

Das mit der Sicherheit dürfte uns allen klar sein, aber ich bin nicht der Meinung, dass man führ eine hohe Geschwindigkeit, CPU nahe Instruktionen nutzen sollte.


@PorkChicken:X = sin(y)/pi+pi*360
if Bedingung1 then A = X
print(X)
X = sin(y)/pi+pi*360
if X < 100 then A = X

Bei diesen beiden Varianten wird X explizit von einer folgenden Berechnung benötigt, daher spielt es keine Rolle ob sie woanders eventuell benötigt oder nicht benötigt wird.

Zitat von: PorkChicken
und woher weisst du dass sin(y) nicht vielleicht mehr macht, als nur einen wert zurückzugeben? vielleicht schreibt es was auf den monitor oder so.

Ich weiß es weil ich alles, bis auf die Kernelfunktionen, in Module unterbringen werde. Man kann daher relativ einfach ein neues Modul den den schon vorhandenen Modulen verschmelzen.

Als Beispiel: mal angenommen man hätte 2 Module, wobei das eine auf die angebotenen Funkionen des anderen zugreift.

X = 8663
Funktion XY
{
 return X
}

A = 1337
B = A + XY


Hierbei kann man erkennen das XY nur eine Konstante zurück gibt und man dann zwei Konstandten addieren kann:
B = 10000

Das ist natürlich ein sehr einfaches Beispiel, es soll auch nur zeigen, dass man sehr wohl wissen kann was die einzelnen Funktionen aufrufen wenn man ein ganzes System darauf aufbaut.

Zitat von: PorkChicken
und was machst du hiermit:
X = 1/sin(y)
if Bedingung1 then A = X

unter bestimmten bedingungen wird sin(y) gleich 0 und es gibt eine divide-by-zero-exceptionen. was, wenn das programm in diesem fall auch genau eine exception braucht?
if Bedingung1 then A = 1/sin(y)

dieser code kann nur dann eine divide-by-zero-exception auslösen, wenn auch Bedingung1 erfüllt ist. die beiden code abschnitte sind also nicht gleichwertig.

Ein Programm das eine exception brauch? Ich würde vorschreiben das man Teiler auf <> 0 Testen müsste um unvorhergesehene "Porgrammeigenschaften" zu vermeiden. Nein, ehrlich gesagt weiß ich noch nicht wie man X/0 handhaben sollte.

if Bedingung1 or Bedingung2 then
     X = sin(y)/pi+pi*360
     if Bedingung1 then A = X
     if Bedingung2 then B = X
end if

Ich würde die doppelten if then vermeiden. Hab ich zwar schon weiter oben hingeschrieben, aber hier nochmal:if Bedingung1 then
{
 X = sin(y)/pi+pi*360
 A = X
 if Bedingung2 then B = X
}
else
 if Bedingung2 then B = sin(y)/pi+pi*360


Ich hoffe das mit dem Modlen, als System und Optimierungsbasis, ist rübergekommen.

Nochmal in Kurzform:
Es gibt nur ganz wenige Funktionen die direkt vom JIT bzw. Kernel zur Verfügung gestellt werden. Alle anderen Funktionen werden von Modulen bereitgestellt.
Der JIT kann auf die Module übergreifende Optimierungen vornehmen.
Wieder ein kleines Beispiel anhand eines Treibermodules und eines Programmmodules (das keine I/O Rechte besitzt).

Wenn das Programm auf eine Funktion des Treibermodules zugreift, die auf einen Port schreibt, kann es sein, dass der JIT zur Optimierung den CALL-Aufruf umgeht indem er einfach den entsprächenden Code des Treibers im Laufzeitcode des Programms einfügt.
Dann ist im Laufzeitcode des Programms ein I/O Zugrif obwohl das Programm garkeine I/O berechtigung hat.
db 0x55AA

SSJ7Gohan

  • Beiträge: 398
    • Profil anzeigen
Gespeichert
« Antwort #22 am: 16. November 2005, 18:35 »
Was für eine VM hast du denn im Sinn, die nicht Register oder Stackbasiert ist?

Legend

  • Beiträge: 635
    • Profil anzeigen
    • http://os.joachimnock.de
Gespeichert
« Antwort #23 am: 16. November 2005, 19:10 »
Eine Standard Lib, na ja, Little und Big Endian in den Datenpaketen selber, welche von der Anwendung generiert werden, wird damit aber schon schwierig.

Mit CPU-fernen Instruktionen wirst du mehr nachbauen müssen und das ganze wird daher wohl auch langsamer. Instruktionen die komplexe Operationen in einem Schritt machen kann man auch nicht bei Bedarf aufteilen und anpassen.
*post*

Osbios

  • Beiträge: 247
    • Profil anzeigen
Gespeichert
« Antwort #24 am: 16. November 2005, 20:53 »
Zitat von: Legend
Eine Standard Lib, na ja, Little und Big Endian in den Datenpaketen selber, welche von der Anwendung generiert werden, wird damit aber schon schwierig.

Mit CPU-fernen Instruktionen wirst du mehr nachbauen müssen und das ganze wird daher wohl auch langsamer. Instruktionen die komplexe Operationen in einem Schritt machen kann man auch nicht bei Bedarf aufteilen und anpassen.

Das ist der nachteil von Cisc.
db 0x55AA

Legend

  • Beiträge: 635
    • Profil anzeigen
    • http://os.joachimnock.de
Gespeichert
« Antwort #25 am: 17. November 2005, 00:55 »
Durchaus, auch wenn manche Operation durchaus schneller in Hardware gegossen werden kann als aus kleineren zusammen gebaut werden kann. Da muss man halt ne Balance finden.

Der Java Compiler kaut dem JIT eigentlich nichts vor, er optimiert kaum - es gibt auch kaum was zu optimieren. Das was der ausspuckt ist so unspezifisch in der Art "Erstelle Objekt", "Rufe Funktion XY virtuell auf" das es da auch nichts zur Kompilierzeit gibt was man tun könnte.
*post*

SSJ7Gohan

  • Beiträge: 398
    • Profil anzeigen
Gespeichert
« Antwort #26 am: 17. November 2005, 17:19 »
So, mein Java-Compiler ist nun fast vollständig (von den Instructions her, Optimierungen fehlen noch), bis auf einige Einschränkungen:
-> lookupswitch und tableswitch Instructions, die lassen sich aber auch sehr leicht einbauen.
-> monitorenter und monitorleave, solle auch nicht schwer sein.
-> Double, Float und Long Datentyp. Ich weiß nicht wie ich 64 Bit Longs dividieren soll, siehe Thread in Lowlevel Coding.
-> Kein Classfile Verifizierer.
-> Kann im Moment nur AOT nasm kompatiblen ASM-Code ausgeben, und nicht JIT x86-Code ausgeben
-> Wie gesagt keine Optimierungen, der Code den er im Moment generiert sieht etwa so aus:
public static void main (String[] args) {
int zahl = 500;
zahl -= 50;
zahl /= 10;

System.out.println("Hello World");
System.out.println(zahl);
}

Wird durch meinen JIT zu (nachdem es von javac von Sun zu einem classfile kompiliert wurde und dann von meinem "JIT" zu ASM kompiliert wurde):
[extern java_new] ;macht das gleiche wie malloc

[global vmtest_47test__class]
vmtest_47test__class:
dd vmtest_47test__clist ;pointer auf superclass-liste
dd vmtest_47test__ilist ;pointer auf interface-liste
dd vmtest_47test__fields ;pointer auf statische felder

vmtest_47test__vtable: ;virtual method table
[extern java_47lang_47Object__m0]
dd java_47lang_47Object__m0
dd vmtest_47test__m0
dd vmtest_47test__m1

vmtest_47test__clist:
dd 2 ;anzahl der klassen, die diese klasse ableitet
[extern java_47lang_47Object__class]
dd vmtest_47test__class ;pointer auf die klasse
dd java_47lang_47Object__class

vmtest_47test__ilist:
dd 0 ;anzahl der interfaces die diese klasse implementiert

vmtest_47test__fields: ;statische felder

;der string "Hello World" als UTF8 Array
vmtest_47test__c23:
dd 11 ;anzahl der elemente in einem array
dw 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100 ;elemente

;funktion no.0 der klasse vmtest.test (Konstruktor)
[global vmtest_47test__m0]
vmtest_47test__m0:
; stacksize: 4 bytes
push ebp
mov ebp, esp
sub esp, 4

m0__i0:
; instLoad
;lokale variable auf den stack pushen
;parameter0 ist immer der this-pointer
mov eax, [ebp + 8]
push eax

m0__i1:
; instInvoke
[extern java_47lang_47Object__class]
.invoke:
mov eax, [esp + 0] ;pointer auf objekt holen
mov ebx, [eax] ;dword an offset 0 des objekts = pointer zu klasse holen
mov ecx, [ebx] ;anzahl elemente in der klassenliste holen (offset 0 in der klasse = pointer zu klassenliste; offset 0 in der klassenliste = anzahl klassen)
.search:
mov eax, [ecx * 4 + ebx] ;gucken ob klasse an der position die gesuchte ist
cmp eax, java_47lang_47Object__class
jne .next ;wenn nicht, dann nächste klasse überprüfen
call [eax + 12] ;sprung in die vtable der klasse
jmp .done ;fertig
.next:
loop .search ;nächste klasse
.unresolvable: ;hier sollte noch ne exception hin
.done:
add esp, 4 ;elemente vom stack popen

m0__i4:
; instReturn
mov esp, ebp
pop ebp
ret

; methode no.1 der klasse (main)
[global vmtest_47test__m1]
vmtest_47test__m1:
; stacksize: 8 bytes
push ebp
mov ebp, esp
sub esp, 4

m1__i0:
; instConst
mov eax, 500
push eax

m1__i3:
; instStore
pop eax
mov [ebp - 4], eax

m1__i4:
; instIncrement
mov eax, [ebp - 4]
add eax, -50
mov [ebp - 4], eax

m1__i7:
; instLoad
mov eax, [ebp - 4]
push eax

m1__i8:
; instConst
mov eax, 10
push eax

m1__i10:
; instArithmetic
pop ebx
pop eax


xor edx, edx
idiv ebx
push eax

m1__i11:
; instStore
pop eax
mov [ebp - 4], eax

m1__i12:
; instFieldGet
;statisches feld holen
[extern java_47lang_47System__f0]
mov eax, [java_47lang_47System__f0]
push eax

m1__i15:
; instPop
add esp, 4

m1__i16:
; instNew
;speicher allokieren
;anzahl bytes in eax
mov eax, 8
call java_new
mov dword [eax], java_47lang_47String__class
push eax

; instDuplicate
mov eax, [esp]
push eax

; instConst
mov eax, vmtest_47test__c23
push eax

;Konstruktor aufrufen, siehe oben
; instInvoke
[extern java_47lang_47String__class]
.invoke:
mov eax, [esp + 4]
mov ebx, [eax]
mov ecx, [ebx]
.search:
mov eax, [ecx * 4 + ebx]
cmp eax, java_47lang_47String__class
jne .next
call [eax + 20]
jmp .done
.next:
loop .search
.unresolvable:
.done:
add esp, 8

m1__i18:
;statische funktion aufrufen
; instInvoke
[extern java_47io_47PrintStream__m1]
call java_47io_47PrintStream__m1
add esp, 4

m1__i21:
;statisches feld holen und auf den stack pushen
;der javac hat den code hier so generiert, man könnte ihn auch weglassen, da im Moment System.out.println eine statische Funktion ist, und man den Pointer auf System.out nicht braucht.
; instFieldGet
[extern java_47lang_47System__f0]
mov eax, [java_47lang_47System__f0]
push eax

m1__i24:
; instPop
add esp, 4

m1__i25:
; instLoad
mov eax, [ebp - 4]
push eax

m1__i26:
; instInvoke
;statische funktion aufrufen
;und einen parameter vom stack popen
[extern java_47io_47PrintStream__m2]
call java_47io_47PrintStream__m2
add esp, 4

m1__i29:
; instReturn
mov esp, ebp
pop ebp
ret

Ich hab den Code mal etwas kommentiert.

Einige Instructions überprüfen auch nicht auf Null-Pointer, das muss ich noch einbauen.

Die Geschwindigkeit eines so simplen JITs hat mich schon beeindruckt, er ist auch ohne Optimierung nur 5-15% langsammer als Suns Hotspot VM, und hat einen erheblich kleineren Speicherbedarf.^^

Ich werden in der nächsten Zeit (ich denke so in einer Woche) mal den schon vorhandenen Code unter der GPL online stellen und hoffen, das sich hier jemand an dem Projekt mitbeteiligen will :)
Ich werde dann mit der Programmierung der Treiber mit Java beginnen, und irgentwann auch x86-code output in den kompiler einbauen und einen JIT-Linker schreiben.

Osbios

  • Beiträge: 247
    • Profil anzeigen
Gespeichert
« Antwort #27 am: 17. November 2005, 18:35 »
Zitat von: Legend
Durchaus, auch wenn manche Operation durchaus schneller in Hardware gegossen werden kann als aus kleineren zusammen gebaut werden kann. Da muss man halt ne Balance finden.

Das ist falsch.
http://de.wikipedia.org/wiki/CISC

Zitat von: Legend
Der Java Compiler kaut dem JIT eigentlich nichts vor, er optimiert kaum - es gibt auch kaum was zu optimieren. Das was der ausspuckt ist so unspezifisch in der Art "Erstelle Objekt", "Rufe Funktion XY virtuell auf" das es da auch nichts zur Kompilierzeit gibt was man tun könnte.

Langsam bekomme ich auch ein besseres Bild von Java und kann nur sagen, dass sich meine Philosophie stark davon unterscheidet. Ich tendiere immer mehr in die Richtung komplette Erzeugung von x86 Code aus den Modulen, da Optimierungen wie Hotspot den CPU Cache zerschießen, was wiederum einiges an Performanceverlust nach sich zieht.

@SSJ7Gohan:
Nur 5-15 % langsammer als dieses Hotspotteil? Oo
Das muss ich mir dann auch mal angucken.
db 0x55AA

SSJ7Gohan

  • Beiträge: 398
    • Profil anzeigen
Gespeichert
« Antwort #28 am: 17. November 2005, 18:45 »
Bei VMs ist aber ein komplexes Instructionsset besser, auf RISC Prozessoren kann man dann ja mit dem JIT die komplexen Instructions zerlegen und der JIT kann für prozessorspezifische Instructions wie MMX, SSE usw. optimieren.

Legend

  • Beiträge: 635
    • Profil anzeigen
    • http://os.joachimnock.de
Gespeichert
« Antwort #29 am: 26. November 2005, 17:43 »
Nun ja, Osbios, ganz so falsch ist das nicht. Der extreme CISC Ansatz ist zwar veraltet, der extreme RISC Ansatz auch. Wenn man es mit RISC übertreibt bestehen die Programme nachher aus so vielen Instruktionen das auch eine höhere MISP Leistung da nicht gegensteuern kann. Da brauch man ne Balance. Ein Beispiel wär der Extremfall das eine Maschine nur eine Instruktion kann, da gibt es eine mit der man eigentlich alles programmieren kann. Aber dann brauchen die einfachsten Sachen schon ellenlange Codelistings.

@SSJ7Gohan: In der Theorie ja. Du lässt den JIT die Arbeit übernehmen, weil mit einem Instruktionsset wie bei Java der javac eigentlich auch gar nichts machen kann. Du musst nur aufpassen das dein JIT dann nachher nicht so viel tun muss das er mehr Zeit bräuchte als Code mit einem einfacherem Instruktionsset. Das ist zumindestens meine Meinung, ob da irgendwas bewiesen ist in der Theorie weiss ich ehrlich gesagt nicht.
Ich glaube das dürfte ein Problem von Hotspot sein. Das misst ja verschiedene Performancedaten zur Laufzeit, das klingt wie ein Lauf mit nem Profiler. Den kann Hotspot natürlich effizienter einbauen als man dies bei C++ Code wohl machen kann, aber ich weiss nicht ob dies nicht evtl. stark auf die Performance gehen kann. ;)
*post*

Osbios

  • Beiträge: 247
    • Profil anzeigen
Gespeichert
« Antwort #30 am: 28. November 2005, 12:49 »
Ja stimmt scho, sonst kann man auch gleich mit Brainfuck anfangen.   ](*,)
Zitat
Ein Beispiel wär der Extremfall das eine Maschine nur eine Instruktion kann, da gibt es eine mit der man eigentlich alles programmieren kann.

Nein das glaube ich nicht. :P
Min. 2 braucht man schon!
db 0x55AA

Legend

  • Beiträge: 635
    • Profil anzeigen
    • http://os.joachimnock.de
Gespeichert
« Antwort #31 am: 28. November 2005, 14:33 »
Also mit Substract and branch on less then zero konnte man schon multiplizieren und schleifen bauen. if, addieren usw. geht auch ... ;)
*post*

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #32 am: 28. November 2005, 17:24 »
Zitat von: Legend
Also mit Substract and branch on less then zero konnte man schon multiplizieren und schleifen bauen. if, addieren usw. geht auch ... ;)
und es lässt sich mathematisch beweisen, dass so eine one-instruction-maschine turing-vollständig ist. also alles machen kann, was andere cpus auch können.
Dieser Text wird unter jedem Beitrag angezeigt.

Legend

  • Beiträge: 635
    • Profil anzeigen
    • http://os.joachimnock.de
Gespeichert
« Antwort #33 am: 28. November 2005, 17:41 »
Bloß glaube ich das man mit ein paar mehr Instruktionen ne schnellere CPU bauen kann ...
*post*

SSJ7Gohan

  • Beiträge: 398
    • Profil anzeigen
Gespeichert
« Antwort #34 am: 28. November 2005, 17:46 »
So, ich hab jetzt den JIT-Compiler nochmal überarbeitet.
Er hat jetzt einen Classfile Verifizierer, der die Typchecks wärend Compilierens vornimmt. Da der Compiler wegen des Verifizierers nun weiß, auf welche Elemente des Stacks er zugreift, kann der Stack auf eine Maschiene mit unendlich vielen Registern gemappt werden.

Beispiel:
Java Code zum addieren von zwei Ints und zurückgeben des Ergebnisses:
// bei java werden die parameter einer methode als die ersten paar lokalen variablen übergeben.
// bei nicht-statischen Methoden enthällt die lokale Variable 0 eine Referenz auf das Object (this-pointer)
// ich stelle lokale vars mal als v<n> da.
iload v1 //pusht lokale int variable 1 auf den stack
iload v2 //pusht lokale int variable 2 auf den stack
iadd // popt 2 int-operanden vom stack, addiert sie und pusht das ergebniss
ireturn // popt einen intwert und gibt ihn zurück


Mein Compiler wandelt das um in:
// r<n> sind hier die register
move int v1, r0 //lokale variable 1 in register 0
move int v2, r1 //lokale variable 2 in register 1
add int r0, r0, r1 //addiere r0 und r1, ergebniss in r0
return int r0 // gebe r0 zurück


Dieser Zwischencode kann natürlich viel besser optimiert werden als der Java-Bytecode, und es können fast alle herkömmlichen Optimierungen angewandt werden. Eventuell könnte man auch sowas wie HotSpot einbauen und langsame Optimierungen nur bei Bedarf vornehmen. Dann müsste man aber für das Onstack-Replacement extream viele Informationen speichern, und man sieht ja wie viel Speicher die HotSpot VM verbraucht.

 

Einloggen