ARM的朋友们,我想用NEON代码把2个s32缩小并饱和到2个s16,然后把它们打包在一个GPR中.我需要符合一定的API,所以请不要在这里讨论效率或设计:) 这里是片段。
int32x2_t stuff32 = ...;
int16x4_t stuff16 = vqmovn_s32(vcombine_s32(stuff32, stuff32));
return vget_lane_u32(stuff16, 0)
这段代码产生了
mov v0.d[1], v0.d[0]
sqxtn v0.4h, v0.4s
fmov w0, s0
ret
有谁知道有什么方法可以让类型系统满意,并且让d寄存器的后半部分不被初始化?我想避免使用内联汇编。谢谢你!
我不知道有什么好的解决方案,使用通用的 arm_neon.h
但至少在Clang中,可以使用Clang特有的buildins来生成一个矢量,其中一些元素被设置为未定义,这样代码生成就不需要用任何特定的值来填充它们。
一个使用这种方法的设置是这样的。
$ cat test.c
#include <arm_neon.h>
int32_t narrow_saturate(int32x2_t stuff32) {
int16x4_t stuff16 = vqmovn_s32(__builtin_shufflevector(stuff32, stuff32, 0, 1, -1, -1));
return vget_lane_s32(vreinterpret_s32_s16(stuff16), 0);
}
$ clang -target aarch64-linux-gnu test.c -S -o - -O2
[...]
narrow_saturate:
sqxtn v0.4h, v0.4s
fmov w0, s0
ret
请看 https:/clang.llvm.orgdocsLanguageExtensions.html#builtin-shufflevector。 的文件 __builtin_shufflevector
.
EDIT: 在Clang中,似乎也可以通过使用一个未初始化的变量来实现同样的目的(尽管这会产生`-Wuninitialized的警告)。
$ cat test.c
#include <arm_neon.h>
int32_t narrow_saturate(int32x2_t stuff32) {
int32x2_t uninitialized;
int16x4_t stuff16 = vqmovn_s32(vcombine_s32(stuff32, uninitialized));
return vget_lane_s32(vreinterpret_s32_s16(stuff16), 0);
}
Clang产生的结果和上面一样 (https:/godbolt.orgzTzHuon),而GCC仍然包括一个 mov v0.8b, v0.8b
(https:/godbolt.orgzwZTAU9).
$ cat a.c
#include <arm_neon.h>
int32_t narrow_saturate(int32x2_t stuff32) {
int32x2_t zero = {0, 0};
int16x4_t stuff16 = vqmovn_s32(vcombine_s32(stuff32, zero));
return vget_lane_s32(vreinterpret_s32_s16(stuff16), 0);
}
$ gcc -O2 a.c -S -o-
[...]
narrow_saturate:
mov v0.8b, v0.8b
sqxtn v0.4h, v0.4s
umov w0, v0.s[0]
ret