提问人:Guichi 提问时间:11/3/2023 最后编辑:Sep RolandGuichi 更新时间:11/5/2023 访问量:93
为什么需要LEA(负载有效地址)?
Why is LEA (Load Effective Address) necessary?
问:
首先,我了解它们可以达到的结果之间的区别,简单地说:lea
mov
mov eax, ebp ;put the value in ebp register into eax register
lea eax, [ebp] ;same as above, and they are equivalent
然而:
mov eax, ebp+8 ;invalid register set size
lea eax, [ebp+8] ;calculate in sum of ebp value and 8, then assign it to eax
那么,为什么虽然可以,但是非法的呢?我的书说:mov eax, ebp+8
lea eax, [ebp+8]
MOV 存储到 EAX 中的值必须由汇编程序计算 (也就是说,它最终必须是一个常数)
但这对我来说毫无意义!CONSTANT是什么意思?显而易见的理解是,在程序运行之前,CONSTANT应该由汇编器/链接器计算,但是,考虑是一个LEGAL指令。在程序运行之前,汇编器/链接器无法知道(作为 C 术语)的值!mov eax, [ebp+8]
[ebp+8]
*(ebp+8)
答:
你的书似乎只在谈论 mov-immediate,比如 or(符号地址)。mov eax, 1234
mov eax, foo
一个更完整的规则是,源操作数必须是:mov
- 链接时间常量值 (mov-immediate)
- 或已存在于其他位置的值(内存或其他寄存器)。
mov
只能复制,在写入目的地之前,它不会通过 ALU 馈送数据。
内存源操作数可以使用寻址模式(如 或),但这与从该地址加载或存储到该地址的数据发生的情况无关。地址生成是在 CPU 的单独部分完成的(或者在原始 8086 上,在处理指令的微码的单独阶段)。[ebp+8]
[edx + eax*4 + my_array]
x86 的机器码格式对几乎所有指令的寻址模式(以及寄存器与内存源)的编码方式都相同,因此不必做任何特殊的事情来支持,就像在进行地址计算和加载后,它也对数据进行添加。只有操作码字节之间不同,指定如何处理数据,而不是操作数的位置(寄存器与内存寻址模式)。(大多数指令有两个操作码,一个是源可以是内存,另一个是目标可以是内存。
我在这里说的是两个内存源操作码。mov
mov eax, [ebp+8]
add eax, [ebp+8]
的机器码也与操作码字节以外的机器码相同。LEA 的特殊之处在于,它使用寻址模式的机器码格式来编码实际上不访问内存的 shift/add 指令。请参阅对不是地址/指针的值使用 LEA?lea eax, [ebp+8]
没有其他指令可以做到这一点,因此您永远不能用作任何指令的源操作数。例如,没有办法只用一条指令来做。eax+3
imul ecx, eax+3
有两个独立的概念:指令的数据来源(即时、寄存器或由寻址模式寻址的内存)与该数据发生的情况(复制、imul、sub、popcount 和...)。从这个意义上说,LEA 没有数据输入,因为它不会取消 .[ebp+8]
LEA 只获取地址,就像 C 的 address-of 运算符一样,它抵消了本来会被取消引用的内容,例如 相同,但语法不同。LEA 有趣的一件事是 x86 ADD 只能执行 ,就地修改寄存器(或内存),但 LEA 可以像 .&
&ptr[3]
ptr+3
ptr+=3
tmp = &ptr[3]
(对于 32 位或 64 位寻址模式,还可以添加移位寄存器,例如tmp = &ptr[x*4 + 3]
)
有点跑题了,但可能相关:
大多数经典的 x86 整数指令只有一个或两个操作数(x86 cpu 有什么样的地址指令?虽然可以在将结果写入不是输入的寄存器时进行数学运算。(与大多数直接源指令不同,这种新的 in-186 形式不会将 ModRM 中的字段作为额外的操作码位窃取,因此它可以有 3 个操作数,包括由操作码发出信号的直接操作数。但是,硬件乘法器的输入仍然是直接来自内存的即时值; 不可编码。imul ecx, [edx+8], 123
imul
/r
imul ecx, edx+8, 123
评论
mov
add
[ebp+3]
ebp+3
lea
out of thin air
ebp+3
lea
mov
add
ebp+3
[ebp+3]
[ebp+3]
&
[ebp+3]
评论
MOVL $8(BP), AX
lea eax, [rbp+8]