MMX指令集
這篇來介紹intel cpu的高級特性,SIMD-單指令多數據,從名字來看,就是執行一條指令可以計算多個數據。先從最簡單的mmx指令集來看,在寄存器那篇已經提 到,mmx有 mm0-mm7 共8個64位寄存器,但是寄存器并非獨立寄存器,而是復用了上篇說到的fpu數據堆棧寄存器,也就是說使用mmx指令集會破壞fpu的計算,如果同時使用 著兩種特性,一定要注意這點,避免出現莫名的錯誤。
首先mmx指令集需要cpu的支持,但不是所有cpu都支持,不然也不會稱之為高級特性 了,所以在使用之前需要檢測,檢測指令為cpuid,獲得cpu的特性,cpuid雖然只有一條指令,但是其隱含的內容太多,這里僅僅介紹檢測SIMD指 令集所需要的部分,其他一些信息可參閱Intel 手冊獲得。
當eax為1時,cpuid指令返回cpu簽名信息,放入ecx和edx寄存器中,相應位為1表示支持。檢測SIMD指令集的結果如下:
| EDX | 23 | 支持MMX |
| EDX | 25 | 支持SSE |
| EDX | 26 | 支持SSE2 |
| ECX | 0 | 支持SSE3 |
具體檢測代碼如下(AT&T 語法):
.section .datammxstring: .asciz "支持mmx指令集\n"ssestring: .asciz "支持sse指令集\n"sse2string: .asciz "支持sse2指令集\n"sse3string: .asciz "支持sse3指令集\n".section .text .globl _main _main:movl $1, %eaxcpuidmmxop:test $0x800000, %edxjz sseoppushl $mmxstringcall _printfsseop:test $0x2000000, %edxjz sse2oppushl $ssestringcall _printfsse2op:test $0x4000000, %edxjz sse3oppushl $sse2stringcall _printfsse3op:test $0x01, %ecxjz endpushl $sse3stringcall _printf end:pushl $0call _exit下面正式開始mmx指令集的介紹,使用mmx需要三個步驟:
- 從整數值創建打包整數,載入mmx寄存器
- 使用mmx指令集計算
- 從mmx獲得結果,存入內存
第一個和最后一個步驟比較簡單,僅僅是數據移動而已,這里提到打包,因為這里要單指令多數據,所以需要把多數據合成一個操作數進行計算,存入64位的mmx寄存器中,打包的過程就是把 8個字節/4個字/2個雙字 合成一個64位數據。
從加減法說起,對于普通數據,如果數據溢出可以置標記位,但是對于多數據的運算,由于同時計算多個加法,就不能單純的設置標志,對mmx計算有幾種情況:
環繞運算 截斷其值,丟棄進位 帶符號飽和 最大/最小 帶符號值 無符號飽和 最大/最小 無符號值其中飽和運算的預設值根據結果的位數決定,有符號8位最大為127,如果超過127,結果按127計算,其他情況與此類似,這里方便與一些圖形處理,比如色彩黑色為0,為無符號最小值,小于其值也按黑色處理。
好 了,到此可以看一下具體的指令,這里的指令有相同的格式,instruction source, destination;其中source可以是mmx寄存器或者64位內存,destination為mmx寄存器。這是AT&T語法,對于 MASM語法源目的操作數相反。
| paddb | 環繞打包字節整數加法 |
| paddw | 環繞打包字整數加法 |
| paddd | 環繞打包雙字整數加法 |
| paddsb | 帶符號飽和打包字節整數加法 |
| paddsw | 帶符號飽和打包字整數加法 |
| paddusb | 無符號飽和打包字節整數加法 |
| paddusw | 無符號飽和打包字整數加法 |
| psubb | 環繞打包字節整數減法 |
| psubw | 環繞打包字整數減法 |
| psubd | 環繞打包雙字整數減法 |
| psubsb | 帶符號飽和打包字節整數減法 |
| psubsw | 帶符號飽和打包字整數減法 |
| psubusb | 無符號飽和打包字節整數減法 |
| psubusw | 無符號飽和打包字整數減法 |
下面以AT&T加法為例進行說明,這里以飽和方式計算4個無符號字之和:
# add four word # output : result is 18932, 7631, 65535, 510 .section .datavalue1: .short 12300, 2384, 60000, 456value2: .short 6632, 5247, 40000, 54outstring: .asciz "result is %u, %u, %u, %u\n" .section .text .globl _main _main:movq value1, %mm0movq value2, %mm1paddusw %mm1, %mm0movq %mm0, value1movl $value1, %ebxxorl %eax, %eaxmovw 6(%ebx), %axpushl %eaxmovw 4(%ebx), %axpushl %eaxmovw 2(%ebx), %axpushl %eaxmovw (%ebx), %axpushl %eaxpushl $outstringcall _printfpushl $0call _exitmovq 指令把內存中的數據傳送至mmx寄存器,如果數據之前在內存中不是連續的,則需要集中存放,即進行打包,之后使用paddusw進行加法計算,輸出時 word需轉化成dword放入堆棧,可以看到以飽和方式第三個結果為65535,即16位無符號數的最大值。從這里例子可以看出,通過一條指令計算了四 個word整數相加,很大程度上提高了計算的效率,但是同時需要注意,整數的打包以及傳送過程也需要耗時,如果打包操作很多,結果不是提高效率而是降低效 率了。
mmx指令集的加法根據需要有飽和方式和環繞方式計算,但對于乘法而言,由于結果的寬度可能是操作數的兩倍,所以兩種方式看上去都不合適,所以intel提供了兩個指令,一個得到計算結果的低字節,另一個得到計算結果的高字節。
| pmulluw | 對無符號16位整數相乘取結果低16位 |
| pmulhuw | 對無符號16位整數相乘取結果高16位 |
| pmullw | 對有符號16位整數相乘取結果低16位 |
| pmulhw | 對有符號16位整數相乘取結果高16位 |
| pmaddwd | 對4個帶符號整數相乘,高位兩個結果相加存入高32位,低位相同 |
mmx指令集還提供對四字值進行布爾邏輯操作和移位指令:
| pand | 對源和目標操作數按位與操作 |
| pandn | 對目標操作數進行按位邏輯非操作,然后對源和目標操作數按位與操作 |
| por | 對源和目標操作數按位或操作 |
| pxor | 對源和目標操作數按位異或操作 |
| psll | 對目標操作數執行邏輯左移操作,使用0填充空位 |
| psra | 對目標操作數執行邏輯右移操作,使用0填充空位 |
其AT&T指令格式如下:
pand source, destination其中source是mmx寄存器或者64位內存,destination必須是mmx寄存器。移位指令可以使用字,雙字或者四字操作數,還有移位的位置數量。MASM格式的源目的操作數相反。
mmx構架提供了用于比較兩個值的指令:
| pcmpeqb | 比較打包字節整數值的相等性 |
| pcmpeqw | 比較打包字整數值的相等性 |
| pcmpeqd | 比較打包雙字整數值的相等性 |
| pcmpgtb | 判斷打包字節整數值是否大于另一個 |
| pcmpgtw | 判斷打包字整數值是否大于另一個 |
| pcmpgtd | 判斷打包雙字整數值是否大于另一個 |
因為mmx同時比較多個數據,所以不能設置標志,替換的做法是把判斷結果放到目標打包整數值中,如果打包整數值滿足對比提交,就把結果設置為全1,否則設置為全0。
由于mmx指令集并非所有cpu都可以支持,所以對c語言這種編譯通用性的程序而言,是不會貿然使用mmx指令集的,這也對我們手工匯編優化程序提供了很大的空間,但是需要注意打包整數的效率損耗。
另外,intel除了mmx指令集,另有SIMD指令如sse指令集,將會再下篇詳細說明。
轉載:http://fancymore.com/reading/assembler-mmx-instruct.html
轉載于:https://www.cnblogs.com/DeeLMind/p/7367775.html
總結
- 上一篇: Intel汇编-传送MMX整数
- 下一篇: MMX 指令