当前位置:  开发笔记 > 编程语言 > 正文

具有内在和汇编的嵌入式广播

如何解决《具有内在和汇编的嵌入式广播》经验,为你挑选了1个好方法。

在英特尔架构指令集扩展编程参考的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将使用广播内存操作数.



1> Ross Ridge..:

正如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;
}

推荐阅读
家具销售_903
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有