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:
LEAsignifica dirección efectiva de cargaMOVsignifica valor de cargaEn resumen,
LEAcarga un puntero al elemento que está direccionando, mientras que MOV carga el valor real en esa dirección.El propósito de
LEAes 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 varpara obtener un movimiento inmediato en lugar de una carga.fuente
mov eax, vares una carga, igual quemov eax, [var], y debe usarmov eax, OFFSET varpara usar una etiqueta como una constante inmediata.leaes la peor opción, excepto en el modo de 64 bits para el direccionamiento relativo a RIP.mov r32, imm32Se 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, LabelFromBssSegmentpuedes cuando tienes algo. como.bss .lcomm LabelFromBssSegment, 4, tendrías que hacerlomovl $TextLabel, LabelFromBssSegment, ¿no?learequiere un destino de registro, peromovpuede tener unimm32origen 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_addrDepende del ensamblador utilizado, porque
en MASM funciona como
Entonces carga los primeros bytes
table_addry NO el desplazamiento atable_addr. Deberías usar en su lugaro
que funciona igual
leala versión también funciona bien sitable_addres 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
leaoperaciones tratan el uso de paréntesis diferente de cómo lomovhace.Piense en C. Digamos que tengo una variedad de
longeso 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
movylea. 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), %r9yleaq (%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 * 8en el registro%rbp. Es decir: obtener el valor en el registro%rdiy 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 searrayconvierte%rdiy seiconvierte%rsiy sexconvierte%rbp. El8es la longitud del tipo de datos contenido en la matriz.Ahora considere un código similar que usa
lea:Así como el uso de
movqcorresponde a la desreferenciación, el uso deleaqaquí 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 deleaqcambia 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
%rdiy 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
leaqymovqes quemovqhace una desreferencia, yleaqno lo hace. De hecho, para escribir laleaqdescripción, básicamente copié + pegué la descripciónmovqy luego eliminé "Buscar el valor en esta ubicación".Para resumir:
movqvs.leaqes 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 enleaqellos no lo hacen y son una sintaxis puramente conveniente.[1] He dicho que cuando
arrayes 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
papunta a valores de tipoT. La expresiónp + ihace no media "en la posiciónpmásibytes". En cambio, la expresión enp + irealidad significa "la posición enpmási * sizeof(T)bytes".La conveniencia de esto es que para obtener "el siguiente valor" solo tenemos que escribir
p + 1lugar dep + 1 * sizeof(T).Esto significa que el código C
long x = array[5];es realmente equivalente aporque C multiplicará automáticamente el
5porsizeof(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
leahace 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,cccon-O2optimizació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), %eaxsolo 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
leaes 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:
MOVse agarra los datos a la dirección dentro de los corchetes y lugar que los datos en el destino operando.LEArealizará 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 porLEAestá 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
LEAveces se usa para agregar o multiplicar registros juntos sin usar una instrucción explícitaADDoMUL(o equivalente).Como todos muestran ejemplos en la sintaxis de Intel, aquí hay algunos en la sintaxis de AT&T:
fuente
lea label, %eaxun[disp32]modo de direccionamiento absoluto . Usar en sumov $label, %eaxlugar. 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 [labeles 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