Buscar..


Introducción

La familia x86 ha existido durante mucho tiempo, y como tal, hay muchos trucos y técnicas que se han descubierto y desarrollado que son de conocimiento público, o tal vez no tanto. La mayoría de estos trucos aprovechan el hecho de que muchas instrucciones hacen lo mismo de manera efectiva, pero las diferentes versiones son más rápidas, ahorran memoria o no afectan a los indicadores. Aquí hay una serie de trucos que se han descubierto. Cada uno tiene sus pros y contras, por lo que deben ser enumerados.

Observaciones

En caso de duda, siempre puede consultar el Manual de referencia de optimización de arquitecturas Intel 64 y IA-32 , que es un gran recurso de la compañía detrás de la arquitectura x86.

Poner a cero un registro

La forma obvia de poner a cero un registro es MOV en un 0 ejemplo:

B8 00 00 00 00    MOV eax, 0

Tenga en cuenta que esta es una instrucción de 5 bytes.

Si está dispuesto a pegar las banderas ( MOV nunca afecta a las banderas), puede usar la instrucción XOR para hacer bitwise-XOR el registro consigo mismo:

33 C0             XOR eax, eax

Esta instrucción requiere solo 2 bytes y se ejecuta más rápido en todos los procesadores .

Mover la bandera de Carry a un registro

Fondo

Si el indicador de Carry ( C ) tiene un valor que desea poner en un registro, la forma más ingenua es hacer algo como esto:

    mov  al, 1
    jc   NotZero
    mov  al, 0
NotZero:

Utilice 'sbb'

Una forma más directa, evitando el salto, es usar "Restar con préstamo":

    sbb  al,al    ; Move Carry to al

Si C es cero, entonces al será cero. De lo contrario será 0xFF ( -1 ). Si necesita que sea 0x01 , agregue:

    and  al, 0x01 ; Mask down to 1 or 0

Pros

  • Sobre el mismo tamaño
  • Dos o una instrucciones menos.
  • Ningún salto caro

Contras

  • Es opaco para un lector que no está familiarizado con la técnica.
  • Altera otras banderas.

Prueba un registro para 0

Fondo

Para averiguar si un registro contiene un cero, la técnica ingenua es hacer esto:

    cmp   eax, 0

Pero si miras el código de operación para esto, obtienes esto:

83 F8 00      cmp   eax, 0

test uso

    test   eax, eax      ; Equal to zero?

Examine el código de operación que obtiene:

85 c0         test   eax, eax

Pros

  • Sólo dos bytes!

Contras

  • Opaco a un lector no familiarizado con la técnica.

También puede consultar la pregunta de preguntas y respuestas sobre esta técnica .

Sistema Linux llama con menos hinchazón

En Linux de 32 bits, las llamadas al sistema generalmente se realizan mediante la instrucción sysenter (yo digo que generalmente porque los programas más antiguos usan el int 0x80 ahora en desuso), sin embargo, esto puede ocupar mucho espacio en un programa, por lo que hay formas Puede cortar esquinas para acortar y acelerar las cosas.
Este suele ser el diseño de una llamada al sistema en Linux de 32 bits:

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

¡Eso es masivo! Pero hay algunos trucos que podemos tirar para evitar este lío.
El primero es establecer ebp al valor de esp disminuido por el tamaño de 3 registros de 32 bits, es decir, 12 bytes. Esto es genial siempre y cuando esté de acuerdo con sobrescribir ebp, edx y ecx con basura (como cuando moverá un valor a esos registros directamente después de todos modos), podemos hacerlo usando la instrucción LEA para que no sea necesario. Para afectar el valor de ESP en sí.

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

Sin embargo, no hemos terminado, si la llamada al sistema es sys_exit, podemos evitar que no pongamos nada en absoluto en la pila.

mov eax, 1
xor ebx, ebx ;Set the exit status to 0
mov ebp, esp
sysenter

Multiplica por 3 o 5

Fondo

Para obtener el producto de un registro y una constante y almacenarlo en otro registro, la forma ingenua es hacer esto:

    imul ecx, 3      ; Set ecx to 5 times its previous value
    imul edx, eax, 5 ; Store 5 times the contend of eax in edx

Usar lea

Las multiplicaciones son operaciones caras. Es más rápido usar una combinación de turnos y adiciones. Para el caso particular de muliplying el contendiente de un registro de 32 o 64 bits que no es esp o rsp por 3 o 5, puede usar la instrucción lea. Esto utiliza el circuito de cálculo de dirección para calcular el producto rápidamente.

    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

Muchos ensambladores también entenderán

    lea ecx, [3*ecx]
    lea edx, [5*edx]

Para todos los multiplicandos posibles, otros ebp o rbp , la ebp instrucción resultante es la misma que con el uso de imul .

Pros

  • Ejecuta mucho más rápido

Contras

  • Si su multiplicando es ebp o rbp , toma un byte más usando imul
  • Más para escribir si su ensamblador no admite los accesos directos
  • Opaco a un lector no familiarizado con la técnica.


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow