leaq 的第一个操作数必须是内存地址,第二个操作数必须是寄存器吗?

Must the first operand of leaq be a memory address and the second operand be a register?

在ATT汇编语言中,使用leaq指令时,其第一个操作数是否必须是内存地址而不是寄存器或常量(前缀$)?它的第二个操作数必须是寄存器吗?我从阅读《计算机系统:程序员的视角》中得到了这种印象,并且从未见过与我的猜测不同的例子。谢谢

是的,没错。虽然技术上可以对具有两个寄存器操作数的 lea 进行编码,但这样的编码是无效的并会导致 #UD 异常。有关详细信息,请参阅 this reference or this one

即使它是可编码的,您也永远不想使用它。

如果你想把一个常量放在一个寄存器中,你不应该使用leamov 34, %eaxlea 1234, %eax(disp32 寻址模式中的绝对地址)更短且更高效。

LEA 静态地址的唯一用例是具有 RIP 相对寻址模式的 64 位代码,例如 lea symbol(%rip), %rax(7 字节),在 mov $symbol, %eax(5 字节)的情况下不可用,因为您需要与位置无关的代码,and/or 地址不适合 32 位零扩展立即数。

有关 mov $symbol, %rdi 为何不是最佳选择的更多信息,请参阅


在 32 位代码中,lea symbol, %edi 是 6 个字节(opcode + modrm + disp32),并且在 Intel Sandybridge 系列 CPU 上只运行一个端口 1 或端口 5。 (https://agner.org/optimize/)

mov $symbol, %edi 是 5 个字节 (opcode + imm32 short form with no ModRM byte),并在任何 ALU 端口上运行。

16 位代码相同:mov $symbol, %di 是 3 个字节,而 lea symbol, %di 是 4 个字节,具有相同的执行端口差异。 (或者在 NASM 语法中,lea di, [symbol]mov di, symbol,或者 GAS .intel_syntax 或 MASM 中的 mov di, OFFSET symbol。)


不过,LEA 对于 base=register 寻址模式很有用。如果地址适合 32 位符号扩展 disp32,则像 lea symbol(%rdi), %rax

或用于任意移位和添加用法,例如 lea 123(%rdi, %rdi, 2), %eax 执行 eax = 3*edi + 123