Intel x86 Assembly Language & Microarchitecture
optimalisatie
Zoeken…
Invoering
Opmerkingen
Bij twijfel kunt u altijd verwijzen naar de behoorlijk uitgebreide Intel 64 en IA-32 Architectures Optimization Reference Manual , een geweldige bron van het bedrijf achter de x86-architectuur zelf.
Een register op nul zetten
De voor de hand liggende manier om een register op nul te zetten, is om MOV in een 0 , bijvoorbeeld:
B8 00 00 00 00 MOV eax, 0
Merk op dat dit een instructie van 5 bytes is.
Als je bereid bent om de vlaggen te verpesten ( MOV beïnvloedt nooit de vlaggen), kun je de XOR instructie gebruiken om het register bitgewijs met zichzelf te XORen:
33 C0 XOR eax, eax
Deze instructie vereist slechts 2 bytes en wordt sneller uitgevoerd op alle processors .
Vlag verplaatsen naar een register
Achtergrond
Als de vlag Carry ( C ) een waarde bevat die u in een register wilt plaatsen, is de naïeve manier om zoiets te doen:
mov al, 1
jc NotZero
mov al, 0
NotZero:
Gebruik 'sbb'
Een meer directe manier om de sprong te vermijden is om "Aftrekken met lenen" te gebruiken:
sbb al,al ; Move Carry to al
Als C nul is, is al nul. Anders is dit 0xFF ( -1 ). Als het 0x01 , voeg dan toe:
and al, 0x01 ; Mask down to 1 or 0
Pros
- Ongeveer even groot
- Twee of een minder instructies
- Geen dure sprong
Cons
- Het is ondoorzichtig voor een lezer die niet bekend is met de techniek
- Het verandert andere vlaggen
Test een register voor 0
Achtergrond
Om na te gaan of een register een nul bevat, is de naïeve techniek om dit te doen:
cmp eax, 0
Maar als je hier naar de opcode kijkt, krijg je dit:
83 F8 00 cmp eax, 0
Gebruik test
test eax, eax ; Equal to zero?
Bekijk de opcode die u krijgt:
85 c0 test eax, eax
Pros
- Slechts twee bytes!
Cons
- Ondoorzichtig voor een lezer die niet bekend is met de techniek
U kunt ook een kijkje nemen in de Q & A-vraag over deze techniek .
Linux-systeem roept met minder bloat
In 32-bit Linux worden systeemaanroepen meestal gedaan met behulp van de sysenter-instructie (ik zeg meestal omdat oudere programma's de inmiddels verouderde int 0x80 ), dit kan echter heel veel ruimte in beslag nemen in een programma en er zijn manieren die kan hoeken snijden om dingen in te korten en te versnellen.
Dit is meestal de lay-out van een systeemaanroep op 32-bits 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
Dat is enorm goed! Maar er zijn een paar trucs die we kunnen doen om deze puinhoop te voorkomen.
De eerste is om ebp in te stellen op de waarde van esp verminderd met de grootte van 3 32-bit registers, dat wil zeggen 12 bytes. Dit is geweldig zolang je ok bent met het overschrijven van ebp, edx en ecx met afval (zoals wanneer je toch meteen een waarde naar die registers verplaatst), we kunnen dit doen met behulp van de LEA-instructie zodat we niet nodig hebben om de waarde van ESP zelf te beïnvloeden.
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
We zijn echter nog niet klaar, als de systeemaanroep sys_exit is, kunnen we wegkomen door helemaal niets naar de stapel te duwen!
mov eax, 1
xor ebx, ebx ;Set the exit status to 0
mov ebp, esp
sysenter
Vermenigvuldig met 3 of 5
Achtergrond
Om het product van een register en een constante te krijgen en op te slaan in een ander register, is de naïeve manier om dit te doen:
imul ecx, 3 ; Set ecx to 5 times its previous value
imul edx, eax, 5 ; Store 5 times the contend of eax in edx
Gebruik lea
Vermenigvuldigingen zijn dure bewerkingen. Het is sneller om een combinatie van diensten en toevoegingen te gebruiken. Voor het specifieke geval van het mulipiëren van de stelling van een 32- of 64-bits register dat niet 3 of 5 esp of rsp , kunt u de instructie lea gebruiken. Dit maakt gebruik van het adresberekeningscircuit om het product snel te berekenen.
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
Veel monteurs zullen het ook begrijpen
lea ecx, [3*ecx]
lea edx, [5*edx]
Voor alle mogelijke veelvouden, behalve ebp of rbp , is de resulterende instructielengte hetzelfde als bij het gebruik van imul .
Pros
- Werkt veel sneller
Cons
- Als uw vermenigvuldigtal
ebpofrbphet duurt een byte meer ze met behulp vanimul - Meer om te typen als uw assembler de sneltoetsen niet ondersteunt
- Ondoorzichtig voor een lezer die niet bekend is met de techniek