如果變量不是16字節對齊的,你會看到段錯誤問題。 The CPU can't MOVDQA to/from unaligned memory addresses,並且會生成處理器級別的「GP異常」,提示操作系統對您的應用進行段錯誤檢測。
您聲明(堆棧,全局)或堆上分配的C變量通常不與16字節的邊界對齊,但有時候偶爾會得到一個對齊的變量。您可以通過使用__m128或__m128i數據類型來指示編譯器確保正確對齊。其中每個都聲明一個正確對齊的128位值。
此外,讀取objdump時,它看起來像編譯器用代碼包裝asm序列,使用MOVQ指令將操作數從堆棧複製到xmm2和xmm3寄存器,然後將您的asm代碼複製到xmm0和xmm1。在進入xmm0之後,包裝器將結果複製到xmm2,然後將其複製回堆棧。總的來說,效率不是很高。 MOVQ一次複製8個字節,and expects (under some circumstances), an 8-byte aligned address。獲取未對齊的地址,它可能會像MOVDQA一樣失敗。但是,包裝器代碼會向BP寄存器添加一個對齊的偏移量(-0x80,-0x88和之後的-0x78),該寄存器可能包含或不包含對齊的值。總的來說,在生成的代碼中沒有對齊的保證。
下保證了參數和結果被存儲在正確對準的存儲器位置,並且似乎很好地工作:
#include <stdio.h>
#include <emmintrin.h>
void print128(__m128i value) {
int64_t *v64 = (int64_t*) &value;
printf("%.16llx %.16llx\n", v64[1], v64[0]);
}
void main() {
__m128i a = _mm_setr_epi32(0x00ffff00, 0x00ffff00, 0x00ffff00, 0x10ffff00), /* low dword first! */
b = _mm_setr_epi32(0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff),
x;
asm (
"movdqa %1, %%xmm0;" /* xmm0 <- a */
"movdqa %2, %%xmm1;" /* xmm1 <- b */
"pxor %%xmm1, %%xmm0;" /* xmm0 <- xmm0 xor xmm1 */
"movdqa %%xmm0, %0;" /* x <- xmm0 */
:"=x"(x) /* output operand, %0 */
:"x"(a), "x"(b) /* input operands, %1, %2 */
:"%xmm0","%xmm1" /* clobbered registers */
);
/* printf the arguments and result as 2 64-bit hex values */
print128(a);
print128(b);
print128(x);
}
編譯(GCC,ubuntu的32位)
gcc -msse2 -o app app.c
輸出:
10ffff0000ffff00 00ffff0000ffff00
0000ffff0000ffff 0000ffff0000ffff
10ff00ff00ff00ff 00ff00ff00ff00ff
在上面的代碼中,_mm_setr_epi32用於初始化a和b 128位值,因爲編譯器可能不支持128整數文字。
print128寫出128位整數的十六進制表示,因爲printf可能無法這樣做。
以下內容較短,避免了一些重複複製。編譯器會將其隱藏的包裝MOVDQA的使PXOR%2,%0您無需加載在自己的寄存器神奇的工作:
#include <stdio.h>
#include <emmintrin.h>
void print128(__m128i value) {
int64_t *px = (int64_t*) &value;
printf("%.16llx %.16llx\n", px[1], px[0]);
}
void main() {
__m128i a = _mm_setr_epi32(0x00ffff00, 0x00ffff00, 0x00ffff00, 0x10ffff00),
b = _mm_setr_epi32(0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff);
asm (
"pxor %2, %0;" /* a <- b xor a */
:"=x"(a) /* output operand, %0 */
:"x"(a), "x"(b) /* input operands, %1, %2 */
);
print128(a);
}
像以前一樣編譯:
gcc -msse2 -o app app.c
輸出:
10ff00ff00ff00ff 00ff00ff00ff00ff
另外,如果你想避免的內聯彙編,你可以使用SSE intrinsics instead(PDF)。這些內聯函數/宏使用類C語法封裝MMX/SSE指令。 _mm_xor_si128降低你的任務是單一的呼叫:
#include <stdio.h>
#include <emmintrin.h>
void print128(__m128i value) {
int64_t *v64 = (int64_t*) &value;
printf("%.16llx %.16llx\n", v64[1], v64[0]);
}
void main()
{
__m128i x = _mm_xor_si128(
_mm_setr_epi32(0x00ffff00, 0x00ffff00, 0x00ffff00, 0x10ffff00), /* low dword first !*/
_mm_setr_epi32(0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff));
print128(x);
}
編譯:
gcc -msse2 -o app app.c
輸出:
10ff00ff00ff00ff 00ff00ff00ff00ff
Roberto,回答你的問題,它有多長時間:要xor對齊已有的128位值,只需調用_mm_xor_si128。它位於emmintrin.h中,並在答案中列出的英特爾PDF中記錄。彙編時,它是一個在128位xmm寄存器上工作的單個操作碼(pxor)。如果代碼的其餘部分也處理128位值,它會開始增加值;否則你只需要向xmm regs推送值。對於64位或更少的值,我假設你知道你可以使用C xor操作符^ –
同樣,讀取objdump時,它看起來像生成的代碼將值加載到xmm2/3中,然後將它們複製到xmm0/1中,執行xor,然後在將結果複製到內存之前將結果複製回xmm2。總的來說,效率不高。我建議(1)開啓優化,並且(2)考慮在asm中編寫包含循環,以避免冗餘副本。 –