Test auf Größer und Kleiner?
slt $t0, $s3, $s4 # $t0 = 1 wenn $s3 < $s4 slti $t0, $s2, 10 # $t0 = 1 wenn $s2 < 10
Beispiel: springe nach Exit, wenn $s2 < 42 ...
slti $t0, $s2, 42
bne $t0, $zero, Exit ...
Exit:
Signed und unsigned Vergleiche
Registerinhalt von $s0 sei:
1111 1111 1111 1111 1111 1111 1111 1111 Registerinhalt von $s1 sei:
0000 0000 0000 0000 0000 0000 0000 0001
Was ist der Wert von $t0 nach Ausführung der folgenden Zeile:
slt $t0, $s0, $s1 # Signed-Vergleich $s0<$s1
Was ist der Wert von $t1 nach Ausführung der folgenden Zeile:
sltu $t0, $s0, $s1 # Unsigned-Vergleich $s0<$s1
Beispiel: Test auf 0 <= $s0 < $s1 in einer Code‐Zeile
Umständlicher Test in zwei Zeilen:
slti $t0, $s0, 0 # $t0=1 wenn $s0<0 sonst $t0=0
bne $t0, $zero, OutOfBound # gehe nach OutOfBound wenn $t0!=0 slt $t0, $s0, $s1 # $t0=1 wenn $s0<$s1 sonst $t0=0 beq $t0, $zero, OutOfBound # gehe nach OutOfBound wenn $t0==0 ...
OutOfBound:
Test in einer Zeile wenn $s1 immer größer oder gleich 0 ist?
Unterstützung von Jump‐Tables
Assembler‐Code:
Label_1: ...
...
Label_2: ...
...
Label_n: ...
Nr Label Adresse 0 Label_1 0x05342120 1 Label_2 0x05443004
... ...
n‐2
n‐1 Label_n 0x06756900 Jump‐Table
# Gewünschter Label sei in $s0 gespeichert und
# Startadresse der Jump-Table sei in $s1
# Lade Adresse für gewünschtes Label in $t0 sll $t0, $s0, 2
add $t0, $t0, $s1 lw $t0, 0($t0)
# Springe an die in $t0 gespeicherte Adresse jr $t0
Maschinen‐Code:
0x05342120: 1011010110...
...
0x05443004: 0001011101...
...
0x06756900: 0000111000...
Floating‐Point und Branches
MIPS‐Floating‐Point‐Instruktionen erlauben Vergleiche der Form:
c.x.s $f2,$f3 # Vergleiche Single $f2 mit $f3 c.x.d $f2,$f4 # Vergleiche Double $f2 mit $f3 Hierbei ist kann x in c.x.s bzw. c.x.d stehen für:
eq = equal
lt = less than
le = less or equal Beispiele:
c.eq.s $f2,$f3 # $f2 = $f3 ?
c.lt.d $f2,$f4 # ($f2,$f3) < ($f4,$f5)?
c.le.s $f2,$f3 # $f2 = $f3?
Und dann findet der Branch wie statt?
Instruktion bc1t und bc1f nach dem Floating‐Point‐Vergleich:
bc1t Label # springe nach Label, wenn der
# vorige Floating-Point-Vergleich
# erfüllt ist
bc1f Label # springe nach Label, wenn der
# vorige Floating-Point-Vergleich
# nicht erfüllt ist
(Bemerkung c1 steht für Coprozessor 1; Erinnerung: die FPU ist dort) Beispiel:
c.lt.d $f2,$f4 # ($f2,$f3) < ($f4,$f5)?
bc1t Label # springe nach Label, wenn
# ($f2,$f3) < ($f4,$f5) gilt.
...
Label: ...
Condition‐Flags
CPU Coprocessor 1 (FPU)
$f0 . . .
$f31
Arithmetic Unit Registers Memory
0 0 0 1 0 0 1 0 0 1 2 3 4 5 6 7
Condition‐Flags Die Floating‐Point‐Vergleichsbefehle c.x.s und
c.x.d setzen Default‐mäßig das Condition‐Flag 0.
Die Floating‐Point‐Sprungbefehle bc1tund bc1f springen, wenn das Flag 0 gesetzt bzw. nicht gesetzt ist.
Alternativ kann man auch die anderen Flags verwenden. Dann gibt man diese mit den Instruktionen an. Beispiel:
c.eq.s 2 $f2,$f3 # Setze Cond.-Flag
# wenn $f2=$f3.
bc1t 2 Lab # springe nach Lab
# wenn Cond.-Flag
# gesetzt ist.
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
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“)
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
mul $v0, $a0, $v0 # es wird n * fact(n-1) zurück gegeben
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 dynamisch (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+8, $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 #
jr $ra #
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.