c-在32位机器上实现64位算术



下面的代码计算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_lowy_highy的最低和最高有效部分,就像x'sx_lowx_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代表长)

最新更新