LEA指令的目的是什么?
对我来说,它看起来像一个时髦的MOV。 它的目的是什么,我应该什么时候使用它?
正如其他人指出的那样,LEA(加载有效地址)通常被用作执行某些计算的“技巧”,但这不是它的主要目的。 x86指令集旨在支持像Pascal和C这样的高级语言,其中数组(特别是int或小结构数组)很常见。 例如,考虑一个表示(x,y)坐标的结构:
struct Point
{
int xcoord;
int ycoord;
};
现在想像一下这样的陈述:
int y = points[i].ycoord;
其中points[]
是一个Point
数组。 假设数组的基数已经在EBX
,并且变量i
在EAX
,并且xcoord
和ycoord
每个都是32位(所以ycoord
在结构体中的偏移量是4个字节),可以将此语句编译为:
MOV EDX, [EBX + 8*EAX + 4] ; right side is "effective address"
这将土地y
在EDX
。 比例因子8是因为每个Point
大小是8个字节。 现在考虑与“地址”运算符&相同的表达式:
int *p = &points[i].ycoord;
在这种情况下,你不需要ycoord
的价值,但它的地址。 这就是LEA
(加载有效地址)的地方。编译器可以生成,而不是MOV
LEA ESI, [EBX + 8*EAX + 4]
这将在ESI
加载地址。
Abrash的“大会之禅”:
LEA
,这是执行内存寻址计算但实际上并不寻址内存的唯一指令。 LEA
接受一个标准的存储器寻址操作数,但只不过是将计算的存储器偏移量存储在指定的寄存器中,该寄存器可以是任何通用寄存器。
这给了我们什么? ADD
没有提供两件事:
LEA
不会改变标志。
例子
LEA EAX, [ EAX + EBX + 1234567 ]
计算EAX + EBX + 1234567
(这是三个操作数) LEA EAX, [ EBX + ECX ]
计算EBX + ECX
而不会超出结果。 LEA EAX, [ EBX + N * EBX ]
(N可以是1,2,4,8)。 其他用例很方便循环使用: LEA EAX, [ EAX + 1 ]
和INC EAX
之间的区别在于后者改变了EFLAGS
但前者不改变; 这保持了CMP
状态。
LEA
指令的另一个重要特征是它不会改变条件代码(如CF
和ZF
,同时通过ADD
或MUL
等算术指令来计算地址。 该特性降低了指令间的依赖程度,因此为编译器或硬件调度程序的进一步优化留出了空间。