在SSE寄存器中随机播放偶数和奇数的vaues

我加载了两个具有16位值的SSE 128位寄存器。 值按以下顺序排列:

src[0] = [E_3, O_3, E_2, O_2, E_1, O_1, E_0, O_0] src[1] = [E_7, O_7, E_6, O_6, E_5, O_5, E_4, O_4] 

我想要实现的是这样的订单:

 src[0] = [E_7, E_6, E_5, E_4, E_3, E_2, E_1, E_0] src[1] = [O_7, O_6, O_5, O_4, O_3, O_2, O_1, O_0] 

你知道是否有一个很好的方法(使用SSE内在函数直到SSE 4.2)?

我现在卡住了,因为我不能在128位寄存器的上半部分和下半部分之间移动16位值。 我发现只有_mm_shufflelo_epi16_mm_shufflehi_epi16内在函数。

更新:

感谢Paul,我曾考虑将epi8内在函数用于16位值。

我的解决方案如下:

 shuffle_split = _mm_set_epi8(15, 14, 11, 10, 7, 6, 3, 2, 13, 12, 9, 8, 5, 4, 1, 0); xtmp[0] = _mm_load_si128(src_vec); xtmp[1] = _mm_load_si128(src_vec+1); xtmp[0] = _mm_shuffle_epi8(xtmp[0], shuffle_split); xtmp[1] = _mm_shuffle_epi8(xtmp[1], shuffle_split); xsrc[0] = _mm_unpacklo_epi16(xtmp[0], xtmp[1]); xsrc[0] = _mm_shuffle_epi8(xsrc[0], shuffle_split); xsrc[1] = _mm_unpackhi_epi16(xtmp[0], xtmp[1]); xsrc[1] = _mm_shuffle_epi8(xsrc[1], shuffle_split); 

还有更好的解决方案吗?

SSE的排列并不容易。 有许多方法可以通过各种指令组合来实现相同的结果。 不同的组合可能需要不同数量的指令,寄存器或存储器访问。 我不想手动解决这样的谜题,而是只想看看LLVM编译器的作用,所以我用LLVM的中间语言写了一个简单版本的你想要的排列,它利用了一个非常灵活的向量shuffle指令:

 define void @shuffle_even_odd(<8 x i16>* %src0) { %src1 = getelementptr <8 x i16>* %src0, i64 1 %a = load <8 x i16>* %src0, align 16 %b = load <8 x i16>* %src1, align 16 %x = shufflevector <8 x i16> %a, <8 x i16> %b, <8 x i32>  %y = shufflevector <8 x i16> %a, <8 x i16> %b, <8 x i32>  store <8 x i16> %x, <8 x i16>* %src0, align 16 store <8 x i16> %y, <8 x i16>* %src1, align 16 ret void } 

使用LLVM IR-to-ASM编译器编译它: llc shuffle_even_odd.ll -o shuffle_even_odd.s你会得到类似下面的x86程序集:

 movdqa (%rdi), %xmm0 movdqa 16(%rdi), %xmm1 movdqa %xmm1, %xmm2 pshufb LCPI0_0(%rip), %xmm2 movdqa %xmm0, %xmm3 pshufb LCPI0_1(%rip), %xmm3 por %xmm2, %xmm3 movdqa %xmm3, (%rdi) pshufb LCPI0_2(%rip), %xmm1 pshufb LCPI0_3(%rip), %xmm0 por %xmm1, %xmm0 movdqa %xmm0, 16(%rdi) 

我排除了上面LCPIO_ *引用的常量数据部分,但这大致转换为以下C代码:

 void shuffle_even_odd(__m128i * src) { __m128i shuffle0 = _mm_setr_epi8(128, 128, 128, 128, 128, 128, 128, 128, 2, 3, 6, 7, 10, 11, 14, 15); __m128i shuffle1 = _mm_setr_epi8(2, 3, 6, 7, 10, 11, 14, 15, 128, 128, 128, 128, 128, 128, 128, 128); __m128i shuffle2 = _mm_setr_epi8(128, 128, 128, 128, 128, 128, 128, 128, 0, 1, 4, 5, 8, 9, 12, 13); __m128i shuffle3 = _mm_setr_epi8(0, 1, 4, 5, 8, 9, 12, 13, 128, 128, 128, 128, 128, 128, 128, 128); __m128i a = src[0]; __m128i b = src[1]; src[0] = _mm_or_si128(_mm_shuffle_epi8(b, shuffle0), _mm_shuffle_epi8(a, shuffle1)); src[1] = _mm_or_si128(_mm_shuffle_epi8(b, shuffle2), _mm_shuffle_epi8(a, shuffle3)); } 

那只是4个shuffle和2个bitwise或者指令。 我怀疑这些按位指令可以在CPU管道中比你提出的解包指令更有效地调度。

您可以在LLVM的下载页面的“Clang Binaries”包中找到“llc”编译器: http : //www.llvm.org/releases/download.html