Gostaria de saber qual é a diferença entre estas instruções:
MOV AX, [TABLE-ADDR]
e
LEA AX, [TABLE-ADDR]
assembly
x86
instruction-set
naveen
fonte
fonte
Respostas:
LEA
significa carregar endereço efetivoMOV
significa carregar valorEm resumo,
LEA
carrega um ponteiro para o item que você está endereçando, enquanto o MOV carrega o valor real nesse endereço.O objetivo de
LEA
é permitir realizar um cálculo de endereço não trivial e armazenar o resultado [para uso posterior]Onde há apenas constantes envolvidas
MOV
(por meio dos cálculos constantes do montador), às vezes pode parecer se sobrepor aos casos mais simples de uso deLEA
. É útil se você tiver um cálculo de várias partes com vários endereços base etc.fonte
LAHF
é: Carregue FLAGS no registro AH . No CIL do CLR (que é uma máquina abstrata baseada em pilha de nível superior, o termo carga refere-se a colocar um valor na pilha nocional e é normalmentel
... e os
equivalente ... faz o inverso). Essas notas: cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html ) sugerem que existem de fato arquiteturas nas quais sua distinção se aplica.Na sintaxe do NASM:
Na sintaxe MASM, use
OFFSET var
para obter um movimento imediato em vez de uma carga.fonte
mov eax, var
é uma carga, o mesmo quemov eax, [var]
, e você deve usarmov eax, OFFSET var
para usar um rótulo como uma constante imediata.lea
é a pior opção, exceto no modo de 64 bits, para o endereçamento relativo ao RIP.mov r32, imm32
roda em mais portas.lea eax, [edx*4]
é uma cópia e troca que não pode ser executada em uma instrução, mas no mesmo registro o LEA leva apenas mais bytes para codificar porque[eax*4]
requer adisp32=0
. (Porém, ele roda em portas diferentes das turnos.) Consulte agner.org/optimize e stackoverflow.com/tags/x86/info .A instrução MOV reg, addr significa ler uma variável armazenada no endereço addr no registrador reg. A instrução LEA reg, addr significa ler o endereço (não a variável armazenada no endereço) no registro reg.
Outra forma da instrução MOV é MOV reg, immdata, que significa ler os dados imediatos (ie constantes) immdata no registrador reg. Observe que se o addr no LEA reg, addr é apenas uma constante (ou seja, um deslocamento fixo), então a instrução LEA é essencialmente exatamente igual a um MOV reg equivalente, instrução immdata que carrega a mesma constante que os dados imediatos.
fonte
Se você especificar apenas um literal, não haverá diferença. LEA tem mais habilidades, no entanto, e você pode ler sobre elas aqui:
http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136
fonte
leal TextLabel, LabelFromBssSegment
quando você tem algo. tipo.bss .lcomm LabelFromBssSegment, 4
, você precisariamovl $TextLabel, LabelFromBssSegment
, não é?lea
requer um destino de registro, masmov
pode ter umaimm32
fonte e um destino de memória. Obviamente, essa limitação não é específica para o montador GNU.MOV AX, [TABLE-ADDR]
, o que é uma carga. Portanto, há uma grande diferença. A instrução equivalente émov ax, OFFSET table_addr
Depende do montador usado, porque
no MASM funciona como
Portanto, ele carrega os primeiros bytes de
table_addr
e NÃO o deslocamento paratable_addr
. Você deveria usarou
que funciona da mesma maneira.
lea
versão também funciona bem setable_addr
for uma variável local, por exemplofonte
Nenhuma das respostas anteriores chegou ao fundo da minha própria confusão, então eu gostaria de adicionar a minha.
O que eu estava perdendo é que as
lea
operações tratam o uso de parênteses de maneira diferente de comomov
.Pense em C. Digamos que eu tenha uma matriz
long
que eu chamoarray
. Agora a expressãoarray[i]
executa uma desreferência, carregando o valor da memória no endereçoarray + i * sizeof(long)
[1].Por outro lado, considere a expressão
&array[i]
. Isso ainda contém a subexpressãoarray[i]
, mas nenhuma desreferenciação é realizada! O significado dearray[i]
mudou. Não significa mais executar uma deferência, mas age como uma espécie de especificação , informando&
o endereço de memória que estamos procurando. Se quiser, você pode pensar alternadamente&
em "cancelar" a desreferência.Como os dois casos de uso são semelhantes em muitos aspectos, eles compartilham a sintaxe
array[i]
, mas a existência ou ausência de uma&
alteração na forma como essa sintaxe é interpretada. Sem&
, é uma desreferência e, na verdade, lê da matriz. Com&
, não é. O valor quearray + i * sizeof(long)
ainda é calculado, mas não é desreferenciado.A situação é muito parecida com
mov
elea
. Commov
, ocorre uma desreferência que não acontece comlea
. Isso ocorre apesar do uso de parênteses que ocorre em ambos. Por exemplo,movq (%r8), %r9
eleaq (%r8), %r9
. Commov
, esses parênteses significam "desreferência"; comlea
, eles não. Isso é semelhante a comoarray[i]
apenas significa "desreferência" quando não há&
.Um exemplo está em ordem.
Considere o código
Isso carrega o valor no local da memória
%rdi + %rsi * 8
no registro%rbp
. Ou seja: obtenha o valor no registro%rdi
e o valor no registro%rsi
. Multiplique o último por 8 e adicione-o ao primeiro. Encontre o valor neste local e coloque-o no registro%rbp
.Este código corresponde à linha C
x = array[i];
, ondearray
se torna e se torna%rdi
ei
se torna . o%rsi
x
%rbp
8
é o comprimento do tipo de dados contido na matriz.Agora considere código semelhante que usa
lea
:Assim como o uso de
movq
correspondeu à desreferenciação, o usoleaq
aqui corresponde a não desreferenciação. Esta linha de montagem corresponde à linha Cx = &array[i];
. Lembre-se de que&
altera o significado dearray[i]
desreferenciar para simplesmente especificar um local. Da mesma forma, o uso deleaq
altera o significado de(%rdi, %rsi, 8)
desreferenciar para especificar um local.A semântica desta linha de código é a seguinte: obtenha o valor no registro
%rdi
e o valor no registro%rsi
. Multiplique o último por 8 e adicione-o ao primeiro. Coloque esse valor no registro%rbp
. Nenhuma carga da memória está envolvida, apenas operações aritméticas [2].Observe que a única diferença entre minhas descrições de
leaq
emovq
é quemovq
faz uma desreferência eleaq
não. De fato, para escrever aleaq
descrição, basicamente copio + colei a descrição demovq
e removi "Encontre o valor neste local".Resumir:
movq
vs.leaq
é complicado porque eles tratam o uso de parênteses, como em(%rsi)
e(%rdi, %rsi, 8)
, de maneira diferente. Emmovq
(e em todas as outras instruções, excetolea
), esses parênteses denotam uma desreferência genuína, enquanto queleaq
eles não são e são uma sintaxe puramente conveniente.[1] Eu disse que quando
array
é uma matriz delong
, a expressãoarray[i]
carrega o valor do endereçoarray + i * sizeof(long)
. Isso é verdade, mas há uma sutileza que deve ser abordada. Se eu escrever o código Cisso não é o mesmo que digitar
Parece que deve basear-se em minhas declarações anteriores, mas não é.
O que está acontecendo é que a adição de ponteiro C tem um truque. Digamos que eu tenho um ponteiro
p
apontando para valores do tipoT
. A expressãop + i
faz não significativo "a posição emp
maisi
bytes". Em vez disso, a expressãop + i
realmente significa "a posição emp
maisi * sizeof(T)
bytes".A conveniência disso é que, para obter "o próximo valor", basta escrever em
p + 1
vez dep + 1 * sizeof(T)
.Isso significa que o código C
long x = array[5];
é realmente equivalente aporque C multiplicará automaticamente o
5
porsizeof(long)
.Portanto, no contexto desta questão do StackOverflow, como isso é relevante? Isso significa que, quando digo "o endereço
array + i * sizeof(long)
", não quero que "array + i * sizeof(long)
" seja interpretado como uma expressão C. Estou fazendo a multiplicaçãosizeof(long)
sozinho para tornar minha resposta mais explícita, mas entendo que, devido a isso, essa expressão não deve ser lida como C. Assim como a matemática normal que usa a sintaxe C.[2] Nota: como tudo o que
lea
faz são operações aritméticas, seus argumentos não precisam se referir a endereços válidos. Por esse motivo, é frequentemente usado para executar aritmética pura em valores que podem não ter a intenção de ser desreferenciados. Por exemplo,cc
com-O2
otimização traduznas seguintes linhas (linhas irrelevantes removidas):
fonte
&
operador de C é uma boa analogia. Talvez valha a pena ressaltar que LEA é o caso especial, enquanto MOV é como todas as outras instruções que podem levar uma memória ou um operando de registro. por exemplo,add (%rdi), %eax
apenas usa o modo de endereçamento para endereçar a memória, o mesmo que o MOV. Também relacionado: Usando o LEA em valores que não são endereços / ponteiros? leva esta explicação adiante: LEA é como você pode usar o suporte HW da CPU para matemática de endereços para fazer cálculos arbitrários.%rdi
" - isso é estranhamente redigido. Você quer dizer que o valor no registrordi
deve ser usado. Seu uso de "at" parece significar uma desreferência de memória onde não há nenhuma.%rdi
" ou "o valor em%rdi
". Seu "valor no registro%rdi
" é longo, mas bom, e talvez possa ajudar alguém que está lutando para entender registros versus memória.Basicamente ... "Mude para o REG ... depois de calculá-lo ..." parece ser bom para outros fins também :)
se você simplesmente esquecer que o valor é um ponteiro, você pode usá-lo para otimizações / minimizações de código ... seja como for.
EAX = 8
originalmente seria:
fonte
lea
é uma instrução shift-and-add que usa codificação e sintaxe da máquina operadora de memória, porque o hardware já sabe decodificar ModR / M + SIB + disp0 / 8/32.Como indicado nas outras respostas:
MOV
vai pegar os dados no endereço dentro dos colchetes e colocá- los no operando de destino.LEA
realizará o cálculo do endereço entre colchetes e colocará esse endereço calculado no operando de destino. Isso acontece sem realmente sair para a memória e obter os dados. O trabalho realizado porLEA
está no cálculo do "endereço efetivo".Porque a memória pode ser abordada de várias maneiras diferentes (veja exemplos abaixo),
LEA
é por vezes utilizado para adicionar ou registos multiplicam em conjunto sem usar um explícitaADD
ouMUL
instrução (ou equivalente).Como todos estão mostrando exemplos na sintaxe da Intel, eis alguns na sintaxe da AT&T:
fonte
lea label, %eax
um[disp32]
modo de endereçamento absoluto . Use emmov $label, %eax
vez disso. Sim, funciona, mas é menos eficiente (código de máquina maior e roda com menos unidades de execução). Desde que você mencionou a AT&T, usando LEA em valores que não são endereços / ponteiros? usa a AT&T, e minha resposta tem alguns outros exemplos da AT&T.Vamos entender isso com um exemplo.
mov eax, [ebx] e
lea eax, [ebx] Suponha que o valor em ebx seja 0x400000. Em seguida, mov irá para o endereço 0x400000 e copiará 4 bytes de dados presentes no registro eax. Enquanto lea copiará o endereço 0x400000 para o eax. Assim, após a execução de cada valor de instrução de eax em cada caso, será (assumindo que a memória 0x400000 contém 30).
eax = 30 (no caso de mov) eax = 0x400000 (no caso de lea) Para definição, mov copie os dados de rm32 para o destino (mov dest rm32) e lea (carregar endereço efetivo) copiará o endereço para o destino (mov dest rm32 )
fonte
LEA (Load Effective Address) é uma instrução shift-and-add. Foi adicionado ao 8086 porque o hardware existe para decodificar e calcular os modos de endereçamento.
fonte
MOV pode fazer o mesmo que LEA [rótulo], mas a instrução MOV contém o endereço efetivo dentro da própria instrução como uma constante imediata (calculada previamente pelo montador). O LEA usa parente do PC para calcular o endereço efetivo durante a execução da instrução.
fonte
lea [label
é um desperdício inútil de bytes versus um mais compactomov
; portanto, você deve especificar as condições de que está falando. Além disso, para alguns montadores,[label]
não é a sintaxe correta para um modo de endereçamento relativo ao RIP. Mas sim, isso é preciso. Como carregar o endereço da função ou etiqueta no registro no GNU Assembler explica mais detalhadamente.A diferença é sutil, mas importante. A instrução MOV é um 'MOVe' efetivamente uma cópia do endereço que a etiqueta TABLE-ADDR representa. A instrução LEA é um 'Endereço Efetivo de Carregamento', que é uma instrução indireta, o que significa que TABLE-ADDR aponta para um local de memória no qual o endereço a ser carregado é encontrado.
Usar LEA efetivamente é equivalente a usar ponteiros em idiomas como C, pois é uma instrução poderosa.
fonte