C 这个128位整数乘法在汇编(x86-64)中是如何工作的?

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

我正在阅读,作业是描述这个算法是如何工作的

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, %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:is
imulq%rax,%rcx
2^64表示需要将其添加到高位

2^64*yh*xl:is
imulq%rsi,%rdx
2^64表示需要将其添加到高位

2^128*xh*yh:不需要此操作,因为
2^128*xh*yh
不适合128位整数。它只表示符号位信息,可以忽略

xl*yl:is
mulq%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