Intel x86 Assembly Language & Microarchitecture
Optimierung
Suche…
Einführung
Bemerkungen
Im Zweifelsfall können Sie sich immer auf das ziemlich umfassende Intel 64- und IA-32-Architekturen-Referenzhandbuch beziehen, das eine großartige Ressource des Unternehmens hinter der x86-Architektur ist.
Ein Register auf Null setzen
Der offensichtliche Weg, ein Register auf Null zu setzen, ist das MOV in einer 0 - zum Beispiel:
B8 00 00 00 00 MOV eax, 0
Beachten Sie, dass dies eine 5-Byte-Anweisung ist.
Wenn Sie bereit sind, die Flags zu löschen ( MOV wirkt sich nicht auf die Flags aus), können Sie die XOR Anweisung verwenden, um das Register bitweise mit XOR zu versehen:
33 C0 XOR eax, eax
Diese Anweisung benötigt nur 2 Byte und wird auf allen Prozessoren schneller ausgeführt .
Carry-Flag in ein Register verschieben
Hintergrund
Wenn das Carry-Flag ( C ) einen Wert enthält, den Sie in ein Register aufnehmen möchten, besteht die naive Methode in der folgenden Weise:
mov al, 1
jc NotZero
mov al, 0
NotZero:
Verwenden Sie 'sbb'
Ein direkterer Weg, den Sprung zu vermeiden, ist "Subtrahieren mit Ausleihen":
sbb al,al ; Move Carry to al
Wenn C Null ist, ist al Null. Ansonsten ist es 0xFF ( -1 ). Wenn Sie 0x01 , fügen Sie 0x01 hinzu:
and al, 0x01 ; Mask down to 1 or 0
Pros
- Ungefähr dieselbe Größe
- Zwei oder eine weniger Anweisungen
- Kein teurer Sprung
Cons
- Es ist für einen Leser, der mit der Technik nicht vertraut ist, undurchsichtig
- Es ändert andere Flaggen
Testen Sie ein Register für 0
Hintergrund
Um herauszufinden, ob ein Register eine Null enthält, ist die naive Technik folgendes:
cmp eax, 0
Wenn Sie sich jedoch den Opcode ansehen, erhalten Sie Folgendes:
83 F8 00 cmp eax, 0
test
test eax, eax ; Equal to zero?
Überprüfen Sie den Opcode, den Sie erhalten:
85 c0 test eax, eax
Pros
- Nur zwei Bytes!
Cons
- Undurchsichtig für einen Leser, der mit der Technik nicht vertraut ist
Sie können auch einen Blick auf die Q & A-Frage zu dieser Technik werfen.
Linux-Systemaufrufe mit weniger Aufblähung
In 32-Bit-Linux werden Systemaufrufe normalerweise mit der sysenter-Anweisung ausgeführt (ich sage normalerweise, weil ältere Programme das nun veraltete int 0x80 ). Dies kann jedoch recht viel Speicherplatz in einem Programm beanspruchen und daher gibt es Möglichkeiten kann Ecken schneiden, um die Dinge zu verkürzen und zu beschleunigen.
Dies ist normalerweise das Layout eines Systemaufrufs unter 32-Bit-Linux:
mov eax, <System call number>
mov ebx, <Argument 1> ;If applicable
mov ecx, <Argument 2> ;If applicable
mov edx, <Argument 3> ;If applicable
push <label to jump to after the syscall>
push ecx
push edx
push ebp
mov ebp, esp
sysenter
Das ist riesig richtig! Aber es gibt ein paar Tricks, um dieses Chaos zu vermeiden.
Das erste ist, ebp auf den Wert von esp zu setzen, der um die Größe von 3 32-Bit-Registern, d. H. 12 Bytes, verringert ist. Dies ist großartig, solange Sie mit dem Überschreiben von ebp, edx und ecx in Ordnung sind (zum Beispiel, wenn Sie einen Wert direkt in diese Register verschieben), können Sie dies mit der LEA-Anweisung tun, so dass wir dies nicht brauchen den Wert von ESP selbst beeinflussen.
mov eax, <System call number>
mov ebx, <Argument 1>
mov ecx, <Argument 2>
mov edx, <Argument 3>
push <label to jump to after the syscall>
lea ebp, [esp-12]
sysenter
Wir sind jedoch noch nicht fertig, wenn der Systemaufruf sys_exit ist, können wir davonkommen, dass wir nichts auf den Stack legen.
mov eax, 1
xor ebx, ebx ;Set the exit status to 0
mov ebp, esp
sysenter
Multiplizieren Sie mit 3 oder 5
Hintergrund
Um das Produkt eines Registers und einer Konstanten zu erhalten und in einem anderen Register zu speichern, ist dies auf naive Weise:
imul ecx, 3 ; Set ecx to 5 times its previous value
imul edx, eax, 5 ; Store 5 times the contend of eax in edx
lea
Multiplikationen sind kostspielige Operationen. Es ist schneller, eine Kombination aus Schichten und Additionen zu verwenden. Für den besonderen Fall von muliplying des behaupten ein 32- oder 64 - Bit - Register , das nicht esp oder rsp von 3 oder 5 ist , können Sie den LEA - Befehl verwenden. Diese verwendet die Adressberechnungsschaltung, um das Produkt schnell zu berechnen.
lea ecx, [2*ecx+ecx] ; Load 2*ecx+ecx = 3*ecx into ecx
lea edx, [4*edx+edx] ; Load 4*edx+edx = 5*edx into edx
Viele Monteure werden es auch verstehen
lea ecx, [3*ecx]
lea edx, [5*edx]
Für alle möglichen Multiplikanden mit ebp oder rbp ist die resultierende Befehlslänge dieselbe wie bei der Verwendung von imul .
Pros
- Führt viel schneller aus
Cons
- Wenn Ihr Multiplikand
ebpoderrbp, dauert es ein Byte mehr,imulSieimul - Mehr zu tippen, wenn Ihr Assembler die Verknüpfungen nicht unterstützt
- Undurchsichtig für einen Leser, der mit der Technik nicht vertraut ist