将 SSE 翻译成 Neon:如何打包然后提取 32 位结果
Translating SSE to Neon: How to pack and then extract 32bit result
我必须将以下指令从 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
。好像是packing指令(在SSE我好像记得我用了shuffle因为比packing省了一条指令,this example说明了packing指令的使用)
这个操作如何在 Neon 中转换?
我应该使用打包指令吗?
然后我如何提取 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]
我找到了 this excellent guide。
我正在努力,似乎我的操作可以用一个 VTBL 指令完成(查找 table),但我将用 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);
我通过灵活的数据类型实现了:
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);
编辑:
这是随机播放 mask/look 向上 table 的版本。它确实使我的内部循环更快了一点。同样,我使用了 .
描述的数据类型
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);
}
在最新的 GCC 版本中编译为:
extract:
vuzp.8 d0, d1
vuzp.8 d0, d1
vmov.32 r0, d0[0]
bx lr
我必须将以下指令从 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
。好像是packing指令(在SSE我好像记得我用了shuffle因为比packing省了一条指令,this example说明了packing指令的使用)
这个操作如何在 Neon 中转换?
我应该使用打包指令吗?
然后我如何提取 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]
我找到了 this excellent guide。 我正在努力,似乎我的操作可以用一个 VTBL 指令完成(查找 table),但我将用 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);
我通过灵活的数据类型实现了
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);
编辑:
这是随机播放 mask/look 向上 table 的版本。它确实使我的内部循环更快了一点。同样,我使用了
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);
}
在最新的 GCC 版本中编译为:
extract:
vuzp.8 d0, d1
vuzp.8 d0, d1
vmov.32 r0, d0[0]
bx lr