Gostaria de saber qual é a diferença entre estas instruções:
MOV AX, [TABLE-ADDR]
e
LEA AX, [TABLE-ADDR]
Gostaria de saber qual é a diferença entre estas instruções:
MOV AX, [TABLE-ADDR]
e
LEA AX, [TABLE-ADDR]
Respostas:
LEA significa carregar endereço efetivoMOV significa carregar valorEm resumo, LEAcarrega 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]
LEA ax, [BP+SI+5] ; Compute address of value
MOV ax, [BP+SI+5] ; Load value at that address
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 de LEA. É útil se você tiver um cálculo de várias partes com vários endereços base etc.
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 é normalmente l... e o sequivalente ... 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:
mov eax, var == lea eax, [var] ; i.e. mov r32, imm32
lea eax, [var+16] == mov eax, var+16
lea eax, [eax*4] == shl eax, 2 ; but without setting flags
Na sintaxe MASM, use OFFSET varpara obter um movimento imediato em vez de uma carga.
mov eax, varé uma carga, o mesmo que mov eax, [var], e você deve usar mov eax, OFFSET varpara 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, imm32roda 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 a disp32=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.
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
leal TextLabel, LabelFromBssSegmentquando você tem algo. tipo .bss .lcomm LabelFromBssSegment, 4, você precisaria movl $TextLabel, LabelFromBssSegment, não é?
learequer um destino de registro, mas movpode ter uma imm32fonte 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
mov ax,table_addr
no MASM funciona como
mov ax,word ptr[table_addr]
Portanto, ele carrega os primeiros bytes de table_addre NÃO o deslocamento para table_addr. Você deveria usar
mov ax,offset table_addr
ou
lea ax,table_addr
que funciona da mesma maneira.
leaversão também funciona bem se table_addrfor uma variável local, por exemplo
some_procedure proc
local table_addr[64]:word
lea ax,table_addr
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 leaoperações tratam o uso de parênteses de maneira diferente de comomov .
Pense em C. Digamos que eu tenha uma matriz longque eu chamo array. Agora a expressão array[i]executa uma desreferência, carregando o valor da memória no endereço array + i * sizeof(long)[1].
Por outro lado, considere a expressão &array[i]. Isso ainda contém a subexpressão array[i], mas nenhuma desreferenciação é realizada! O significado de array[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 move lea. Com mov, ocorre uma desreferência que não acontece com lea. Isso ocorre apesar do uso de parênteses que ocorre em ambos. Por exemplo, movq (%r8), %r9e leaq (%r8), %r9. Com mov, esses parênteses significam "desreferência"; com lea, eles não. Isso é semelhante a como array[i]apenas significa "desreferência" quando não há& .
Um exemplo está em ordem.
Considere o código
movq (%rdi, %rsi, 8), %rbp
Isso carrega o valor no local da memória %rdi + %rsi * 8no registro %rbp. Ou seja: obtenha o valor no registro %rdie 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];, onde arrayse torna e se torna %rdie ise torna . o%rsix%rbp8 é o comprimento do tipo de dados contido na matriz.
Agora considere código semelhante que usa lea:
leaq (%rdi, %rsi, 8), %rbp
Assim como o uso de movqcorrespondeu à desreferenciação, o uso leaqaqui corresponde a não desreferenciação. Esta linha de montagem corresponde à linha C x = &array[i];. Lembre-se de que &altera o significado de array[i]desreferenciar para simplesmente especificar um local. Da mesma forma, o uso de leaqaltera 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 %rdie 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 leaqe movqé que movqfaz uma desreferência e leaqnão. De fato, para escrever a leaqdescrição, basicamente copio + colei a descrição de movqe removi "Encontre o valor neste local".
Resumir: movqvs. leaqé complicado porque eles tratam o uso de parênteses, como em (%rsi)e (%rdi, %rsi, 8), de maneira diferente. Em movq(e em todas as outras instruções, exceto lea), esses parênteses denotam uma desreferência genuína, enquanto que leaqeles não são e são uma sintaxe puramente conveniente.
[1] Eu disse que quando arrayé uma matriz de long, a expressão array[i]carrega o valor do endereço array + i * sizeof(long). Isso é verdade, mas há uma sutileza que deve ser abordada. Se eu escrever o código C
long x = array[5];
isso não é o mesmo que digitar
long x = *(array + 5 * sizeof(long));
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 papontando para valores do tipo T. A expressão p + ifaz não significativo "a posição em pmais ibytes". Em vez disso, a expressão p + i realmente significa "a posição em pmais i * sizeof(T)bytes".
A conveniência disso é que, para obter "o próximo valor", basta escrever em p + 1vez dep + 1 * sizeof(T) .
Isso significa que o código C long x = array[5];é realmente equivalente a
long x = *(array + 5)
porque C multiplicará automaticamente o 5por sizeof(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ção sizeof(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 leafaz 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, cccom -O2otimização traduz
long f(long x) {
return x * 5;
}
nas seguintes linhas (linhas irrelevantes removidas):
f:
leaq (%rdi, %rdi, 4), %rax # set %rax to %rdi + %rdi * 4
ret
&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), %eaxapenas 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 registro rdi 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.
MOV EBX , 1
MOV ECX , 2
;//with 1 instruction you got result of 2 registers in 3rd one ...
LEA EAX , [EBX+ECX+5]
EAX = 8
originalmente seria:
MOV EAX, EBX
ADD EAX, ECX
ADD EAX, 5
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:
MOVvai pegar os dados no endereço dentro dos colchetes e colocá- los no operando de destino.LEArealizará 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 por LEAestá 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ícita ADDou MULinstrução (ou equivalente).
Como todos estão mostrando exemplos na sintaxe da Intel, eis alguns na sintaxe da AT&T:
MOVL 16(%ebp), %eax /* put long at ebp+16 into eax */
LEAL 16(%ebp), %eax /* add 16 to ebp and store in eax */
MOVQ (%rdx,%rcx,8), %rax /* put qword at rcx*8 + rdx into rax */
LEAQ (%rdx,%rcx,8), %rax /* put value of "rcx*8 + rdx" into rax */
MOVW 5(%bp,%si), %ax /* put word at si + bp + 5 into ax */
LEAW 5(%bp,%si), %ax /* put value of "si + bp + 5" into ax */
MOVQ 16(%rip), %rax /* put qword at rip + 16 into rax */
LEAQ 16(%rip), %rax /* add 16 to instruction pointer and store in rax */
MOVL label(,1), %eax /* put long at label into eax */
LEAL label(,1), %eax /* put the address of the label into eax */
lea label, %eaxum [disp32]modo de endereçamento absoluto . Use em mov $label, %eaxvez 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 )
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.
lea [labelé um desperdício inútil de bytes versus um mais compacto mov; 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.