Zusammenfassung der Sprung‐Instruktionen
Instruktion Beispiel Bedeutung des Beispiels
Ganzz ahlig
beq, bne beq $s1, $s2, x Springe nach x wenn $s1 =
$s2
j j label Springe immer nach
„label“
jr jr $s1 Springe nach in $s1
gespeicherte Adresse slt, slti, sltu, sltiu slt $s1,$s2,$s3 $s1=1 wenn $s2<$s3
(signed)
Floa ting ‐ Po in t bc1t, bc1f bc1t label Springe nach „label“ wenn letzter Floating‐Point‐
Vergleich true ergab c.x.s (x=eq, lt, le),
c.x.d (x=eq, lt, le)
c.eq.s $f1, $f2 Teste auf $f1=$f2 (single
precision)
Quiz
Im folgenden Codeabschnitt soll nach continue gesprungen werden, wenn $s1 kleiner gleich $s2 ist:
loop: ...
j loop continue: ...
Tipp: wir brauchen
beq, slt und bne
Und noch ein Quiz
Annahme:
$s1 = 0xFFFFFFFF
$s2 = 0x00000001
In welchem der beiden Code‐Abschnitte wird gesprungen?
...
slt $t0,$s1,$s2
bne $t0,$zero, lab ...
...
lab: ...
...
...
sltu $t0,$s1,$s2 beq $t0,$zero, lab ...
...
lab: ...
Sprung: ...
ja nein
Sprung:
ja nein
Prozeduren
Das Prinzip von Prozeduren
Hauptprogramm:
. . .
x = 2*fakultät(10) .
. . Programm‐
abarbeitung
Prozedur mit dem Namen fakultät
. .
Berechne n!
. . Prozeduraufruf
mit Parameter n=10
Prozedurrücksprung mit Ergebnis n!
Randbemerkung: was ist n! ?
Programmzähler und Rücksprungadresse
0x0040000 : 0011 ... 1001 0x0040004 : 0001 ... 1000 0x0040008 : 1001 ... 1111 0x004000c : 1011 ... 0001 0x0040010 : 0011 ... 1000 0x0040014 : 1001 ... 1111 0x0040018 : 0001 ... 0001 0x004001c : 1011 ... 0011 0x0040020 : 1011 ... 1100 0x0040024 : 0101 ... 1001 0x0040028 : 1000 ... 0011 0x004002c : 1000 ... 1011 0x0040030 : 0001 ... 1100 0x0040034 : 1001 ... 1111 0x0040038 : 1001 ... 1111
Startadresse des Hauptprogramms Aufruf der Prozedur
Register $pc
Prozedur Fakultät
Rücksprung aus der Prozedur
Adresse Maschineninstruktion
Register $ra
Assembler‐Beispiel
Hauptprogramm: ...
0x004000c addi $a0,$zero,10 # setze $a0 auf 10 0x0040010 jal Fakultaet # rufe Prozedur auf 0x0040014 sll $v0,2 # Berechne Rückgabe*2
...
Fakultaet:
# Die Prozedur Fakultaet
# erwartet den Übergabeparameter in $a0
# gibt das Ergebnis in $v0 zurück
0x0040024 ... # Berechnung der Fakultät ... # Das Ergebnis sei in $a0 0x004002c add $v0,$a0,$zero # speichere Ergebnis in $v0 0x0040030 jr $ra
Register $pc Register $ra Register $a0 Register $v0
Problem
Hauptprogramm:
. .
$s0 = 42 vor Aufruf
. .
x = 2*fakultät(10) .
.
Annahme immer noch $s0=42 !?!
. . Programm‐
abarbeitung
Prozedur mit dem Namen fakultät
. .
Berechne n!
Überschreibe dabei
$s0 mit 114 .
. Prozeduraufruf
mit Parameter n=10
Prozedurrücksprung mit Ergebnis n!
Register $s0
Lösung
Hauptprogramm:
. .
$s0 = 42 vor Aufruf
.
x = 2*fakultät(10) .
.
Es gilt immer noch $s0=42 !!!
. .
Prozedur mit dem Namen fakultät
Rette Inhalt von $s0 auf dem Stack
.
Berechne n!
($s0 wird dabei überschrieben) Restauriere Inhalt von $s0 mittels Stack
. Register $s0 .
0x7fffffff : ...
0x7ffffffe : ...
0x7ffffffd : ...
0x7ffffffc : ...
0x7ffffffb : ...
0x7ffffffa : ...
0x7ffffff9 : ...
0x7ffffff8 : ...
0x7ffffff7 : ...
0x7ffffff6 : ...
0x7ffffff5 : ...
0x7ffffff4 : ...
0x7ffffff3 : 0x7ffffff2 : 0x7ffffff1 : 0x7ffffff0 : 0x7fffffef : 0x7fffffee : 0x7fffffec :
.
Register $sp
Stack
Assembler‐Beispiel
Fakultaet: addi $sp, $sp, -4 # erhöhe Stack-Pointer um ein Word sw $s0, 0($sp) # rette $s0 auf Stack
# berechne Fakultät
# $s0 wird dabei überschrieben
lw $s0, 0($sp) # restauriere $s0 vom Stack addi $sp, $sp, 4 # dekrementiere Stack-Pointer jr $ra # Springe zurück zum Aufrufer
...
0x7ffffff7 : ...
0x7ffffff6 : ...
0x7ffffff5 : ...
0x7ffffff4 : ...
0x7ffffff3 : 0x7ffffff2 : 0x7ffffff1 : 0x7ffffff0 : 0x7fffffef : 0x7fffffee : 0x7fffffec :
...
Register $s0 Register $sp
(sei $s0=0xffef2314 vor Aufruf von Fakultaet)
Registerkonvention
Name Nummer Verwendung Wird über Aufrufgrenzen bewahrt?
$zero 0 Konstante 0 n.a.
$at 1 nein
$v0‐$v1 2‐3 Prozedur‐Rückgabe nein
$a0‐$a3 4‐7 Prozedur‐Parameter nein
$t0‐$t7 8‐15 Temporäre nein
$s0‐$s7 16‐23 Temporär gesicherte ja
$t8‐$t9 24‐25 Temporäre nein
$k0‐$k1 26‐27 nein
$gp 28 ja
$sp 29 Stack‐Pointer ja
$fp 30 ja
$ra 31 Return‐Adresse ja
Rekursive Prozeduren
Hauptprogramm:
. . .
x = 2*fakultät(10) .
. .
Prozedur mit dem Namen fakultät
. .
Berechne n!
. . Prozeduraufruf
Letzter Rücksprung mit Gesamtergebnis
Wenn Rekursionsende noch nicht erreicht, dann erneuter Prozeduraufruf
(„mit kleinerem Parameter“)
Alle vorigen Rücksprünge
Verwendung des Stacks
Haupt‐
programm
Fakultät
Fakultät
Fakultät
Rekursionsende Stack
$sp
Fakultät
Assembler‐Beispiel
Auf der nächste Folie wollen wir die Fakultät n! nach folgendem Algorithmus berechnen
int fact (int n) { if (n < 1) {
return 1;
}
else {
return n * fact(n-1);
}
}
Assembler‐Beispiel
# Berechnung der Fakultät von n (also n!)
# Eingabeparameter n ist in $a0 gespeichert
# Rückgabeparameter der Berechnung ist $v0
fact: addi $sp, $sp, -8 # push Stack-Pointer um zwei Word sw $ra, 4($sp) # rette Rücksprungadresse auf Stack sw $a0, 0($sp) # rette Eingabeparameter auf Stack slti $t0, $a0, 1 # teste n < 1
beq $t0, $zero, L1 # wenn n >= 1 dann gehe nach L1 addi $v0, $zero, 1 # es wird 1 zurückgegeben
addi $sp, $sp, 8 # pop Stack-Pointer um zwei Word jr $ra # Rücksprung zum Prozedur-Aufrufer L1: addi $a0, $a0, -1 # setze Argument auf n-1
jal fact # rufe fact rekursiv mit n-1 auf
lw $a0, 0($sp) # restauriere Eingabeparam vom Stack lw $ra, 4($sp) # restauriere Rücksprungadr vom Stack addi $sp, $sp, 8 # pop Stack-Pointer um zwei Word
Procedure‐Frame und Frame‐Pointer
Null bis vier Argument‐
Register ($a0‐$a3) Return‐Adresse $ra Null bis acht Saved‐
Register ($s0‐$s7) Möglicher zusätzlicher
Speicher der während der Ausführung der
Prozedur benötigt wird und nach Prozedurrückkehr nicht
mehr
Hohe Adresse
Niedrige Adresse Stack‐Pointer $sp
Frame‐Pointer $fp
Unbenutzer Stack‐Speicher
Benutzer Stack‐Speicher
Procedure‐Frame
Argument 5 Argument 6
Speicherbelegungskonvention
Reserviert
Text (d.h. das Programm in Form von Maschinen‐
instruktionen) Statische Daten (z.B.
Konstanten oder statische Variablen)
Stack
Der Heap speichert alle dynamischen (d.h.
während der Laufzeit angelegten) Daten.
Heap
0x00400000 0x10000000 0x10008000 0x7ffffffc
0x00000000
$pc
$sp
$gp
Die Sprunginstruktionen zusammengefasst
Instruktion Beispiel Beduetung
j j Label $pc = Sprungadresse
jal jal Label $ra = $pc+4, $pc = Sprungadresse jr jr $s1 $pc = Registerinhalt
$pc ist der Program‐Counter
$ra ist das 32te CPU‐Register
Schwieriges Quiz
Rmul: addi $sp, $sp, -4 # rette Rücksprungadresse sw $ra, 0($sp) #
add $t0, $a0, $zero # $t0 = n addi $a1, $a1, -1 # m = m - 1
beq $a1, $zero, End # wenn m=0, dann Ende jal Rmul # $v0 = Rmul(n,m-1) add $t0, $t0, $v0 # $t0 = $t0 + $v0 End: add $v0, $t0, $zero # $v0 = $t0
lw $ra, 0($sp) # springe zurück addi $sp, $sp, 4 #
Rekursive Berechnung von n*m in der Form Rmul(n,m) = n+Rmul(n,m‐1) Eingabeparameter: $a0 für n und $a1 für m>0
Rückgabeparameter: $v0 Temporäre Berechnung: $t0
$a0, $a1, $v0, $t0 brauchen nach Registerkonvention alle nicht über Aufrufgrenzen bewahrt zu werden.
Registerkonvention, dass ein Register über Aufrufgrenzen nicht bewahrt wird bedeutet:
• Register darf nach belieben überschreiben werden.
• Register muss vor dem Rücksprung nicht restauriert werden.
• Prozedur muss aber das Register für sich selbst sichern!
• Beispiel:
Verwendung von $t0 Sichern von $t0
Aufruf einer anderen Prozedur Restaurieren von $t0
• Ausnahme: wir wissen genau, dass das Register in keiner der aufgerufenen Prozeduren verwendet wird.
• Prozeduren, die keine anderen aufruft muss natürlich temporäre Register nie sichern.
Prozedur, die keine andere aufruft nennt man auch Leaf‐Procedure
Bemerkung zu vorigem Quiz
32‐Bit‐Konstanten und Adressierung
Immediate kann nur 16‐Bit lang sein
Erinnerung: Laden einer Konstante in ein Register addi $t0, $zero, 200
Als Maschinen‐Instruktion:
001000 00000 01000 0000000011001000 addi $zero $t0 200
Inhalt von $t0 nach Instruktionsausführung:
00000000|00000000|00000000|11001000 Byte 3 Byte 2 Byte 1 Byte 0
Also, Byte 0 und Byte 1 von $t0 kann man so mit einem Wert initialisieren.
Was ist mit Byte 2 und 3?
Lösung: Load‐Upper‐Immediate
Aufgabe: Lade folgende 32‐Bit‐Konstante in Register $s0
0000 0000 0011 1101 0000 1001 0000 0000 Neuer Befehl: Lade 16‐Bit‐Wert in obere 16 Bits von Register $s0
lui $s0, 61 # 61 dezimal = 0000 0000 0011 1101 binär Registerinhalt von $s0 ist danach:
0000 0000 0011 1101 0000 0000 0000 0000 Füge anschließend die unteren 16 Bits ein:
ori $s0, $s0, 2304 # 2304 dez = 0000 1001 0000 0000 bin Registerinhalt von $s0 ist danach:
0000 0000 0011 1101 0000 1001 0000 0000
Immediate in Branches sind nur 16 Bit lang
Erinnerung: Bedingter Sprung
bne $s0, $s1, Exit # gehe nach Exit, wenn $s0!=$s1 Als Maschinen‐Instruktion (I‐Typ):
000101 10001 10000 1010001011001000 bne $s1 $s0 Exit (immediate) Also, nur 16‐Bit für die Branch‐Adresse verfügbar!
Konsequenz, wenn Exit eine absolute Adresse wäre:
0x00000000 : ...
...
0x0000EFF0 : bne $s0, $s1, 0x00010000 # ?!?
...
0x0000FFFF : ...
0x00010000 : ...
Lösung: Branches sind PC‐Relativ
Betrachte folgendes Beispiel (Adressen seien Dezimal dargestellt):
80012 : bne $s0, $s1, Exit 80016 : addi $s3,$s3,1
80020 : j Loop 80024 : Exit:
Label Exit könnte doch nur die Sprungdifferenz 80024‐80012 = 12 codieren, d.h.
80012 : bne $s0, $s1, 12 80016 : addi $s3,$s3,1 80020 : j Loop
80024 : Exit:
Noch besser, codiere nur die Anzahl zu überspringender Befehle (also 3 = 12/4):
80012 : bne $s0, $s1, 3 80016 : addi $s3,$s3,1 80020 : j Loop
80024 : Exit:
(Erinnerung: Instruktionen haben immer Word‐Länge, also 32‐Bit)
Lösung: Branches sind PC‐Relativ
Sei der Program‐Counter $pc= 80012 und $s0!=$s1 sei erfüllt:
80012 : bne $s0, $s1, 3 80016 : addi $s3,$s3,1 80020 : j Loop
80024 : Exit:
Auf welchen Wert muss der Program‐Counter als nächstes gesetzt werden?
Achtung: obiges Beispiel ist nicht korrekt. MIPS setzt $pc=$pc+4 schon vor Abarbeitung des Befehls. Wie muss damit Zeile 80012 korrekt lauten?
Immediate in Jumps sind nur 26 Bit lang
Erinnerung: Unbedingter Sprung
j Exit # spinge immer nach Exit Als Maschinen‐Instruktion (J‐Typ):
000010 10001100001010001011001000 j Exit (address)
Also, nur 26 Bit für die Adresse verfügbar! Also folgender Adressbereich:
von 0x00000000 bis 0x03FFFFFF Konsequenz, wenn Exit eine absolute Adresse wäre:
0x10000000 : j 0x10000010 # ?!?
...
0x10000010 : ...
Lösung: Jumps sind Pseudo‐Direkt
Betrachte voriges Beispiel aber mit korrektem 26‐Bit Adressfeld:
0x10000000 : j 0x10 # 00 0000 0000 0000 0000 0001 0000 0x10000004 : ...
...
0x10000010 : ...
Der Program‐Counter sei $pc=0x10000004. Wie kommt man nach 0x10000010?
(0x10000004 = 0001 0000 0000 0000 0000 0000 0000 0100 0xFC000000 = 1111 1100 0000 0000 0000 0000 0000 0000 0x00000010 = 00 0000 0000 0000 0000 0001 0000 0x10000010 = 0001 0000 0000 0000 0000 0000 0001 0000)
Lösung: Jumps sind Pseudo‐Direkt
Auch hier wieder, nutze die Tatsache, dass Befehle immer 4 Bytes lang sind:
0x10000000 : j 0x4 # 00 0000 0000 0000 0000 0000 0100 0x10000004 : ...
...
0x10000010 : ...
Der Program‐Counter sei $pc=0x10000004. Wie kommt man nach 0x10000010?
(0x10000004 = 0001 0000 0000 0000 0000 0000 0000 0100 0xF0000000 = 1111 0000 0000 0000 0000 0000 0000 0000 0x00000004 = 00 0000 0000 0000 0000 0000 0100 0x00000010 = 0000 0000 0000 0000 0000 0001 0000 0x10000010 = 0001 0000 0000 0000 0000 0000 0001 0000)
Achtung: Programm‐Address‐Boundary
Berachte folgendes Beispiel:
0x10EFFF10 : j Label ...
0x20000000 : Label: ...
Wie vorher hergeleitet muss das Label folgendes erfüllen:
$pc = ($pc AND 0xF0000000) OR (Label LSHIFT 2) Wie muss das Label übersetzt werden?
(0x10EFFF14 = 0001 0000 1110 1111 1111 1111 0001 0100 0xF0000000 = 1111 0000 0000 0000 0000 0000 0000 0000 0x20000000 = 0010 0000 0000 0000 0000 0000 0000 0000) Also, Sprung von 0x1??????? Nach 0x2??????? So nicht möglich.
Achtung: Programm‐Address‐Boundary
Allgemein: Sprünge in der Form beschränkt auf die 256MB Speicherblöcke 0x00000000 bis 0x0FFFFFFF
0x10000000 bis 0x1FFFFFFF ...
0xF0000000 bis 0xFFFFFFFF
Und wenn man doch über Blockgrenzen springen will?
Beispiel: Sprung aus beliebigem Speicherbereich nach 0x20002000:
Zusammenfassung der neuen Befehle
Instruktion Beispiel Beduetung
lui lui $s1, 61 Lade 16‐Bit‐Wert in obere 16 Bits
von Register $s1
Quiz
80000 : Loop: sll $t1,$s3,2 # Temp-Reg $t1 = i * 4
80004 : add $t1,$t1,$s6 # $t1 = Adresse von safe[i]
80008 : lw $t0,0($t1) # Temp-Reg $t0 = save[i]
80012 : bne $t0,$s5,Exit # gehe nach Exit, wenn save[i]!=k 80016 : addi $s3,$s3,1 # i = i + 1
80020 : j Loop # gehe wieder nach Loop 80024 : Exit:
Adresse Opcode rs rt rd shamt Funct
80000 0 0 19 9 2 0
80004 0 9 22 9 0 32
80008 35 9 8 0
80012 5 8 21
80016 8 19 19 1
80020 2
80024 ...
Was steht hier?