MOV和LEA有什么区别?

nav*_*een 117 x86 assembly instruction-set

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

MOV AX, [TABLE-ADDR]
Run Code Online (Sandbox Code Playgroud)

LEA AX, [TABLE-ADDR]
Run Code Online (Sandbox Code Playgroud)

Rub*_*ink 144

  • LEA 加载有效地址
  • MOV 表示负载值

简而言之,LEA加载指向您正在寻址的项目的指针,而MOV加载该地址的实际值.

目的LEA是允许一个人执行非平凡的地址计算并存储结果[供以后使用]

LEA ax, [BP+SI+5] ; Compute address of value

MOV ax, [BP+SI+5] ; Load value at that address
Run Code Online (Sandbox Code Playgroud)

如果只涉及常数,MOV(通过汇编程序的常量计算)有时似乎与最简单的使用情况重叠LEA.如果您具有多个基址的多部分计算等,它很有用.

  • +1感谢清楚的解释,帮助我[回答](http://stackoverflow.com/a/25824111/183120)另一个问题. (6认同)
  • @josephGarvin IIRC 术语 fetch 将应用于该方面;加载就是您从头开始替换寄存器中的值的方式。例如`LAHF`是:_Load FLAGS into AH register_。在 CLR 的 CIL(它是一种基于堆栈的更高级别的抽象机,术语 _load_ 指的是将一个值放到概念堆栈上,通常是 `l`...,而 `s`... 等价物则相反) . 这些注释:https://www.cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html)表明确实存在适用于您的区别的架构。 (2认同)
  • @JosephGarvin:在计算机体系结构术语中,如果您谈论的是它的内部工作原理,那么“加载”一词可以用于任何写入寄存器的内容。特别是在 RISC ISA 中,立即加载(只是将机器代码中的常量放入寄存器中),例如 MIPS `lui $t0, 1`(立即加载上层),它将 `$a0` 设置为 `1<<16` ,尽管在这种情况下该值作为机器代码的一部分存在于内存中。(或者在像 AArch64 这样的现代 ISA 中,以某种方式进行编码,而不是字面上的编码。)是的,当代码中有像“在 EDI 中存储 1”这样的注释时,它让我发疯。 (2认同)

Ami*_*mar 43

在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
Run Code Online (Sandbox Code Playgroud)

在MASM语法中,用于OFFSET var获取mov-immediate而不是load.

  • 仅限NASM语法.在MASM语法中,`mov eax,var`是一个加载,与`mov eax,[var]`相同,你必须使用`mov eax,OFFSET var`来使用标签作为直接常量. (2认同)
  • 请注意,在所有这些示例中,除了用于 RIP 相对寻址的 64 位模式外,“lea”是最差的选择。`mov r32, imm32` 在更多端口上运行。`lea eax, [edx*4]` 是一种复制和移位,否则无法在一条指令中完成,但在同一个寄存器中,LEA 只需要更多字节进行编码,因为 `[eax*4]` 需要一个`disp32=0`。(不过,它运行在与班次不同的端口上。)请参阅http://agner.org/optimize/和https://stackoverflow.com/tags/x86/info。 (2认同)

Bil*_*ter 26

指令MOV reg,addr意味着将存储在地址addr中的变量读入寄存器reg.LEA reg,addr指令意味着将地址(不是存储在地址中的变量)读入寄存器reg.

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


Que*_*lef 14

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

我所缺少的是,lea操作处理括号的使用方式与处理方式不同mov

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

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

因为这两个用例在许多方面相似,所以它们共享语法array[i],但是 a 的存在与否会&改变该语法的解释方式。没有&,它是一个取消引用,实际上是从数组中读取的。与&,它不是。该值array + i * sizeof(long)仍会计算,但不会取消引用。

情况与mov和非常相似lea。With mov,会发生取消引用,而lea. 尽管在两者中都使用了括号。例如,movq (%r8), %r9leaq (%r8), %r9。有了mov这些括号表示“解引用”; 有lea,他们没有。这类似于 how array[i]only 在没有&.

一个例子是有序的。

考虑代码

movq (%rdi, %rsi, 8), %rbp
Run Code Online (Sandbox Code Playgroud)

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

此代码对应于 C 行x = array[i];,其中array变成%rdii变成%rsix变成%rbp。的8是包含在数组中的数据类型的长度。

现在考虑使用的类似代码lea

leaq (%rdi, %rsi, 8), %rbp
Run Code Online (Sandbox Code Playgroud)

正如使用movq相当于取消引用,使用leaq此对应于提领。这条装配线对应于 C 线x = &array[i];。回想一下,&array[i]取消引用的含义更改为简单地指定位置。同样, 的使用leaq将 的含义(%rdi, %rsi, 8)从取消引用更改为指定位置。

这行代码的语义如下:获取寄存器中%rdi的值和寄存器中的值%rsi。将后者乘以 8,然后将其与前者相加。将此值放入寄存器%rbp。不涉及内存加载,只涉及算术运算 [2]。

请注意,我对leaq和 的描述之间的唯一区别movqmovq取消了引用,而leaq没有取消引用。其实写leaq描述,我基本上就是把 的描述复制+粘贴movq,然后去掉“在这个位置查找值”。

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


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

long x = array[5];
Run Code Online (Sandbox Code Playgroud)

这和打字一样

long x = *(array + 5 * sizeof(long));
Run Code Online (Sandbox Code Playgroud)

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

发生的事情是 C 指针添加有一个技巧。假设我有一个指向ptype 值的指针T。表达p + i确实平均“的位置p加上i字节”。相反,该表达式p + i 实际上表示“pi * sizeof(T)字节的位置”。

这样做的便利在于,要获得“下一个值”,我们只需编写p + 1而不是p + 1 * sizeof(T).

这意味着 C 代码long x = array[5];实际上等效于

long x = *(array + 5)
Run Code Online (Sandbox Code Playgroud)

因为C会自动繁衍5sizeof(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;
}
Run Code Online (Sandbox Code Playgroud)

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

f:
  leaq (%rdi, %rdi, 4), %rax  # set %rax to %rdi + %rdi * 4
  ret
Run Code Online (Sandbox Code Playgroud)

  • 是的,很好的解释,比其他答案更详细,是的,C 的 `&` 运算符是一个很好的类比。也许值得指出的是,LEA 是特例,而 MOV 就像其他可以采用内存或寄存器操作数的指令一样。例如`add(%rdi),%eax`只是使用寻址方式来寻址内存,与MOV相同。还相关:[在不是地址/指针的值上使用 LEA?](//stackoverflow.com/a/46597375) 进一步解释了这一点:LEA 是如何使用 CPU 对地址数学的硬件支持来进行任意计算。 (2认同)

Lar*_*s D 10

如果您只指定文字,则没有区别.LEA有更多的能力,你可以在这里阅读它们:

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

  • 另外,这个答案基本上是错误的,因为问题是询问“MOV AX,[TABLE-ADDR]”,这是一个负载。所以有一个很大的区别。等效指令是“mov ax, OFFSET table_addr” (2认同)

Bar*_*cik 10

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

mov ax,table_addr
Run Code Online (Sandbox Code Playgroud)

在MASM中工作

mov ax,word ptr[table_addr]
Run Code Online (Sandbox Code Playgroud)

所以它加载了第一个字节table_addr而不是偏移量table_addr.你应该使用

mov ax,offset table_addr
Run Code Online (Sandbox Code Playgroud)

要么

lea ax,table_addr
Run Code Online (Sandbox Code Playgroud)

它的工作方式相同.

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

some_procedure proc

local table_addr[64]:word

lea ax,table_addr
Run Code Online (Sandbox Code Playgroud)

  • x86指令MOV和LEA之间的区别绝对不依赖于汇编程序. (5认同)
  • @Bartosz Wójcik - 为什么你要回滚编辑,使你的答案的格式/风格变得更糟?在一行中使用“coz”(“because”的俚语拼写)本身只会减慢读者的速度并分散读者的注意力。 (2认同)
  • @BartoszWójcik 编辑得很好。避免进一步回滚。请参阅 https://stackoverflow.com/help/editing (2认同)

Sir*_*dom 6

如其他答案所述:

  • 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 */
Run Code Online (Sandbox Code Playgroud)