以下代码计算x和y的乘积并将结果存储在内存中。数据 type ll_t被定义为等于long long。
typedef long long ll_t;
void store_prod(ll_t *dest, int x, ll_t y) {
*dest = x*y;
}
gcc生成以下汇编代码来实现计算: dest at%ebp + 8,x at%ebp + 12,y at%ebp + 16
1 movl 16(%ebp), %esi
2 movl 12(%ebp), %eax
3 movl %eax, %edx
4 sarl $31, %edx
5 movl 20(%ebp), %ecx
6 imull %eax, %ecx
7 movl %edx, %ebx
8 imull %esi, %ebx
9 addl %ebx, %ecx
10 mull %esi
11 leal (%ecx,%edx), %edx
12 movl 8(%ebp), %ecx
13 movl %eax, (%ecx)
14 movl %edx, 4(%ecx)
此代码使用三次乘法来实现多精度算法 需要在32位机器上实现64位运算。描述一下 用于计算产品的算法,并注释要显示的汇编代码 它如何实现你的算法。
我不理解上面汇编代码中的第8行和第9行。有人可以帮忙吗?
答案 0 :(得分:5)
我已将其转换为英特尔语法。
mov esi, y_low
mov eax, x
mov edx, eax
sar edx, 31
mov ecx, y_high
imul ecx, eax ; ecx = y_high *{signed} x
mov ebx, edx
imul ebx, esi ; ebx = sign_extension(x) *{signed} y_low
add ecx, ebx ; ecx = y_high *{signed} x_low + x_high *{signed} y_low
mul esi ; edx:eax = x_low *{unsigned} y_low
lea edx, [ecx + edx] ; edx = high(x_low *{unsigned} y_low + y_high *{signed} x_low + x_high *{signed} y_low)
mov ecx, dest
mov [ecx], eax
mov [ecx + 4], edx
上面的代码所做的是乘以2个64位有符号整数,它们保留了产品中最不重要的64位。
其他64位被乘数来自哪里?它的x
符号扩展从32位到64位。sar
指令用于将x's
符号位复制到{{1}的所有位中。 1}}。我将此值称为仅包含edx
符号x's
。 x_high
是实际传递给例程的x_low
的值。
x
和y_low
是y_high
中最不重要且最重要的部分,就像y
x's
和x_low
一样。
从这里开始很简单:
product = x_high
* {signed} y
=
(x
* 2 32 + y_high
)* {signed}(y_low
* 2 32 + x_high
) =
x_low
* {signed} y_high
* 2 64 +
x_high
* {signed} y_high
* 2 32 +
x_low
* {signed} y_low
* 2 32 +
x_high
* {signed} y_low
x_low
* {signed} y_high
* 2 64 未计算,因为它不会对产品的最低64位做出贡献。如果我们对完整的128位产品感兴趣(完整的96位产品),我们会计算它。
x_high
* {signed} y_low
使用无符号乘法计算。这样做是合法的,因为2的补码有符号乘法给出了与无符号乘法相同的最低有效位。例如:
-1 * {signed} -1 = 1
0xFFFFFFFFFFFFFFFF * {unsigned} 0xFFFFFFFFFFFFFFFF = 0xFFFFFFFFFFFFFFFE0000000000000001(64个最低有效位相当于1)
答案 1 :(得分:2)
考虑第8和9行的背景。
此时,ESI
包含y
的下半部分,EBX
包含sgn(x)
。因此,第8行只是计算sgn(x) * (y % 2^32)
并将其存储在EBX
。
第9行借鉴了这一结果。到第9行发生时,ECX
包含乘法的部分上半部分,即x * (y >> 32)
签名。所以EBX+ECX
最终成为我们在上一步中计算的内容加上我们在前一行找到的部分上半部分。
完整的算法本身非常整洁;)
编辑:回应以下评论......
第4行:考虑SAR EDX, 31
(或者如果你愿意,sar $31, %edx
)的真正含义。由于EDX
是一个32位寄存器,因此最终会得到两个值中的一个。哪两个?考虑它们在带符号算术的背景下的含义。
第7行:此时EDX
包含对以下操作非常有用的内容。我只是将它移动到需要去的地方。
答案 2 :(得分:0)
imul的作用是将eax的内容与ecx相乘,并将ex中的低32位和edx中的高32位保存。
addl据我记得添加了两个寄存器并将其保存在第一个寄存器上,所以在本例中为ebx。 (我不确定它是否会做任何其他事情并且在addl代表之后l)