我正在使用 C++ 代码中的 NEON 内在函数进行 ARM 优化。我理解并掌握了大多数打字问题,但我被困在这个问题上:
该指令vzip_u8
返回一个uint8x8x2_t
值(实际上是一个由两个组成的数组uint8x8_t
)。我想将返回的值分配给一个普通的uint16x8_t
. 我认为没有适当vreinterpretq
的内在实现这一目标,并且拒绝了简单的演员表。
我正在使用 C++ 代码中的 NEON 内在函数进行 ARM 优化。我理解并掌握了大多数打字问题,但我被困在这个问题上:
该指令vzip_u8
返回一个uint8x8x2_t
值(实际上是一个由两个组成的数组uint8x8_t
)。我想将返回的值分配给一个普通的uint16x8_t
. 我认为没有适当vreinterpretq
的内在实现这一目标,并且拒绝了简单的演员表。
一些定义要清楚地回答......
NEON有 32 个寄存器,64 位宽(双视图为 16 个寄存器,128 位宽)。
NEON 单元可以查看相同的寄存器组:
- 十六个 128 位四字寄存器,Q0-Q15
- 三十二个 64 位双字寄存器,D0-D31。
uint16x8_t
是一种需要 128 位存储的类型,因此它需要在quadword
寄存器中。
ARM NEON Intrinsics 有一个vector array data type
在ARM® C 语言扩展中调用的定义:
...用于加载和存储操作、表查找操作以及作为返回一对向量的操作的结果类型。
vzip指令
... 交错两个向量的元素。
vzip Dd, Dm
并且有一个内在的喜欢
uint8x8x2_t vzip_u8 (uint8x8_t, uint8x8_t)
从这些我们可以得出结论,uint8x8x2_t 实际上是两个随机编号的双字寄存器的列表,因为 vzip 指令对输入寄存器的顺序没有任何要求。
现在答案是……
uint8x8x2_t
可以包含不连续的两个双字寄存器,而uint16x8_t
是由两个连续的双字寄存器组成的数据结构,其中第一个具有偶数索引 (D0-D31 -> Q0-Q15)。
因此,您不能将vector array data type
两个双字寄存器转换为四字寄存器......很容易。
编译器可能足够聪明,可以帮助您,或者您可以强制转换,但是我会检查生成的程序集的正确性和性能。
您可以使用 vcombine_* 内在函数从两个 64 位向量构造一个 128 位向量。因此,您可以像这样实现您想要的。
#include <arm_neon.h>
uint8x16_t f(uint8x8_t a, uint8x8_t b)
{
uint8x8x2_t tmp = vzip_u8(a,b);
uint8x16_t result;
result = vcombine_u8(tmp.val[0], tmp.val[1]);
return result;
}
我找到了一种解决方法:鉴于该类型的val
成员uint8x8x2_t
是一个数组,因此它被视为一个指针。铸造和遵守指针的作品![而获取数据地址会引发“临时地址”警告。]
uint16x8_t Value= *(uint16x8_t*)vzip_u8(arg0, arg1).val;
事实证明,这可以按应有的方式编译和执行(至少在我尝试过的情况下)。我没有看过汇编代码,所以我不能授予它正确实现(我的意思是将值保存在寄存器中而不是写入/读取到/从内存中。)
我遇到了同样的问题,所以我引入了一个灵活的数据类型。
因此,我现在可以定义以下内容:
typedef NeonVectorType<uint8x16_t> uint_128bit_t; //suitable for uint8x16_t, uint8x8x2_t, uint32x4_t, etc.
typedef NeonVectorType<uint8x8_t> uint_64bit_t; //suitable for uint8x8_t, uint32x2_t, etc.
它是 4.5 和 4.6 系列的 GCC(现已修复)中的一个错误。
Bugzilla 链接http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48252
请从此错误中修复并应用到 gcc 源并重建它。