CSAPP题目 3.59

题目描述:
下面的代码计算两个 64 64 64 位有符号值 x x x y y y 128 128 128 位乘积,并将结果储存在内存中:

typedef __int128 int128_t;

void store_prod(int128_t *dest, int64_t x, int64_t y) {
	*dest = x * (int128_t) y;
}

G C C GCC GCC 编译器产出了下面的汇编代码来实现计算:

store_prod:
	movq	%rdx, %rax
	cqto
	movq	%rsi, %rcs
	sarq 	$63, %rcx
	imulq	%rax, %rcx
	imulq	%rsi, %rdx
	addq	%rdx, %rcx
	mulq	%rsi
	addq	%rcx, %rdx
	movq	%rax, (%rdi)
	movq	%rdx, 8(%rdi)

为了满足在 64 64 64 位机器上实现128位运算所需的多精度计算,这段代码用了三个乘法。描述用来计算乘积的算法,对汇编代码加注释,说明它是如何实现你的算法的。提示:在参数 x x x 和 参数 y y y 拓展 128 128 128 位时,他们可以重写为 x = 2 64 × x h + x l x = 2^{64}\times x_h + x_l x=264×xh+xl y = 2 64 × y h + y l y = 2^{64} \times y_h+y_l y=264×yh+yl 这里 x h , x l , y h , y l x_h,x_l,y_h,y_l xh,xl,yh,yl 都是 64 64 64 位值。请解释这段代码是如何用 x h , x l , y h , y l x_h,x_l,y_h,y_l xh,xl,yh,yl 来计算 p h p_h ph p l p_l pl 的。

分析:
说实话第一次接触 C C C 语言底层的汇编逻辑挺不适应的,这些寄存器跑到在这儿有哪儿真的很麻烦。不过最终发现其实我对拓展运算的不熟悉导致的。

因为 64 64 64 位机器,所以一个寄存器最大也就 64 64 64 位,这导致如果要拓展到 128 128 128 位的运算,得借助两个寄存器。最初我十分不解 sarq $63,%rcx 的作用是什么,真的脑袋完全无法想到。前面书本中介绍的拓展就是只有 c p t o cpto cpto 8 8 8 字节拓展到 16 16 16 字节),以及 c l t q cltq cltq 这两种符号拓展。

既然是符号拓展,注意到这右移运算也是算术右移,好了,真相大白,在这之前还有一句 movq %rsi, %rcx 即此时 % r c x \%rcx %rcx 执行了算术右移后相当于是对 x x x 进行了符号拓展,而 % r c x \%rcx %rcx 存的就是 x h x_h xh

好了,理解了这个右移后,就知道在执行 imulq %rax,%rcx ,前每个寄存器存的数据的实际意义了,如下表所示:

%rdx%rax%rcx%rsi
y h y_h yh y l y_l yl x h x_h xh x l x_l xl

此时现理解数学上的 128 128 128 位乘法,定义 × 128 t \times_{128}^{t} ×128t 运算符为 128 128 128 为的补码乘法运算。根据第二章可知 x × 128 t y = x × y ( m o d 2 128 ) x \times_{128}^t y=x\times y \pmod{2^{128}} x×128ty=x×y(mod2128) 。所以
x × 128 t y = x × y ( m o d 2 128 ) = ( x h × 2 64 + x l ) ( y h × 2 64 + y l ) ( m o d 2 128 ) x \times_{128}^t y=x\times y \pmod{2^{128}} = (x_h\times 2^{64}+x_l)(y_h\times 2^{64}+y_l)\pmod{2^{128}} x×128ty=x×y(mod2128)=(xh×264+xl)(yh×264+yl)(mod2128)

展开可得
x × 128 t y = ( x h × y l + x l × y h + ⌊ x l × y l 2 64 ⌋ ( m o d 2 64 ) ) × 2 64 + ( x l × y l ( m o d 2 64 ) ) x \times_{128}^t y = (x_h \times y_l + x_l \times y_h + \lfloor\frac{x_l\times y_l}{2^{64}}\rfloor \pmod{2^{64}}) \times 2^{64} + (x_l\times y_l\pmod{2^{64}}) x×128ty=(xh×yl+xl×yh+264xl×yl(mod264))×264+(xl×yl(mod264))

所以乘积结果 p = p h × 2 64 + p l p = p_h\times2^{64}+p_l p=ph×264+pl p h = x h × y l + x l × y h + ⌊ x l × y l 2 64 ⌋ ( m o d 2 64 ) p_h = x_h \times y_l + x_l \times y_h + \lfloor\frac{x_l\times y_l}{2^{64}}\rfloor \pmod{2^{64}} ph=xh×yl+xl×yh+264xl×yl(mod264) ,而 p l = x l × y l ( m o d 2 64 ) p_l = x_l\times y_l\pmod{2^{64}} pl=xl×yl(mod264)

这时再来看下面的乘法逻辑,我们也使用表来看看

%rdx%rax%rcx%rsi
y h y_h yh y l y_l yl x h x_h xh x l x_l xl
imulq %rax, %rcx y h y_h yh y l y_l yl x h × 64 t y l x_h\times_{64}^ty_l xh×64tyl x l x_l xl
imulq %rsi, %rdx x l × 64 t y h x_l\times_{64}^ty_h xl×64tyh y l y_l yl x h × 64 t y l x_h\times_{64}^ty_l xh×64tyl x l x_l xl
addq %rdx, %rcx x l × 64 t y h x_l\times_{64}^ty_h xl×64tyh y l y_l yl( x h × 64 t y l ) + 64 t ( x l × 64 t y h ) x_h\times_{64}^ty_l) +_{64}^t (x_l\times_{64}^ty_h) xh×64tyl)+64t(xl×64tyh) x l x_l xl
mulq %rsi ⌊ x l × y l 2 64 ⌋ \lfloor\frac{x_l\times y_l}{2^{64}}\rfloor 264xl×yl y l × 64 t x l y_l\times_{64}^t x_l yl×64txl( x h × 64 t y l ) + 64 t ( x l × 64 t y h ) x_h\times_{64}^ty_l) +_{64}^t (x_l\times_{64}^ty_h) xh×64tyl)+64t(xl×64tyh) x l x_l xl
addq %rcx, %rdx ( x h × 64 t y l ) + 64 t ( x l × 64 t y h ) + 64 t ⌊ x l × y l 2 64 ⌋ (x_h\times_{64}^ty_l) +_{64}^t (x_l\times_{64}^ty_h) +_{64}^t \lfloor\frac{x_l\times y_l}{2^{64}}\rfloor (xh×64tyl)+64t(xl×64tyh)+64t264xl×yl y l × 64 t x l y_l\times_{64}^t x_l yl×64txl ( x h × 64 t y l ) + 64 t ( x l × 64 t y h ) (x_h\times_{64}^ty_l) +_{64}^t (x_l\times_{64}^ty_h) (xh×64tyl)+64t(xl×64tyh) x l x_l xl

这里比较反常的就是 mulq %rsi ,之前我一直没注意到 m u l q mulq mulq 无符号拓展就导致原本的 % r d x \%rdx %rdx 寄存器里的数据就只剩下 ⌊ x l × y l 2 64 ⌋ \lfloor\frac{x_l\times y_l}{2^{64}}\rfloor 264xl×yl 。所以一直不理解后面还要执行 addq %rcx, %rdx

最后再把低位的 p l p_l pl 赋值到内存中 R [ % r d i ] R[\%rdi] R[%rdi] 中,把 p h p_h ph 赋值到 8 + R [ % r d i ] 8 + R[\%rdi] 8+R[%rdi] 中去。

所以这就是这道题的完整解释了。中间有很多废话,不过这都是我在思考中不理解的部分。

欢迎各位大犇来指出我的错误和交流!

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值