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】:

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

如果你想把一个常量放在一个寄存器中,你永远不应该使用leamov $1234, %eaxlea 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 的第一个操作数必须是内存地址,第二个操作数必须是寄存器吗?的主要内容,如果未能解决你的问题,请参考以下文章

存储管理学习笔记

MongoDB各种查询操作详解

使用依赖注入时,如何修复“在前一个操作完成之前在此上下文中启动的第二个操作......”?

CSAPP:第三章程序的机器级表示2

TP关于create方法的第二个参数

对 ITVF 的引用会引发“在前一个操作完成之前在此上下文上启动的第二个操作”异常