C 这个128位整数乘法在汇编(x86-64)中是如何工作的?
我正在阅读,作业是描述这个算法是如何工作的 C功能:C 这个128位整数乘法在汇编(x86-64)中是如何工作的?,c,assembly,x86-64,128-bit,C,Assembly,X86 64,128 Bit,我正在阅读,作业是描述这个算法是如何工作的 C功能: void store_prod(__int128 *dest, int64_t x, int64_t y) { *dest = x * (__int128)y; } 大会: movq %rdx, %rax cqto movq %rsi, %rcx sarq $63, %rcx imulq %rax, %rcx imulq %rsi, %rdx addq %rdx, %rcx mulq %rsi addq %rcx, %r
void store_prod(__int128 *dest, int64_t x, int64_t y) {
*dest = x * (__int128)y;
}
大会:
movq %rdx, %rax
cqto
movq %rsi, %rcx
sarq $63, %rcx
imulq %rax, %rcx
imulq %rsi, %rdx
addq %rdx, %rcx
mulq %rsi
addq %rcx, %rdx
movq %rax, (%rdi)
movq %rdx, 8(%rdi)
ret
我不知道它为什么会执行:
xh*yl+yh*xl=我们在无符号乘法后添加的值
一如既往,编译器选项很重要。源代码使用gcc-Og
(针对调试进行优化)(强制转换符号将两个操作数扩展到128位,然后再执行完整的128x128->128乘法)。这正是C标准所说的应该发生的事情(整数提升)。如果您要谈论编译器输出,您应该总是说哪个编译器的哪个版本有哪些选项。或者只是在上面贴一个链接
(编辑:oops、source和asm来自一本没有提供这些信息的书。)
对于gcc-O3
,gcc利用了两个操作数实际上仍然只有64位这一事实
sar$63,%rcx
是符号扩展rsi
到rcx:rsi
的一部分,就像cqto
符号扩展rax
到rdx:rax
一样
其他人在评论中已经给出了大部分答案,但我认为没有人注意到,
gcc-Og
/-O1
几乎完全给出了asm输出。为了理解我们为什么要进行此操作,请尝试将int128\t解释为:2^64*xh+xl
因此,如果我们想将两个整数相乘,我们将执行以下操作:
x=2^64*xh+xl
y=2^64*yh+yl
所以x*y=(2^128*xh*yh)+(2^64*xh*yl)+(2^64*yh*xl)+(yl*xl)
这正是汇编代码的作用:
yh=%rdx yl=%rax
xh=%rcx xl=%rsi
2^64*xh*yl:isimulq%rax,%rcx
2^64表示需要将其添加到高位
2^64*yh*xl:isimulq%rsi,%rdx
2^64表示需要将其添加到高位
2^128*xh*yh:不需要此操作,因为2^128*xh*yh
不适合128位整数。它只表示符号位信息,可以忽略
xl*yl:ismulq%rsi
我希望这能把事情弄清楚 GCC所做的是使用有符号乘法可以使用的属性 例如,64位字的伪指令中的
sign\u ext(x)*y
sarq $63, x ; sign_ext(x)
imulq y, x ; sign_ext(x)*y
所以现在你问(或打算问):
为什么这个公式是正确的
这是一个很好的解释。我也问了同样的问题
@Zboson:它直接来自于二的补语表示。例如,32位整数-n
和-m
表示为无符号数x=2**32-n,y=2**32-m
。如果你把你拥有的乘起来,x*y=2**64-2**32*n-2**32*m+n*m
。中间术语表示产品上半部分的必要修正。使用-1*-1完成一个简单的示例应该非常有启发性
乘法的两个操作数必须是同一类型。为此,
x
被提升为类型\uuu int128
,因为y
在强制转换后属于此类型,并且\uu int128
的整数提升等级高于int64\t
。其中一个转换是由cqto
完成的,但这只在rax
上起作用,因此另一个转换是由sarq
完成的。您不使用1
或-1
进行乘法,而是使用0
或-1
进行乘法。算术右移与cqto
完全一样:符号扩展到整个寄存器(sarq
,%rdx
用于cqto
),因为imul
已经提供了64x64->128位乘法,我看不出这有什么意义。当然,您仍然可以解释它是如何工作的:)可能是禁用优化的常见情况,否则编译器足够聪明,可以使用一个imul
@EOF无需,正如我所说,至少可以将精确的C代码转换为一个imul
。出于某种原因,icc不是。我觉得你想问一个更一般的问题。例如,如果您的处理器只能执行32*32到64或64*64到64,如何执行128位乘法?这是一个更有趣的问题。您不想执行xh*yl+yh*xl
。你可以这样做,但你必须找出溢出。有一种方法可以在不担心溢出的情况下进行乘法运算。谢谢你的回答。正如我所说,这是书中写的家庭作业,所以我不知道使用了哪种编译器,也不知道使用了哪种优化级别标志。@TomZych:谢谢你的整理。轻微的改进,但绝对是一种改进。:)De rien-几乎拥有我的复印编辑徽章:)
hi += sign_ext(x)*y + sign_ext(y)*x
sarq $63, x ; sign_ext(x)
imulq y, x ; sign_ext(x)*y