¿Cuál es el propósito de la instrucción LEA?
Para mí, parece un MOV original. ¿Cuál es su propósito y cuándo debo usarlo?
Como otros han señalado, LEA (dirección efectiva de carga) se utiliza a menudo como un "truco" para realizar ciertos cálculos, pero ese no es su propósito principal. El conjunto de instrucciones x86 fue diseñado para admitir lenguajes de alto nivel como Pascal y C, donde las matrices, especialmente matrices de ints o estructuras pequeñas, son comunes. Considere, por ejemplo, una estructura que representa las coordenadas (x, y):
struct Point
{
int xcoord;
int ycoord;
};
Ahora imagina una declaración como:
int y = points[i].ycoord;
donde points[]
hay una matriz de Point
. Suponiendo que la base de la matriz ya está en EBX
, y la variable i
está en EAX
, y xcoord
y ycoord
son cada uno de 32 bits (por lo tanto ycoord
, hay un desplazamiento de 4 bytes en la estructura), esta declaración se puede compilar para:
MOV EDX, [EBX + 8*EAX + 4] ; right side is "effective address"
que aterrizará y
en EDX
. El factor de escala de 8 se debe a que cada uno Point
tiene un tamaño de 8 bytes. Ahora considere la misma expresión utilizada con el operador "dirección de" &:
int *p = &points[i].ycoord;
En este caso, no desea el valor de ycoord
, sino su dirección. Ahí es donde LEA
entra (cargar dirección efectiva). En lugar de un MOV
, el compilador puede generar
LEA ESI, [EBX + 8*EAX + 4]
que cargará la dirección en ESI
.
Del "Zen de la Asamblea" de Abrash:
LEA
, la única instrucción que realiza cálculos de direccionamiento de memoria pero en realidad no direcciona la memoria.LEA
acepta un operando de direccionamiento de memoria estándar, pero no hace más que almacenar el desplazamiento de memoria calculado en el registro especificado, que puede ser cualquier registro de propósito general.¿Qué nos aporta eso? Dos cosas que
ADD
no proporciona:
- la capacidad de realizar sumas con dos o tres operandos, y
- la capacidad de almacenar el resultado en cualquier registro; no solo uno de los operandos fuente.
Y LEA
no altera las banderas.
Ejemplos
LEA EAX, [ EAX + EBX + 1234567 ]
calculaEAX + EBX + 1234567
(son tres operandos)LEA EAX, [ EBX + ECX ]
calculaEBX + ECX
sin anular ninguno de los dos con el resultado.- multiplicación por constante (por dos, tres, cinco o nueve), si la usas como
LEA EAX, [ EBX + N * EBX ]
(N puede ser 1,2,4,8).
Otro caso de uso es útil en bucles: la diferencia entre LEA EAX, [ EAX + 1 ]
y INC EAX
es que el último cambia EFLAGS
pero el primero no; esto preserva CMP
el estado.
Otra característica importante de la LEA
instrucción es que no altera los códigos de condición como CF
y ZF
, mientras calcula la dirección mediante instrucciones aritméticas como ADD
o MUL
lo hace. Esta característica disminuye el nivel de dependencia entre instrucciones y, por lo tanto, deja espacio para una mayor optimización por parte del compilador o programador de hardware.
A pesar de todas las explicaciones, LEA es una operación aritmética:
LEA Rt, [Rs1+a*Rs2+b] => Rt = Rs1 + a*Rs2 + b
Es sólo que su nombre es extremadamente estúpido para una operación shift+sum. El motivo de esto ya se explicó en las respuestas mejor calificadas (es decir, fue diseñado para mapear directamente referencias de memoria de alto nivel).