assembly imulw从内存,但到更大的寄存器-在一个命令?

zbsbpyhn  于 2022-11-30  发布在  其他
关注(0)|答案(1)|浏览(213)

我已经对程序集进行了一些尝试(具体来说是:AT&T,x86-64)。
我的数据节如下所示:

.section .data
num: .short 0b1111111111111111  #16 ones, so the maximum unsigned short value
junk: .quad 0x5555555555555555

在%rax中,我有一个扩展为零的16位值,它与num的值的乘积可以保证最多为32位。我想做一些沿着的事情:
imulw (num), %eax
换句话说就是:抓住机会(短)在num中,将其乘以**%ax中的值,然后得到结果-这可能需要多达32位,大小为%eax**-然后,实际上,将其存储在%eax中......我希望使用单个命令完成此操作,而不需要在中间寄存器中存储任何内容。为了将%eax作为我的目标寄存器,我必须使用imull,但是n只是一个短整型,这意味着如果我这样做,我将从内存中获取太多字节(特别是,我将在适当命名的“junk”标签后面获取一些0x 55字节)。
使用mulimul的单操作数版本也是不可能的,因为我需要%rax的最终结果。
有没有什么方法可以实现上述目标?

**TL;DR:**是否有一条指令可以将存储在内存中的2字节乘以2字节寄存器,并将结果存储在4字节寄存器中,而不是将其截断?

5fjcxozz

5fjcxozz1#

几乎所有x86指令都要求操作数大小相同movzx/movsx(AT&T movsbl/movswl等)、SIMD广播以及某些其他特殊情况除外。

x86-64的操作码位已经很拥挤了,没有足够的操作码位来给予每个指令零扩展或符号扩展一个更窄的8位或16位源代码的能力。加上另一个比特来表示源很窄。(或者是一些指令的第二个操作码,以允许窄源格式。)8086使用了大部分的编码空间(可能是1字节操作码),只留下了一小部分空间供将来以简单/合理的方式扩展。
乘法的特殊之处仅在于它提供了一种扩展形式,但当乘积可以放入一个寄存器时,通常并不需要这种扩展形式。正如你所说,mulw num(%rip)会执行DX:AX = AX*num,它具有一个假依赖,写入RDX的低16位,而保留高位不做修改。这也忽略了EAX源代码的高16位,因此它是不等价的。
你需要shl $16, %edx/mov %ax, %dx这样的代码来得到EDX中的一个32位值,通过SHL写32位EDX来零扩展到RDX(RAX的高位字节仍然保存着原始的垃圾,所以or %eax, %edxor %edx, %eax是不正确的)。

movzwl num(%rip), %edx
   imul   %edx, %eax          # This is your best option.

如果你真的想避免零扩展加载,你需要为num预留更多的内存,在你想使用的值的部分之后保留2个零字节。注意,紧接着16位存储的32位加载将有额外的延迟a store-forwarding stall。但是如果窄存储有时间提交到缓存,就没有损失。
@fuz指出了一个有趣的事实,即传统的x87浮点可以使用窄整数源操作数,如fimuls num(%rip)来执行%st(0) *= int16_to_longdouble(num)s用于short不同于通常的w用于wordfimull使用32位整数源,即使使用x86-64,也没有采用64位整数源的形式。https://www.felixcloutier.com/x86/fmul:fmulp:fimul因此,您必须使用fildq/fmulp

相关问题