SSE指令集
本篇介绍sse指令接,sse是流化SIMD扩展(Streaming SIMD Extension, SSE),提供全新的一组寄存器,处理128位打包数据。
sse提供了xmm寄存器,xmm一组8个128位的寄存器,分别名为xmm0-xmm7,sse构架提供对打包单精度浮点数的SIMD支持。
sse提供了两个版本的指令,其一以后缀ps结尾,这组指令对打包单精度浮点值执行类似mmx操作运算,而第二种后缀ss,这些指令对一个量标单精度浮点 值进行运算操作,这些指令不对打包值中的所有浮点值操作,而只对打包值中的低位双字节执行操作,源操作数中剩余的3个值直接传送给结果。
指令 | 说明 |
---|---|
movaps | 把4个对准的单精度值传送到xmm寄存器或者内存 |
movups | 把4个不对准的单精度值传送到xmm寄存器或者内存 |
movss | 把1个单精度值传送到内存或者寄存器的低位双字 |
movlps | 把2个单精度值传送到内存或者寄存器的低四字 |
movhps | 把2个单精度值传送到内存或者寄存器的高四字 |
movlhps | 把2个单精度值从低四字传送到高四字 |
movhlps | 把2个单精度值从高四字传送到低四字 |
其中对准操作movaps要求数据在内存中对准16字节的边界,以提交效率,否则应使用movups传送数据。
运算指令:
指令 | 说明 |
---|---|
addps | 将两个打包值相加 |
subps | 将两个打包值相减 |
mulps | 将两个打包值相乘 |
divps | 将两个打包值相除 |
rcpps | 计算打包值的倒数 |
sqrtps | 计算打包值的平方根 |
rsqrtps | 计算打包值的平方根倒数 |
maxps | 计算两个打包值中的最大值 |
minps | 计算两个打包值中的最小值 |
andps | 计算两个打包值的按位逻辑与 |
andnps | 计算两个打包值的按位逻辑非 |
orps | 计算两个打包值的按位逻辑或 |
xorps | 计算两个打包值的按位逻辑异或 |
以上指令都是用两个操作数:源操作数可以是128位内存或者xmm寄存器,目标操作数必须是xmm寄存器。
这里举一个简单的例子,使用gdb查看最后结果:
.section .data
value1: .float 12.12, 34.89, 56.23, 78.45
value2: .float 31.12, 57.124, 234.23, 67.246
.section .text
.globl _main
_main:
enter $0, $0
movups value1, %xmm0
movups value2, %xmm1
addps %xmm0, %xmm1
movups value2, %xmm1
maxps %xmm0, %xmm1
leave
ret
编译时加-g参数加入调试信息,调用addps后查看xmm1寄存器的结果,命令如下:
(gdb) print $xmm1
$1 = {v4_float = {43.2400017, 92.0139999, 290.459991, 145.695999},
v2_double = {26419069594869.762, 1245245520236216.2}, v16_int8 = {-61, -11,
44, 66, 43, 7, -72, 66, -31, 58, -111, 67, 45, -78, 17, 67}, v8_int16 = {
-2621, 16940, 1835, 17080, 15073, 17297, -19923, 17169}, v4_int32 = {
1110242755, 1119356715, 1133591265, 1125233197}, v2_int64 = {
4807600484593235395, 4832839782622116577},
uint128 = 0x4311b22d43913ae142b8072b422cf5c3}
(gdb)
可以看到,调用加法指令之后,四组和都存储在xmm1寄存器中,gdb查看时由于不知道如何解析xmm1寄存器的内容,因为可能是单精度,也可能是双精度或者不同宽度的整数,所以只能按不同的解析方式全部显示,查看v4_float即四个单精度浮点数的显示。
下面介绍一下sse构架下的比较指令,sse的比较指令单独比较128位打包单精度浮点的每个元素,结果是一个掩码,满足比较条件的结果全为1值,不满足结果的全为0值(量标只对最低的双字执行)。
指令 | 说明 |
---|---|
cmpps | 比较打包值 |
cmpss | 比较标量值 |
comiss | 比较标量值并且设置eflags寄存器 |
ucomiss | 比较标量值(包括非法值)并设置eflags寄存器 |
看到这里,仅仅有一个比较指令,并没有说明大小,何为满足条件全1,不满足全0呢,这样说一下指令的使用:
cmpps imp, source, destination
其中多出来的imp是一个无符号整数,这个整数表示的含义就是条件,这个条件值如下表所示:
整数 | 说明 |
---|---|
0 | 等于 |
1 | 小于 |
2 | 小于或等于 |
3 | 无序 |
4 | 不等于 |
5 | 不小于 |
6 | 不小于或等于 |
7 | 有序 |
如果需要比较两个数是否相等,传imp为0即可作为条件,满足条件结果全1,这是sse的比较方式。这里说明一下条件中的无序,因为是浮点比较,寄存器或内存中的有些值并不符合规定的浮点存储格式,相互比较是没有意义的,称为无序。
除了对浮点数的支持,sse指令集也有指令对mmx提供的功能进行扩展,他们对mmx寄存器中的数据执行操作:
指令 | 说明 |
---|---|
pavgb | 计算打包无符号字节整数的平均值 |
pavgw | 计算打包无符号字整数的平均值 |
pextrw | 把一个字从mmx寄存器复制到通用寄存器 |
pinsrw | 把一个字从通用寄存器复制到mmx寄存器 |
pmaxub | 计算打包无符号字节整数的最大值 |
pmaxsw | 计算打包有符号字整数的最大值 |
pminub | 计算打包无符号字节整数的最小值 |
pminsw | 计算打包有符号字整数的最小值 |
pmulhuw | 将打包无符号字整数相乘并且存储高位结果 |
psadbw | 计算无符号字节整数的绝对差的总和 |
SSE2 指令集又对 SSE 指令集做了很多扩充,主要对操作双精度浮点数和128位打包整数值执行数学操作,下面介绍SSE2的使用,先来看数据传送指令:
指令 | 说明 |
---|---|
movapd | 把2个对准的双精度值传送到xmm寄存器或者内存 |
movupd | 把2个不对准的双精度值传送到xmm寄存器或者内存 |
movdqa | 把2个对准的四字节整数传送到xmm寄存器或者内存 |
movdqu | 把2个不对准的四字节整数传送到xmm寄存器或者内存 |
movsd | 把1个双精度值传送到内存或者寄存器的低四字 |
movhpd | 把1个双精度值传送到内存或者寄存器的高四字 |
movlpd | 把1个双精度值传送到内存或者寄存器的低四字 |
SSE2指令集提供处理打包双精度浮点数,打包字整数,打包双字整数和打包四字整数值的数学指令,这里列举SSE2的加法指令来说明这一系列指令格式:
指令 | 说明 |
---|---|
addpd | 将打包双精度浮点值相加 |
addsd | 将量标双精度浮点值相加 |
paddsb | 将打包带符号字节整数相加 |
paddsw | 将打包带符号字整数相加 |
paddd | 将打包带符号双字整数相加 |
paddq | 将打包带符号四字整数相加 |
这里虽然只列举add系列指令,这些选项也存在于乘法和除法操作中(mulpd, mulsd, divpd, divsd等)。 另外同sse指令集,sse2指令集也提供专门的数学操作,sqrt, max, min。
最后我们来看SSE3指令集,SSE3构架并没有提供任何新的数据类型,仅仅添加了几条指令,用于更快的执行标准函数,下面是新指令的列表:
指令 | 说明 |
---|---|
fisttp | 把第一个fpu寄存器的值转换为整数(舍入)并且从fpu堆栈弹出 |
lddqu | 快速从内存加载128位不对准的数据值 |
movshdup | 传送128位值,复制第2个和第4个32位数据元素 |
movsldup | 传送128位值,复制第1个和第3个32位数据元素 |
movddup | 传送64位值,赋值值,使之成为128位值 |
addsubps | 对于打包单精度浮点数,对第2个和第4个32位执行加法,第1和第3个32位执行减法 |
addsubpd | 对于打包单精度浮点数,对第2对64位值执行加法,第1对位执行减法 |
haddps | 对操作数的相邻的元素执行单精度浮点加法操作 |
haddpd | 对操作数的相邻的元素执行双精度浮点加法操作 |
hsubps | 对操作数的相邻的元素执行单精度浮点减法操作 |
hsubpd | 对操作数的相邻的元素执行双精度浮点减法操作 |
SSE指令繁多,这里举得例子却很少,以后我会在此文继续附加一些说明例子,方便理解。