Intel x86 Assembly Language & Microarchitecture
Ottimizzazione
Ricerca…
introduzione
Osservazioni
In caso di dubbio, è sempre possibile consultare il manuale di riferimento sull'ottimizzazione dell'architettura Intel 64 e IA-32 , che è una grande risorsa dell'azienda dietro l'architettura x86.
Azzerare un registro
Il modo ovvio per azzerare un registro è MOV in un 0 -per esempio:
B8 00 00 00 00 MOV eax, 0
Si noti che questa è un'istruzione a 5 byte.
Se sei disposto a clobberare i flag ( MOV non ha mai effetto sui flag), puoi usare l'istruzione XOR per bitwise-XOR il registro con se stesso:
33 C0 XOR eax, eax
Questa istruzione richiede solo 2 byte ed esegue più velocemente su tutti i processori .
Spostamento della bandiera Carry in un registro
sfondo
Se la bandiera Carry ( C ) contiene un valore che vuoi mettere in un registro, il modo ingenuo è di fare qualcosa del genere:
mov al, 1
jc NotZero
mov al, 0
NotZero:
Usa 'sbb'
Un modo più diretto, evitando il salto, è usare "Sottrai con il prestito":
sbb al,al ; Move Carry to al
Se C è zero, allora al sarà zero. Altrimenti sarà 0xFF ( -1 ). Se hai bisogno che sia 0x01 , aggiungi:
and al, 0x01 ; Mask down to 1 or 0
Professionisti
- Circa della stessa dimensione
- Due o un paio di istruzioni
- Nessun salto costoso
Contro
- È opaco per un lettore che non conosce la tecnica
- Altera altre bandiere
Prova un registro per 0
sfondo
Per scoprire se un registro ha uno zero, la tecnica ingenua è di fare questo:
cmp eax, 0
Ma se guardi l'opcode per questo, ottieni questo:
83 F8 00 cmp eax, 0
Usa test
test eax, eax ; Equal to zero?
Esamina l'opcode che ottieni:
85 c0 test eax, eax
Professionisti
- Solo due byte!
Contro
- Opaco per un lettore che non conosce la tecnica
Puoi anche dare uno sguardo alla domanda di domande e risposte su questa tecnica .
Chiamate di sistema Linux con meno ingombro
In Linux a 32 bit, le chiamate di sistema vengono solitamente eseguite utilizzando l'istruzione sysenter (dico di solito perché i programmi più vecchi utilizzano l'ora obsoleto int 0x80 ) tuttavia, questo può richiedere molto spazio in un programma e quindi ci sono modi in cui uno può tagliare gli angoli per accorciare e accelerare le cose.
Questo di solito è il layout di una chiamata di sistema su Linux a 32 bit:
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
È enorme, vero! Ma ci sono alcuni trucchi che possiamo tirare per evitare questo casino.
Il primo è di impostare ebp sul valore di esp diminuito della dimensione di 3 registri a 32 bit, cioè 12 byte. Questo è fantastico finché si sta bene sovrascrivendo ebp, edx ed ecx con garbage (come quando si trasferirà un valore in quei registri direttamente dopo comunque), possiamo farlo usando l'istruzione LEA in modo che non ci sia bisogno influenzare il valore di ESP stesso.
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
Tuttavia, non abbiamo finito, se la chiamata di sistema è sys_exit possiamo farcela senza spingere nulla allo stack!
mov eax, 1
xor ebx, ebx ;Set the exit status to 0
mov ebp, esp
sysenter
Moltiplicare per 3 o 5
sfondo
Per ottenere il prodotto di un registro e una costante e memorizzarlo in un altro registro, il modo ingenuo è di farlo:
imul ecx, 3 ; Set ecx to 5 times its previous value
imul edx, eax, 5 ; Store 5 times the contend of eax in edx
Usa lea
Le moltiplicazioni sono operazioni costose. È più veloce usare una combinazione di turni e aggiunte. Per il caso particolare di moltiplicare il contenuto di un registro a 32 o 64 bit che non è esp o rsp di 3 o 5, è possibile utilizzare l'istruzione lea. Questo utilizza il circuito di calcolo dell'indirizzo per calcolare rapidamente il prodotto.
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
Molti assemblatori capiranno anche
lea ecx, [3*ecx]
lea edx, [5*edx]
Per tutti i possibili moltiplicativi altri ebp o rbp , la lunghezza dell'istruzione risultante è la stessa di con l'uso di imul .
Professionisti
- Esegue molto più velocemente
Contro
- Se il tuo multiplo è
ebporbp, richiede un byte in più usandoimul - Altro da digitare se il tuo assemblatore non supporta le scorciatoie
- Opaco per un lettore che non conosce la tecnica