x86-64 指令集,AT&T 语法,关于 lea 和括号的混淆

x86-64 instruction set, AT&T syntax, confusion regarding lea and brackets

有人告诉我 lea %rax, %rdx 是无效语法,因为源代码需要放在括号中,即 lea (%rax), %rdx

我想我显然误解了 lea 和方括号的用途。

我以为 lea %rax, %rdx 会将存储在 %rax 中的内存地址移动到 %rdx,但显然这就是 lea (%rax), %rdx 的作用?

让我感到困惑的是,我认为括号表示转到内存中的一个地址,并获取该地址的值。因此,通过使用括号,lea 会将存储在 %rax 中的内存地址中的值移动到目标寄存器中。

因此我认为如果您只是想将存储在 %rax 中的地址移动到 %rdx 中,将使用 lea %rax、%rdx

有人可以向我解释括号在 lea 指令中的意义吗?

永远不要真正使用 lea (%rax), %rdx。使用 mov %rax, %rdx 因为 CPUs 运行 它更有效,并且两种方式都复制寄存器 value (不管该值是否是有效指针或不)。

LEA只能工作在内存寻址模式下,不能工作在裸寄存器上。 LEA 有点“撤消”括号,采用地址计算的结果而不是该地址内存中的值。如果一开始就没有内存操作数,就不会发生这种情况。

这使您可以使用它对任意值执行 shift/add 操作,无论它们是否为有效指针: LEA 使用 内存-使用 x86 的正常寻址模式编码将 shift/add 操作编码为单个指令的操作数语法和机器代码 CPU 硬件已经知道如何解码。


mov相比,它就像一个C &寻址运算符。而且您不能获取寄存器的地址。 (或者在 C 语言中,register 变量。) 你只能用它来取消引用。

  register char *rax = ...;

  register char dl = *rax;       // mov   (%rax), %dl
  register char *rcx = rax;      // mov   %rax, %rcx
  register char *rdi = &rax[0];  // lea   (%rax), %rdi  // never do this, mov is more efficient
  register char *rbx = &rax[rdx*4 + 1234];  // lea 1234(%rax, %rdx, 4), %rbx  // a real use-case
  
  register char **rsi = &rax;    // lea %rax, %rsi   // ERROR: can't take the address of a register

当然,如果您要求实际的 C 编译器编译它,您会得到 mov %rax, %rdi,而不是 lea (%rax), %rdi,即使它没有优化代码。 这是概念上的等价物,使用 C 语法和运算符来解释 asm,而不是说明任何东西将或应该如何实际编译。