是的,与-1
aka不同,将不适合符号扩展的 32 位的立即数移至寄存器,然后移至内存0xFFFFFFFFFFFFFFFF
。但是,为什么部分是有趣的问题:
请记住,asm 只允许您做机器代码中可能的事情。因此,这确实是一个关于 ISA 设计的问题。这样的决定通常涉及硬件解码的容易程度,以及编码效率的考虑。(在很少使用的指令上使用操作码会很糟糕。)
它不是为了让事情变得更难,而是为了不需要任何新的操作码mov
。 并且还将 64 位立即数限制为一种特殊的指令格式。 mov
是唯一可以使用 64 位立即数(或 64 位绝对地址,用于加载/存储 AL/AX/EAX/RAX)的指令。
查看英特尔的手册以了解以下形式(请注意,它首先使用英特尔语法,目标优先,我的答案也是如此。)我还总结了mov
x86-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 前缀不会更改这些操作码的指令长度。 当寻址模式是可变长度时,指令长度解码已经够难了。
movq
64 位操作数大小也是如此,但其他指令格式相同(mov r/m64, imm32
成为符号扩展立即数形式,与只有一种立即数形式的所有其他指令相同),和mov r/m64, r64
or 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 中,你得到一个mov
64 位立即数,感激不尽;像 AArch64(具有固定宽度的 32 位指令)这样的 RISC ISA 需要更像 4 条指令才能将 64 位值放入寄存器。(除非它是重复的位模式;AArch64 实际上非常酷。不像早期的 RISC,如 MIPS64 或 PowerPC64)
如果 AMD64 要为 引入新的操作码mov
,mov 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 个字节的情况下将寄存器归零。
允许mov
imm64 进入内存很少有用,以至于 AMD 认为不值得让解码器变得更复杂。在这种情况下,我同意他们的观点,但 AMD 在添加新操作码方面非常保守。错过了很多清理 x86 疣的机会,比如扩大范围setcc
会很好。但我认为 AMD 不确定 AMD64 是否会流行起来,并且不希望被困在需要大量额外晶体管 / 电源来支持如果人们不使用它的功能。
脚注 1:
一般来说 32 位立即数对于代码大小来说显然是一个不错的决定。想要add
立即访问 +-2GiB 范围之外的东西是非常罕见的。它可能对像这样的按位内容很有用,但对于设置/清除/AND
翻转单个位,bts
//指令很好(将位位置作为 8 位立即数,而不需要掩码)。你不想成为一个 11 字节的指令;7已经够糟糕了。btr
btc
sub 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 更好。