x86 Assembler-Programmierung (1)

Transcrição

x86 Assembler-Programmierung (1)
MMX – Varianten der Arithmetik
Arithmetik wahlweise im
Wraparound-Modus:
Unter- bzw. Überläufe werden abgeschnitten
und nur die niederwertigen
(dargestellten) Bits abgebildet
Saturations-Modus:
Kein Überlauf oder Unterlauf, stattdessen
Abbildung auf kleinste bzw. größte Werte.
Sinnvoll z.B. bei Berechung einer
Darstellungsfarbe
F000h
a2
a1
a0
+
+
+
+
3000h
b2
b1
b0
2000h a2+b2 a1+b1 a0+b0
F000h
a2
a1
a0
+
+
+
+
3000h
b2
b1
b0
FFFFh a2+b2 a1+b1 a0+b0
Peter Sobe
68
Anwendungsbeispiel
Anwendungsbeispiel Sum of absolute Differences (SAD), eine zeitkritische Operation bei der Videokompression:
SAD (dx, dy ) 
y0  N 1 x0  N 1
 I
n  y0
m  x0
K
(m, n)  I K 1 (m  dx, n  dy )
Manipulation n,m
Lade jeweils
8 aufeinanderfolgende Werte
Lade IK(m,n) bis IK(m+7,n) nach mm0
Lade IK-1(m+dx,n+dy) bis IK-1(m+dx+7,n+dy) nach mm1
MOVQ mm2, mm0
; mm2 <-mm0
PSUBUSB mm0, mm1 ; mm0 <- mm0 - mm1
PSUBUSB mm1, mm2 ; mm1 <- mm1 – mm2
POR mm0, mm1
Absolute
Differenzen für
die 8 Werte
MOVQ [esi], mm0
ADD esi, 8
j
n,m-Bereich
durchlaufen?
n
Summierung der absoluten
Differenzen
69
Peter Sobe
Leistungszuwachs
Dokumentierter Leistungszuwachs durch MMX:
Gewinn bei Übergang von skalarem C-Code zu MMX-Code
RGB  YUV Umwandlung:
Inverse DCT 2D 8x8:
Absolute Differences:
Matrix-Vektor-Multiplikation:
>10
3.5
5
14.6
Geschwindigkeitsgewinn größer 8 u.a. durch Multiply-Add-Befehl
Code wird von Prozessorherstellern (Intel, AMD) als so genannte
„Application Notes“ veröffentlicht
Peter Sobe
70
MMX, SSE - Weiterentwicklung
•
SSE2: 2x64-Bit FloatingPoint, Zwischenergebnisse nur 64 Bit anstatt der 80
Bit bei 8087, schneller aber geringere Genauigkeit
•
SSE3: horizontale Operationen – arithmetische Operationen über
Subwörter innerhalb eines Registers
•
SSE4:
Integer 2x32-zu-64 Bit Multiplikation ohne Überlauf,
mehrfache Multiplikation und Aufsummieren (Skalarprodukt),
MPSADBW-Befehl: Summe acht absoluter 8-Bit-Differenzen (SSE4.1)
hardwareseitige CRC-32-Prüfsummenbildung (SSE 4.2),
Stringverarbeitung (SSE 4.2)
Angekündigt: AVX (Advanced Vector Extensions) mit 256 Bit SIMD-Mode
Peter Sobe
71
SIMD-Programmierung
Möglichkeiten:
•
•
•
•
Maschinensprache
Inline-Assembling
Nutzung von Bibliotheken, z.B. Small Matrix Library für SSE
Compiler unterstützen teilweise SIMD-Erweiterungen
Intel C++ Compiler ab Version 6:
SIMD-Datentypen und Makros für MMX, SSE und SSE2
Vektorisierer für einfache Schleifen
GNU C-Compiler:
SIMD-Datentypen und Makros für MMX, SSE und 3DNow
Peter Sobe
72
x86 Architektur (1)
Betrachtet ausgehend vom äußeren Erscheinungsbild:
Registersatz: Anzahl der Register, Freiheiten bzw.
Beschränkungen bei deren Verwendung
Befehlssatz: Befehlsliste und evtl. verschiedene Varianten der
Befehle, wenn unterschiedliche Adressierungsarten
zugelassen sind.
Alles andere betrifft die Implementierung und Realisierung
Peter Sobe
73
x86 Architektur (2)
Intel 80x86-Familie
80186
8086
Co-Proz. Busbreite
Daten/Adress.
(Bit)
16/20
8087
80188
80286
80287
80386DX
80386SX
80387
80486DX
80486SX
80487
Pentium
Verbesserung der
Implementierung der Architektur
von 12 CPI beim 8086 auf 1.5 - 3
CPI beim Pentium
(CPI = Cycles Per Instruction).
Große Bedeutung durch Einsatz
in IBM-kompatiblen PCs.
Aufgrund der hohen
16/24
Stückzahlen ‚Mainstream’ der
16/24 (SX) derzeitigen
32/32 (DX) Rechnerentwicklung.
32/32
64/32
CISC-Prozessoren aus
historischen Gründen „binär
abwärtskompatibel“
zum ‚Urahnen’ 8086
Stetige Verbesserung der
Technologie (Taktfrequenz von
4.77 bis 10 MHz beim
8086/8088 auf über 3 GHz beim
Pentium).
Peter Sobe
74
Instruction Set Architecture (Intel IA-32)
8 x 32-Bit-Register mit 16-BitRegistern des 8086 in unteren
beiden Bytes
8 x 80-Bit-Gleitkommaregister
(internes IEEE-Format)
6 x 16-Bit-Segmentregister (8086:
4): Codesegment CS,
Stacksegment SS, 4 Datensegmente DS, ES, FS, GS
32-Bit-Befehlszähler, 32-BitFlagregister (8086: je 16 Bit)
Diverse Zusatzregister z. B. für
Kontrolle und Ausnahmebehandlung
Peter Sobe
75
IA-32 Datentypen
CISC-Befehlsformat (variable Länge)
Adressierungsarten
- unmittelbar
- Register indirekt
- direkt
- indiziert
- Register
Peter Sobe
76
IA-32 Befehlssatz ohne Gleitkommabefehle
Typischer
CISC-Befehlssatz
Peter Sobe
77
IA-32 Befehlssatz ohne Gleitkommabefehle
Peter Sobe
78
Mikroarchitektur Pentium 4
 Umsetzung der IA-32 CISC-Befehle in 1
bis 4 Ops (interne RISC-Befehle) durch
Decoder.
 Ausführung in supersklarer RISCArchitektur
 Trace Execution Cache (TEC) für Ops
mit eigener Sprungvorhersage,
 3 Ops pro Takt wie PentiumIII
 Verbesserte Sprungvorhersage für x86Befehle mit größerem BTB
 20-stufige I-Pipe, Taktraten bis über 3
GHz
 13 Funktionseinheiten, davon max. 6
gleichzeitig aktivierbar
 8 KB Datencache (klein, aber schnell);
Hardware-Prefetching mit Quad Pumped
Speicherschnittstelle (3,2 GByte/s)
 Befehlssätze (MMX, SSE, SSE2)
 Optional: Hyperthreading (SMT)
Peter Sobe
79
Hyperthreading
Intels Implementierung von SMT: 2-fach Hyper-Threading für den P4, auch für
Atom CPUs,
Verhält sich für das Betriebssystem wie zwei logische Prozessoren, d. h.
Multiprozessor-Software ist ohne Änderung lauffähig.
P4-Pipeline mit SMT
Pipeline-Register (Queues) und
einige Pipelinestufen verdoppelt,
die meisten Stufen werden
abwechselnd von beiden Threads
genutzt. Verdoppelung der
Register durch RegisterRenaming implementiert.
Nur 5% zusätzliche Chipfläche.
Konflikte beim Nutzen der
gemeinsamen Caches (Cache
Aliase) können Leistung
einschränken.
Peter Sobe
80
x86 Architektur (2)
Allgemeine Register
AX – Akkumulator-Register, Ziel und Quelle für Rechenoperationen
Teilung in hohes Byte (AH) und niedriges Byte (AL)
BX - Basis-Register für Anfangsadressen,
Teilung in hohes Byte (BH) und niedriges Byte (BL)
CX – Count Register,
Teilung in hohes Byte (CH) und niedriges Byte (CL),
allgemein verwendbar, spezielle Bedeutung bei Schleifen
DX - Daten-Register , Teilung in hohes Byte (DH) und
niedriges Byte (DL)
RAX (bei x86-64)
EAX
EAX
AX AX
AH AH
64 Bit
Peter Sobe
32 Bit
16 Bit
ALAL
8 Bit
81
x86 Architektur (3)
Pointer-Register
SP Stack-Pointer: zur Adressierung des Stacks verwendet
BP Base-Pointer: zur Adressierung des Stacks verwendet
IP Instruction-Pointer: Offset des nächsten Befehls
Index-Register
SI Source-Index: Unterstützung von Adressierungen
esi Quelle (eng: source) für Stringoperationen
DI Destination-Index: Unterstützung von Adressierungen
edi Ziel (eng: destination) für Stringoperationen
Segment-Register
CS Code-Segment: zeigt auf aktuelles Codesegment
DS Daten-Segment: zeigt auf aktuelles Datensegment
SS Stack-Segment: zeigt auf aktuelles Stapelsegment
ES Extra-Segment: zeigt auf weiteres Datensegment
Peter Sobe
82
x86 Architektur (4)
Statusflags
CF Carry-Flag Übertragflag
AF Auxiliary Carry-Flag Hilfsübertragflag
ZF Zero-Flag Nullflag
SF Sign-Flag Vorzeichenflag
PF Parity-Flag Paritätsflag
OF Overflow-Flag Überlaufflag
Kontrollflags
TF Trap-Flag Einzelschrittflag
IF Interrupt Enable-Flag Interruptflag
Peter Sobe
83
x86 Assembler-Programmierung (1)
Die C-Anweisung
summe = a + b + c + d;
würde beim 80x86 Assembler so aussehen:
mov eax,[a]
add eax,[b]
add eax,[c]
add eax,[d]
mov [s], eax
Mit eax ist das 32 Bit breite AX Register gemeint. Alle
Operationen beziehen sich damit auf 32 Bit
Verarbeitungsbreite.
Peter Sobe
84
x86 Assembler-Programmierung (2)
Einfache if-then-else Konstrukte müssen in der AssemblerSprache in Compare und einen bedingten Sprung
umgewandelt werden …
if (a == 4711) {...}
else { ... }
Im x86 Assembler sieht das dann so aus:
cmp eax,4711
jne ungleich
gleich: ...
jmp weiter
ungleich: ...
weiter: ...
Peter Sobe
85
x86 Assembler-Programmierung (3)
Einfache Zählschleifen werden von einem x86 Prozessor
besser unterstützt. Das folgende C-Programm
for (i=0; i<100; i++)
{ summe = summe + a;
}
sieht im 80x86 Assembler so aus:
mov ecx,100
schleife: add eax,[a]
loop schleife
Der Loop-Befehl dekrementiert implizit das ecx Register
und führt den Sprung nur aus, wenn der Inhalt des ecx
Registers anschließend nicht 0 ist.
Peter Sobe
86
x86 Assembler-Programmierung (4)
Speicherzugriff
Meistens reichen die Register nicht aus, um ein Problem zu
lösen. In diesem Fall muss auf den Hauptspeicher des
Computers zugegriffen werden, der erheblich mehr
Information speichern kann.
Für den Assemblerpogrammierer sieht der Hauptspeicher wie
ein riesiges Array von Registern aus, die je nach Wunsch 8, 16
oder 32 Bits "breit" sind (je nach Datentyp).
Die kleinste adressierbare Einheit ist ein Byte (= 8 Bits).
Um auf einen bestimmten Eintrag des
Arrays "Hauptspeicher" zugreifen zu können, muss der
Programmierer die Adresse des Eintrages
kennen. Das erste Byte des Hauptspeichers bekommt dabei
die Adresse 0, das zweite die Adresse 1 usw.
Peter Sobe
87
x86 Assembler-Programmierung (5)
In einem Assemblerprogramm können Variablen angelegt
werden, indem einer Speicheradresse ein Label zugeordnet
und dabei Speicherplatz in der gewünschten Größe
reserviert wird.
[SECTION .data]
gruss:
db 'hello, world'
unglueck:
dw 13
million:
dd 1000000
[SECTION .text]
mov ax,[million]
...
db … define byte, dw … define word (2 Bytes),
dd … define double word
Peter Sobe
88
x86 Assembler-Programmierung (6)
Stack
Nicht immer will man sich ein neues Label ausdenken, nur um kurzfristig
mal den Wert eines Registers zu speichern, beispielsweise, weil man das
Register für eine bestimmte Anweisung benötigt, den alten Wert aber
nicht verlieren möchte. In diesem Fall wünscht man sich sowas wie einen
„Ablagehaufen“. Den bekommt man mit dem Stack. Der Stack
ist eigentlich nichts weiter als ein Stück des
Hauptspeichers, nur dss dort nicht mit festen Adressen
gearbeitet wird, sondern die zu sichernden Daten einfach
immer oben drauf geschrieben (push) bzw. von oben
heruntergeholt werden (pop). Der Zugriff ist also ganz
einfach, vorausgesetzt man erinnert sich daran, in welcher Reihenfolge
die Daten auf den Stapel gelegt wurden.
Ein spezielles Register, der Stackpointer esp zeigt stets auf das oberste
Element des Stacks. Da push und pop immer nur 32 Bits auf einmal
transferieren können, ist der Stack in der folgenden Abbildung vier Bytes
breit dargestellt.
Peter Sobe
89
x86 Assembler-Programmierung (7)
Adressierungsarten
Die meisten Befehle des x86 können ihre Operanden
wahlweise aus Registern, aus dem Speicher oder
unmittelbar einer Konstante entnehmen. Beim mov
Befehl sind (u. a.) folgende Formen möglich, wobei
der erste Operand stets das Ziel und der zweite stets
die Quelle der Kopieraktion angeben:
Registeradressierung:
Der Wert eines Registers wird in ein anderes übertragen.
mov ebx,edi
Peter Sobe
90
x86 Assembler-Programmierung (8)
Unmittelbare Adressierung:
Die Konstante wird in das Register übertragen.
mov ebx,1000
Direkte Adressierung:
Der Wert der an der angegebenen Speicherstelle
steht, wird in das Register übertragen.
mov ebx,[1000]
Register-Indirekte Adressierung:
Der Wert, der an der Speicherstelle steht, die durch
das zweite Register bezeichnet wird, wird in das
erste Register übertragen.
mov ebx,[eax]
Peter Sobe
91
x86 Assembler-Programmierung (9)
Basis-Register Adressierung:
Der Wert, der an der Speicherstelle steht, die sich
durch die Summe des Inhalts des zweiten Registers
und der Konstanten ergibt, wird in das erste
Register übertragen.
mov eax,[10+esi]
Peter Sobe
92
Assembler-Einbindung in C (1)
In einem C-Programm kann jede Anweisung durch einen Block von
Assembler-Befehlen durch folgende Syntax ersetzt werden:
_asm { <Folge von Assembler-Befehlen> } ;
Jeder Assemblerbefehl muss durch Semikolon abgeschlossen
sein.
Die in den Assembler-Befehlen vorkommenden
Hauptspeicheroperanden können Bezeichnungen des C-Programms
sein. Die interne Darstellung und vor allem die Länge der
Operanden muss gemäß der C-Deklaration so sein, dass sie
kompatibel zum angewandten Befehl ist.
Damit kann ein Datenaustausch zwischen den Assembler- und den
C-Passagen erfolgen.
Peter Sobe
93
Assembler-Einbindung in C (2)
Beispiel:
mov buf,cx;
Mit cx ist das counter-Register (16 Bit) bezeichnet, folglich muss die
angenommene C-Variable buf auch als eine vorzeichenlose Variable
mit 16 Bit deklariert sein, d.h.
unsigned short buf;
Soll dagegen das 32-Bit-counter-Register adressiert werden (ecx):
mov buf,ecx;
so ist buf folgendermaßen zu deklarieren:
unsigned int buf;
Wird dies nicht beachtet, treten beim kompilieren Fehler auf. Da der
C-Compiler einen Inline-Assembler benutzt, sind nicht alle Codes,
wie bei einem eigenständigen Assembler zugelassen.
Peter Sobe
94
Assembler-Einbindung in C (3)
Beispiel:
#include <stdio.h>
void main()
{ unsigned short erg;
unsigned short eingabe = 2;
unsigned char z;
unsigned int buf;
_asm { //xor cx,cx;
// cx=0
mov cx, eingabe;
inc cx;
// cx++
inc cx;
// cx++
shl cx,3;
// *8
mov erg,cx;
// erg=cx
mov bl,102;
// bl='f‘
mov z,bl;
// z=bl
};
printf("\n erg=%u z=%c \n",erg,z);
}
Peter Sobe
95