Me gustaría saber cuál es la diferencia entre estas instrucciones:
MOV AX, [TABLE-ADDR]
y
LEA AX, [TABLE-ADDR]
assembly
x86
instruction-set
naveen
fuente
fuente
Respuestas:
LEA
significa dirección efectiva de cargaMOV
significa valor de cargaEn resumen,
LEA
carga un puntero al elemento que está direccionando, mientras que MOV carga el valor real en esa dirección.El propósito de
LEA
es permitirle a uno realizar un cálculo de dirección no trivial y almacenar el resultado [para su uso posterior]Donde solo hay constantes involucradas,
MOV
(a través de los cálculos constantes del ensamblador) a veces parece superponerse con los casos más simples de uso deLEA
. Es útil si tiene un cálculo de varias partes con múltiples direcciones base, etc.fuente
LAHF
: cargar banderas en el registro AH . En el CIL de CLR (que es una máquina abstracta basada en una pila de nivel superior, el término carga se refiere a poner un valor en la pila nocional y normalmente esl
..., y els
... equivalente es el inverso). Estas notas: cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html ) sugieren que efectivamente existen arquitecturas en las que se aplica su distinción.En sintaxis NASM:
En la sintaxis MASM, use
OFFSET var
para obtener un movimiento inmediato en lugar de una carga.fuente
mov eax, var
es una carga, igual quemov eax, [var]
, y debe usarmov eax, OFFSET var
para usar una etiqueta como una constante inmediata.lea
es la peor opción, excepto en el modo de 64 bits para el direccionamiento relativo a RIP.mov r32, imm32
Se ejecuta en más puertos.lea eax, [edx*4]
es una copia y cambio que no se puede hacer en una sola instrucción, pero en el mismo registro LEA solo necesita más bytes para codificar porque[eax*4]
requiere adisp32=0
. (Sin embargo, se ejecuta en diferentes puertos que los turnos). Consulte agner.org/optimize y stackoverflow.com/tags/x86/info .La instrucción MOV reg, addr significa leer una variable almacenada en la dirección addr en el registro de registro. La instrucción LEA reg, addr significa leer la dirección (no la variable almacenada en la dirección) en el registro de registro.
Otra forma de la instrucción MOV es MOV reg, immdata, que significa leer los datos inmediatos (es decir, constantes) immdata en el registro de registro. Tenga en cuenta que si el addr en LEA reg, addr es solo una constante (es decir, un desplazamiento fijo), entonces esa instrucción LEA es esencialmente exactamente la misma que un MOV reg equivalente, instrucción immdata que carga la misma constante que los datos inmediatos.
fuente
Si solo especifica un literal, no hay diferencia. Sin embargo, LEA tiene más habilidades, y puedes leer sobre ellas aquí:
http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136
fuente
leal TextLabel, LabelFromBssSegment
puedes cuando tienes algo. como.bss .lcomm LabelFromBssSegment, 4
, tendrías que hacerlomovl $TextLabel, LabelFromBssSegment
, ¿no?lea
requiere un destino de registro, peromov
puede tener unimm32
origen y un destino de memoria. Por supuesto, esta limitación no es específica del ensamblador GNU.MOV AX, [TABLE-ADDR]
, que es una carga. Entonces hay una gran diferencia. La instrucción equivalente esmov ax, OFFSET table_addr
Depende del ensamblador utilizado, porque
en MASM funciona como
Entonces carga los primeros bytes
table_addr
y NO el desplazamiento atable_addr
. Deberías usar en su lugaro
que funciona igual
lea
la versión también funciona bien sitable_addr
es una variable local, por ejemplofuente
Ninguna de las respuestas anteriores llegó al fondo de mi propia confusión, así que me gustaría agregar la mía.
Lo que me faltaba es que las
lea
operaciones tratan el uso de paréntesis diferente de cómo lomov
hace.Piense en C. Digamos que tengo una variedad de
long
eso que llamoarray
. Ahora la expresiónarray[i]
realiza una desreferencia, cargando el valor de la memoria en la direcciónarray + i * sizeof(long)
[1].Por otro lado, considere la expresión
&array[i]
. ¡Esto todavía contiene la sub-expresiónarray[i]
, pero no se realiza la desreferenciación! El significado dearray[i]
ha cambiado. Ya no significa realizar una deferencia, sino que actúa como una especie de especificación , indicando&
qué dirección de memoria estamos buscando. Si lo desea, puede pensar alternativamente&
como "cancelar" la desreferenciación.Debido a que los dos casos de uso son similares en muchos aspectos, comparten la sintaxis
array[i]
, pero la existencia o ausencia de un&
cambio cambia la forma en que se interpreta esa sintaxis. Sin&
, es una desreferencia y en realidad se lee de la matriz. Con&
, no lo es. El valorarray + i * sizeof(long)
aún se calcula, pero no se desreferencia.La situación es muy similar con
mov
ylea
. Conmov
, se produce una desreferencia que no sucede conlea
. Esto es a pesar del uso de paréntesis que ocurre en ambos. Por ejemplo,movq (%r8), %r9
yleaq (%r8), %r9
. Conmov
, estos paréntesis significan "desreferencia"; conlea
, no lo hacen. Esto es similar a cómoarray[i]
solo significa "desreferencia" cuando no existe&
.Un ejemplo está en orden.
Considera el código
Esto carga el valor en la ubicación de la memoria
%rdi + %rsi * 8
en el registro%rbp
. Es decir: obtener el valor en el registro%rdi
y el valor en el registro%rsi
. Multiplique el último por 8 y luego agréguelo al primero. Encuentre el valor en esta ubicación y colóquelo en el registro%rbp
.Este código corresponde a la línea C
x = array[i];
, donde searray
convierte%rdi
y sei
convierte%rsi
y sex
convierte%rbp
. El8
es la longitud del tipo de datos contenido en la matriz.Ahora considere un código similar que usa
lea
:Así como el uso de
movq
corresponde a la desreferenciación, el uso deleaq
aquí corresponde a no desreferenciar. Esta línea de montaje corresponde a la línea Cx = &array[i];
. Recuerde que&
cambia el significado dearray[i]
desreferenciar a simplemente especificar una ubicación. Del mismo modo, el uso deleaq
cambia el significado de(%rdi, %rsi, 8)
desreferenciar a especificar una ubicación.La semántica de esta línea de código es la siguiente: obtener el valor en el registro
%rdi
y el valor en el registro%rsi
. Multiplique el último por 8 y luego agréguelo al primero. Coloque este valor en el registro%rbp
. No hay carga de memoria involucrada, solo operaciones aritméticas [2].Tenga en cuenta que la única diferencia entre mis descripciones de
leaq
ymovq
es quemovq
hace una desreferencia, yleaq
no lo hace. De hecho, para escribir laleaq
descripción, básicamente copié + pegué la descripciónmovq
y luego eliminé "Buscar el valor en esta ubicación".Para resumir:
movq
vs.leaq
es complicado porque tratan el uso de paréntesis, como en(%rsi)
y(%rdi, %rsi, 8)
, de manera diferente. Enmovq
(y en todas las demás instrucciones exceptolea
), estos paréntesis denotan una desreferencia genuina, mientras que enleaq
ellos no lo hacen y son una sintaxis puramente conveniente.[1] He dicho que cuando
array
es una matriz delong
, la expresiónarray[i]
carga el valor de la direcciónarray + i * sizeof(long)
. Esto es cierto, pero hay una sutileza que debe abordarse. Si escribo el código Cesto no es mismo que escribir
Parece que debería basarse en mis declaraciones anteriores, pero no lo es.
Lo que sucede es que la adición del puntero C tiene un truco. Digamos que tengo un puntero que
p
apunta a valores de tipoT
. La expresiónp + i
hace no media "en la posiciónp
mási
bytes". En cambio, la expresión enp + i
realidad significa "la posición enp
mási * sizeof(T)
bytes".La conveniencia de esto es que para obtener "el siguiente valor" solo tenemos que escribir
p + 1
lugar dep + 1 * sizeof(T)
.Esto significa que el código C
long x = array[5];
es realmente equivalente aporque C multiplicará automáticamente el
5
porsizeof(long)
.Entonces, en el contexto de esta pregunta de StackOverflow, ¿cómo es todo esto relevante? Significa que cuando digo "la dirección
array + i * sizeof(long)
", no digo por "array + i * sizeof(long)
" debe interpretarse como una expresión C. Estoy haciendo la multiplicación porsizeof(long)
mí mismo para hacer mi respuesta más explícita, pero entiendo que debido a eso, esta expresión no debe leerse como C. Igual que las matemáticas normales que usan la sintaxis de C.[2] Nota al margen: debido a que todo lo que
lea
hace son operaciones aritméticas, sus argumentos en realidad no tienen que referirse a direcciones válidas. Por esta razón, a menudo se usa para realizar operaciones aritméticas puras en valores que pueden no ser desreferenciados. Por ejemplo,cc
con-O2
optimización se traduceen lo siguiente (líneas irrelevantes eliminadas):
fuente
&
operador de C es una buena analogía. Quizás valga la pena señalar que LEA es el caso especial, mientras que MOV es como cualquier otra instrucción que pueda tomar una memoria o registrar un operando. por ejemplo,add (%rdi), %eax
solo usa el modo de direccionamiento para direccionar memoria, igual que MOV. También relacionado: ¿ Usar LEA en valores que no son direcciones / punteros? lleva esta explicación más allá: LEA es cómo puede usar el soporte HW de la CPU para la matemática de direcciones para hacer cálculos arbitrarios.%rdi
": esto está redactado de forma extraña. Quiere decir que se debe usar el valor en el registrordi
. Su uso de "at" parece significar una desreferencia de memoria donde no la hay.%rdi
" o "el valor en%rdi
". Su "valor en el registro%rdi
" es largo pero está bien, y quizás podría ayudar a alguien que lucha por comprender los registros frente a la memoria.Básicamente ... "Pasar a REG ... después de calcularlo ..." parece ser bueno para otros propósitos también :)
si olvida que el valor es un puntero, puede usarlo para optimizar / minimizar el código ... lo que sea ...
EAX = 8
originalmente sería:
fuente
lea
es una instrucción shift-and-add que usa codificación y sintaxis de máquina de operandos de memoria, porque el hardware ya sabe cómo decodificar ModR / M + SIB + disp0 / 8/32.Como se indica en las otras respuestas:
MOV
se agarra los datos a la dirección dentro de los corchetes y lugar que los datos en el destino operando.LEA
realizará el cálculo de la dirección dentro de los corchetes y colocará esa dirección calculada en el operando de destino. Esto sucede sin salir realmente a la memoria y obtener los datos. El trabajo realizado porLEA
está en el cálculo de la "dirección efectiva".Debido a que la memoria se puede abordar de varias maneras diferentes (ver ejemplos a continuación), a
LEA
veces se usa para agregar o multiplicar registros juntos sin usar una instrucción explícitaADD
oMUL
(o equivalente).Como todos muestran ejemplos en la sintaxis de Intel, aquí hay algunos en la sintaxis de AT&T:
fuente
lea label, %eax
un[disp32]
modo de direccionamiento absoluto . Usar en sumov $label, %eax
lugar. Sí, funciona, pero es menos eficiente (código de máquina más grande y se ejecuta en menos unidades de ejecución). Como menciona AT&T, ¿ usa LEA en valores que no son direcciones / punteros? usa AT&T, y mi respuesta allí tiene algunos otros ejemplos de AT&T.Vamos a entender esto con un ejemplo.
mov eax, [ebx] y
lea eax, [ebx] Suponga que el valor en ebx es 0x400000. Luego, mov irá a la dirección 0x400000 y copiará 4 bytes de datos para registrarlos en eax. Mientras que lea copiará la dirección 0x400000 en eax. Entonces, después de la ejecución de cada instrucción, el valor de eax en cada caso será (suponiendo que la memoria 0x400000 contiene es 30).
eax = 30 (en caso de mov) eax = 0x400000 (en caso de lea) Para la definición mov, copie los datos de rm32 al destino (mov dest rm32) y lea (dirección efectiva de carga) copiará la dirección al destino (mov dest rm32 )
fuente
LEA (Dirección efectiva de carga) es una instrucción shift-and-add. Se agregó a 8086 porque el hardware está allí para decodificar y calcular los modos de dirección.
fuente
MOV puede hacer lo mismo que LEA [etiqueta], pero la instrucción MOV contiene la dirección efectiva dentro de la instrucción como una constante inmediata (calculada de antemano por el ensamblador). LEA usa PC-relative para calcular la dirección efectiva durante la ejecución de la instrucción.
fuente
lea [label
es un desperdicio de bytes sin sentido frente a un más compactomov
, por lo que debe especificar las condiciones de las que habla. Además, para algunos ensambladores[label]
no es la sintaxis correcta para un modo de direccionamiento relativo a RIP. Pero sí, eso es exacto. Cómo cargar la dirección de la función o etiqueta en el registro en GNU Assembler se explica con más detalle.La diferencia es sutil pero importante. La instrucción MOV es un 'MOVe' efectivamente una copia de la dirección que representa la etiqueta TABLE-ADDR. La instrucción LEA es una 'Dirección efectiva de carga' que es una instrucción indirecta, lo que significa que TABLE-ADDR apunta a una ubicación de memoria en la que se encuentra la dirección para cargar.
Efectivamente, usar LEA es equivalente a usar punteros en lenguajes como C, como tal, es una instrucción poderosa.
fuente