下面的代码计算x和y的乘积,并将结果存储在内存中。数据类型llt被定义为等效于long-long。
typedef long long ll_t;
void store_prod(ll_t *dest, int x, ll_t y) {
*dest = x*y;
}
gcc生成以下实现计算的汇编代码:dest在%ebp+8,x在%ebk+12,y在%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行。有人能帮忙吗
我已经将其转换为intel语法。
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位被乘数来自哪里?它是从32位扩展到64位的x
符号。sar
指令用于将x's
符号位复制到edx
的所有位中。我称这个值仅由x's
符号x_high
组成。x_low
是实际传递到例程中的x
的值。
y_low
和y_high
是y
的最低和最高有效部分,就像x's
、x_low
和x_high
一样。
从这里开始很容易:
product=y
*{签名}x
=
(y_high
*232+y_low
)*{有符号}(x_high
*232+x_low
)=y_high
*{签名}x_high
*264+y_high
*{签名}x_low
*232+y_low
*{签名}x_high
*232+y_low
*{签名}x_low
y_high
*{signed}x_high
*264未被计算,因为它对乘积的最低有效64位没有贡献。如果我们对完整的128位产品感兴趣(对于挑剔的人来说是完整的96位产品),我们会计算它。
使用无符号乘法来计算y_low
*{有符号}x_low
。这样做是合法的,因为2的补码有符号乘法给出的最低有效位与无符号乘法相同。示例:
-1*{已签名}-1<1
0xFFFFFFFFFFFFF*{unsigned}
考虑第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
包含了对以下操作非常有用的内容。我只是把它移到需要的地方。
imul所做的是将eax的内容与ecx相乘,并将较低的32位保存在eax中,将较高的32位存储在edx中。
据我记忆所及,addl将两个寄存器相加,并将其保存在第一个寄存器上,因此在本例中为ebx。(我不确定它是否还有其他作用,addl后面的l代表长)