深入理解计算机系统——第三章—3.11浮点代码

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_40199047/article/details/102725549

3.11 浮点代码

处理器的浮点体系结构包括多个方面,会影响对浮点数据操作的程序如何被映射到机器上,包括:
1) 如何存储和访问浮点数据。通常是通过某种寄存器方式来完成。
2) 对浮点数据操作的指令。
3) 想函数传递浮点数参数和从函数返回浮点数结构的规则。
4) 函数调用过程保持寄存器的规则——例如,一些寄存器被指定为调用者保存,而其他的被指定为被调用者保存

历史回顾:

单指令多数据或SIMD(sim-dee),允许多个数据并行执行同一操作。

名字变迁过程:MMX       \implies SSE(Streaming SIMD Extension,流式SIMD扩展)       \implies AVX(Advanced Vector Extension,高级向量扩展)

每个扩展都是管理寄存器组中的数据:
MM寄存器       \impliedby MMX64位;
XMM寄存器       \impliedby SSE128位;
YMM寄存器       \impliedby AVX256位。

AVX浮点体系结构允许数据存储在16个***YMM***寄存器中,它们的名字为%ymm0~%ymm15。每个YMM寄存器都是256位(32位)。这些寄存器只保存浮点数,只使用低32位(float)或64位)(double)。每个SSE XMM寄存器对应YMM寄存器低32位。
这里写图片描述

3.11.1 浮点传送和转换操作

引用内存的指令是标量指令,意味着它们只对单个而不是一组封装好的数据值进行操作。

指令 目的 描述
vmovss M 32 M_{32} X X 传送单精度数
vmovss X X M 32 M_{32} 传送单精度数
vmovsd M 64 M_{64} X X 传送双精度数
vmovsd X X M 64 M_{64} 传送双精度数
vmovaps X X X X 传送对齐的封装好的单精度数
vmovapd X X X X 传送对齐的封装好的双精度数

   3 46    \blue{图\;3-46}\;浮点传送指令。这些操作在内存和寄存器之间以及一对寄存器之间的传送值

GCC只用标量传送操作从内存传送数据到XMM寄存器或从XMM寄存器传送数据到内存。
对于两个XMM寄存器之间传送数据,GCC会使用两种指令(vmovss传送单精度或vmovsd传送双精度),两者不会影响执行速度。
指令中的a表示.aligned的意思。当用于读写内存时如果地址不满足16字节对齐,会导致异常。
示例:
这里写图片描述
把浮点值转换成整数时,指令会执行截断(truncation),把值指向0进行舍入,这是C和大多数其他编程语言的要求。

指令 目的 描述
vcvttss2si X / M 32 X/M_{32} R 32 R_{32} 用截断的方法把单精度数转换成整数
vcvttsd2si X / M 64 X/M_{64} R 64 R_{64} 用截断的方法把单双度数转换成整数
vcvttss2siq X / M 32 X/M_{32} R 64 R_{64} 用截断的方法把单精度数转换成四字整数
vcvttsd2siq X / M 64 X/M_{64} R 64 R_{64} 用截断的方法把双精度数转换成四字整数

3 47       \blue {图3-47}\qquad 双操作数浮点指令。浮点数\implies整数

指令 1 2 目的 描述
vcvttsi2ss M 32 M_{32} / R 32 R_{32} X X X X 把整数转换成单精度数
vcvttsi2sd M 32 M_{32} / R 32 R_{32} X X X X 把整数转换成双精度数
vcvttsi2ssq M 64 M_{64} / R 64 R_{64} X X X X 把四字整数转换成单精度数
vcvttsi2sdq M 64 M_{64} / R 64 R_{64} X X X X 把四字整数转换成双精度数

3 48       \blue {图3-48}\qquad 三操作数浮点指令。第一个源数据类型\implies目的的数据类型, 第二个源值对结果的低位字节没有影响。
3-48中的指令把整数转换成浮点数。第一个操作数读自于内存或一个通用目的寄存器。第二个操作数只会影响结果的高位字节
例如如下指令:

扫描二维码关注公众号,回复: 7608496 查看本文章
vcvtsi2sdq    %rax, %xmm1, %xmm1

该指令从寄存器%rax读出一个长整数,把它转换成数据类型double,并把结果存放进XMM寄存器%xmm1的低字节中。
假设%xmm0的低位4字节保存着一个单精度值,使用如下指令:

vcvtss2sd    %xmm0, %xmm0, %xmm0

把它转换成一个双精度值,并将结果存储在寄存器%xmm0的低8字节。
GCC生成的代码:

Conversion from single to double precision
1    vunpcklps    %xmm0, %xmm0, %xmm0      `Replace first vector element`
2    vcvtps2pd    %xmm0, %xmm0             `Convert two element to double`

vunpcklps指令通常用来交叉放置来自两个XMM寄存器的值,把它们存储到第三个寄存器中(例如:源 R 1 R_1 = [ s 3 s_3 , s 2 s_2 , s 1 s_1 , s 0 s_0 ], 源 R 2 R_2 = [ d 3 d_3 , d 2 d_2 , d 1 d_1 , d 0 d_0 ],那么目的寄存器 R 3 R_3 = [ s 1 s_1 , d 1 d_1 , s 0 s_0 , d 0 d_0 ]。)。
对于使用三个相同寄存器%xmm0的, R R = [ x 3 x_3 , x 2 x_2 , x 1 x_1 , x 0 x_0 ]       \implies [ x 1 x_1 , x 1 x_1 , x 0 x_0 , x 0 x_0 ]。
对于指令vcvtps2pd \Downarrow
R R = [ x 3 x_3 , x 2 x_2 , x 1 x_1 , x 0 x_0 ]       \implies [ x 1 x_1 , x 1 x_1 , x 0 x_0 , x 0 x_0 ]
对于指令vunpcklps \Downarrow
[ [ x_1$, x 1 x_1 , x 0 x_0 , x 0 x_0 ]       \implies [ d x 0 dx_0 , d x 0 dx_0 ],这里的 d x 0 dx_0 是将 x x 转换成双精度后的结果。

对于双精度转换为单精度,·GCC会产生类似的代码:

Conversion from double to single precsion
1    vmovddup    %xmm0, %xmm0      `Replicate first vector element`
2    vcvtpd2psx    %xmm0, %xmm0    `Convert two vector elements to single`

假设这些指令开始执行前寄存器%xmm0保存着两个双精度值[ x 1 x_1 , x 2 x_2 ]。然后vmovddup指令把它设置为[ x 0 x_0 , x 0 x_0 ]。vcvtpd2psx指令把这两个值转换成单精度,再存放到该寄存器的低位一般中,并将高位一半置0,得到结果[0.0, 0.0, x 0 x_0 , x 0 x_0 ]。
这里写图片描述
**fcvt**的所有参数都是通过通用寄存器传递的,因为它们既不是整数也不是指针。

浮点传送和转换操作指令汇总

指令 源1 源2 目的 描述
vmovss M32 NULL X 传送单精度数
vmovss X NULL M32 传送单精度数
vmovsd M64 NULL X 传送双精度数
vmovsd X NULL M64 传送双精度数
vmovaps X NULL X 传送对齐的封装好的单精度数
vmovapd X NULL X 传送对齐的封装好的双精度数
vcvttss2si X/M32 NULL R32 用截断的方法把单精度数转换成整数
vcvttsd2si X/M64 NULL R32 用截断的方法把双精度数转换成整数
vcvttss2siq X/M32 NULL R64 用截断的方法把单精度数转换成四字整数
vcvttsd2siq X/M64 NULL R64 用截断的方法把双精度数转换成四字整数
vcvtsi2ss M32/R32 X X 把整数转换成单精度数
vcvtsi2sd M32/R32 X X 把整数转换成双精度数
vcvtsi2ssq M64/R64 X X 把四字整数转换成单精度数
vcvtsi2sdq M64/R64 X X 把四字整数转换成双精度数
vcvtps2pd X1 NULL X2 **X1中两个低位单精度值扩展成X2中的两个双精度值
vunpcklps X1 X2 X3 交叉放置X1X2的值存储到X3

注:NULL表示没有该源。

练习巩固:
在这里插入图片描述

答案:
在这里插入图片描述

3.11.2 过程中的浮点代码

x86-64中,XMM寄存器用来向函数传递浮点参数,以及从函数返回浮点值。
1) XMM寄存器%xmm0~%xmm7最多可以传递8个浮点参数(额外的可以通过栈传递)。
2) 函数使用寄存器xmm0来返回浮点值
3) 所有的XMM寄存器都是调用者保存的。被调用者可以不用保存就覆盖这些寄存器中任一个。
示例:
这里写图片描述

练习巩固:(易错点:注意指针)
在这里插入图片描述
答案:
在这里插入图片描述

3.11.3 浮点运算操作

3-49描述了一组执行算术运算的标量AVX2浮点指令。

单精度 双精度 效果 描述
vaddss vaddsd D S 2 + S 1 D\leftarrow S_2+S_1 浮点数加
vsubss vsubsd D S 2 S 1 D\leftarrow S_2-S_1 浮点数减
vmulss vmulsd D S 2 × S 1 D\leftarrow S_2\times S_1 浮点数乘
vdivss vdivsd D S 2 / S 1 D\leftarrow S_2/S_1 浮点数除
vmaxss vmaxsd D m a x ( S 2 , S 1 ) D\leftarrow max(S_2,S_1) 浮点数最大值
vminss vminsd D m i n ( S 2 , S 1 ) D\leftarrow min(S_2,S_1) 浮点数最小值
sqrtss sqrtsd D S 1 D\leftarrow \sqrt{S_1} 浮点数最大值

3 49 \blue{图3-49}\qquad 标量浮点算术运算。
示例:
在这里插入图片描述
在这里插入图片描述

练习巩固:
在这里插入图片描述

3.11.4 定义和使用浮点常数

和整数运算操作不同,AVX浮点操作不能以立即数值作为操作数。相反,编译器必须为所有的常量值分配和初始化存储空间。
示例:
在这里插入图片描述

标号 4 4 数值
.LC2 .long 3435973837       \implies .long 0xcccc cccd .long 1073532108       \implies .long 0x3ffc cccc 1.8
.LC3 .long 0       \implies .long 0x0 .long 1077936128       \implies .long 0x4040 0000 32.0

具体编码过程请使劲点击它       \implies 二进制小数编码
简述过程:
1.8编码过程:
0 x3ffc    cccc    cccc    cccd = 0 x 0 011    1111    1111    1010    1010    1010    1010    1010    1010    1010    1010    1010    1010    1010    1010    1011 0\text{x3ffc}\;\text{cccc}\;\text{cccc}\;\text{cccd} = 0\text{x}\red{0}\blue{011\;1111\;1111}\;1010\;1010\;1010\;1010\;1010\;1010\;1010\;1010\;1010\;1010\;1010\;1010\;1011 ,
s = 0    e x p = k = 11    f r a c = n = 52    s=0\;exp=k=11\;frac=n=52\; exp位不全为零,属于情况1,规格化的值, e = 1023 e=1023 8位蓝色二进制的值。
V = ( 1 ) s × M × 2 E = ( 1 ) s × ( 1 + f ) × 2 e B i a s = ( 1 ) s × ( 1 + f ) × 2 e 2 k 1 1 = ( 1 ) 0 × ( 1 + 225179981368525 281474976710655 ) × 2 1023 2 11 1 1 = 1 × ( 1 + 0.8 ) × 2 0 = 1.8 V=(-1)^s\times M \times 2^E=(-1)^s\times (1+f) \times 2^{e-Bias}=(-1)^s\times (1+f) \times 2^{e-2^{k-1}-1}\\[2ex]=(-1)^0\times (1+\frac{225179981368525}{281474976710655}) \times 2^{1023-2^{11-1}-1}=1\times(1+0.8)\times 2^0=1.8

32.0编码过程:
0 x4040    0000    0000    0000 = 0 x 0 100    0000    0100    0000    0000    0000    0000    0000    0000    0000    0000    0000    0000    0000    0000    0000 0\text{x4040}\;\text{0000}\;0000\;0000 = 0\text{x}\red{0}\blue{100\;0000\;0100}\;0000\;0000\;0000\;0000\;0000\;0000\;0000\;0000\;0000\;0000\;0000\;0000\;0000 ,
s = 0    e x p = k = 11    f r a c = n = 52    s=0\;exp=k=11\;frac=n=52\; exp位不全为零,属于情况1,规格化的值, e = 1028 e=1028 8位蓝色二进制的值。
V = ( 1 ) s × M × 2 E = ( 1 ) s × ( 1 + f ) × 2 e B i a s = ( 1 ) s × ( 1 + f ) × 2 e 2 k 1 1 = ( 1 ) 0 × ( 1 + 0 281474976710655 ) × 2 1028 2 11 1 1 = 1 × ( 1 + 0 ) × 2 5 = 32.0 V=(-1)^s\times M \times 2^E=(-1)^s\times (1+f) \times 2^{e-Bias}=(-1)^s\times (1+f) \times 2^{e-2^{k-1}-1}\\[2ex]=(-1)^0\times (1+\frac{0}{281474976710655}) \times 2^{1028-2^{11-1}-1}=1\times(1+0)\times 2^5=32.0

3.11.5 在浮点代码中使用位级操作

单精度 双精度 效果 描述
vxorps vorps D S 2 D\leftarrow S_2 ^ S 1 S_1 位级异或(EXCLUSIVE-OR
vandps andpd D S 2 D\leftarrow S_2 & S 1 S_1 位级与(AND

3 50 X M M 128 \blue{图3-50} 对封装数据的位级操作(这些指令对一个XMM寄存器中的所有128位进行布尔操作)
tips:
产生浮点数0.0的汇编:

vxorpd	%xmm0, %xmm0, %xmm0

3.11.6 浮点比较操作

AVX提供了两条比较浮点值的指令(类似与CMP指令,但是操作数顺序相反):

指令 基于 描述
ucomiss S 1 , S 2 S_1, S_2 S 2 S 1 S_2-S_1 比较单精度值
ucomisd S 1 , S 2 S_1, S_2 S 2 S 1 S_2-S_1 比较双精度值

cmpq一样,遵循以相反顺序列出操作数的ATT格式惯例。参数 S 2 S_2 必须在XMM寄存器中。
浮点比较指令会设置三个条件码:零标志位 ZF、进位标志位CF和奇偶标记位PF
两个操作数中任意一个是NaN时,PF会置1
这里写图片描述
当任一操作数为NaN时,就会出现无序的情况。可以通过奇偶标志位发现这种情况。通常jp(jump on parity)指令是条件跳转,条件就是浮点比较得到一个无序的结果。
示例:
这里写图片描述
解析如下图
这里写图片描述
练习巩固:
在这里插入图片描述
答案:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_40199047/article/details/102725549