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

3.11 浮点代码

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

历史回顾:

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

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

每个扩展都是管理寄存器组中的数据:
MM寄存器    \impliedbyMMX64位;
XMM寄存器    \impliedbySSE128位;
YMM寄存器    \impliedbyAVX256位。

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

3.11.1 浮点传送和转换操作

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

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

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

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

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

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

指令 1 2 目的 描述
vcvttsi2ss M32M_{32}/R32R_{32} XX XX 把整数转换成单精度数
vcvttsi2sd M32M_{32}/R32R_{32} XX XX 把整数转换成双精度数
vcvttsi2ssq M64M_{64}/R64R_{64} XX XX 把四字整数转换成单精度数
vcvttsi2sdq M64M_{64}/R64R_{64} XX XX 把四字整数转换成双精度数

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

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寄存器的值,把它们存储到第三个寄存器中(例如:源R1R_1 = [s3s_3, s2s_2, s1s_1, s0s_0], 源R2R_2 = [d3d_3, d2d_2, d1d_1, d0d_0],那么目的寄存器R3R_3 = [s1s_1, d1d_1, s0s_0, d0d_0]。)。
对于使用三个相同寄存器%xmm0的,RR = [x3x_3, x2x_2, x1x_1, x0x_0]     \implies[x1x_1, x1x_1, x0x_0, x0x_0]。
对于指令vcvtps2pd\Downarrow
RR = [x3x_3, x2x_2, x1x_1, x0x_0]     \implies[x1x_1, x1x_1, x0x_0, x0x_0]
对于指令vunpcklps\Downarrow
[[x_1$, x1x_1, x0x_0, x0x_0]     \implies [dx0dx_0, dx0dx_0],这里的dx0dx_0是将xx转换成双精度后的结果。

对于双精度转换为单精度,·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保存着两个双精度值[x1x_1, x2x_2]。然后vmovddup指令把它设置为[x0x_0, x0x_0]。vcvtpd2psx指令把这两个值转换成单精度,再存放到该寄存器的低位一般中,并将高位一半置0,得到结果[0.0, 0.0, x0x_0, x0x_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 DS2+S1D\leftarrow S_2+S_1 浮点数加
vsubss vsubsd DS2S1D\leftarrow S_2-S_1 浮点数减
vmulss vmulsd DS2×S1D\leftarrow S_2\times S_1 浮点数乘
vdivss vdivsd DS2/S1D\leftarrow S_2/S_1 浮点数除
vmaxss vmaxsd Dmax(S2,S1)D\leftarrow max(S_2,S_1) 浮点数最大值
vminss vminsd Dmin(S2,S1)D\leftarrow min(S_2,S_1) 浮点数最小值
sqrtss sqrtsd DS1D\leftarrow \sqrt{S_1} 浮点数最大值

349\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编码过程:
0x3ffc  cccc  cccc  cccd=0x0011  1111  1111  1010  1010  1010  1010  1010  1010  1010  1010  1010  1010  1010  1010  10110\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  exp=k=11  frac=n=52  s=0\;exp=k=11\;frac=n=52\;exp位不全为零,属于情况1,规格化的值,e=1023e=10238位蓝色二进制的值。
V=(1)s×M×2E=(1)s×(1+f)×2eBias=(1)s×(1+f)×2e2k11=(1)0×(1+225179981368525281474976710655)×2102321111=1×(1+0.8)×20=1.8V=(-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编码过程:
0x4040  0000  0000  0000=0x0100  0000  0100  0000  0000  0000  0000  0000  0000  0000  0000  0000  0000  0000  0000  00000\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  exp=k=11  frac=n=52  s=0\;exp=k=11\;frac=n=52\;exp位不全为零,属于情况1,规格化的值,e=1028e=10288位蓝色二进制的值。
V=(1)s×M×2E=(1)s×(1+f)×2eBias=(1)s×(1+f)×2e2k11=(1)0×(1+0281474976710655)×2102821111=1×(1+0)×25=32.0V=(-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 DS2D\leftarrow S_2 ^ S1S_1 位级异或(EXCLUSIVE-OR
vandps andpd DS2D\leftarrow S_2 & S1S_1 位级与(AND

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

vxorpd	%xmm0, %xmm0, %xmm0

3.11.6 浮点比较操作

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

指令 基于 描述
ucomiss S1,S2S_1, S_2 S2S1S_2-S_1 比较单精度值
ucomisd S1,S2S_1, S_2 S2S1S_2-S_1 比较双精度值

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章