使用ARM Neon内在函数进行128位旋转

时间:2012-06-29 09:48:29

标签: c rotation intrinsics neon

我正在尝试使用Neon内在函数优化我的代码。我在128位阵列上进行24位旋转(每个uint16_t 8个)。

这是我的c代码:

uint16_t rotated[8];
uint16_t temp[8];
uint16_t j;
for(j = 0; j < 8; j++)
{
     //Rotation <<< 24  over 128 bits (x << shift) | (x >> (16 - shift)
     rotated[j] = ((temp[(j+1) % 8] << 8) & 0xffff) | ((temp[(j+2) % 8] >> 8) & 0x00ff);
}

我查看了有关Neon Intrinsics的gcc文档,但没有关于向量旋转的说明。此外,我尝试使用vshlq_n_u16(temp, 8)执行此操作,但移位到uint16_t字之外的所有位都将丢失。

如何使用霓虹内在函数实现这一目标?那么有关于GCC Neon Intrinsics的更好的文档吗?

3 个答案:

答案 0 :(得分:6)

在对Arm Community Blogs进行一些阅读后,我发现了这一点:

Neon Arm Bitwise Rotation

  

VEXT:提取   VEXT从一对现有向量中提取新的字节向量。新向量中的字节来自第一个操作数的顶部和第二个操作数的底部。这允许您生成包含跨越一对现有向量的元素的新向量。 VEXT可用于实现两个矢量数据的移动窗口,在FIR滤波器中很有用。 对于置换,当对两个输入操作数使用相同的向量时,它也可用于模拟逐字节旋转操作。

以下Neon GCC Intrinsic与图片中提供的程序集相同:

uint16x8_t vextq_u16 (uint16x8_t, uint16x8_t, const int)

因此,完整的128位向量(不是在每个元素上)的24位旋转可以通过以下方式完成:

uint16x8_t input;
uint16x8_t t0;
uint16x8_t t1;
uint16x8_t rotated;

t0 = vextq_u16(input, input, 1);
t0 = vshlq_n_u16(t0, 8);
t1 = vextq_u16(input, input, 2);
t1 = vshrq_n_u16(t1, 8);
rotated = vorrq_u16(t0, t1);

答案 1 :(得分:4)

我不是100%肯定,但我不认为NEON有旋转说明。

您可以使用左移,右撇子和或等组合所需的旋转操作:

uint8_t ror(uint8_t in, int rotation)
{
    return (in >> rotation) | (in << (8-rotation));
}

对于左移,右屎和或者。或者对霓虹内在函数做同样的事。

uint16x8_t temp;
uint8_t rot;

uint16x8_t rotated =  vorrq_u16 ( vshlq_n_u16(temp, rot) , vshrq_n_u16(temp, 16 - rot) );

请参阅http://en.wikipedia.org/wiki/Circular_shift“实施循环转换。”

这将旋转通道内的值。如果您想要自动旋转车道,请使用其他答案中所述的VEXT。

答案 2 :(得分:2)

使用vext.8与自身连接向量,并为您提供所需的16字节窗口(在这种情况下偏移3个字节)。

使用内在函数requires casting执行此操作以使编译器满意,但它仍然只是一条指令:

#include <arm_neon.h>

uint16x8_t byterotate3(uint16x8_t input) {
    uint8x16_t tmp = vreinterpretq_u8_u16(input);
    uint8x16_t rotated = vextq_u8(tmp, tmp, 16-3);
    return vreinterpretq_u16_u8(rotated);
}

g++5.4 -O3 -march=armv7-a -mfloat-abi=hard -mfpu=neonon Godbolt)将其编译为:

byterotate3(__simd128_uint16_t):
    vext.8  q0, q0, q0, #13
    bx      lr

16-3的计数意味着我们左旋3个字节。 (这意味着我们从左向量中获取13个字节,从右向量中获取3个字节,因此它也向右旋转13个。)

相关:x86还有一个指令,它将滑动窗口放入两个寄存器的串联中:palignr(在SSSE3中添加)。

也许我错过了一些关于NEON的内容,但我不明白为什么OP的自我回答是使用vext.16(vextq_u16),它具有16位粒度。它甚至不是一个不同的指令,只是vext.8的别名,这使得无法使用奇数计数,需要额外的指令。 The manual for vext.8 says

  

VEXT伪指令

     

您可以指定数据类型为16,32或64而不是8.在此处   case,#imm是指半字,单词或双字而不是   引用字节,相应地允许范围   降低。