为什么 8 位 MUL 合并到 AX 中,而 16 位和 32 位 MUL 将结果拆分为 [E]DX:[E]AX?

Les*_*mim 4 assembly x86-64 multiplication instructions x86-16

  • MUL CL CL 是 BYTE 大小,它等于 AX = AL * CL

  • MUL BX BX 是 WORD 大小,它等于 DX:AX = AX * BX

  • MUL EBX EBX 是 DWORD 大小,它等于 EDX:EAX = EAX * EBX

我想知道为什么 8 位大小的MUL指令给出的结果是 inAX而不是 in DL:AL

是因为对于 16 位 MUL,结果可以是EAX,对于 32 位 MUL 结果可以是RAX,但是对于 64 位mul rcx,不会有足够宽的单个寄存器来保存结果吗?

Nat*_*dge 7

8086 有 16 位寄存器(AX、BX、...)。因此,8*8=16 加宽乘法(即,具有 8 位输入和 16 位结果)可以将其结果放入单个寄存器中。将它拆分到两个寄存器会很不方便并且没有任何好处。

但是 16*16=32 加宽乘法无法将其结果放入单个寄存器中,因为没有 32 位寄存器。必须将它拆分到两个寄存器中,因此选择了 DX 和 AX。

同样,386 有 32 位寄存器(EAX、EBX、...),因此它的 32*32=64 加宽乘法必须将其结果拆分。选择 EDX:EAX 是因为与 8086 相似。

英特尔此时可以添加一个新版本的 16*16=32 MUL,将结果保留在单个 32 位寄存器中,例如 EAX,但他们选择不这样做,可能是为了兼容性或避免不必要的额外复杂性,或者从简单的惯性。因此MUL,即使在 32 位模式下,386 的 16*16=32仍会将其结果拆分为 DX:AX。

(然而,他们确实添加了一个非扩展的 32*32=32 形式的有符号乘法IMUL指令,将其结果留在一个 32 位寄存器中。可以将其用于有符号 16*16=32 乘法 -扩展输入,为此MOVSX也增加了便利。它可以用于 16*16=32 无符号乘法,通过零扩展输入,如果知道乘积将小于2^31。)

同样,x86-64 也有 64 位寄存器。对于现有的乘法指令,他们保持相同的行为(因此 32*32=64 仍然将其结果拆分到 EDX:EAX 而不是使用单个 64 位寄存器),并且他们添加了一个 64*64=128 加宽乘法,同样,必须拆分其结果,并将其留在 RDX:RAX 中。还有一个非加宽的 64*64=64 有符号IMUL,它将结果留在一个 64 位寄存器中。

  • 请注意,“imul r64, r64”对于两个已零扩展为 64 位的 32 位无符号输入完全可以正常工作。https://godbolt.org/z/xM8PT1 显示 GCC 和 clang 对 `a * (uint64_t)b` 执行此操作。较宽寄存器的符号位始终是清晰的,因此它们将被视为带正号(因此具有正确的值)。上半部分有符号乘法和无符号乘法之间的差异仅是由于输入的 MSB 的解释所致。除FLAGS外,产品(MSB集)的“有符号溢出”不会执行任何操作(例如带有imul r16,r16的“0x00ff * 0x00ff = 0xfe01”)。 (2认同)