4

movq首先,我对和之间的区别有点困惑movabsq,我的教科书说:

常规movq指令只能具有可以表示为 32 位二进制补码的立即源操作数。然后对该值进行符号扩展以生成目标的 64 位值。该movabsq指令可以有一个任意的 64 位立即数作为其源操作数,并且只能有一个寄存器作为目标。

我对此有两个问题。

问题 1

movq指令只能具有可以表示为 32 位二进制补码数的直接源操作数。

所以这意味着我们不能做

movq    $0x123456789abcdef, %rbp

我们必须这样做:

movabsq $0x123456789abcdef, %rbp

但是为什么movq设计为不适用于 64 位立即数,这确实违背了q(四分字)的目的,我们需要另一个movabsq只是为了这个目的,不是很麻烦吗?

问题2

由于目标movabsq必须是寄存器,而不是内存,所以我们不能将 64 位立即数移动到内存中:

movabsq $0x123456789abcdef, (%rax)

但有一个解决方法:

movabsq $0x123456789abcdef, %rbx
movq    %rbx, (%rax)   // the source operand is a register, not immediate constant, and the destination of movq can be memory

那么为什么该规则旨在使事情变得更难呢?

4

2 回答 2

8

是的,与-1aka不同,将不适合符号扩展的 32 位的立即数移至寄存器,然后移至内存0xFFFFFFFFFFFFFFFF。但是,为什么部分是有趣的问题:


请记住,asm 只允许您做机器代码中可能的事情。因此,这确实是一个关于 ISA 设计的问题。这样的决定通常涉及硬件解码的容易程度,以及编码效率的考虑。(在很少使用的指令上使用操作码会很糟糕。)

它不是为了让事情变得更难,而是为了不需要任何新的操作码mov 并且还将 64 位立即数限制为一种特殊的指令格式。 mov是唯一可以使用 64 位立即数或 64 位绝对地址,用于加载/存储 AL/AX/EAX/RAX)的指令。

查看英特尔的手册以了解以下形式(请注意,它首先使用英特尔语法,目标优先,我的答案也是如此。)我还总结了movx86-64 中 movq 和 movabsq 之间的差异中的形式(及其指令长度),正如所做的那样@MargaretBloom 回答x86-64 AT&T 指令 movq 和 movabsq 有什么区别?.

允许 imm64 和 ModR/M 寻址模式也可以很容易地达到指令长度的 15 字节上限,例如 REX + opcode + imm64 是 10 字节,而 ModRM+SIB+disp32 是 6。所以mov [rdi + rax*8 + 1234], imm64即使有mov r/m64, imm64.

这是假设他们重新利用了通过使某些指令在 64 位模式下无效而释放的 1 字节操作码之一(例如aaa),这对于解码器(和指令长度预解码器)可能不方便,因为在其他模式下这些操作码不采用 ModRM 字节或立即数。


movq用于具有普通 ModRM 字节的形式,mov以允许任意寻址模式作为目标。 (或作为 的来源movq r64, r/m64)。AMD 选择将这些立即数保留为​​ 32 位,与 32 位操作数大小1相同。

这些形式的mov指令格式与其他指令(如add. 为了便于解码,这意味着 REX 前缀不会更改这些操作码的指令长度。 当寻址模式是可变长度时,指令长度解码已经够难了。

movq64 位操作数大小也是如此,但其他指令格式相同(mov r/m64, imm32成为符号扩展立即数形式,与只有一种立即数形式的所有其他指令相同),和mov r/m64, r64or mov r64, r/m64

movabs是现有 no-ModRM 短格式的 64 位格式mov reg, imm32。这已经是一种特殊情况了(因为 no-modrm 编码,寄存器编号来自操作码字节的低 3 位)。小的正常数可以只使用 32 位操作数大小来隐式零扩展至 64 位,而不会损失效率(如32 位或 64 位模式下的5 字节mov eax, 123/AT&T )。mov $123, %eax并且拥有 64 位绝对值mov很有用,因此 AMD 这样做是有道理的。

由于没有 ModRM 字节,它只能对寄存器目标进行编码。添加一个可以采用内存操作数的表单将需要一个完全不同的操作码。


从一个 POV 中,你得到一个mov64 位立即数感激不尽;像 AArch64(具有固定宽度的 32 位指令)这样的 RISC ISA 需要更像 4 条指令才能将 64 位值放入寄存器。(除非它是重复的位模式;AArch64 实际上非常酷。不像早期的 RISC,如 MIPS64 或 PowerPC64)

如果 AMD64 要为 引入新的操作码movmov r/m, sign_extended_imm8那么对于节省代码大小将非常有用。 编译器发出多mov qword ptr [rsp+8], 0条指令将本地数组或结构归零的情况并不罕见,每条指令都包含一个 4 字节0立即数。将一个非零的小数放入寄存器是相当普遍的,并且会产生mov eax, 123一个 3 字节指令(从 5 下降)和mov rax, -123一个 4 字节指令(从 7 下降)。它还可以在不破坏 FLAGS 3 个字节的情况下将寄存器归零。

允许movimm64 进入内存很少有用,以至于 AMD 认为不值得让解码器变得更复杂。在这种情况下,我同意他们的观点,但 AMD 在添加新操作码方面非常保守。错过了很多清理 x86 疣的机会,比如扩大范围setcc会很好。但我认为 AMD 不确定 AMD64 是否会流行起来,并且不希望被困在需要大量额外晶体管 / 电源来支持如果人们不使用它的功能。

脚注 1
一般来说 32 位立即数对于代码大小来说显然是一个不错的决定。想要add立即访问 +-2GiB 范围之外的东西是非常罕见的。它可能对像这样的按位内容很有用,但对于设置/清除/AND翻转单个位,bts//指令很好(将位位置作为 8 位立即数,而不需要掩码)。你不想成为一个 11 字节的指令;7已经够糟糕了。btrbtcsub rsp, 1024


巨指令?效率不是很高

在设计 AMD64 时(2000 年代初),具有 uop 缓存的 CPU 还不是一回事。(带有跟踪缓存的 Intel P4 确实存在,但事后看来,它被认为是一个错误。)指令获取/解码发生在最多 16 个字节的块中,因此拥有一个接近 16 个字节的指令对于前端比movabs $imm64, %reg

当然,如果后端跟不上前端,那么这个周期中只有 1 条指令解码的气泡可以通过阶段之间的缓冲来隐藏。

为一条指令跟踪这么多数据也是一个问题。CPU 必须将这些数据放在某个地方,如果在寻址模式中有 64 位立即数32 位位移,那就是很多位。 通常一条指令最多需要 64 位空间用于 imm32 + disp32。


顺便说一句,对于大多数使用 RAX 和立即数的操作,都有特殊的 no-modrm 操作码。(x86-64 是从 8086 演变而来的,其中 AX/AL 更为特殊,请参阅了解更多历史和解释)。add/sub/cmp/and/or/xor/... rax, sign_extended_imm32对于那些没有 ModRM 的表单来代替使用完整的 imm64,这将是一个合理的设计。RAX 最常见的情况是立即数使用 8 位符号扩展立即数 (-128..127),无论如何都不是这种形式,它只为需要 4 字节立即数的指令节省 1 个字节。但是,如果您确实需要一个 8 字节的常量,则将其放入寄存器或内存中以供重用要比在循环中执行 10 字节的 and-imm64 更好。

于 2020-07-07T09:36:39.030 回答
4

对于第一个问题:

来自gnu assembler 的官方文档

在 64 位代码中,movabs可用于对mov64 位位移或立即操作数的指令进行编码。

mov reg64, imm(在 intel 语法中,目标优先)是唯一接受 64 位立即数作为参数的指令。这就是为什么不能将 64 位立即数直接写入内存,而只能写入寄存器的原因。该形式mov使用包含寄存器号的操作码,而不是通过 ModRM 字节指定 reg/mem 目标。


对于第二个问题:

对于其他目的地,例如内存位置,32 位立即数可以符号扩展为 64 位立即数(这意味着前 33 位在那里相同)。在这种情况下,您使用该movq指令。

如果目标是寄存器,这也是可能的,节省 3 个字节:

C8 B0 FF FF FF 7F 00 00 00 00   movabs $0x7FFFFFFF, %rax
C8 C7 C0 FF FF FF 7F            movq   $0x7FFFFFFF, %rax

在 64 位立即数0xFFFFFFFF,前 33 位不一样,所以movl不能在这里使用。这就是我选择0x7FFFFFFF这个例子的原因。但还有另一种选择:

写入 32 位寄存器(64 位寄存器的低位)时,寄存器的高 32 位清零。因此,对于高 32 位为零的 64 位立即数,movl也可以使用,这样可以节省另一个字节:

C7 C0 FF FF FF 7F               movl   $0xFFFFFFFF, %eax

GAS 不会自动执行此操作,但它可以在movabsmovq如果您使用之间进行选择mov,具体取决于立即数的大小。

信用:感谢 Peter Cordes 指出我最初在回答中搞砸了一些东西并添加了更多信息。

于 2020-07-07T09:10:31.953 回答