本篇介紹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指令繁多,這里舉得例子卻很少,以后我會在此文繼續附加一些說明例子,方便理解。
