GNU C本机向量:如何广播标量,如x86的_mm_set1_epi16

我如何编写一个可移植的GNU C内置向量版本,它不依赖于x86 set1内在函数?

typedef uint16_t v8su __attribute__((vector_size(16))); v8su set1_u16_x86(uint16_t scalar) { return (v8su)_mm_set1_epi16(scalar); // cast needed for gcc } 

当然必须有一个更好的方式

 v8su set1_u16(uint16_t s) { return (v8su){s,s,s,s, s,s,s,s}; } 

我不想写一个用于广播单个字节的AVX2版本!

对于你想要分配给一个变量而不是仅仅用作二元运算符的操作数(这与gcc一起使用,见下文)的情况, 即使只是gcc-only或clang-only这个部分的答案也会很有趣


如果我想使用广播标量作为二元运算符的一个操作数,则可以使用gcc( 如手册中所述 ),但不能使用clang:

 v8su vecdiv10(v8su v) { return v / 10; } // doesn't compile with clang 

有了clang,如果我只针对x86并且只使用本机向量语法来让编译器为我生成模数乘法逆常数和指令 ,我可以写:

 v8su vecdiv_set1(v8su v) { return v / (v8su)_mm_set1_epi16(10); // gcc needs the cast } 

但是如果我加宽向量(到_mm256_set1_epi16 ),我必须更改内在函数,而不是通过在一个地方更改为vector_size(32)将整个代码转换为AVX2(对于不需要改组的纯垂直SIMD)。 它也会破坏本机向量的部分目的,因为它不会为ARM或任何非x86目标编译。

丑陋的演员v8us {aka __vector(8) short unsigned int}是必需的,因为与clang不同,gcc不会考虑与__m128i {aka __vector(2) long long int}兼容的v8us {aka __vector(8) short unsigned int} __m128i {aka __vector(2) long long int}

顺便说一句,所有这些都是用gcc和clang编译成好的asm( 在Godbolt上看到它 )。 这只是一个如何优雅地编写的问题,可读的语法不会重复标量N次 。 例如, v / 10足够紧凑,甚至不需要将其置于自己的function中。

与ICC有效编译是一个奖励,但不是必需的。 GNU C本机向量显然是ICC的事后想法,甚至像这样的简单东西也无法有效编译 。 set1_u16编译为8个标量存储和向量加载,而不是MOVD / VPBROADCASTW(启用-xHOST ,因为它不识别-march=haswell ,但Godbolt在支持AVX2的服务器上运行)。 纯粹投射_mm_内在函数的结果是可以的,但是除法调用SVML函数!

可以使用两个观察结果为GCC和Clang找到通用广播解决方案

  1. Clang的OpenCL向量扩展和GCC的向量扩展支持scalar - vector运算。
  2. x - 0 = x ( 但由于有符号零, x + 0不起作用 )。

这是四个浮点数向量的解决方案。

 #if defined (__clang__) typedef float v4sf __attribute__((ext_vector_type(4))); #else typedef float v4sf __attribute__ ((vector_size (16))); #endif v4sf broadcast4f(float x) { return x - (v4sf){}; } 

https://godbolt.org/g/PXr3Xb

相同的通用解决方案可用于不同的向量。 这是一个八个无符号短路向量的例子。

 #if defined (__clang__) typedef unsigned short v8su __attribute__((ext_vector_type(8))); #else typedef unsigned short v8su __attribute__((vector_size(16))); #endif v8su broadcast8us(short x) { return x - (v8su){}; } 

ICC(17)支持GCC向量扩展的子集,但不支持vector + scalarvector*scalar ,因此广播仍然需要内在函数。 MSVC不支持任何矢量扩展。