对我来说,这就像一部时髦的电影。它的用途是什么?我应该什么时候使用它?


当前回答

尽管有各种解释,LEA是一种算术运算:

LEA Rt, [Rs1+a*Rs2+b] =>  Rt = Rs1 + a*Rs2 + b

只是它的名字对于shift+add操作来说非常愚蠢。其原因已经在最高评级的答案中解释过(即,它是为了直接映射高级内存引用而设计的)。

其他回答

lea是“加载有效地址”的缩写。它将源操作数的位置引用地址加载到目标操作数。例如,您可以使用它:

lea ebx, [ebx+eax*8]

用一条指令进一步移动ebx指针eax项(在64位/元素数组中)。基本上,您可以从x86体系结构支持的复杂寻址模式中受益,从而有效地操作指针。

8086有一大系列指令,它们接受寄存器操作数和有效地址,执行一些计算以计算该有效地址的偏移部分,并执行一些涉及寄存器和由计算地址引用的存储器的操作。除了跳过实际的内存操作之外,让该家族中的一个指令的行为与上面一样非常简单。因此,说明:

mov ax,[bx+si+5]
lea ax,[bx+si+5]

在内部实现几乎相同。区别在于跳过了一步。这两个指令的作用类似于:

temp = fetched immediate operand (5)
temp += bx
temp += si
address_out = temp  (skipped for LEA)
trigger 16-bit read  (skipped for LEA)
temp = data_in  (skipped for LEA)
ax = temp

至于英特尔为什么认为这条指令值得包括在内,我并不完全确定,但它的实现成本低是一个重要因素。另一个因素是Intel的汇编器允许相对于BP寄存器定义符号。如果fnord被定义为BP相对符号(例如BP+8),可以说:

mov ax,fnord  ; Equivalent to "mov ax,[BP+8]"

如果想使用stosw之类的东西将数据存储到BP的相对地址

mov ax,0 ; Data to store
mov cx,16 ; Number of words
lea di,fnord
rep movs fnord  ; Address is ignored EXCEPT to note that it's an SS-relative word ptr

比:

mov ax,0 ; Data to store
mov cx,16 ; Number of words
mov di,bp
add di,offset fnord (i.e. 8)
rep movs fnord  ; Address is ignored EXCEPT to note that it's an SS-relative word ptr

注意,忘记世界“偏移”将导致位置[BP+8]的内容而不是值8被添加到DI中。哎呀。

所有正常的“计算”指令,如加法、异或设置状态标志,如零、符号。如果使用复杂的地址AX-xor:=mem[0x33+BX+8*CX],则标志将根据xor操作设置。

现在您可能需要多次使用该地址。将这样的地址加载到寄存器中并不是为了设置状态标志,幸运的是,它并没有。短语“加载有效地址”使程序员意识到这一点。这就是奇怪表情的由来。

很明显,一旦处理器能够使用复杂的地址来处理其内容,它就能够将其计算用于其他目的。实际上,它可以用于在一条指令中执行转换x<-3*x+1。这是汇编程序设计中的一条一般规则:无论它如何摇晃你的船,都要使用指令。唯一重要的是指令所体现的特定转换是否对您有用。

要旨

MOV, X| T| AX'| R| BX|

and

LEA, AX'| [BX]

对AX具有相同的影响,但对状态标志没有影响。(这是ciasdis符号。)

LEA指令的另一个重要特征是它不会改变条件代码(如CF和ZF),而通过ADD或MUL等算术指令计算地址则会改变。这一特性降低了指令之间的依赖程度,从而为编译器或硬件调度器的进一步优化腾出了空间。

来自Abrash的“装配禅”:

LEA,唯一执行内存寻址计算但实际上不寻址内存的指令。LEA接受标准内存寻址操作数,但只会将计算出的内存偏移量存储在指定寄存器中,该寄存器可以是任何通用寄存器。这给了我们什么?ADD没有提供的两件事:使用两个或三个操作数执行加法的能力,以及将结果存储在任何寄存器中的能力;而不仅仅是源操作数之一。

执法机关不改变旗帜。

示例

LEA EAX,[EAX+EBX+1234567]计算EAX+EBX+134567(即三个操作数)LEA EAX,[EBX+ECX]计算EBX+ECX,而不使用结果覆盖两者。乘以常数(乘以2、3、5或9),如果你像LEA EAX那样使用,[EBX+N*EBX](N可以是1,2,4,8)。

其他用例在循环中很方便:LEA EAX、[EAX+1]和INC EAX之间的区别在于后者更改EFLAGS,但前者不更改;这保持了CMP状态。