将SSE翻译成Neon:如何打包然后提取32位结果

时间:2015-03-20 13:29:38

标签: 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, -1, -1);

所以基本上我必须从寄存器中取出第4,第8,第12和第16个字节并将其放入uint32_t。看起来像一个包装说明(在SSE中我似乎记得我使用shuffle因为它与包装相比保存了一条指令,this example显示了包装说明的使用)。

此操作如何在霓虹灯中转换?
我应该使用包装说明吗?
如何提取32位? (有没有等同于_mm_cvtsi128_si32?)

修改
首先,vgetq_lane_u32应该允许替换_mm_cvtsi128_si32 (但我必须将我的uint8x16_t投射到uint32x4_t)

uint32_t  vgetq_lane_u32(uint32x4_t vec, __constrange(0,3) int lane);

或直接存储车道vst1q_lane_u32

void  vst1q_lane_u32(__transfersize(1) uint32_t * ptr, uint32x4_t val, __constrange(0,3) int lane); // VST1.32 {d0[0]}, [r0]

2 个答案:

答案 0 :(得分:4)

我找到了this excellent guide。 我正在研究这个问题,似乎我的操作可以通过一个VTBL指令(查找表)来完成,但是我将使用2个去交错操作来实现它,因为目前它看起来更简单。

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);

最后一个不会使用__attribute__((optimize("lax-vector-conversions")))

发出警告

但是,由于数据转换,无法进行2次分配。一种解决方法是这样的(编辑:这打破了严格的别名规则!编译器可以假设a在分配d的地址时不会改变。):

uint8x8x2_t* d = (uint8x8x2_t*) &a;
*d = vuzp_u8(vget_low_u8(a), vget_high_u8(a) );
*d = vuzp_u8(vget_low_u8(a), vget_high_u8(a) );
vst1q_lane_u32(out,a,0);

我通过灵活的数据类型实现了more general workaround

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);

修改

这是带有shuffle mask / look up table的版本。它确实使我的内循环更快一点。我再次使用了here所描述的数据类型。

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);

答案 1 :(得分:2)

我会这样写:

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