在英特尔架构指令集扩展编程参考的2.5.3节"广播"中我们学习的比AVX512(和骑士角落)有
用于编码一些加载操作指令的数据广播的位字段,即从存储器加载数据并执行一些计算或数据移动操作的指令.
例如,使用英特尔汇编语法,我们可以在存储的地址广播标量,rax
然后乘以16浮点数zmm2
并将结果写入zmm1
这样的
vmulps zmm1, zmm2, [rax] {1to16}
但是,没有内在的东西可以做到这一点.因此,使用内在函数,编译器应该能够折叠
__m512 bb = _mm512_set1_ps(b); __m512 ab = _mm512_mul_ps(a,bb);
一个指令
vmulps zmm1, zmm2, [rax] {1to16}
但我没有观察到GCC这样做.我发现了一个GCC错误报告.
我观察到与GCC类似的FMA.例如,GCC 4.9不会崩溃_mm256_add_ps(_mm256_mul_ps(areg0,breg0)
为单个fma指令-Ofast
.但是,GCC 5.1确实将它崩溃为一个单一的fma.至少有内在函数可以用FMA做到这一点,例如_mm256_fmadd_ps
.但是没有例如_mm512_mulbroad_ps(vector,scalar)
内在的.
海湾合作委员会可能会在某个时候解决这个问题,但在此之前,装配是唯
所以我的问题是如何在GCC中进行内联汇编?
我想我可能已经为上面的例子提出了GCC内联汇编的正确语法(但我不确定).
"vmulps (%%rax)%{1to16}, %%zmm1, %%zmm2\n\t"
我真的在寻找这样的功能
static inline __m512 mul_broad(__m512 a, float b) { return a*b; }
如果b
在内存中指向rax
它产生
vmulps (%rax){1to16}, %zmm0, %zmm0 ret
如果b
它在xmm1
它产生
vbroadcastss %xmm1, %zmm1 vmulps %zmm1, %zmm0, %zmm0 ret
GCC已经vbroadcastss
使用内在函数执行-from-register案例,但如果b
在内存中,则将其编译为vbroadcastss
内存.
__m512 mul_broad(__m512 a, float b) { __m512 bb = _mm512_set1_ps(b); __m512 ab = _mm512_mul_ps(a,bb); return ab; }
如果b
在内存中,clang将使用广播内存操作数.
正如Peter Cordes所说,GCC不允许您为不同的约束选择指定不同的模板.因此,我的解决方案是让汇编程序根据所选的操作数选择正确的指令.
我没有支持ZMM寄存器的GCC版本,因此下面的示例使用XMM寄存器和一些不存在的指令来演示如何实现您正在寻找的内容.
typedef __attribute__((vector_size(16))) float v4sf; v4sf foo(v4sf a, float b) { v4sf ret; asm(".ifndef isxmm\n\t" ".altmacro\n\t" ".macro ifxmm operand, rnum\n\t" ".ifc \"\\operand\",\"%%xmm\\rnum\"\n\t" ".set isxmm, 1\n\t" ".endif\n\t" ".endm\n\t" ".endif\n\t" ".set isxmm, 0\n\t" ".set regnum, 0\n\t" ".rept 8\n\t" "ifxmm <%2>, %%regnum\n\t" ".set regnum, regnum + 1\n\t" ".endr\n\t" ".if isxmm\n\t" "alt-1 %1, %2, %0\n\t" ".else\n\t" "alt-2 %1, %2, %0\n\t" ".endif\n\t" : "=x,x" (ret) : "x,x" (a), "x,m" (b)); return ret; } v4sf bar(v4sf a, v4sf b) { return foo(a, b[0]); }
此示例应编译gcc -m32 -msse -O3
并应生成两个类似于以下内容的汇编程序错误消息:
t103.c: Assembler messages:
t103.c:24: Error: no such instruction: `alt-2 %xmm0,4(%esp),%xmm0'
t103.c:22: Error: no such instruction: `alt-1 %xmm0,%xmm1,%xmm0'
这里的基本思想是汇编程序检查第二个operand(%2
)是否是XMM寄存器或其他东西,可能是一个内存位置.由于GNU汇编程序对字符串的操作方式不太支持,所以第二个操作数在.rept
循环中一次一个地与每个可能的XMM寄存器进行比较.的isxmm
宏用于粘贴%xmm
和寄存器编号一起.
对于您的具体问题,您可能需要重写它,如下所示:
__m512 mul_broad(__m512 a, float b) { __m512 ret; __m512 dummy; asm(".ifndef isxmm\n\t" ".altmacro\n\t" ".macro ifxmm operand, rnum\n\t" ".ifc \"\\operand\",\"%%zmm\\rnum\"\n\t" ".set isxmm, 1\n\t" ".endif\n\t" ".endm\n\t" ".endif\n\t" ".set isxmm, 0\n\t" ".set regnum, 0\n\t" ".rept 32\n\t" "ifxmm <%[b]>, %%regnum\n\t" ".set regnum, regnum + 1\n\t" ".endr\n\t" ".if isxmm\n\t" "vbroadcastss %x[b], %[b]\n\t" "vmulps %[a], %[b], %[ret]\n\t" ".else\n\t" "vmulps %[b] %{1to16%}, %[a], %[ret]\n\t" "# dummy = %[dummy]\n\t" ".endif\n\t" : [ret] "=x,x" (ret), [dummy] "=xm,x" (dummy) : [a] "x,xm" (a), [b] "m,[dummy]" (b)); return ret; }