使用向量扩展时让GCC生成PTEST指令

当使用C的GCC向量扩展时,如何检查向量上的所有值是否为零?

例如:

#include  typedef uint32_t v8ui __attribute__ ((vector_size (32))); v8ui* foo(v8ui *mem) { v8ui v; for ( v = (v8ui){ 1, 1, 1, 1, 1, 1, 1, 1 }; v[0] || v[1] || v[2] || v[3] || v[4] || v[5] || v[6] || v[7]; mem++) v &= *(mem); return mem; } 

SSE4.2具有PTEST指令,允许运行类似于for条件的测试for但GCC生成的代码只是解包向量并逐个检查单个元素:

 .L2: vandps (%rax), %ymm1, %ymm1 vmovdqa %xmm1, %xmm0 addq $32, %rax vmovd %xmm0, %edx testl %edx, %edx jne .L2 vpextrd $1, %xmm0, %edx testl %edx, %edx jne .L2 vpextrd $2, %xmm0, %edx testl %edx, %edx jne .L2 vpextrd $3, %xmm0, %edx testl %edx, %edx jne .L2 vextractf128 $0x1, %ymm1, %xmm0 vmovd %xmm0, %edx testl %edx, %edx jne .L2 vpextrd $1, %xmm0, %edx testl %edx, %edx jne .L2 vpextrd $2, %xmm0, %edx testl %edx, %edx jne .L2 vpextrd $3, %xmm0, %edx testl %edx, %edx jne .L2 vzeroupper ret 

有没有办法让GCC在不恢复使用内在函数的情况下为其生成有效的测试?

更新 :作为参考,使用内置于(V)PTEST不可移植GCC的代码:

 typedef uint32_t v8ui __attribute__ ((vector_size (32))); typedef long long int v4si __attribute__ ((vector_size (32))); const v8ui ones = { 1, 1, 1, 1, 1, 1, 1, 1 }; v8ui* foo(v8ui *mem) { v8ui v; for ( v = ones; !__builtin_ia32_ptestz256((v4si)v, (v4si)ones); mem++) v &= *(mem); return mem; } 

gcc 4.9.2 -O3 -mavx2 (在64位模式下)没有意识到它可以使用ptest ,使用|| 或者 。

| version使用vmovdvpextrd提取向量元素,并将内容与7位or 32位寄存器之间的insn组合在一起。 所以它非常糟糕,并没有利用任何仍然会产生相同逻辑真值的简化。

|| 版本同样糟糕,并且每次都提取相同的元素,但每个元素都会test / jne

所以在这一点上,你不能指望GCC识别这样的测试并做任何远程高效的事情。 ( pcmpeq / movmsk / test是另一个不错的序列,但gcc也不生成它。)

vptest不会有帮助吗? 如果您正在考虑性能,有时您会对本机类型提供的内容感到惊讶。 下面是一些使用vanilla memcmp()以及vptest指令的代码(通过相应的内部函数使用)。 我没有计时function。

 #include  #include  #include  #include  typedef uint32_t v8ui __attribute__ ((vector_size (32))); v8ui* foo1(v8ui *mem) { v8ui v = (v8ui){ 1, 1, 1, 1, 1, 1, 1, 1 }; if (memcmp(mem, &v, sizeof (v8ui)) == 0) { printf("Ones\n"); } else { printf("NOT Ones\n"); } return mem; } v8ui* foo2(v8ui *mem) { v8ui v = (v8ui){ 1, 1, 1, 1, 1, 1, 1, 1 }; __m256i a, b; a = _mm256_loadu_si256((__m256i *)(&v)); b = _mm256_loadu_si256((__m256i *)(&mem)); if (!_mm256_testz_si256(a, b)) { printf("NOT Ones\n"); } else { printf("Ones\n"); } return mem; } int main() { v8ui v = (v8ui){ 1, 1, 1, 1, 1, 1, 1, 1 }; foo1(&v); foo2(&v); } 

编译标志:

gcc -mavx2 foo.c

卫生署! 直到现在我才发现你想让GCC在不使用内在函数的情况下生成vptest指令。 无论如何我都会留下代码。

如果编译器不够自动以自动生成优化,则有三个选项:

  • 获取新的编译器。
  • 手动生成优化(例如,使用诸如测试和其他答案中的内在函数)。
  • 修改编译器以自动生成优化。

您已经通过使用gcc扩展自动排除了第一个选项,尽管llvm / clang可能会为您扩展这些扩展。

你已经公然排除了第二种选择。

第三种选择似乎是我最好的选择。 gcc是开源的,因此您可以对其进行(并提交)自己的更改。 如果你可以修改gcc来自动生成这个优化(理想情况下来自100%标准C),那么你不仅可以实现产生这种优化的目标,而不会将crud引入你的程序,但你也可以节省无数的手动优化(特别是将来锁定您使用特定编译器的非标准版本。