本篇介绍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指令繁多,这里举得例子却很少,以后我会在此文继续附加一些说明例子,方便理解。