当您不关心新车道时,如何使用 clang/AArch64 上的 NEON 内在函数将 int32x2_t 扩展到 int32x4_t?

How to extend a int32x2_t to a int32x4_t with NEON intrinsics on clang/AArch64 when you don't care about the new lanes?

各位 ARMists,
我想用 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 寄存器的后半部分未初始化的方法吗?我想避免内联汇编。
谢谢!

$ 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

https://godbolt.org/z/ATr4D7

我不知道使用一般 arm_neon.h 内在函数有什么好的解决方案,但至少对于 Clang,有可能使用 Clang 特定的内置函数来生成一个向量,其中某些元素被设置为未定义,因此代码生成器不需要特别为它们填充任何值。

使用它的设置如下所示:

$ 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://godbolt.org/z/N_NsSE

有关 __builtin_shufflevector 的文档,请参阅 https://clang.llvm.org/docs/LanguageExtensions.html#builtin-shufflevector

编辑:似乎也可以通过使用未初始化的变量在 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.org/z/TzHuon), while GCC still includes a mov v0.8b, v0.8b (https://godbolt.org/z/wZTAU9)。