2012-04-02 93 views
12

固有的_mm_slli_si128將執行128位寄存器左側的邏輯移位,但僅限於立即移位值,並且按字節移位而不移位。尋找對於非即時移位值的sse 128位移位操作

我可以使用像_mm_sll_epi64_mm_sll_epi32這樣的內在函數在__m128i寄存器中左移一組值,但這些值不包含「溢出」位。

對於由N位的轉變想象我可以做一個類似的東西:

  • _mm_sll_epi64
  • _mm_srr_epi64(對位我想繼續:它們移到低位)
  • 洗牌srr結果
  • 或這些在一起。

(但可能還必須包括N相對於64的檢查)。

有沒有更好的方法?

+1

我不認爲有更好的辦法。我寫了一個最近重複這個問題的答案:http://stackoverflow.com/q/34478328/224132。對於編譯時常量計數,它會變成4個insns,或者2個insns的count> = 64。通過變量計數,它會分支並將count和64-count從整數轉移到向量寄存器。如果數據已經在整數寄存器中,'__uint128_t'在這種情況下會更好。 – 2015-12-27 17:04:12

回答

4

不是你理想的解決方案,但是如果你想旋轉或移位一個8位倍數的SSE寄存器,那麼PSHUFB指令(和_mm_shuffle_epi8()內部)可以提供幫助。它需要第二個SSE寄存器作爲輸入;寄存器中的每個字節都保存一個用於索引第一個輸入寄存器中字節的值。

+4

我認爲OP在具體說明他想要的是位粒度,而不是限於立即數。 '_mm_shuffle_epi8()'既是字節粒度,也需要立即。 – Mysticial 2012-04-02 18:51:04

+4

我知道他需要一點粒度,因此我的答案中的第一個條款。另外'_mm_shuffle_epi8()'不需要立即;第二個參數是一個'__m128i'值。 [請參閱此處的文檔](http://msdn.microsoft.com/en-us/library/bb531427.aspx)。 – 2012-04-02 18:57:51

+1

我應該注意到,這個函數需要SSSE3支持,如果你想在老的機器上運行,這可能是不夠的。 – 2012-04-02 18:59:47

4

這是在unusual C preprocessor uses的博客文章(我的)發現的一個側面問題。 對於127個不同的偏移量,有四個不同的SSE2指令的最佳序列用於移位。預處理器使得構造一個相當於129路轉換語句的移位函數變得合理。原諒這裏的原始代碼;我不熟悉直接在這裏發佈代碼。 查看博客文章,瞭解正在發生的事情。

#include <emmintrin.h> 

typedef __m128i XMM; 
#define xmbshl(x,n) _mm_slli_si128(x,n) // xm <<= 8*n -- BYTE shift left 
#define xmbshr(x,n) _mm_srli_si128(x,n) // xm >>= 8*n -- BYTE shift right 
#define xmshl64(x,n) _mm_slli_epi64(x,n) // xm.hi <<= n, xm.lo <<= n 
#define xmshr64(x,n) _mm_srli_epi64(x,n) // xm.hi >>= n, xm.lo >>= n 
#define xmand(a,b) _mm_and_si128(a,b) 
#define xmor(a,b) _mm_or_si128(a,b) 
#define xmxor(a,b) _mm_xor_si128(a,b) 
#define xmzero  _mm_setzero_si128() 

XMM xm_shl(XMM x, unsigned nbits) 
{ 
    // These macros generate (1,2,5,6) SSE2 instructions, respectively: 
    #define F1(n) case 8*(n): x = xmbshl(x, n); break; 
    #define F2(n) case n: x = xmshl64(xmbshl(x, (n)>>3), (n)&15); break; 
    #define F5(n) case n: x = xmor(xmshl64(x, n), xmshr64(xmbshl(x, 8), 64-(n))); break; 
    #define F6(n) case n: x = xmor(xmshl64(xmbshl(x, (n)>>3), (n)&15),\ 
            xmshr64(xmbshl(x, 8+((n)>>3)), 64-((n)&155))); break; 
    // These macros expand to 7 or 49 cases each: 
    #define DO_7(f,x) f((x)+1) f((x)+2) f((x)+3) f((x)+4) f((x)+5) f((x)+6) f((x)+7) 
    #define DO_7x7(f,y) DO_7(f,(y)+1*8) DO_7(f,(y)+2*8) DO_7(f,(y)+3*8) DO_7(f,(y)+4*8) \ 
             DO_7(f,(y)+5*8) DO_7(f,(y)+6*8) DO_7(f,(y)+7*8) 
    switch (nbits) { 
    case 0: break; 
    DO_7(F5, 0) // 1..7 
    DO_7(F1, 0) // 8,16,..56 
    DO_7(F1, 7) // 64,72,..120 
    DO_7x7(F6, 0) // 9..15 17..23 ... 57..63 i.e. [9..63]\[16,24,..,56] 
    DO_7x7(F2,56) // 65..71 73..79 ... 121..127 i.e. [65..127]\[64,72,..,120] 
    default: x = xmzero; 
    } 
    return x; 
} 

xm_shr量以上但交換 「SHL」 和無處不在的F [1256]宏 「SHR」。 HTH。

+2

實際上,上面的代碼不適用於大約一半的移位值。我對128位整數的標準移位進行了測試(gcc支持__uint128_t),結果明顯不同。例如,高於120的所有移位都將零位全部清零。 – seba 2015-11-09 00:10:47

+1

對於編譯時常量移位計數,您永遠不需要超過4條指令(或5個不帶AVX:額外的'movdqa')。對於計數<64,字節移位由64b左移,然後移位右移64位。 'OR'與'psllq xmm0,64'進位。我用'if'編寫了它,它編譯好的時間是編譯時常數:http://goo.gl/O14GhI。請參閱http://stackoverflow.com/a/34482688/224132 – 2015-12-27 17:13:44

+0

要修復代碼,只需用&7替換每個&15或&155表達式即可。這就是說,這段代碼非常慢(你知道分支嗎?!),Peter Cordes的建議看起來更有希望。 – 2016-01-03 13:36:07