leaq 的第一个操作数必须是内存地址,第二个操作数必须是寄存器吗?
Posted
技术标签:
【中文标题】leaq 的第一个操作数必须是内存地址,第二个操作数必须是寄存器吗?【英文标题】:Must the first operand of leaq be a memory address and the second operand be a register? 【发布时间】:2018-10-25 22:09:06 【问题描述】:在ATT汇编语言中,使用leaq指令时,它的第一个操作数必须是内存地址而不是寄存器或常量(前缀$
)吗?它的第二个操作数必须是寄存器吗?我从阅读 Computer Systems: a Programmer's Perspective 中获得了这种印象,并且从未见过与我的猜测不同的示例。谢谢。
【问题讨论】:
是的,没错。如有疑问,请查阅官方指令集参考。请注意,内存地址当然可以使用寄存器或位移(没有$
,因为那将是立即数)。
谢谢。我在网上找了一个参考文档,但我不确定它在哪里。
Intel® 64 and IA-32 Architectures Software Developer Manuals PS:使用 intel 语法而不是 at&t 所以你必须做一些脑力劳动:)
@Jester 谢谢。什么样的脑力劳动?
@fuz,您可能想删除该评论并重试。
【参考方案1】:
是的,没错。虽然在技术上可以对具有两个寄存器操作数的lea
进行编码,但这样的编码是无效的并导致#UD
异常。详情请见this reference 或this one。
【讨论】:
【参考方案2】:即使它是可编码的,你也永远不想使用它。
如果你想把一个常量放在一个寄存器中,你永远不应该使用lea
。 mov $1234, %eax
比 lea 1234, %eax
(disp32 寻址模式中的绝对地址)更短且更高效。
LEA 用于静态地址的唯一用例是具有 RIP 相对寻址模式的 64 位代码,例如 lea symbol(%rip), %rax
(7 个字节),在 mov $symbol, %eax
(5 个字节)由于您需要而无法使用的情况下与位置无关的代码,和/或地址不适合 32 位零扩展立即数。
请参阅Difference between movq and movabsq in x86-64 了解为什么mov $symbol, %rdi
不是最佳选择。
在 32 位代码中,lea symbol, %edi
为 6 个字节(操作码 + modrm + disp32),并且在英特尔 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 中的 mov di, OFFSET symbol
.intel_syntax
或 MASM。)
不过,LEA 对 base=register 寻址模式很有用。如果地址适合 32 位符号扩展 disp32,则类似于 lea symbol(%rdi), %rax
。
或用于任意移位和添加用法,例如 lea 123(%rdi, %rdi, 2), %eax
执行 eax = 3*edi + 123
。 Using LEA on values that aren't addresses / pointers?
【讨论】:
以上是关于leaq 的第一个操作数必须是内存地址,第二个操作数必须是寄存器吗?的主要内容,如果未能解决你的问题,请参考以下文章