C++ 将SSE转换为Neon:如何打包然后提取32位结果

C++ 将SSE转换为Neon:如何打包然后提取32位结果,c++,arm,sse,neon,intrinsics,C++,Arm,Sse,Neon,Intrinsics,我必须将以下说明从SSE翻译成Neon uint32_t a = _mm_cvtsi128_si32(_mm_shuffle_epi8(a,SHUFFLE_MASK) ); 其中: static const __m128i SHUFFLE_MASK = _mm_setr_epi8(3, 7, 11, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -

我必须将以下说明从SSE翻译成Neon

 uint32_t a = _mm_cvtsi128_si32(_mm_shuffle_epi8(a,SHUFFLE_MASK) );
其中:

static const __m128i SHUFFLE_MASK = _mm_setr_epi8(3,  7,  11, 15, -1, -1, -1, -1,
                                                  -1, -1, -1, -1, -1, -1, -1, -1);
因此,基本上我必须从寄存器中取出第4、第8、第12和第16字节,并将其放入
uint32\u t
。看起来像一个打包指令(在SSE中,我似乎记得我使用了shuffle,因为它比打包节省了一条指令,显示了打包指令的使用)

这个操作如何在Neon中转换?
我应该使用打包说明吗?
然后如何提取32位?(是否有等同于
\u mm\u cvtsi128\u si32
?)

编辑:
首先,应允许更换
\u mm\u cvtsi128\u si32
(但我必须将我的uint8x16\u t转换为uint32x4\u t)

或者直接储存在车道上

我找到了。 我正在进行这项工作,似乎我的操作可以用一条VTBL指令(查找表)完成,但我将用两个解交错操作来实现它,因为目前它看起来更简单

uint8x8x2_t   vuzp_u8(uint8x8_t a, uint8x8_t b);
比如:

uint8x16_t a;
uint8_t* out;
[...]

//a = 138 0 0 0 140 0 0 0 146 0 0 0 147 0 0 0

a = vuzp_u8(vget_low_u8(a), vget_high_u8(a) );
//a = 138 0 140 0 146 0 147 0 0 0 0 0 0 0 0 0

a = vuzp_u8(vget_low_u8(a), vget_high_u8(a) );
//a = 138 140 146 147 0 0 0 0 0 0 0 0 0 0 0 0

vst1q_lane_u32(out,a,0);
最后一个使用
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

但是,由于数据转换,这两个赋值是不可能的。一种解决方法是这样的(Edit:这打破了严格的别名规则!编译器可以假设
a
在分配
d
的地址时不会更改):

我通过一种灵活的数据类型实现了:

NeonVectorType<uint8x16_t> a; //a can be used as a uint8x16_t, uint8x8x2_t, uint32x4_t, etc.
a = vuzp_u8(vget_low_u8(a), vget_high_u8(a) );
a = vuzp_u8(vget_low_u8(a), vget_high_u8(a) );
vst1q_lane_u32(out,a,0);
NeonVectorType a//a可用作uint8x16、UINT8x2、uint32x4等。
a=vuzp_u8(vget_low_u8(a),vget_high_u8(a));
a=vuzp_u8(vget_low_u8(a),vget_high_u8(a));
vst1q_lane_u32(out,a,0);
编辑:

这是一个带有洗牌掩码/查找表的版本。它确实使我的内部循环快了一点。同样,我使用了描述的数据类型

static const uint8x8\u t MASK={0x00,0x04,0x08,0x0C,0xff,0xff,0xff,0xff};
新溶媒a型//a可用作uint8x16、UINT8x2、uint32x4等。
新投资者类型//res可以用作uint8x8\u t、uint32x2\u t等。
[...]
res=vtbl2_u8(a,遮罩);
vst1_lane_u32(out,res,0);

我会这样写:

uint32_t extract (uint8x16_t x)
{
  uint8x8x2_t a = vuzp_u8 (vget_low_u8 (x), vget_high_u8 (x));
  uint8x8x2_t b = vuzp_u8 (a.val[0], a.val[1]);
  return vget_lane_u32 (vreinterpret_u32_u8 (b.val[0]), 0);
}
在最新的GCC版本上编译为:

extract:
    vuzp.8  d0, d1
    vuzp.8  d0, d1
    vmov.32 r0, d0[0]
    bx  lr

查一下他们图书馆的来源。是的,我看到了。但它是另一种方式的移植指南……提供了VREInterpress_*内部函数以避免指针转换的需要。您的版本更尊重数据类型:)
NeonVectorType<uint8x16_t> a; //a can be used as a uint8x16_t, uint8x8x2_t, uint32x4_t, etc.
a = vuzp_u8(vget_low_u8(a), vget_high_u8(a) );
a = vuzp_u8(vget_low_u8(a), vget_high_u8(a) );
vst1q_lane_u32(out,a,0);
static const uint8x8_t MASK = {0x00,0x04,0x08,0x0C,0xff,0xff,0xff,0xff};
NeonVectorType<uint8x16_t> a; //a can be used as a uint8x16_t, uint8x8x2_t, uint32x4_t, etc.
NeonVectorType<uint8x8_t> res; //res can be used as uint8x8_t, uint32x2_t, etc.
[...]
res = vtbl2_u8(a, MASK);
vst1_lane_u32(out,res,0);
uint32_t extract (uint8x16_t x)
{
  uint8x8x2_t a = vuzp_u8 (vget_low_u8 (x), vget_high_u8 (x));
  uint8x8x2_t b = vuzp_u8 (a.val[0], a.val[1]);
  return vget_lane_u32 (vreinterpret_u32_u8 (b.val[0]), 0);
}
extract:
    vuzp.8  d0, d1
    vuzp.8  d0, d1
    vmov.32 r0, d0[0]
    bx  lr