繁体   English   中英

MOV 和 LEA 有什么区别?

[英]What is the difference between MOV and LEA?

我想知道这些说明之间的区别是什么:

MOV AX, [TABLE-ADDR]

LEA AX, [TABLE-ADDR]
  • 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使用的最简单情况重叠。 如果您有多个基地址等的多部分计算,它很有用。

在 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 reg,addr 表示将存储在地址 addr 的变量读入寄存器 reg。 指令 LEA reg,addr 表示将地址(不是存储在地址处的变量)读入寄存器 reg。

MOV指令的另一种形式是MOV reg,immdata,意思是将立即数(即常数)immdata读入寄存器reg。 请注意,如果 LEA reg,addr 中的 addr 只是一个常量(即固定偏移量),那么该 LEA 指令本质上与加载与立即数据相同的常量的等效 MOV reg,immdata 指令完全相同。

以前的答案都没有完全解决我自己的困惑,所以我想添加我自己的。

我所缺少的是lea操作对待括号的使用与mov不同。

想想 C。假设我有一个long数组,我称之为array 现在表达式array[i]执行取消引用,从内存中的地址array + i * sizeof(long) [1] 加载值。

另一方面,考虑表达式&array[i] 这仍然包含子表达式array[i] ,但没有执行解引用! array[i]的含义已经改变。 它不再手段来执行的尊重,而是作为一种规范的,告诉&什么内存地址,我们要寻找的。 如果您愿意,您也可以将&视为“取消”取消引用。

由于两个用例在许多方面是相似的,它们共享语法array[i]但存在或不存在的&变化该语法的解释方式。 没有& ,它是一个取消引用,实际上是从数组中读取的。 & ,它不是。 value array + i * sizeof(long)仍然被计算,但它没有被取消引用。

这种情况与movlea非常相似。 使用mov时,会发生lea不会发生的取消引用。 尽管在两者中都使用了括号。 例如, movq (%r8), %r9leaq (%r8), %r9 对于mov ,这些括号表示“取消引用”; lea ,他们没有。 这类似于array[i]在没有&时仅表示“取消引用”的方式。

一个例子是有序的。

考虑代码

movq (%rdi, %rsi, 8), %rbp

这会将内存位置%rdi + %rsi * 8处的值加载到寄存器%rbp 即:获取寄存器%rdi的值和寄存器%rsi的值。 将后者乘以 8,然后将其与前者相加。 找到该位置的值并将其放入寄存器%rbp

此代码对应于 C 行x = array[i]; ,其中array变为%rdii变为%rsix变为%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]。

请注意,我对leaqmovq描述之间的唯一区别是movq会取消引用,而leaq不会。 其实写leaq描述,我基本上是复制+粘贴了movq的描述,然后去掉了“Find the value at this location”。

总结一下: movqleaq比较棘手,因为他们对待括号的使用,如(%rsi)(%rdi, %rsi, 8) ,不同。 movq (以及除lea之外的所有其他指令)中,这些括号表示真正的取消引用,而在leaq它们不是,并且纯粹是方便的语法。


[1] 我说过当arraylong的数组时,表达式array[i]从地址array + i * sizeof(long)加载值。 这是真的,但有一个微妙之处需要解决。 如果我写C代码

long x = array[5];

这和打字一样

long x = *(array + 5 * sizeof(long));

好像应该是根据我之前的说法,其实不然。

发生的事情是 C 指针添加有一个技巧。 假设我有一个指针p指向类型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所做的只是算术运算,它的参数实际上不必引用有效地址。 出于这个原因,它通常用于对可能不打算取消引用的值执行纯算术。 例如,带有-O2优化的cc翻译

long f(long x) {
  return x * 5;
}

进入以下(删除不相关的行):

f:
  leaq (%rdi, %rdi, 4), %rax  # set %rax to %rdi + %rdi * 4
  ret

如果仅指定文字,则没有区别。 不过,LEA 拥有更多能力,您可以在此处阅读有关它们的信息:

http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136

这取决于使用的汇编程序,因为

mov ax,table_addr

在 MASM 中作为

mov ax,word ptr[table_addr]

所以,它加载从第一个字节table_addr而不是偏移table_addr 你应该改用

mov ax,offset table_addr

或者

lea ax,table_addr

它的工作原理相同。

如果table_addr是局部变量, lea版本也可以正常工作,例如

some_procedure proc

local table_addr[64]:word

lea ax,table_addr

正如其他答案中所述:

  • MOV括号和地方数据到目标操作数内的地址获取数据
  • LEA将执行括号内地址的计算,并将计算出的地址放入目标操作数。 发生这种情况时并没有真正进入内存并获取数据。 LEA所做的工作是计算“有效地址”。

由于内存可以通过多种不同的方式寻址(参见下面的示例), LEA有时用于在不使用显式ADDMUL指令(或等效指令)的情况下将寄存器相加或相乘。

由于每个人都使用 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 */

基本上......“进入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 eax, [ebx] 和

lea eax, [ebx] 假设 ebx 中的值为 0x400000。 然后 mov 将转到地址 0x400000 并将呈现它们的 4 字节数据复制到 eax 寄存器。而 lea 将地址 0x400000 复制到 eax。 所以,每条指令执行后,eax 在每种情况下的值都会是(假设在内存 0x400000 处包含的是 30)。

eax = 30 (在 mov 的情况下) eax = 0x400000 (在 lea 的情况下) 对于定义 mov 将数据从 rm32 复制到目标 (mov dest rm32) 和 lea(加载有效地址) 将地址复制到目标 (mov dest rm32) )。

MOV 可以做与 LEA [label] 相同的事情,但 MOV 指令包含指令本身内部的有效地址作为立即数(由汇编程序预先计算)。 LEA 使用 PC-relative 来计算指令执行期间的有效地址。

LEA(加载有效地址)是一种移位加法指令。 它被添加到 8086 是因为硬件可以解码和计算寻址模式。

区别很微妙,但很重要。 MOV 指令是一个“MOVe”,实际上是 TABLE-ADDR 标签所代表的地址的副本。 LEA 指令是“加载有效地址”,它是一条间接指令,这意味着 TABLE-ADDR 指向要加载的地址所在的内存位置。

有效地使用 LEA 相当于在 C 等语言中使用指针,因此它是一条强大的指令。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM