C++ 寻找非立即移位值的sse 128位移位操作
内部C++ 寻找非立即移位值的sse 128位移位操作,c++,c,sse,C++,C,Sse,内部\u mm\u slli\u si128将对128位寄存器进行逻辑左移位,但仅限于立即移位值,并按字节而不是位移位 我可以使用像\u mm\u sll\u epi64或\u mm\u sll\u epi32这样的内在函数将\u m128i寄存器中的一组值向左移位,但这些值不携带“溢出”位 对于N位的移位,想象一下我可以做如下操作: \u mm\u sll\u epi64 \u mm\u srr\u epi64(对于我要携带的位:将它们移到低位) 洗牌srr结果 或者把这些放在一起 (但可
\u mm\u slli\u si128
将对128位寄存器进行逻辑左移位,但仅限于立即移位值,并按字节而不是位移位
我可以使用像\u mm\u sll\u epi64
或\u mm\u sll\u epi32
这样的内在函数将\u m128i
寄存器中的一组值向左移位,但这些值不携带“溢出”位
对于N位的移位,想象一下我可以做如下操作:
\u mm\u sll\u epi64
(对于我要携带的位:将它们移到低位)\u mm\u srr\u epi64
- 洗牌srr结果
- 或者把这些放在一起
有更好的方法吗?不是理想的解决方案,但是如果您想将SSE寄存器旋转或移位8的倍数,那么
PSHUFB
指令(以及\u mm\u shuffle\u epi8()
内在指令)可以提供帮助。它将第二个SSE寄存器作为输入;寄存器中的每个字节都有一个值,用于索引第一个输入寄存器中的字节。这是我在上一篇博文中提到的一个次要问题。
对于127个不同的移位偏移,有四个不同的SSE2指令的最优序列用于位移位。预处理器可以合理地构造一个相当于129路switch语句的移位函数。请原谅这里的原始代码;我不熟悉直接在这里发布代码。
查看博客文章,了解发生了什么
#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;
}
#包括
类型定义uu m128i XMM;
#定义xmbshl(x,n)\u mm\u slli\u si128(x,n)//xm=8*n——字节右移
#定义xmshl64(x,n)\u mm\u slli\u epi64(x,n)//xm.hi=n
#定义xmand(a,b)_mm_和_si128(a,b)
#定义xmor(a,b)_mm_或_si128(a,b)
#定义xmxor(a,b)\u mm\u xor\u si128(a,b)
#定义xmzero\u mm\u setzero\u si128()
XMM xm_shl(XMM x,无符号NBIT)
{
//这些宏分别生成(1,2,5,6)SSE2指令:
#定义F1(n)情况8*(n):x=xmbshl(x,n);中断;
#定义F2(n)情况n:x=xmshl64(xmbshl(x,(n)>>3,(n)&15);中断;
#定义F5(n)情况n:x=xmor(xmshl64(x,n),xmshr64(xmbshl(x,8),64-(n));break;
#定义F6(n)情况n:x=xmor(xmshl64(xmbshl(x,(n)>>3)、(n)&15)\
xmshr64(xmbshl(x,8+((n)>>3)),64-((n)和155));断裂;
//这些宏扩展到每个7或49个案例:
#定义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)
#定义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)
开关(nbits){
案例0:断裂;
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即[9..63]\[16,24,…,56]
DO_7x7(F2,56)//65..71 73..79..121..127即[65..127]\[64,72,…,120]
默认值:x=xmzero;
}
返回x;
}
xm_shr相当于上述内容,但在F[1256]宏中到处交换“shl”和“shr”。HTH.我认为OP明确表示,他需要比特粒度,而不限于即时
\u mm\u shuffle\u epi8()
是字节粒度,需要立即数。我知道他想要位粒度,因此我的答案中的第一个子句。而且,\u mm\u shuffle\u epi8()
不需要立即执行;第二个参数是一个\uuu m128i
值。我应该注意,此函数需要SSSE3支持,如果您想在旧机器上运行,这可能不够。@Mysticial:Jason是对的,pshufb
具有16个预先计算的值,可用于模拟变量的字节移位。在这种情况下,可以使用它进行qword移位(当然是0或1Qword;-),剩余的64位移位可以按照OP的建议进行。实际上,上面的代码对大约一半的移位值不起作用。我对128位整数(gcc支持128位整数)进行了标准移位测试,结果明显不同。例如,120以上的所有移位只需将所有位归零。对于编译时常量移位计数,您永远不需要超过4条指令(或5条没有AVX的指令:额外的movdqa
)。对于计数<64,字节左移64b,然后位右移64计数或
带psllq xmm0,64的进位。我用一个if
编写了它,它很好地编译了一个编译时常量计数:。请参阅以修复代码,只需将每个&15或&155表达式替换为&7即可。这就是说,这段代码非常慢(你知道分支吗?!),而且Peter Cordes的建议看起来更有希望。:-)这段代码源于一个例子,它扩展了c预处理器在模板类代码中的使用,只使用了一个开关。感谢您深入阅读。不久前,我发现使用带有可变移位计数的直接int64更快。但是我会修改示例代码。我认为没有更好的方法。我写了一个关于这个问题的最新副本的答案:。对于编译时常量计数,它将变成4个INSN,或计数>=64的2个INSN。使用变量count,它从整数到向量寄存器进行分支,并必须movd
计数和64计数<如果数据已经在整数寄存器中,则代码>\uuuu uint128\u t
在这种情况下效果更好。