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.
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 # ?!?
...
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?
Synchronisation
Data‐Race
Prozessor 1: berechne x = x + 2
lw $t0, 0($s0) # lade x nach $t0 addi $t0, $t0, 2 # $t0 = $t0 + 2
sw $t0, 0($s0) # speichere $t0 nach x
Gemeinsamer Speicher
Variable x Prozessor 2: berechne x = x – 1
lw $t0, 0($s0) # lade x nach $t0 addi $t0, $t0, -1 # $t0 = $t0 – 1
sw $t0, 0($s0) # speichere $t0 nach x
Es gelte zu Beginn: x=10 Gilt nach Durchlauf beider Code‐Abschnitte immer x=11?
Problem: Zugriff auf x ist nicht atomar
Prozessor 1: berechne x = x + 2
lw $t0, 0($s0) # lade x nach $t0 addi $t0, $t0, 2 # $t0 = $t0 + 2
sw $t0, 0($s0) # speichere $t0 nach x
Prozessor 2: berechne x = x – 1
lw $t0, 0($s0) # lade x nach $t0 addi $t0, $t0, -1 # $t0 = $t0 – 1
sw $t0, 0($s0) # speichere $t0 nach x
Inhalt von x 10
Mögliche Lösung: Atomic‐Swap
Speicher
Variable lock 1.) Speicherinhalt lock in Register $t1 kopieren
2.) Alten Wert von $t1 nach lock kopieren
swap $t1, lock
Beispiel
$t1 lock
Vor Ausführung von swap 1 0
Nach Ausführung von swap 0 1
MIPS‐ISA hat kein swap, dennoch gibt es andere ISAs die so einen Befehl haben.
Also, zunächst ein Beispiel, wie man mittels swap synchronisieren kann.
swap ist hierbei atomar, d.h. während des swap wird jeglicher Speicherzugriff anderer Prozesse verzögert bis swap vollständig ausgeführt wurde!
Mögliche Lösung: Atomic‐Swap
Prozessor 1: berechne x = x + 2
addi $t1, $zero, 1 # setze $t1 auf 1
loop: swap $t1, lock # tausche $t1 und lock bne $t1, $zero, loop # nochmal wenn $t1!=0 lw $t0, 0($s0) # lade x nach $t0
addi $t0, $t0, 2 # $t0 = $t0 + 2
sw $t0, 0($s0) # speichere $t0 nach x swap $t1, lock # gib lock wieder frei
Gemeinsamer Speicher
Variable x
Variable lock (initial=0) Prozessor 2: berechne x = x – 1
addi $t1, $zero, 1 # setze $t1 auf 1
loop: swap $t1, lock # tausche $t1 und lock bne $t1, $zero, loop # nochmal wenn $t1!=0 lw $t0, 0($s0) # lade x nach $t0
addi $t0, $t0, -1 # $t0 = $t0 – 1
Mögliche Lösung: Atomic‐Swap
Prozessor 1: berechne x = x + 2
addi $t1, $zero, 1 # setze $t1 auf 1
loop: swap $t1, lock # tausche $t1 und lock bne $t1, $zero, loop # nochmal wenn $t1!=0 lw $t0, 0($s0) # lade x nach $t0
addi $t0, $t0, 2 # $t0 = $t0 + 2
sw $t0, 0($s0) # speichere $t0 nach x swap $t1, lock # gib lock wieder frei Prozessor 2: berechne x = x – 1
addi $t1, $zero, 1 # setze $t1 auf 1
loop: swap $t1, lock # tausche $t1 und lock bne $t1, $zero, loop # nochmal wenn $t1!=0 lw $t0, 0($s0) # lade x nach $t0
addi $t0, $t0, -1 # $t0 = $t0 – 1
sw $t0, 0($s0) # speichere $t0 nach x swap $t1, lock # gib lock wieder frei
Zeit x 10 lock
0
Weitere Lösung: Load Linked und Store Conditional
Speicher
Variable lock Lade den Inhalt der Speicherstelle 0($s1) in das
Register $t1
ll $t1, 0($s1) # load linked
sc $t0, 0($s1) # store conditional 1. Wenn seit dem letztem load linked keiner
auf den Speicherblock zugegriffen hat , dann Speichere den Inhalt von Register $t0 auf die Speicherstelle 0($s1) und setze $t0 auf 1.
2. Sonst lasse den Speicherblock unberührt und setze $t0 auf 0.
Weitere Lösung: Load Linked und Store Conditional
Prozessor 1: berechne x = x + 2
loop: ll $t0, 0($s0) # $t0 = x
addi $t0, $t0, 2 # $t0 = $t0 + 2 sc $t0, 0($s0) # x = $t0
beq $t0, $zero, loop # nochmal bei failure
Gemeinsamer Speicher
Variable x
Prozessor 2: berechne x = x – 1
loop: ll $t0, 0($s0) # $t0 = x
addi $t0, $t0, -1 # $t0 = $t0 – 1 sc $t0, 0($s0) # x = $t0
beq $t0, $zero, loop # nochmal bei failure
Weitere Lösung: Load Linked und Store Conditional
Prozessor 1: berechne x = x + 2
loop: ll $t0, 0($s0) # $t0 = x
addi $t0, $t0, 2 # $t0 = $t0 + 2 sc $t0, 0($s0) # x = $t0
beq $t0, $zero, loop # nochmal bei failure
Prozessor 2: berechne x = x – 1
loop: ll $t0, 0($s0) # $t0 = x
addi $t0, $t0, -1 # $t0 = $t0 – 1 sc $t0, 0($s0) # x = $t0
beq $t0, $zero, loop # nochmal bei failure
x 10