我想知道这些指令之间的区别是:
MOV AX, [TABLE-ADDR]
和
LEA AX, [TABLE-ADDR]
我想知道这些指令之间的区别是:
MOV AX, [TABLE-ADDR]
和
LEA AX, [TABLE-ADDR]
Answers:
LEA 表示加载有效地址MOV 表示负载值简而言之,LEA加载指向您要寻址的项目的指针,而MOV加载该地址处的实际值。
的目的LEA是允许用户执行非平凡的地址计算并存储结果[供以后使用]
LEA ax, [BP+SI+5] ; Compute address of value
MOV ax, [BP+SI+5] ; Load value at that address
在仅涉及常量的地方MOV(通过汇编程序的常量计算)有时似乎与的最简单用法重叠LEA。如果您要进行包含多个基地址等的多部分计算,则它很有用。
LAHF:将FLAGS加载到AH寄存器中。在CLR的CIL(这是一个基于更高级别堆栈的抽象机中,术语“ 负载”是指将一个值放到概念堆栈上,通常是l...,而s...等效项是相反的)。这些注释:cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html)建议确实存在确实适用您的区别的体系结构。
使用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
在MASM语法中,用于OFFSET var获取mov-immediate而不是加载。
mov eax, var是与相同的负载,mov eax, [var]并且您必须使用mov eax, OFFSET var来将标签用作立即常量。
lea除了在64位模式下相对RIP寻址之外,是最差的选择。 mov r32, imm32在更多端口上运行。 lea eax, [edx*4]是一个复制移位操作,否则不能在一条指令中完成,但是在同一寄存器中,LEA只需要占用更多字节即可进行编码,因为[eax*4]需要使用disp32=0。(不过,它运行在与shift不同的端口上。)请参阅agner.org/optimize和stackoverflow.com/tags/x86/info。
如果仅指定文字,则没有区别。但是,LEA具有更多功能,您可以在此处阅读有关它们的信息:
http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136
leal TextLabel, LabelFromBssSegment时候,你真的无法做到。就像.bss .lcomm LabelFromBssSegment, 4,您将不得不movl $TextLabel, LabelFromBssSegment,不是吗?
lea需要一个寄存器目标,但是mov可以有一个imm32源和一个存储器目标。当然,此限制并非特定于GNU汇编程序。
MOV AX, [TABLE-ADDR],这是一个负担。因此有一个很大的不同。等效的指示是mov ax, OFFSET table_addr
这取决于使用的汇编程序,因为
mov ax,table_addr
在MASM中
mov ax,word ptr[table_addr]
因此,它将加载的第一个字节,table_addr而不是的偏移量table_addr。您应该改用
mov ax,offset table_addr
要么
lea ax,table_addr
相同的。
lea如果table_addr是局部变量,则版本也可以正常工作
some_procedure proc
local table_addr[64]:word
lea ax,table_addr
以前的答案都没有完全弄清我自己的困惑,因此我想添加自己的答案。
我所缺少的是lea运算对括号的使用不同于对括号的使用mov。
想想C。假设我有一个long我称之为的数组array。现在,表达式array[i]执行取消引用,从内存中的值array + i * sizeof(long)[1] 加载值。
另一方面,考虑表达式&array[i]。它仍然包含子表达式array[i],但是不执行任何解引用!的含义array[i]已更改。它不再意味着执行引用,而是充当一种规范,告诉&我们要寻找的内存地址。如果愿意,您也可以将其&视为“取消”取消引用。
因为这两个用例在许多方面都相似,所以它们共享语法array[i],但是存在或不存在&语法解释方式的更改。没有&,它是一个取消引用,实际上是从数组中读取的。使用&,不是。该值array + i * sizeof(long)仍会计算,但不会取消引用。
与mov和的情况非常相似lea。使用mov时,会发生解除引用,而使用不会发生lea。尽管在这两者中都使用了括号,但这是不可行的。例如movq (%r8), %r9和leaq (%r8), %r9。使用mov,这些括号表示“取消引用”;与lea,他们没有。这类似于array[i]没有时仅表示“取消引用”的意思&。
有一个例子。
考虑代码
movq (%rdi, %rsi, 8), %rbp
这会将存储位置的值加载%rdi + %rsi * 8到寄存器中%rbp。即:获取寄存器中%rdi的值和寄存器中的值%rsi。将后者乘以8,然后将其添加到前者。在此位置查找值并将其放入寄存器%rbp。
该代码对应于C线x = array[i];,在那里array变%rdi和i变%rsi和x变%rbp。的8是包含在数组中的数据类型的长度。
现在考虑使用类似的代码lea:
leaq (%rdi, %rsi, 8), %rbp
正如使用movq对应于取消引用一样,leaq此处的使用也对应于不取消引用。该装配线对应于C线x = &array[i];。回想一下,&这array[i]将从取消引用的含义更改为仅指定位置。同样,使用leaq改变(%rdi, %rsi, 8)从取消引用到指定位置的含义。
这行代码的语义如下:获取寄存器中%rdi的值和寄存器中的值%rsi。将后者乘以8,然后将其添加到前者中。将此值放入寄存器%rbp。不涉及来自存储器的负载,仅涉及算术运算[2]。
请注意,我对leaq和的描述之间的唯一区别movq是进行movq了取消引用,并且leaq没有。实际上,要编写leaq描述,我基本上复制并粘贴了的描述movq,然后删除了“在此位置查找值”。
总结一下:movqvs. leaq是棘手的,因为它们对括号的使用与在(%rsi)和中的使用(%rdi, %rsi, 8)不同。在movq(以及除以外的所有其他指令中lea),这些括号表示真正的取消引用,而在括号中leaq则不是,并且纯粹是方便的语法。
[1]我说过,当array是的数组时long,表达式array[i]从address加载值array + i * sizeof(long)。的确如此,但是有一个微妙之处应该解决。如果我写C代码
long x = array[5];
这是不一样的打字
long x = *(array + 5 * sizeof(long));
似乎应该基于我之前的陈述,但事实并非如此。
发生了什么事,就是C指针加法了。假设我有一个指向ptype值的指针T。表达p + i确实不平均“的位置p加上i字节”。相反,该表达式p + i 实际上表示“ p加i * sizeof(T)字节的位置”。
这样做的方便之处在于获得“未来价值”,我们只需要编写p + 1代替p + 1 * sizeof(T)。
这意味着C代码long x = array[5];实际上等效于
long x = *(array + 5)
因为C会自动繁衍5的sizeof(long)。
因此,在这个StackOverflow问题的背景下,这一切有何关系?这意味着当我说“地址array + i * sizeof(long)”时,并不意味着将“ array + i * sizeof(long)”解释为C表达式。sizeof(long)为了使答案更明确,我自己进行了乘法运算,但是请理解,因此,该表达式不应读取为C。就像使用C语法的普通数学一样。
[2]旁注:由于所有lea操作都是算术运算,因此其参数实际上不必引用有效地址。因此,它通常用于对可能不打算取消引用的值执行纯算术。例如,cc使用-O2优化翻译
long f(long x) {
return x * 5;
}
分为以下内容(删除了不相关的行):
f:
leaq (%rdi, %rdi, 4), %rax # set %rax to %rdi + %rdi * 4
ret
&运算符很不错。也许值得指出的是,LEA是特例,而MOV就像其他每个可以占用内存或寄存器操作数的指令一样。例如,add (%rdi), %eax仅使用寻址模式来寻址存储器,与MOV相同。还相关:在不是地址/指针的值上使用LEA?进一步说明:LEA是如何使用CPU的硬件支持对地址数学进行任意计算。
%rdi”-这是奇怪的措辞。您的意思是应该使用寄存器 中的值rdi。您对“ at”的使用似乎意味着在没有引用的情况下进行内存取消引用。
%rdi或”值“ 中 %rdi ”。您的“寄存器中的值%rdi”很长,但是还不错,也许可以帮助那些难以理解寄存器与内存的人。
基本上……“在计算完之后移入REG……”似乎也可以用于其他目的:)
如果您只是忘记了该值是一个指针,则可以将其用于代码优化/最小化。
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
原来是:
MOV EAX, EBX
ADD EAX, ECX
ADD EAX, 5
如其他答案所述:
MOV将抢在数据括号内的地址和位置数据到目标操作数。LEA将在括号内执行地址的计算,并将计算出的地址放入目标操作数。发生这种情况时并没有实际访问内存并获取数据。所做的工作LEA是在“有效地址”的计算中。因为可以用几种不同的方式来寻址内存(请参见下面的示例),LEA所以有时可将其用于将寄存器加或乘在一起而不使用显式ADD或MUL指令(或等效方法)。
由于每个人都以Intel语法显示示例,因此以下是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, %eax绝对[disp32]寻址模式。使用mov $label, %eax代替。是的,它可以工作,但是效率较低(机器代码更大,并且在更少的执行单元上运行)。既然您提到了AT&T,那么在不是地址/指针的值上使用LEA吗?使用AT&T,我的回答还有其他一些AT&T示例。
MOV可以执行与LEA [label]相同的操作,但是MOV指令包含指令内部的有效地址作为立即数常量(由汇编程序预先计算)。LEA使用PC相对值来计算指令执行期间的有效地址。
lea [label下字节的浪费是毫无意义的mov,因此您应该指定要讨论的条件。同样,对于某些汇编器[label]来说,RIP相对寻址模式的语法也不正确。但是,是的,这是正确的。 如何将功能或标签的地址加载到GNU汇编器中的寄存器中,将更加详细地说明。
差异是微妙的,但很重要。MOV指令实际上是TABLE-ADDR标签所代表的地址的“ MOVe”副本。LEA指令是“加载有效地址”,它是一个间接指令,这意味着TABLE-ADDR指向找到要加载地址的内存位置。
有效地使用LEA等效于使用诸如C之类的语言来使用指针,因此,它是功能强大的指令。