可能重复:
LEA 指令的目的是什么?
当我需要某个地址的值时,我可以使用有效地址,例如
push dword [str+4]
。但是,当我需要引用地址时,我无法使用 push dword str+4
(这对我来说是明显且直观的方法)。
而是需要使用
lea EAX, [str+4]
,然后使用 push EAX
。这有点令人困惑,并且还提供了额外的处理器指令,尽管是“零时钟”指令。 (参见
编辑:
好吧,这个评论问了和我同样的问题。正如 Lucero 的回答一样,在此评论中得到了回答 - X86 不支持此类寻址。
(编者注:这不是同一个问题。str+4
是一个链接时间常数,与涉及寄存器的
EBX + 8*EAX + 4
不同。)
offset 关键字:
push offset str+4
LEA 指令可以方便地使用地址生成逻辑的管道。 提供非常便宜的不使用 ALU 的加法和乘法方法。 对于编写代码生成器的程序员来说,这是最重要的技巧。 这里不需要,afaic。
push str+4
确实可以在32位代码中工作,假设
str
是像
str:
这样的普通符号/标签,而不是像
%define str edi
这样的寄存器的宏别名或其他东西。在符号地址上,
str+4
是由链接器在构建可执行文件时使用 NASM 创建的
.o
目标文件中的重定位条目计算的。
push str+4
的机器代码包含与
lea eax, [str+4]
相同的 4 字节绝对地址(作为 imm32)(作为寻址模式中的 disp32)。 (这不是 64 位代码,因此不可能使用
default rel
进行 RIP 相对寻址。)
str
实际上是寄存器的宏,那么
+4
计算必须在运行时进行,并且您确实需要来自
push
的单独指令。汇编指令直接表示 x86 操作码(不像高级语言那样进行转换编译)。操作码在其所能表示的内容方面有其局限性;因此,虽然地址计算可以作为 x86 寻址的一部分,但值计算却不能。 LEA 通过将地址计算的结果存储在任何寄存器中而不是仅在内部消耗它来弥补这一差距。
lea
肯定是不是零时钟指令。其中有一些,例如 Sandy Bridge 上的
fxch
(针对所有具有寄存器重命名的内容)、
nop
(
90
和
0F 1F
),以及将寄存器设置为零的某些习惯用法(
xor
或)
sub
本身,甚至对于 XMM 寄存器),也在 Sandy Bridge 上,以及在较新的处理器上,
mov
-ing 互相注册(但奇怪的是,这不是一个具有相同 64 位寄存器作为源和目标的无操作 mov)。 “消除”移动和归零习语仍然具有有限的吞吐量(即使限制非常高,例如每个周期 5 个),因此它们并不是完全免费的。最近,英特尔 Golden Cove 在重命名期间处理的操作中添加了具有较小立即值的附加内容,使它们在某些情况下看起来具有零延迟。到目前为止,
lea
始终至少需要一个周期(可能某些形式的
lea
,例如
lea r1, [r2 + imm8]
可以由未来处理器上的前端处理,类似于 Golden Cove 上的
add r, imm8
),它是通常在 ALU 而不是 AGU 上执行(某些 AMD 和 Atom 是例外),但即使在其执行于AGU仍然需要一个周期或更长时间。
lea
甚至可能需要超过1个周期,例如P4、Sandy Bridge(似乎我在这篇文章中多次提到SB..)或AMD处理器上的缩放
lea
。事实上,在 AMD K10 上,进入 AGU 的
lea
是慢速情况,它被缩放和/或具有 3 个参数,并且比进入 ALU 的快速循环需要更长的周期。
LEA
让你可以“寻址”内存而不需要寻址它,这在保护模式下非常有用,在保护模式下,一个小的指针失误就会杀死你的应用程序(在实模式下,指针失误可能会杀死 DOS、Windows、机器,这可能会更好) ,并杀死任意数量的东西)。汇编是一种有限的野兽,其中每条指令对应一个“物理电路”。这些说明是通用的,这本身就是一个小奇迹。