Ricerca…


introduzione

La famiglia x86 è in circolazione da molto tempo e in quanto tale ci sono molti trucchi e tecniche che sono stati scoperti e sviluppati che sono di dominio pubblico o forse non così pubblici. La maggior parte di questi trucchi si avvantaggia del fatto che molte istruzioni fanno effettivamente la stessa cosa, ma versioni differenti sono più veloci, o salvano memoria, o non influenzano le Bandiere. Qui sono un certo numero di trucchi che sono stati scoperti. Ognuno ha i suoi pro e contro, quindi dovrebbe essere elencato.

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 è ebp o rbp , richiede un byte in più usando imul
  • Altro da digitare se il tuo assemblatore non supporta le scorciatoie
  • Opaco per un lettore che non conosce la tecnica


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow